Support pour les permissions globales
[auf_django_permissions.git] / auf / django / permissions / __init__.py
CommitLineData
03fab9eb
EMS
1# encoding: utf-8
2
f8b57b7e
EMS
3from collections import defaultdict
4
f8b57b7e 5
03fab9eb
EMS
6class Predicate(object):
7 """
8 Wrapper pour une fonction ``f(user, obj, cls)``.
f8b57b7e 9
03fab9eb 10 Le paramètre ``user`` est l'utilisateur pour lequel la permission est testée.
f8b57b7e 11
03fab9eb
EMS
12 Si le prédicat est testé sur un seul objet, cet objet est passé dans
13 le paramètre ``obj`` et le paramètre ``cls`` est ``None``. Inversement,
14 si le prédicat sert à filtrer un queryset, le modèle du queryset est
15 passé dans le paramètre ``cls`` et le paramètre ``obj`` est ``None``.
f8b57b7e 16
03fab9eb
EMS
17 La fonction peut retourner un booléen ou un objet ``Q``. Un booléen
18 indique si le test a passé ou pas. Un objet ``Q`` indique les filtres à
19 appliquer pour ne garder que les objets qui passent le test. Si un objet
20 ``Q`` est retourné lors d'un test sur un seul objet, le test retournera
21 un booléen indiquant si l'objet satisfait au filtre.
f8b57b7e 22
03fab9eb
EMS
23 Les prédicats peuvent être combinés à l'aide des opérateurs booléens
24 ``&``, ``|`` et ``~``.
25 """
f8b57b7e 26
03fab9eb
EMS
27 def __init__(self, func_or_value):
28 """
29 On peut initialiser un prédicat avec une fonction ayant la signature
30 ``f(user, obj, cls)`` ou avec une valeur constante.
31 """
32 if callable(func_or_value):
33 self.func = func_or_value
34 else:
35 self.func = lambda user, obj, cls: func_or_value
36
37 def __call__(self, user, obj=None, cls=None):
38 """
39 Appelle la fonction encapsulée.
40 """
8f8c8a27
EMS
41 if self.func.func_code.co_argcount == 1:
42 return self.func(user)
43 else:
44 return self.func(user, obj, cls)
03fab9eb
EMS
45
46 def __and__(self, other):
47 def func(user, obj, cls):
48 my_result = self(user, obj, cls)
49 if my_result is False:
50 return False
51 other_result = other(user, obj, cls)
52 if my_result is True:
53 return other_result
54 elif other_result is True:
55 return my_result
56 else:
57 return my_result & other_result
58 return Predicate(func)
59
60 def __or__(self, other):
61 def func(user, obj, cls):
62 my_result = self(user, obj, cls)
63 if my_result is True:
64 return True
65 other_result = other(user, obj, cls)
66 if my_result is False:
67 return other_result
68 elif other_result is False:
69 return my_result
70 else:
71 return my_result | other_result
72 return Predicate(func)
73
74 def __invert__(self):
75 def func(user, obj, cls):
76 result = self(user, obj, cls)
77 if isinstance(result, bool):
78 return not result
79 else:
80 return ~result
81 return Predicate(func)
f8b57b7e 82
f8b57b7e 83
03fab9eb 84class Rules(object):
f8b57b7e 85
03fab9eb
EMS
86 def __init__(self, allow_default=None, deny_default=None):
87 self.allow_rules = defaultdict(lambda: allow_default or Predicate(False))
88 self.deny_rules = defaultdict(lambda: deny_default or Predicate(False))
89
90 def allow(self, perm, cls, predicate):
91 if not isinstance(predicate, Predicate):
92 raise TypeError("the third argument to allow() must be a Predicate")
93 self.allow_rules[(perm, cls)] |= predicate
94
95 def deny(self, perm, cls, predicate):
96 if not isinstance(predicate, Predicate):
97 raise TypeError("the third argument to deny() must be a Predicate")
98 self.deny_rules[(perm, cls)] |= predicate
99
8f8c8a27
EMS
100 def allow_global(self, perm, predicate):
101 self.allow(perm, None, predicate)
102
103 def deny_global(self, perm, predicate):
104 self.deny(perm, None, predicate)
105
03fab9eb
EMS
106 def predicate_for_perm(self, perm, cls):
107 return self.allow_rules[(perm, cls)] & ~self.deny_rules[(perm, cls)]
108
109 def user_has_perm(self, user, perm, obj):
8f8c8a27
EMS
110 cls = None if obj is None else obj.__class__
111 result = self.predicate_for_perm(perm, cls)(user, obj)
03fab9eb
EMS
112 if isinstance(result, bool):
113 return result
114 else:
115 return obj._default_manager.filter(pk=obj.pk).filter(result).exists()
116
117 def filter_queryset(self, user, perm, queryset):
118 result = self.predicate_for_perm(perm, queryset.model)(user, cls=queryset.model)
119 if result is True:
120 return queryset
121 elif result is False:
122 return queryset.none()
123 else:
124 return queryset.filter(result)
125
126 def clear():
127 self.allow_rules.clear()
128 self.deny_rules.clear()
129
130
131class AuthenticationBackend(object):
132 supports_anonymous_user = True
133 supports_inactive_user = True
134 supports_object_permissions = True
135 rules = None
136
137 def has_perm(self, user, perm, obj=None):
8f8c8a27 138 if self.rules is None:
03fab9eb
EMS
139 return False
140 return self.rules.user_has_perm(user, perm, obj)
141
142 def authenticate(self, username=None, password=None):
143 # We don't authenticate
144 return None
145
146 def get_user(self, user_id):
147 # We don't authenticate
148 return None