29 def check_entity_attributes(session, entity, editedattrs=None, creation=False): |
29 def check_entity_attributes(session, entity, editedattrs=None, creation=False): |
30 eid = entity.eid |
30 eid = entity.eid |
31 eschema = entity.e_schema |
31 eschema = entity.e_schema |
32 # ._cw_skip_security_attributes is there to bypass security for attributes |
32 # ._cw_skip_security_attributes is there to bypass security for attributes |
33 # set by hooks by modifying the entity's dictionnary |
33 # set by hooks by modifying the entity's dictionnary |
34 dontcheck = entity._cw_skip_security_attributes |
|
35 if editedattrs is None: |
34 if editedattrs is None: |
36 try: |
35 editedattrs = entity.cw_edited |
37 editedattrs = entity.edited_attributes |
36 dontcheck = editedattrs.skip_security |
38 except AttributeError: |
|
39 editedattrs = entity # XXX unexpected |
|
40 for attr in editedattrs: |
37 for attr in editedattrs: |
41 if attr in dontcheck: |
38 if attr in dontcheck: |
42 continue |
39 continue |
43 rdef = eschema.rdef(attr) |
40 rdef = eschema.rdef(attr) |
44 if rdef.final: # non final relation are checked by other hooks |
41 if rdef.final: # non final relation are checked by other hooks |
45 # add/delete should be equivalent (XXX: unify them into 'update' ?) |
42 # add/delete should be equivalent (XXX: unify them into 'update' ?) |
46 if creation and not rdef.permissions.get('update'): |
43 if creation and not rdef.permissions.get('update'): |
47 continue |
44 continue |
48 rdef.check_perm(session, 'update', eid=eid) |
45 rdef.check_perm(session, 'update', eid=eid) |
49 # don't update dontcheck until everything went fine: see usage in |
|
50 # after_update_entity, where if we got an Unauthorized at hook time, we will |
|
51 # retry and commit time |
|
52 dontcheck |= frozenset(editedattrs) |
|
53 |
46 |
54 |
47 |
55 class _CheckEntityPermissionOp(hook.LateOperation): |
48 class CheckEntityPermissionOp(hook.DataOperationMixIn, hook.LateOperation): |
56 def precommit_event(self): |
49 def precommit_event(self): |
57 #print 'CheckEntityPermissionOp', self.session.user, self.entity, self.action |
|
58 session = self.session |
50 session = self.session |
59 for values in session.transaction_data.pop('check_entity_perm_op'): |
51 for eid, action, edited in self.get_data(): |
60 entity = session.entity_from_eid(values[0]) |
52 entity = session.entity_from_eid(eid) |
61 action = values[1] |
|
62 entity.cw_check_perm(action) |
53 entity.cw_check_perm(action) |
63 check_entity_attributes(session, entity, values[2:], |
54 check_entity_attributes(session, entity, edited, |
64 creation=self.creation) |
55 creation=(action == 'add')) |
65 |
|
66 def commit_event(self): |
|
67 pass |
|
68 |
56 |
69 |
57 |
70 class _CheckRelationPermissionOp(hook.LateOperation): |
58 class CheckRelationPermissionOp(hook.DataOperationMixIn, hook.LateOperation): |
71 def precommit_event(self): |
59 def precommit_event(self): |
72 session = self.session |
60 session = self.session |
73 for args in session.transaction_data.pop('check_relation_perm_op'): |
61 for action, rschema, eidfrom, eidto in self.get_data(): |
74 action, rschema, eidfrom, eidto = args |
|
75 rdef = rschema.rdef(session.describe(eidfrom)[0], |
62 rdef = rschema.rdef(session.describe(eidfrom)[0], |
76 session.describe(eidto)[0]) |
63 session.describe(eidto)[0]) |
77 rdef.check_perm(session, action, fromeid=eidfrom, toeid=eidto) |
64 rdef.check_perm(session, action, fromeid=eidfrom, toeid=eidto) |
78 |
|
79 def commit_event(self): |
|
80 pass |
|
81 |
65 |
82 |
66 |
83 @objectify_selector |
67 @objectify_selector |
84 @lltrace |
68 @lltrace |
85 def write_security_enabled(cls, req, **kwargs): |
69 def write_security_enabled(cls, req, **kwargs): |
96 class AfterAddEntitySecurityHook(SecurityHook): |
80 class AfterAddEntitySecurityHook(SecurityHook): |
97 __regid__ = 'securityafteraddentity' |
81 __regid__ = 'securityafteraddentity' |
98 events = ('after_add_entity',) |
82 events = ('after_add_entity',) |
99 |
83 |
100 def __call__(self): |
84 def __call__(self): |
101 hook.set_operation(self._cw, 'check_entity_perm_op', |
85 CheckEntityPermissionOp.get_instance(self._cw).add_data( |
102 (self.entity.eid, 'add') + tuple(self.entity.edited_attributes), |
86 (self.entity.eid, 'add', self.entity.cw_edited) ) |
103 _CheckEntityPermissionOp, creation=True) |
|
104 |
87 |
105 |
88 |
106 class AfterUpdateEntitySecurityHook(SecurityHook): |
89 class AfterUpdateEntitySecurityHook(SecurityHook): |
107 __regid__ = 'securityafterupdateentity' |
90 __regid__ = 'securityafterupdateentity' |
108 events = ('after_update_entity',) |
91 events = ('after_update_entity',) |
113 self.entity.cw_check_perm('update') |
96 self.entity.cw_check_perm('update') |
114 check_entity_attributes(self._cw, self.entity) |
97 check_entity_attributes(self._cw, self.entity) |
115 except Unauthorized: |
98 except Unauthorized: |
116 self.entity._cw_clear_local_perm_cache('update') |
99 self.entity._cw_clear_local_perm_cache('update') |
117 # save back editedattrs in case the entity is reedited later in the |
100 # save back editedattrs in case the entity is reedited later in the |
118 # same transaction, which will lead to edited_attributes being |
101 # same transaction, which will lead to cw_edited being |
119 # overwritten |
102 # overwritten |
120 hook.set_operation(self._cw, 'check_entity_perm_op', |
103 CheckEntityPermissionOp.get_instance(self._cw).add_data( |
121 (self.entity.eid, 'update') + tuple(self.entity.edited_attributes), |
104 (self.entity.eid, 'update', self.entity.cw_edited) ) |
122 _CheckEntityPermissionOp, creation=False) |
|
123 |
105 |
124 |
106 |
125 class BeforeDelEntitySecurityHook(SecurityHook): |
107 class BeforeDelEntitySecurityHook(SecurityHook): |
126 __regid__ = 'securitybeforedelentity' |
108 __regid__ = 'securitybeforedelentity' |
127 events = ('before_delete_entity',) |
109 events = ('before_delete_entity',) |
154 nocheck = self._cw.transaction_data.get('skip-security', ()) |
136 nocheck = self._cw.transaction_data.get('skip-security', ()) |
155 if (self.eidfrom, self.rtype, self.eidto) in nocheck: |
137 if (self.eidfrom, self.rtype, self.eidto) in nocheck: |
156 return |
138 return |
157 rschema = self._cw.repo.schema[self.rtype] |
139 rschema = self._cw.repo.schema[self.rtype] |
158 if self.rtype in ON_COMMIT_ADD_RELATIONS: |
140 if self.rtype in ON_COMMIT_ADD_RELATIONS: |
159 hook.set_operation(self._cw, 'check_relation_perm_op', |
141 CheckRelationPermissionOp.get_instance(self._cw).add_data( |
160 ('add', rschema, self.eidfrom, self.eidto), |
142 ('add', rschema, self.eidfrom, self.eidto) ) |
161 _CheckRelationPermissionOp) |
|
162 else: |
143 else: |
163 rdef = rschema.rdef(self._cw.describe(self.eidfrom)[0], |
144 rdef = rschema.rdef(self._cw.describe(self.eidfrom)[0], |
164 self._cw.describe(self.eidto)[0]) |
145 self._cw.describe(self.eidto)[0]) |
165 rdef.check_perm(self._cw, 'add', fromeid=self.eidfrom, toeid=self.eidto) |
146 rdef.check_perm(self._cw, 'add', fromeid=self.eidfrom, toeid=self.eidto) |
166 |
147 |