1 """Definition of the identity predicates."""
2
3
4
5 __all__ = [
6 'All',
7 'Any',
8 'NotAny',
9 'CompoundPredicate',
10 'IdentityPredicateHelper',
11 'Predicate',
12 'SecureObject',
13 'SecureResource',
14 'from_host',
15 'from_any_host',
16 'in_all_groups',
17 'in_any_group',
18 'in_group',
19 'has_all_permissions',
20 'has_any_permission',
21 'has_permission',
22 'not_anonymous',
23 'require',
24 ]
25
26
27 from types import MethodType
28
29 from cherrypy import request
30
31 from turbogears import config
32 from turbogears.decorator import weak_signature_decorator
33 from turbogears.util import match_ip
34
35 from turbogears.identity.exceptions import *
36 from turbogears.identity.base import current
37
38
40 """Generic base class for testing true or false for a condition."""
41
43 """Determine whether predicate is True or False for the given object."""
44 raise NotImplementedError
45
50
51
53 """A predicate composed of other predicates."""
54
56 self.predicates = predicates
57
58
59 -class All(CompoundPredicate):
60 """Logical 'and' of all sub-predicates.
61
62 This compound predicate evaluates to true only if all of its sub-predicates
63 evaluate to true for the given input.
64
65 """
66 error_message = "One of the predicates denied access"
67
76
77
78 -class Any(CompoundPredicate):
79 """Logical 'or' of all sub-predicates.
80
81 This compound predicate evaluates to true if any one of its sub-predicates
82 evaluates to true for the given input.
83
84 """
85 error_message = "No predicate was able to grant access"
86
94
95
96 -class NotAny(CompoundPredicate):
97 """Locigal 'nor' of all sub-predicates.
98
99 A compound predicate that evaluates to true only if no sub-predicates
100 evaluate to true for the given input.
101
102 """
103 error_message= "One of the predicates did not deny access"
104
112
113
115 """A mix-in helper class for Identity Predicates."""
116
119
120
121 -class in_group(Predicate, IdentityPredicateHelper):
122 """Predicate for requiring a group."""
123 error_message = "Not member of group: %(group_name)s"
124
127
133
134
136 """Predicate for requiring membership in a number of groups."""
137
141
142
144 """Predicate for requiring membership in at least one group."""
145 error_message = "Not member of any group: %(group_list)s"
146
151
152
154 """Predicate for checking whether current visitor is anonymous."""
155 error_message = "Anonymous access denied"
156
162
163
165 """Predicate for checking whether visitor has a particular permission."""
166 error_message = "Permission denied: %(permission_name)s"
167
170
177
178
180 """Predicate for checking whether the visitor has all permissions."""
181
185
186
188 """Predicate for checking whether visitor has at least one permission."""
189 error_message = "No matching permissions: %(permission_list)s"
190
195
196
198 try:
199 return request.headers.get('X-Forwarded-For', request.headers.get(
200 'Remote-Addr', '')).rsplit(',', 1)[-1].strip() or None
201 except AttributeError:
202 raise RequestRequiredException()
203
204
205 -class from_host(Predicate, IdentityPredicateHelper):
206 """Predicate for checking whether the visitor's host is a permitted host.
207
208 Note: We never want to announce what the list of allowed hosts is, because
209 it is way too easy to spoof an IP address in a TCP/IP packet.
210
211 """
212 error_message = "Access from this host is not permitted."
213
216
218 """Match the visitor's host against the criteria."""
219 ip = _remoteHost()
220 if ip and match_ip(self.host, ip):
221 return True
222 self.append_error_message(errors)
223 return False
224
225
227 """Predicate for checking the visitor against a number of allowed hosts."""
228 error_message = "Access from this host is not permitted."
229
233
234
236 """Function decorator checking requirements for the current user.
237
238 This function decorator checks whether the current user is a member
239 of the groups specified and has the permissions required.
240
241 """
242
243 def entangle(fn):
244 def require(func, self, *args, **kwargs):
245 try:
246 errors = []
247 if (predicate is None
248 or predicate.eval_with_object(current, errors)):
249 return fn(self, *args, **kwargs)
250 except IdentityException, e:
251 errors = [str(e)]
252 raise IdentityFailure(errors)
253 fn._require = predicate
254 return require
255 return weak_signature_decorator(entangle)
256
257
265 _wrapper.exposed = True
266 return _wrapper
267
268
270
272 if name.startswith('_cp') or name == 'require':
273 return object.__getattribute__(self, name)
274 try:
275 value = object.__getattribute__(self, name)
276 try:
277 predicate = object.__getattribute__(self, 'require')
278 except AttributeError:
279 predicate = config.get('identity.require', None)
280 if predicate is None:
281 raise AttributeError("SecureResource requires a 'require'"
282 " attribute either on the controller class itself"
283 " or in the config file")
284 errors = []
285 if (isinstance(value, MethodType) and
286 hasattr(value, 'exposed')):
287 return _check_method(self, value, predicate)
288 from turbogears.controllers import Controller
289 if isinstance(value, Controller):
290 return SecureObject(value, predicate)
291
292 return value
293 except IdentityException, e:
294 errors = [str(e)]
295 raise IdentityFailure(errors)
296
297
299
300 - def __init__(self, obj, require, exclude=[]):
301 self._object = obj
302 self._require = require
303 self._exclude = exclude
304
306 if name in ('_object', '_require', '_exclude'):
307 return object.__getattribute__(self, name)
308 try:
309 obj = object.__getattribute__(self, '_object')
310 value = getattr(obj, name)
311 errors = []
312 predicate = object.__getattribute__(self, '_require')
313 if name in object.__getattribute__(self, '_exclude'):
314 return value
315 if (isinstance(value, MethodType) and
316 hasattr(value, 'exposed')):
317 return _check_method(obj, value, predicate)
318 from turbogears.controllers import Controller
319 if isinstance(value, Controller):
320 return SecureObject(value, predicate)
321
322 return value
323 except IdentityException, e:
324 errors = [str(e)]
325 raise IdentityFailure(errors)
326
332