taiga-back/taiga/base/api/permissions.py

210 lines
6.1 KiB
Python

# Copyright (C) 2014-2016 Andrey Antukh <niwi@niwi.nz>
# Copyright (C) 2014-2016 Jesús Espino <jespinog@gmail.com>
# Copyright (C) 2014-2016 David Barragán <bameda@dbarragan.com>
# Copyright (C) 2014-2016 Alejandro Alonso <alejandro.alonso@kaleidos.net>
# 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/>.
import abc
from functools import reduce
from taiga.base.utils import sequence as sq
from taiga.permissions.services import user_has_perm, is_project_admin
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 IsProjectAdmin(PermissionComponent):
def check_permissions(self, request, view, obj=None):
return is_project_admin(request.user, obj)
class IsObjectOwner(PermissionComponent):
def check_permissions(self, request, view, obj=None):
if obj.owner is None:
return False
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()