[#2711] setting AUF_PERMISSIONS_RULES
[auf_django_permissions.git] / auf / django / permissions / __init__.py
CommitLineData
03fab9eb
EMS
1# encoding: utf-8
2
f8b57b7e
EMS
3from collections import defaultdict
4
3e56a4a6
EMS
5from django.conf import settings
6from django.utils.importlib import import_module
f8b57b7e 7
03fab9eb
EMS
8class Predicate(object):
9 """
10 Wrapper pour une fonction ``f(user, obj, cls)``.
f8b57b7e 11
03fab9eb 12 Le paramètre ``user`` est l'utilisateur pour lequel la permission est testée.
f8b57b7e 13
03fab9eb
EMS
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``.
f8b57b7e 18
03fab9eb
EMS
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.
f8b57b7e 24
03fab9eb
EMS
25 Les prédicats peuvent être combinés à l'aide des opérateurs booléens
26 ``&``, ``|`` et ``~``.
27 """
f8b57b7e 28
03fab9eb
EMS
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 """
8f8c8a27
EMS
43 if self.func.func_code.co_argcount == 1:
44 return self.func(user)
45 else:
46 return self.func(user, obj, cls)
03fab9eb
EMS
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)
f8b57b7e 84
f8b57b7e 85
03fab9eb 86class Rules(object):
f8b57b7e 87
03fab9eb
EMS
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
8f8c8a27
EMS
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
03fab9eb
EMS
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):
8f8c8a27
EMS
112 cls = None if obj is None else obj.__class__
113 result = self.predicate_for_perm(perm, cls)(user, obj)
03fab9eb
EMS
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
3e56a4a6 128 def clear(self):
03fab9eb
EMS
129 self.allow_rules.clear()
130 self.deny_rules.clear()
131
132
133class 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):
8f8c8a27 140 if self.rules is None:
03fab9eb
EMS
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
3e56a4a6
EMS
151
152_rules = None
153def 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