|
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 # import standard library |
|
16 import logging |
|
17 |
|
18 # import interfaces |
|
19 from pyams_security.interfaces import IProtectedObject, IRole, IPrincipalInfo, GrantedRoleEvent, RevokedRoleEvent, \ |
|
20 IDefaultProtectionPolicy, IRoleProtectedObject |
|
21 from zope.annotation.interfaces import IAnnotations |
|
22 |
|
23 # import packages |
|
24 from persistent import Persistent |
|
25 from persistent.dict import PersistentDict |
|
26 from pyams_utils.adapter import adapter_config |
|
27 from pyams_utils.property import request_property |
|
28 from pyams_utils.registry import query_utility |
|
29 from pyams_utils.request import check_request |
|
30 from pyramid.location import lineage |
|
31 from pyramid.security import DENY_ALL, Everyone, Allow, ALL_PERMISSIONS, Authenticated |
|
32 from zope.interface import implementer |
|
33 from zope.lifecycleevent import ObjectCreatedEvent |
|
34 from zope.location.location import locate |
|
35 from zope.schema.fieldproperty import FieldProperty |
|
36 |
|
37 |
|
38 @implementer(IRoleProtectedObject) |
|
39 class RoleProtectedObject(Persistent): |
|
40 """Base class for object protected by roles""" |
|
41 |
|
42 inherit_parent_security = FieldProperty(IRoleProtectedObject['inherit_parent_security']) |
|
43 _everyone_permission = FieldProperty(IRoleProtectedObject['everyone_permission']) |
|
44 _authenticated_permission = FieldProperty(IRoleProtectedObject['authenticated_permission']) |
|
45 inherit_parent_roles = FieldProperty(IRoleProtectedObject['inherit_parent_roles']) |
|
46 |
|
47 def __init__(self): |
|
48 self._principals_by_role = PersistentDict() |
|
49 self._roles_by_principal = PersistentDict() |
|
50 |
|
51 @property |
|
52 def everyone_permission(self): |
|
53 permission = self._everyone_permission |
|
54 if permission is None and self.inherit_parent_security: |
|
55 for parent in lineage(self): |
|
56 if parent is self: |
|
57 continue |
|
58 protection = IProtectedObject(parent, None) |
|
59 if protection is not None: |
|
60 permission = protection.everyone_permission |
|
61 if permission is not None: |
|
62 break |
|
63 return permission |
|
64 |
|
65 @everyone_permission.setter |
|
66 def everyone_permission(self, value): |
|
67 self._everyone_permission = value |
|
68 |
|
69 @property |
|
70 def authenticated_permission(self): |
|
71 permission = self._authenticated_permission |
|
72 if permission is None and self.inherit_parent_security: |
|
73 for parent in lineage(self): |
|
74 if parent is self: |
|
75 continue |
|
76 protection = IProtectedObject(parent, None) |
|
77 if protection is not None: |
|
78 permission = protection.authenticated_permission |
|
79 if permission is not None: |
|
80 break |
|
81 return permission |
|
82 |
|
83 @authenticated_permission.setter |
|
84 def authenticated_permission(self, value): |
|
85 self._authenticated_permission = value |
|
86 |
|
87 def grant_role(self, role_id, principal_ids): |
|
88 registry = check_request().registry |
|
89 if IRole.providedBy(role_id): |
|
90 role_id = role_id.id |
|
91 if isinstance(principal_ids, str): |
|
92 principal_ids = {principal_ids} |
|
93 role_principals = self._principals_by_role.get(role_id) or set() |
|
94 for principal_id in principal_ids: |
|
95 if IPrincipalInfo.providedBy(principal_id): |
|
96 principal_id = principal_id.id |
|
97 if principal_id not in role_principals: |
|
98 principal_roles = self._roles_by_principal.get(principal_id) or set() |
|
99 role_principals.add(principal_id) |
|
100 principal_roles.add(role_id) |
|
101 self._roles_by_principal[principal_id] = principal_roles |
|
102 self._principals_by_role[role_id] = role_principals |
|
103 registry.notify(GrantedRoleEvent(self, role_id, principal_id)) |
|
104 |
|
105 def revoke_role(self, role_id, principal_ids): |
|
106 registry = check_request().registry |
|
107 if IRole.providedBy(role_id): |
|
108 role_id = role_id.id |
|
109 if isinstance(principal_ids, str): |
|
110 principal_ids = {principal_ids} |
|
111 role_principals = self._principals_by_role.get(role_id) or set() |
|
112 for principal_id in principal_ids: |
|
113 if IPrincipalInfo.providedBy(principal_id): |
|
114 principal_id = principal_id.id |
|
115 if principal_id in role_principals: |
|
116 principal_roles = self._roles_by_principal.get(principal_id) or set() |
|
117 role_principals.remove(principal_id) |
|
118 principal_roles.remove(role_id) |
|
119 self._roles_by_principal[principal_id] = principal_roles |
|
120 self._principals_by_role[role_id] = role_principals |
|
121 registry.notify(RevokedRoleEvent(self, role_id, principal_id)) |
|
122 |
|
123 def get_principals(self, role_id): |
|
124 if IRole.providedBy(role_id): |
|
125 role_id = role_id.id |
|
126 return self._principals_by_role.get(role_id) or set() |
|
127 |
|
128 def get_roles(self, principal_id): |
|
129 if IPrincipalInfo.providedBy(principal_id): |
|
130 principal_id = principal_id.id |
|
131 return self._roles_by_principal.get(principal_id) or set() |
|
132 |
|
133 def get_permissions(self, principal_id): |
|
134 registry = check_request().registry |
|
135 result = set() |
|
136 for role_id in self.get_roles(principal_id): |
|
137 role = registry.queryUtility(IRole, role_id) |
|
138 result |= role.permissions or set() |
|
139 return result |
|
140 |
|
141 @request_property(key=None) |
|
142 def __acl__(self): |
|
143 # always grant all permissions to system manager |
|
144 result = [(Allow, 'system:admin', ALL_PERMISSIONS)] |
|
145 # grant permission to everyone and authenticated |
|
146 if self.everyone_permission: |
|
147 result.append((Allow, Everyone, self.everyone_permission)) |
|
148 if self.authenticated_permission: |
|
149 result.append((Allow, Authenticated, self.authenticated_permission)) |
|
150 # grant access to all roles permissions |
|
151 for role_id in self._principals_by_role.keys(): |
|
152 role = query_utility(IRole, role_id) |
|
153 if role is not None: |
|
154 result.append((Allow, 'role:{0}'.format(role_id), role.permissions)) |
|
155 # stop inheritance if required |
|
156 if not self.inherit_parent_security: |
|
157 result.append(DENY_ALL) |
|
158 logging.getLogger('PyAMS').debug(result) |
|
159 print(result) |
|
160 return result |
|
161 |
|
162 |
|
163 ROLES_ANNOTATIONS_KEY = 'pyams_security.roles' |
|
164 |
|
165 |
|
166 @adapter_config(context=IDefaultProtectionPolicy, provides=IRoleProtectedObject) |
|
167 def ProtectedObjectFactory(context): |
|
168 """Default protected object factory""" |
|
169 annotations = IAnnotations(context) |
|
170 protection = annotations.get(ROLES_ANNOTATIONS_KEY) |
|
171 if protection is None: |
|
172 protection = annotations[ROLES_ANNOTATIONS_KEY] = RoleProtectedObject() |
|
173 check_request().registry.notify(ObjectCreatedEvent(protection)) |
|
174 locate(protection, context) |
|
175 return protection |
|
176 |
|
177 |
|
178 class ProtectedObject(object): |
|
179 """Base protected object class""" |
|
180 |
|
181 def __acl__(self): |
|
182 protected = IProtectedObject(self, None) |
|
183 if protected is not None: |
|
184 acl = protected.__acl__() |
|
185 if callable(acl): |
|
186 acl = acl(protected) |
|
187 return acl |
|
188 return [] |