0204c4089b8d96ef7eeb555089b8a84395afd561
[auf_django_permissions.git] / auf / django / permissions / __init__.py
1 # encoding: utf-8
2
3 from collections import defaultdict
4
5
6 class Predicate(object):
7 """
8 Wrapper pour une fonction ``f(user, obj, cls)``.
9
10 Le paramètre ``user`` est l'utilisateur pour lequel la permission est testée.
11
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``.
16
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.
22
23 Les prédicats peuvent être combinés à l'aide des opérateurs booléens
24 ``&``, ``|`` et ``~``.
25 """
26
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 """
41 if self.func.func_code.co_argcount == 1:
42 return self.func(user)
43 else:
44 return self.func(user, obj, cls)
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)
82
83
84 class Rules(object):
85
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
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
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):
110 cls = None if obj is None else obj.__class__
111 result = self.predicate_for_perm(perm, cls)(user, obj)
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
131 class 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):
138 if self.rules is None:
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