# -*- coding: utf-8 -*-
#
# This file is part of RestAuth (https://restauth.net).
#
# RestAuth is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# RestAuth is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with RestAuth. If not, see <http://www.gnu.org/licenses/>.
from __future__ import unicode_literals
from datetime import datetime
from django.db import transaction as dj_transaction
from django.db.utils import IntegrityError
from django.utils import six
from RestAuth.Groups.models import Group
from RestAuth.Users.models import Property
from RestAuth.Users.models import ServiceUser as User
from RestAuth.backends.base import GroupBackend
from RestAuth.backends.base import PropertyBackend
from RestAuth.backends.base import UserBackend
from RestAuth.common.errors import GroupExists
from RestAuth.common.errors import GroupNotFound
from RestAuth.common.errors import PropertyExists
from RestAuth.common.errors import PropertyNotFound
from RestAuth.common.errors import UserExists
from RestAuth.common.errors import UserNotFound
[docs]class DjangoUserBackend(UserBackend):
"""Use the standard Django ORM to store user data.
This backend should be ready-to use as soon as you have :doc:`configured
your database </config/database>`.
All settings used by this backend are documented in the :doc:`settings
reference </config/all-config-values>`.
"""
def _get_user(self, username, *fields):
try:
return User.objects.only(*fields).get(username=username)
except User.DoesNotExist:
raise UserNotFound(username)
def get(self, username):
return self._get_user(username, 'id', 'username')
def list(self):
return list(User.objects.values_list('username', flat=True))
def _create(self, username, password=None, properties=None,
property_backend=None, dry=False, transaction=True):
try:
user = User(username=username)
if password is not None and password != '':
user.set_password(password)
user.save()
except IntegrityError:
raise UserExists("A user with the given name already exists.")
if properties is None:
stamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
properties = {'date joined': stamp}
elif 'date joined' not in properties:
stamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
properties['date joined'] = stamp
property_backend.set_multiple(user, properties, dry=dry,
transaction=transaction)
return user
def create(self, username, password=None, properties=None,
property_backend=None, dry=False, transaction=True):
if dry:
with dj_transaction.commit_manually():
try:
return self._create(username, password, properties,
property_backend, dry=dry,
transaction=transaction)
finally:
dj_transaction.rollback()
elif transaction:
with dj_transaction.commit_on_success():
return self._create(username, password, properties,
property_backend, dry=dry,
transaction=transaction)
else: # pragma: no cover
return self._create(username, password, properties,
property_backend, dry=dry,
transaction=transaction)
def rename(self, username, name):
user = self._get_user(username)
user.username = name
try:
user.save()
except IntegrityError:
raise UserExists("User already exists.")
def exists(self, username):
return User.objects.filter(username=username).exists()
def check_password(self, username, password):
user = self._get_user(username, 'password')
return user.check_password(password)
def set_password(self, username, password=None):
user = self._get_user(username, 'id')
if password is not None and password != '':
user.set_password(password)
else:
user.set_unusable_password()
user.save()
def set_password_hash(self, username, algorithm, hash, salt=None,
**kwargs): # pragma: no cover
user = self._get_user(username, 'password')
parameters = [algorithm]
if kwargs:
parameters += six.itervalues(kwargs)
if salt is not None:
parameters.append(salt)
parameters.append(hash)
user.password = '$'.join(parameters)
user.save()
def remove(self, username):
qs = User.objects.filter(username=username)
if qs.exists():
qs.delete()
else:
raise UserNotFound(username)
def init_transaction(self): # pragma: no cover
dj_transaction.enter_transaction_management()
dj_transaction.managed(True)
def commit_transaction(self): # pragma: no cover
dj_transaction.commit()
def rollback_transaction(self): # pragma: no cover
dj_transaction.rollback()
[docs]class DjangoPropertyBackend(PropertyBackend):
"""Use the standard Django ORM to store user properties.
This backend should be ready-to use as soon as you have :doc:`configured
your database </config/database>`. This backend requires that you also use
the :py:class:`~.DjangoUserBackend`.
All settings used by this backend are documented in the :doc:`settings
reference </config/all-config-values>`.
"""
def list(self, user):
qs = Property.objects.filter(user_id=user.id)
return dict(qs.values_list('key', 'value'))
def create(self, user, key, value, dry=False, transaction=True):
if dry:
with dj_transaction.commit_manually():
try:
prop = user.property_set.create(key=key, value=value)
return prop.key, prop.value
except IntegrityError:
raise PropertyExists()
finally:
dj_transaction.rollback()
elif transaction:
with dj_transaction.commit_on_success():
try:
prop = user.property_set.create(key=key, value=value)
return prop.key, prop.value
except IntegrityError:
raise PropertyExists()
else: # pragma: no cover
try:
prop = user.property_set.create(key=key, value=value)
return prop.key, prop.value
except IntegrityError:
raise PropertyExists()
def get(self, user, key):
try:
qs = Property.objects.filter(user_id=user.id).only('value')
return qs.get(key=key).value
except Property.DoesNotExist:
raise PropertyNotFound(key)
def set(self, user, key, value, dry=False, transaction=True):
if dry:
with dj_transaction.commit_manually():
try:
prop, old_value = user.set_property(key, value)
return prop.key, old_value
finally:
dj_transaction.rollback()
elif transaction:
with dj_transaction.commit_on_success():
prop, old_value = user.set_property(key, value)
return prop.key, old_value
else: # pragma: no cover
prop, old_value = user.set_property(key, value)
return prop.key, old_value
def set_multiple(self, user, props, dry=False, transaction=True):
if dry:
with dj_transaction.commit_manually():
try:
for key, value in six.iteritems(props):
user.set_property(key, value)
finally:
dj_transaction.rollback()
elif transaction:
with dj_transaction.commit_on_success():
for key, value in six.iteritems(props):
user.set_property(key, value)
else: # pragma: no cover
for key, value in six.iteritems(props):
user.set_property(key, value)
def remove(self, user, key):
try:
user.del_property(key)
except Property.DoesNotExist:
raise PropertyNotFound(key)
def init_transaction(self): # pragma: no cover
dj_transaction.enter_transaction_management()
dj_transaction.managed(True)
def commit_transaction(self): # pragma: no cover
dj_transaction.commit()
def rollback_transaction(self): # pragma: no cover
dj_transaction.rollback()
[docs]class DjangoGroupBackend(GroupBackend):
"""Use the standard Django ORM to store groups.
This backend should be ready-to use as soon as you have :doc:`configured
your database </config/database>`. This backend requires that you also use
the :py:class:`~.DjangoUserBackend`.
All settings used by this backend are documented in the :doc:`settings
reference </config/all-config-values>`.
"""
def get(self, name, service=None):
try:
return Group.objects.get(service=service, name=name)
except Group.DoesNotExist:
raise GroupNotFound(name)
def list(self, service, user=None):
if user is None:
groups = Group.objects.filter(service=service)
else:
groups = Group.objects.member(user=user, service=service)
return list(groups.only('id').values_list('name', flat=True))
def create(self, name, service=None, dry=False, transaction=True):
if dry:
with dj_transaction.commit_manually():
try:
return Group.objects.create(
name=name, service=service)
except IntegrityError:
raise GroupExists('Group "%s" already exists' % name)
finally:
dj_transaction.rollback()
elif transaction:
with dj_transaction.commit_on_success():
try:
return Group.objects.create(
name=name, service=service)
except IntegrityError:
raise GroupExists('Group "%s" already exists' % name)
else: # pragma: no cover
try:
return Group.objects.create(name=name, service=service)
except IntegrityError:
raise GroupExists('Group "%s" already exists' % name)
def rename(self, group, name):
try:
group.name = name
group.save()
except IntegrityError:
raise GroupExists("Group already exists.")
def set_service(self, group, service=None):
group.service = service
group.save()
def exists(self, name, service=None):
return Group.objects.filter(name=name, service=service).exists()
def add_user(self, group, user):
group.users.add(user)
def members(self, group, depth=None):
qs = group.get_members(depth=depth)
return list(qs.values_list('username', flat=True))
def is_member(self, group, user):
if group.is_member(user):
return True
return False
def rm_user(self, group, user):
if group.is_member(user):
group.users.remove(user)
else:
raise UserNotFound(user) # 404 Not Found
def add_subgroup(self, group, subgroup):
group.groups.add(subgroup)
def subgroups(self, group, filter=True):
if filter:
qs = group.groups.filter(service=group.service)
return list(qs.values_list('name', flat=True))
else:
return group.groups.all()
def rm_subgroup(self, group, subgroup):
qs = group.groups.filter(name=subgroup.name, service=subgroup.service)
if not qs.exists():
raise GroupNotFound(subgroup.name)
group.groups.remove(subgroup)
def remove(self, group):
group.delete()
def parents(self, group):
return group.parent_groups.all()
def init_transaction(self): # pragma: no cover
dj_transaction.enter_transaction_management()
dj_transaction.managed(True)
def commit_transaction(self): # pragma: no cover
dj_transaction.commit()
def rollback_transaction(self): # pragma: no cover
dj_transaction.rollback()