|
1 # |
|
2 # Copyright (c) 2008-2015 Thierry Florac <tflorac AT ulthar.net> |
|
3 # All Rights Reserved. |
|
4 # |
|
5 # This software is subject to the provisions of the Zope Public License, |
|
6 # Version 2.1 (ZPL). A copy of the ZPL should accompany this distribution. |
|
7 # THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED |
|
8 # WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
|
9 # WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS |
|
10 # FOR A PARTICULAR PURPOSE. |
|
11 # |
|
12 |
|
13 __docformat__ = 'restructuredtext' |
|
14 |
|
15 |
|
16 # import standard library |
|
17 |
|
18 # import interfaces |
|
19 from pyams_security.interfaces import IRole, IProtectedObject, IPrincipalInfo, IRoleProtectedObject |
|
20 from pyams_security.schema import IRoleField |
|
21 from zope.schema.interfaces import IField |
|
22 |
|
23 # import packages |
|
24 |
|
25 |
|
26 class RolePrincipalsFieldProperty(object): |
|
27 """Custom field property used to handle role principals""" |
|
28 |
|
29 def __init__(self, field, role_id=None, name=None, **args): |
|
30 if not IField.providedBy(field): |
|
31 raise ValueError("Provided field must implement IField interface") |
|
32 if (role_id is None) and not IRoleField.providedBy(field): |
|
33 raise ValueError("Provided field must implement IRoleField interface " |
|
34 "or you must provide a role ID") |
|
35 if role_id is None: |
|
36 role_id = field.role_id |
|
37 elif IRole.providedBy(role_id): |
|
38 role_id = role_id.id |
|
39 if role_id is None: |
|
40 raise ValueError("Can't get role ID") |
|
41 if name is None: |
|
42 name = field.__name__ |
|
43 self.__field = field |
|
44 self.__name = name |
|
45 self.__role_id = role_id |
|
46 |
|
47 def __get__(self, instance, klass): |
|
48 if instance is None: |
|
49 return self |
|
50 protection = IProtectedObject(instance, None) |
|
51 if protection is None: |
|
52 return set() |
|
53 return protection.get_principals(self.__role_id) |
|
54 |
|
55 def __set__(self, instance, value): |
|
56 if value is None: |
|
57 value = set() |
|
58 elif isinstance(value, str): |
|
59 value = set(value.split(',')) |
|
60 value = set(map(lambda x: x.id if IPrincipalInfo.providedBy(x) else x, value)) |
|
61 field = self.__field.bind(instance) |
|
62 field.validate(value) |
|
63 if field.readonly: |
|
64 raise ValueError("Field {0} is readonly!".format(self.__name)) |
|
65 protection = IProtectedObject(instance, None) |
|
66 if not IRoleProtectedObject.providedBy(protection): |
|
67 raise ValueError("Can't use role properties on object not providing " |
|
68 "IRoleProtectedObject interface!") |
|
69 old_principals = protection.get_principals(self.__role_id) |
|
70 added = value - old_principals |
|
71 removed = old_principals - value |
|
72 for principal_id in added: |
|
73 protection.grant_role(self.__role_id, principal_id) |
|
74 for principal_id in removed: |
|
75 protection.revoke_role(self.__role_id, principal_id) |