authentik.rbac.permissions
RBAC Permissions
1"""RBAC Permissions""" 2 3from django.contrib.contenttypes.models import ContentType 4from django.db.models import Model 5from guardian.shortcuts import assign_perm 6from rest_framework.permissions import BasePermission, DjangoObjectPermissions 7from rest_framework.request import Request 8from structlog.stdlib import get_logger 9 10from authentik.rbac.models import InitialPermissions 11 12LOGGER = get_logger() 13 14 15class ObjectPermissions(DjangoObjectPermissions): 16 """RBAC Permissions""" 17 18 def has_permission(self, request: Request, view) -> bool: 19 """Always grant permission for object-specific requests 20 as view permission checking is done by `ObjectFilter`, 21 and write permission checking is done by `has_object_permission`""" 22 lookup = getattr(view, "lookup_url_kwarg", None) or getattr(view, "lookup_field", None) 23 if lookup and lookup in view.kwargs: 24 return True 25 # Legacy behaviour: 26 # Allow creation of objects even without explicit permission 27 queryset = self._queryset(view) 28 required_perms = self.get_required_permissions(request.method, queryset.model) 29 if ( 30 len(required_perms) == 1 31 and f"{queryset.model._meta.app_label}.add_{queryset.model._meta.model_name}" 32 in required_perms 33 and getattr(view, "rbac_allow_create_without_perm", False) 34 ): 35 return True 36 return super().has_permission(request, view) 37 38 def has_object_permission(self, request: Request, view, obj: Model) -> bool: 39 queryset = self._queryset(view) 40 model_cls = queryset.model 41 perms = self.get_required_object_permissions(request.method, model_cls) 42 # Rank global permissions higher than per-object permissions 43 if request.user.has_perms(perms): 44 return True 45 # Allow access for owners if configured 46 if owner_field := getattr(view, "owner_field", None): 47 if getattr(obj, owner_field) == request.user: 48 return True 49 return super().has_object_permission(request, view, obj) 50 51 52def HasPermission(*perm: str) -> type[BasePermission]: 53 """Permission checker for any non-object permissions, returns 54 a BasePermission class that can be used with rest_framework""" 55 56 class checker(BasePermission): 57 def has_permission(self, request: Request, view): 58 return bool(request.user and request.user.has_perms(perm)) 59 60 return checker 61 62 63# TODO: add `user: User` type annotation without circular dependencies. 64# The author of this function isn't proficient/patient enough to do it. 65def assign_initial_permissions(user, instance: Model): 66 # Performance here should not be an issue, but if needed, there are many optimization routes 67 initial_permissions_list = InitialPermissions.objects.filter(role__in=user.all_roles()) 68 for initial_permissions in initial_permissions_list: 69 for permission in initial_permissions.permissions.all(): 70 if permission.content_type != ContentType.objects.get_for_model(instance): 71 continue 72 LOGGER.debug( 73 "Adding initial permission", 74 initial_permission=permission, 75 subject=initial_permissions.role, 76 object=instance, 77 ) 78 assign_perm(permission, initial_permissions.role, instance)
LOGGER =
<BoundLoggerLazyProxy(logger=None, wrapper_class=None, processors=None, context_class=None, initial_values={}, logger_factory_args=())>
class
ObjectPermissions(rest_framework.permissions.DjangoObjectPermissions):
16class ObjectPermissions(DjangoObjectPermissions): 17 """RBAC Permissions""" 18 19 def has_permission(self, request: Request, view) -> bool: 20 """Always grant permission for object-specific requests 21 as view permission checking is done by `ObjectFilter`, 22 and write permission checking is done by `has_object_permission`""" 23 lookup = getattr(view, "lookup_url_kwarg", None) or getattr(view, "lookup_field", None) 24 if lookup and lookup in view.kwargs: 25 return True 26 # Legacy behaviour: 27 # Allow creation of objects even without explicit permission 28 queryset = self._queryset(view) 29 required_perms = self.get_required_permissions(request.method, queryset.model) 30 if ( 31 len(required_perms) == 1 32 and f"{queryset.model._meta.app_label}.add_{queryset.model._meta.model_name}" 33 in required_perms 34 and getattr(view, "rbac_allow_create_without_perm", False) 35 ): 36 return True 37 return super().has_permission(request, view) 38 39 def has_object_permission(self, request: Request, view, obj: Model) -> bool: 40 queryset = self._queryset(view) 41 model_cls = queryset.model 42 perms = self.get_required_object_permissions(request.method, model_cls) 43 # Rank global permissions higher than per-object permissions 44 if request.user.has_perms(perms): 45 return True 46 # Allow access for owners if configured 47 if owner_field := getattr(view, "owner_field", None): 48 if getattr(obj, owner_field) == request.user: 49 return True 50 return super().has_object_permission(request, view, obj)
RBAC Permissions
def
has_permission(self, request: rest_framework.request.Request, view) -> bool:
19 def has_permission(self, request: Request, view) -> bool: 20 """Always grant permission for object-specific requests 21 as view permission checking is done by `ObjectFilter`, 22 and write permission checking is done by `has_object_permission`""" 23 lookup = getattr(view, "lookup_url_kwarg", None) or getattr(view, "lookup_field", None) 24 if lookup and lookup in view.kwargs: 25 return True 26 # Legacy behaviour: 27 # Allow creation of objects even without explicit permission 28 queryset = self._queryset(view) 29 required_perms = self.get_required_permissions(request.method, queryset.model) 30 if ( 31 len(required_perms) == 1 32 and f"{queryset.model._meta.app_label}.add_{queryset.model._meta.model_name}" 33 in required_perms 34 and getattr(view, "rbac_allow_create_without_perm", False) 35 ): 36 return True 37 return super().has_permission(request, view)
Always grant permission for object-specific requests
as view permission checking is done by ObjectFilter,
and write permission checking is done by has_object_permission
def
has_object_permission( self, request: rest_framework.request.Request, view, obj: django.db.models.base.Model) -> bool:
39 def has_object_permission(self, request: Request, view, obj: Model) -> bool: 40 queryset = self._queryset(view) 41 model_cls = queryset.model 42 perms = self.get_required_object_permissions(request.method, model_cls) 43 # Rank global permissions higher than per-object permissions 44 if request.user.has_perms(perms): 45 return True 46 # Allow access for owners if configured 47 if owner_field := getattr(view, "owner_field", None): 48 if getattr(obj, owner_field) == request.user: 49 return True 50 return super().has_object_permission(request, view, obj)
Return True if permission is granted, False otherwise.
def
HasPermission(*perm: str) -> type[rest_framework.permissions.BasePermission]:
53def HasPermission(*perm: str) -> type[BasePermission]: 54 """Permission checker for any non-object permissions, returns 55 a BasePermission class that can be used with rest_framework""" 56 57 class checker(BasePermission): 58 def has_permission(self, request: Request, view): 59 return bool(request.user and request.user.has_perms(perm)) 60 61 return checker
Permission checker for any non-object permissions, returns a BasePermission class that can be used with rest_framework
def
assign_initial_permissions(user, instance: django.db.models.base.Model):
66def assign_initial_permissions(user, instance: Model): 67 # Performance here should not be an issue, but if needed, there are many optimization routes 68 initial_permissions_list = InitialPermissions.objects.filter(role__in=user.all_roles()) 69 for initial_permissions in initial_permissions_list: 70 for permission in initial_permissions.permissions.all(): 71 if permission.content_type != ContentType.objects.get_for_model(instance): 72 continue 73 LOGGER.debug( 74 "Adding initial permission", 75 initial_permission=permission, 76 subject=initial_permissions.role, 77 object=instance, 78 ) 79 assign_perm(permission, initial_permissions.role, instance)