174 lines
5.9 KiB
Python
174 lines
5.9 KiB
Python
# authentic2 - versatile identity manager
|
|
# Copyright (C) 2010-2019 Entr'ouvert
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify it
|
|
# under the terms of the GNU Affero General Public License as published
|
|
# by the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program 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 Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
from django.contrib.contenttypes.models import ContentType
|
|
|
|
from django_rbac import utils as rbac_utils
|
|
from django_rbac.managers import AbstractBaseManager
|
|
from django_rbac.managers import RoleManager as BaseRoleManager
|
|
from django_rbac.models import ADMIN_OP
|
|
from django_rbac.utils import get_operation
|
|
|
|
|
|
class OrganizationalUnitManager(AbstractBaseManager):
|
|
def get_by_natural_key(self, slug):
|
|
return self.get(slug=slug)
|
|
|
|
|
|
class RoleManager(BaseRoleManager):
|
|
def get_admin_role(
|
|
self,
|
|
instance,
|
|
name,
|
|
slug,
|
|
ou=None,
|
|
operation=ADMIN_OP,
|
|
update_name=False,
|
|
update_slug=False,
|
|
permissions=(),
|
|
self_administered=False,
|
|
create=True,
|
|
):
|
|
'''Get or create the role of manager's of this object instance'''
|
|
kwargs = {}
|
|
assert not ou or isinstance(
|
|
instance, ContentType
|
|
), 'get_admin_role(ou=...) can only be used with ContentType instances: %s %s %s' % (
|
|
name,
|
|
ou,
|
|
instance,
|
|
)
|
|
|
|
# Does the permission need to be scoped by ou ? Yes if the target is a
|
|
# ContentType and ou is given. It's a general administration right upon
|
|
# all instance of a ContentType, eventually scoped to the given ou.
|
|
defaults = {}
|
|
if isinstance(instance, ContentType):
|
|
if ou:
|
|
kwargs['ou'] = ou
|
|
else:
|
|
kwargs['ou__isnull'] = True
|
|
else: # for non ContentType instances, OU must be set to NULL, always.
|
|
defaults['ou'] = None
|
|
# find an operation matching the template
|
|
op = get_operation(operation)
|
|
Permission = rbac_utils.get_permission_model()
|
|
if create:
|
|
perm, _ = Permission.objects.update_or_create(
|
|
operation=op,
|
|
target_ct=ContentType.objects.get_for_model(instance),
|
|
target_id=instance.pk,
|
|
defaults=defaults,
|
|
**kwargs,
|
|
)
|
|
else:
|
|
try:
|
|
perm = Permission.objects.get(
|
|
operation=op,
|
|
target_ct=ContentType.objects.get_for_model(instance),
|
|
target_id=instance.pk,
|
|
**kwargs,
|
|
)
|
|
except Permission.DoesNotExist:
|
|
return None
|
|
|
|
# in which ou do we put the role ?
|
|
if ou:
|
|
mirror_role_ou = ou
|
|
elif getattr(instance, 'ou', None):
|
|
mirror_role_ou = instance.ou
|
|
else:
|
|
mirror_role_ou = None
|
|
admin_role = self.get_mirror_role(
|
|
perm,
|
|
name,
|
|
slug,
|
|
ou=mirror_role_ou,
|
|
update_name=update_name,
|
|
update_slug=update_slug,
|
|
create=create,
|
|
)
|
|
|
|
if not admin_role:
|
|
return None
|
|
|
|
permissions = set(permissions)
|
|
permissions.add(perm)
|
|
if self_administered:
|
|
self_perm = admin_role.add_self_administration()
|
|
permissions.add(self_perm)
|
|
if set(admin_role.permissions.all()) != permissions:
|
|
admin_role.permissions.set(permissions)
|
|
return admin_role
|
|
|
|
def get_mirror_role(
|
|
self, instance, name, slug, ou=None, update_name=False, update_slug=False, create=True
|
|
):
|
|
"""Get or create a role which mirrors another model, for example a
|
|
permission.
|
|
"""
|
|
ct = ContentType.objects.get_for_model(instance)
|
|
update_fields = {}
|
|
kwargs = {}
|
|
if ou:
|
|
update_fields['ou'] = ou
|
|
else:
|
|
update_fields['ou'] = None
|
|
if update_name:
|
|
update_fields['name'] = name
|
|
if update_slug:
|
|
update_fields['slug'] = slug
|
|
|
|
if create:
|
|
role, _ = self.prefetch_related('permissions').update_or_create(
|
|
admin_scope_ct=ct, admin_scope_id=instance.pk, defaults=update_fields, **kwargs
|
|
)
|
|
else:
|
|
try:
|
|
role = self.prefetch_related('permissions').get(
|
|
admin_scope_ct=ct, admin_scope_id=instance.pk, **kwargs
|
|
)
|
|
except self.model.DoesNotExist:
|
|
return None
|
|
for field, value in update_fields.items():
|
|
setattr(role, field, value)
|
|
role.save(update_fields=update_fields)
|
|
return role
|
|
|
|
def get_by_natural_key(self, slug, ou_natural_key, service_natural_key):
|
|
kwargs = {'slug': slug}
|
|
if ou_natural_key is None:
|
|
kwargs['ou__isnull'] = True
|
|
else:
|
|
OU = rbac_utils.get_ou_model()
|
|
try:
|
|
ou = OU.objects.get_by_natural_key(*ou_natural_key)
|
|
except OU.DoesNotExist:
|
|
raise self.model.DoesNotExist
|
|
kwargs['ou'] = ou
|
|
if service_natural_key is None:
|
|
kwargs['service__isnull'] = True
|
|
else:
|
|
# XXX: prevent an import loop
|
|
from authentic2.models import Service
|
|
|
|
try:
|
|
service = Service.objects.get_by_natural_key(*service_natural_key)
|
|
except Service.DoesNotExist:
|
|
raise self.model.DoesNotExist
|
|
kwargs['service'] = service
|
|
return self.get(**kwargs)
|