 03d5dad867
			
		
	
	03d5dad867
	
	
	
		
			
			* add `InitialPermissions` model to RBAC This is a powerful construct between Permission and Role to set initial permissions for newly created objects. * use safer `request.user` * fixup! use safer `request.user` * force all self-defined serializers to descend from our custom one See https://github.com/goauthentik/authentik/pull/10139 * reorganize initial permission assignment * fixup! reorganize initial permission assignment
		
			
				
	
	
		
			75 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
			
		
		
	
	
			75 lines
		
	
	
		
			3.2 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
| """RBAC Permissions"""
 | |
| 
 | |
| from django.contrib.contenttypes.models import ContentType
 | |
| from django.db.models import Model
 | |
| from guardian.shortcuts import assign_perm
 | |
| from rest_framework.permissions import BasePermission, DjangoObjectPermissions
 | |
| from rest_framework.request import Request
 | |
| 
 | |
| from authentik.rbac.models import InitialPermissions, InitialPermissionsMode
 | |
| 
 | |
| 
 | |
| class ObjectPermissions(DjangoObjectPermissions):
 | |
|     """RBAC Permissions"""
 | |
| 
 | |
|     def has_permission(self, request: Request, view) -> bool:
 | |
|         """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`"""
 | |
|         lookup = getattr(view, "lookup_url_kwarg", None) or getattr(view, "lookup_field", None)
 | |
|         if lookup and lookup in view.kwargs:
 | |
|             return True
 | |
|         # Legacy behaviour:
 | |
|         # Allow creation of objects even without explicit permission
 | |
|         queryset = self._queryset(view)
 | |
|         required_perms = self.get_required_permissions(request.method, queryset.model)
 | |
|         if (
 | |
|             len(required_perms) == 1
 | |
|             and f"{queryset.model._meta.app_label}.add_{queryset.model._meta.model_name}"
 | |
|             in required_perms
 | |
|             and getattr(view, "rbac_allow_create_without_perm", False)
 | |
|         ):
 | |
|             return True
 | |
|         return super().has_permission(request, view)
 | |
| 
 | |
|     def has_object_permission(self, request: Request, view, obj: Model) -> bool:
 | |
|         queryset = self._queryset(view)
 | |
|         model_cls = queryset.model
 | |
|         perms = self.get_required_object_permissions(request.method, model_cls)
 | |
|         # Rank global permissions higher than per-object permissions
 | |
|         if request.user.has_perms(perms):
 | |
|             return True
 | |
|         # Allow access for owners if configured
 | |
|         if owner_field := getattr(view, "owner_field", None):
 | |
|             if getattr(obj, owner_field) == request.user:
 | |
|                 return True
 | |
|         return super().has_object_permission(request, view, obj)
 | |
| 
 | |
| 
 | |
| def HasPermission(*perm: str) -> type[BasePermission]:
 | |
|     """Permission checker for any non-object permissions, returns
 | |
|     a BasePermission class that can be used with rest_framework"""
 | |
| 
 | |
|     class checker(BasePermission):
 | |
|         def has_permission(self, request: Request, view):
 | |
|             return bool(request.user and request.user.has_perms(perm))
 | |
| 
 | |
|     return checker
 | |
| 
 | |
| 
 | |
| # TODO: add `user: User` type annotation without circular dependencies.
 | |
| # The author of this function isn't proficient/patient enough to do it.
 | |
| def assign_initial_permissions(user, instance: Model):
 | |
|     # Performance here should not be an issue, but if needed, there are many optimization routes
 | |
|     initial_permissions_list = InitialPermissions.objects.filter(role__group__in=user.groups.all())
 | |
|     for initial_permissions in initial_permissions_list:
 | |
|         for permission in initial_permissions.permissions.all():
 | |
|             if permission.content_type != ContentType.objects.get_for_model(instance):
 | |
|                 continue
 | |
|             assign_to = (
 | |
|                 user
 | |
|                 if initial_permissions.mode == InitialPermissionsMode.USER
 | |
|                 else initial_permissions.role.group
 | |
|             )
 | |
|             assign_perm(permission, assign_to, instance)
 |