hooks/security.py
brancholdstable
changeset 7074 e4580e5f0703
parent 6426 541659c39f6a
child 8190 2a3c1b787688
child 8238 087bb529035c
equal deleted inserted replaced
6749:48f468f33704 7074:e4580e5f0703
    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