11from inspect import isclass
2- from simple_api .utils import ensure_tuple
32
43
5- # instantiates permission classes (if they are not already, maybe due to get_fn injection) and builds a
6- # function that raises if the permissions are not passed
74def build_permissions_fn (permissions ):
8- instantiated_permissions = []
9- for cls_or_inst in permissions :
10- if isclass (cls_or_inst ) or isinstance (cls_or_inst , LogicalConnector ):
11- instantiated_permissions .append (cls_or_inst ())
12- else :
13- instantiated_permissions .append (cls_or_inst )
14-
155 def fn (** kwargs ):
16- for perm in instantiated_permissions :
17- if not perm .has_permission (** kwargs ):
18- raise PermissionError (perm .error_message (** kwargs ))
6+ for perm in permissions :
7+ if not perm () .has_permission (** kwargs ):
8+ raise PermissionError (perm () .error_message (** kwargs ))
199 return fn
2010
2111
@@ -26,12 +16,21 @@ def __init__(self, **kwargs):
2616 def permission_statement (self , ** kwargs ):
2717 raise NotImplementedError
2818
29- def has_permission (self , exclude_classes = (), ** kwargs ):
19+ def has_permission (self , ** kwargs ):
20+ # to achieve hierarchical checking for permissions (a subclass calls the permission statement of the superclass
21+ # and only if it passes, executes its own), we need to traverse over the whole linearization order of the
22+ # permission class; however, classes like object, which are naturally also a part of the chain, do not
23+ # contain the `permission_statement` method and therefore should be just skipped; the same is true for abstract
24+ # permission classes which contain the method, but is not implemented - like this one for example: to achieve
25+ # this, we try calling the method and if it turns out to not be implemented, we skip it as well
3026 for cls in reversed (self .__class__ .__mro__ ):
31- if cls in (object , BasePermission , LogicalResolver ) + exclude_classes :
27+ if not hasattr (cls , "permission_statement" ):
28+ continue
29+ try :
30+ if not cls .permission_statement (self , ** kwargs ):
31+ return False
32+ except NotImplementedError :
3233 continue
33- if not cls .permission_statement (self , ** kwargs ):
34- return False
3534 return True
3635
3736 def error_message (self , ** kwargs ):
@@ -43,12 +42,10 @@ def __init__(self, *permissions):
4342 self .permissions = permissions
4443
4544 def __call__ (self , ** kwargs ):
46- instantiated_perms = []
4745 for perm in self .permissions :
4846 assert isclass (perm ) or isinstance (perm , LogicalConnector ), \
4947 "Permissions in logical connectors must be classes."
50- instantiated_perms .append (perm (** kwargs ))
51- return LogicalResolver (instantiated_perms , self .resolve_fn )
48+ return LogicalResolver (self .permissions , self .resolve_fn )
5249
5350 def resolve_fn (self , permissions , ** kwargs ):
5451 raise NotImplementedError
@@ -69,23 +66,23 @@ def error_message(self, **kwargs):
6966class Or (LogicalConnector ):
7067 def resolve_fn (self , permissions , ** kwargs ):
7168 for perm in permissions :
72- if perm .has_permission (** kwargs ):
69+ if perm () .has_permission (** kwargs ):
7370 return True
7471 return False
7572
7673
7774class And (LogicalConnector ):
7875 def resolve_fn (self , permissions , ** kwargs ):
7976 for perm in permissions :
80- if not perm .has_permission (** kwargs ):
77+ if not perm () .has_permission (** kwargs ):
8178 return False
8279 return True
8380
8481
8582class Not (LogicalConnector ):
8683 def resolve_fn (self , permissions , ** kwargs ):
8784 assert len (permissions ) == 1 , "`Not` accepts only one permission class as parameter."
88- return not permissions [0 ].has_permission (** kwargs )
85+ return not permissions [0 ]() .has_permission (** kwargs )
8986
9087
9188class AllowAll (BasePermission ):
0 commit comments