[session] cleanup hook / operation / entity edition api
Operation api
~~~~~~~~~~~~~
* commit_event killed, recently introduced postcommit_event is enough and has a better name
* kill SingleOperation class, it's a) currently never used b) superseeded by set_operation if needed.
Entity edition api
~~~~~~~~~~~~~~~~~~
edited_attributes turned into a special object holding edition specific attributes:
- attributes to be edited (simply mirrored in cw_attr_cache, actual values are there)
- former _cw_skip_security set (cw_edited) and querier_pending_relations
It has also been renamed to `cw_edited` on the way (it may also contains inlined relations)
The entity dict interface has been deprecated. One should explicitly use either
cw_attr_cache or cw_edited according to the need.
Also, there is now a control that we don't try to hi-jack edited attributes
once this has no more effect (eg modification have already been saved)
At last, _cw_set_defaults/cw_check internal methods have been moved to this
special object
Hook api
~~~~~~~~
hook.entity_oldnewvalue function now moved to a method of cw_edited object.
# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>."""Security hooks: check permissions to add/delete/update entities according tothe user connected to a session"""__docformat__="restructuredtext en"fromcubicwebimportUnauthorizedfromcubicweb.selectorsimportobjectify_selector,lltracefromcubicweb.serverimportBEFORE_ADD_RELATIONS,ON_COMMIT_ADD_RELATIONS,hookdefcheck_entity_attributes(session,entity,editedattrs=None,creation=False):eid=entity.eideschema=entity.e_schema# ._cw_skip_security_attributes is there to bypass security for attributes# set by hooks by modifying the entity's dictionnaryifeditedattrsisNone:editedattrs=entity.cw_editeddontcheck=editedattrs.skip_securityforattrineditedattrs:ifattrindontcheck:continuerdef=eschema.rdef(attr)ifrdef.final:# non final relation are checked by other hooks# add/delete should be equivalent (XXX: unify them into 'update' ?)ifcreationandnotrdef.permissions.get('update'):continuerdef.check_perm(session,'update',eid=eid)class_CheckEntityPermissionOp(hook.LateOperation):defprecommit_event(self):#print 'CheckEntityPermissionOp', self.session.user, self.entity, self.actionsession=self.sessionforvaluesinsession.transaction_data.pop('check_entity_perm_op'):eid,action,edited=valuesentity=session.entity_from_eid(eid)entity.cw_check_perm(action)check_entity_attributes(session,entity,edited,creation=self.creation)class_CheckRelationPermissionOp(hook.LateOperation):defprecommit_event(self):session=self.sessionforargsinsession.transaction_data.pop('check_relation_perm_op'):action,rschema,eidfrom,eidto=argsrdef=rschema.rdef(session.describe(eidfrom)[0],session.describe(eidto)[0])rdef.check_perm(session,action,fromeid=eidfrom,toeid=eidto)@objectify_selector@lltracedefwrite_security_enabled(cls,req,**kwargs):ifreqisNoneornotreq.write_security:return0return1classSecurityHook(hook.Hook):__abstract__=Truecategory='security'__select__=hook.Hook.__select__&write_security_enabled()classAfterAddEntitySecurityHook(SecurityHook):__regid__='securityafteraddentity'events=('after_add_entity',)def__call__(self):hook.set_operation(self._cw,'check_entity_perm_op',(self.entity.eid,'add',self.entity.cw_edited),_CheckEntityPermissionOp,creation=True)classAfterUpdateEntitySecurityHook(SecurityHook):__regid__='securityafterupdateentity'events=('after_update_entity',)def__call__(self):try:# check user has permission right now, if not retry at commit timeself.entity.cw_check_perm('update')check_entity_attributes(self._cw,self.entity)exceptUnauthorized:self.entity._cw_clear_local_perm_cache('update')# save back editedattrs in case the entity is reedited later in the# same transaction, which will lead to cw_edited being# overwrittenhook.set_operation(self._cw,'check_entity_perm_op',(self.entity.eid,'update',self.entity.cw_edited),_CheckEntityPermissionOp,creation=False)classBeforeDelEntitySecurityHook(SecurityHook):__regid__='securitybeforedelentity'events=('before_delete_entity',)def__call__(self):self.entity.cw_check_perm('delete')classBeforeAddRelationSecurityHook(SecurityHook):__regid__='securitybeforeaddrelation'events=('before_add_relation',)def__call__(self):ifself.rtypeinBEFORE_ADD_RELATIONS:nocheck=self._cw.transaction_data.get('skip-security',())if(self.eidfrom,self.rtype,self.eidto)innocheck:returnrschema=self._cw.repo.schema[self.rtype]rdef=rschema.rdef(self._cw.describe(self.eidfrom)[0],self._cw.describe(self.eidto)[0])rdef.check_perm(self._cw,'add',fromeid=self.eidfrom,toeid=self.eidto)classAfterAddRelationSecurityHook(SecurityHook):__regid__='securityafteraddrelation'events=('after_add_relation',)def__call__(self):ifnotself.rtypeinBEFORE_ADD_RELATIONS:nocheck=self._cw.transaction_data.get('skip-security',())if(self.eidfrom,self.rtype,self.eidto)innocheck:returnrschema=self._cw.repo.schema[self.rtype]ifself.rtypeinON_COMMIT_ADD_RELATIONS:hook.set_operation(self._cw,'check_relation_perm_op',('add',rschema,self.eidfrom,self.eidto),_CheckRelationPermissionOp)else:rdef=rschema.rdef(self._cw.describe(self.eidfrom)[0],self._cw.describe(self.eidto)[0])rdef.check_perm(self._cw,'add',fromeid=self.eidfrom,toeid=self.eidto)classBeforeDeleteRelationSecurityHook(SecurityHook):__regid__='securitybeforedelrelation'events=('before_delete_relation',)def__call__(self):nocheck=self._cw.transaction_data.get('skip-security',())if(self.eidfrom,self.rtype,self.eidto)innocheck:returnrschema=self._cw.repo.schema[self.rtype]rdef=rschema.rdef(self._cw.describe(self.eidfrom)[0],self._cw.describe(self.eidto)[0])rdef.check_perm(self._cw,'delete',fromeid=self.eidfrom,toeid=self.eidto)