# Copyright (C) 2014 Andrey Antukh # Copyright (C) 2014 Jesús Espino # Copyright (C) 2014 David Barragán # 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 . import abc from taiga.base.utils import sequence as sq from taiga.permissions.service import user_has_perm, is_project_owner from django.apps import apps from django.utils.translation import ugettext as _ ###################################################################### # Base permissiones definition ###################################################################### class ResourcePermission(object): """ Base class for define resource permissions. """ enought_perms = None global_perms = None retrieve_perms = None create_perms = None update_perms = None destroy_perms = None list_perms = None def __init__(self, request, view): self.request = request self.view = view def check_permissions(self, action:str, obj:object=None): permset = getattr(self, "{}_perms".format(action)) if isinstance(permset, (list, tuple)): permset = reduce(lambda acc, v: acc & v, permset) elif permset is None: # Use empty operator that always return true with # empty components. permset = And() elif isinstance(permset, PermissionComponent): # Do nothing pass elif inspect.isclass(permset) and issubclass(permset, PermissionComponent): permset = permset() else: raise RuntimeError(_("Invalid permission definition.")) if self.global_perms: permset = (self.global_perms & permset) if self.enought_perms: permset = (self.enought_perms | permset) return permset.check_permissions(request=self.request, view=self.view, obj=obj) class PermissionComponent(object, metaclass=abc.ABCMeta): @abc.abstractmethod def check_permissions(self, request, view, obj=None): pass def __invert__(self): return Not(self) def __and__(self, component): return And(self, component) def __or__(self, component): return Or(self, component) class PermissionOperator(PermissionComponent): """ Base class for all logical operators for compose components. """ def __init__(self, *components): self.components = tuple(components) class Not(PermissionOperator): """ Negation operator as permission composable component. """ # Overwrites the default constructor for fix # to one parameter instead of variable list of them. def __init__(self, component): super().__init__(component) def check_permissions(self, *args, **kwargs): component = sq.first(self.components) return (not component.check_permissions(*args, **kwargs)) class Or(PermissionOperator): """ Or logical operator as permission component. """ def check_permissions(self, *args, **kwargs): valid = False for component in self.components: if component.check_permissions(*args, **kwargs): valid = True break return valid class And(PermissionOperator): """ And logical operator as permission component. """ def check_permissions(self, *args, **kwargs): valid = True for component in self.components: if not component.check_permissions(*args, **kwargs): valid = False break return valid ###################################################################### # Generic components. ###################################################################### class AllowAny(PermissionComponent): def check_permissions(self, request, view, obj=None): return True class DenyAll(PermissionComponent): def check_permissions(self, request, view, obj=None): return False class IsAuthenticated(PermissionComponent): def check_permissions(self, request, view, obj=None): return request.user and request.user.is_authenticated() class IsSuperUser(PermissionComponent): def check_permissions(self, request, view, obj=None): return request.user and request.user.is_authenticated() and request.user.is_superuser class HasProjectPerm(PermissionComponent): def __init__(self, perm, *components): self.project_perm = perm super().__init__(*components) def check_permissions(self, request, view, obj=None): return user_has_perm(request.user, self.project_perm, obj) class HasProjectParamAndPerm(PermissionComponent): def __init__(self, perm, *components): self.project_perm = perm super().__init__(*components) def check_permissions(self, request, view, obj=None): Project = apps.get_model('projects', 'Project') project_id = request.QUERY_PARAMS.get("project", None) try: project = Project.objects.get(pk=project_id) except Project.DoesNotExist: return False return user_has_perm(request.user, self.project_perm, project) class HasMandatoryParam(PermissionComponent): def __init__(self, param, *components): self.mandatory_param = param super().__init__(*components) def check_permissions(self, request, view, obj=None): param = request.GET.get(self.mandatory_param, None) if param: return True return False class IsProjectOwner(PermissionComponent): def check_permissions(self, request, view, obj=None): return is_project_owner(request.user, obj) class IsObjectOwner(PermissionComponent): def check_permissions(self, request, view, obj=None): return obj.owner == request.user ###################################################################### # Generic permissions. ###################################################################### class AllowAnyPermission(ResourcePermission): enought_perms = AllowAny() class IsAuthenticatedPermission(ResourcePermission): enought_perms = IsAuthenticated() class TaigaResourcePermission(ResourcePermission): enought_perms = IsSuperUser()