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