# copyright 2003-2012 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"fromlogilab.common.registryimportobjectify_predicatefromcubicwebimportUnauthorizedfromcubicweb.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 dictionaryifeditedattrsisNone: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)classCheckEntityPermissionOp(hook.DataOperationMixIn,hook.LateOperation):defprecommit_event(self):session=self.sessionforeid,action,editedinself.get_data():entity=session.entity_from_eid(eid)entity.cw_check_perm(action)check_entity_attributes(session,entity,edited,creation=(action=='add'))classCheckRelationPermissionOp(hook.DataOperationMixIn,hook.LateOperation):defprecommit_event(self):session=self.sessionforaction,rschema,eidfrom,eidtoinself.get_data():rdef=rschema.rdef(session.describe(eidfrom)[0],session.describe(eidto)[0])rdef.check_perm(session,action,fromeid=eidfrom,toeid=eidto)@objectify_predicatedefwrite_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):CheckEntityPermissionOp.get_instance(self._cw).add_data((self.entity.eid,'add',self.entity.cw_edited))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# overwrittenCheckEntityPermissionOp.get_instance(self._cw).add_data((self.entity.eid,'update',self.entity.cw_edited))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:CheckRelationPermissionOp.get_instance(self._cw).add_data(('add',rschema,self.eidfrom,self.eidto))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)