server/securityhooks.py
author sylvain.thenault@logilab.fr
Fri, 03 Apr 2009 19:04:00 +0200
changeset 1228 91ae10ffb611
parent 479 ac5c9442b1fd
child 1802 d628defebc17
permissions -rw-r--r--
* refactor ms planner (renaming, reorganization) * fix a bug originaly demonstrated by test_version_depends_on * enhance crossed relation support, though there is still some bug renaming. some tests were actually wrong. Buggy tests (wether they fail or not, they are byggy) marked by XXXFIXME)

"""Security hooks: check permissions to add/delete/update entities according to
the user connected to a session

:organization: Logilab
:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"

from cubicweb import Unauthorized
from cubicweb.server.pool import LateOperation
from cubicweb.server import BEFORE_ADD_RELATIONS, ON_COMMIT_ADD_RELATIONS

def check_entity_attributes(session, entity):
    eid = entity.eid
    eschema = entity.e_schema
    # ._default_set is only there on entity creation to indicate unspecified
    # attributes which has been set to a default value defined in the schema
    defaults = getattr(entity, '_default_set', ())
    for attr in entity.keys():
        if attr in defaults:
            continue
        rschema = eschema.subject_relation(attr)
        if rschema.is_final(): # non final relation are checked by other hooks
            # add/delete should be equivalent (XXX: unify them into 'update' ?)
            rschema.check_perm(session, 'add', eid)
            
    
class CheckEntityPermissionOp(LateOperation):
    def precommit_event(self):
        #print 'CheckEntityPermissionOp', self.session.user, self.entity, self.action
        self.entity.check_perm(self.action)
        check_entity_attributes(self.session, self.entity)
        
    def commit_event(self):
        pass
            
    
class CheckRelationPermissionOp(LateOperation):
    def precommit_event(self):
        self.rschema.check_perm(self.session, self.action, self.fromeid, self.toeid)
        
    def commit_event(self):
        pass
    
def after_add_entity(session, entity):
    if not session.is_super_session:
        CheckEntityPermissionOp(session, entity=entity, action='add')

def after_update_entity(session, entity):
    if not session.is_super_session:
        try:
            # check user has permission right now, if not retry at commit time
            entity.check_perm('update')
            check_entity_attributes(session, entity)
        except Unauthorized:
            entity.clear_local_perm_cache('update')
            CheckEntityPermissionOp(session, entity=entity, action='update')
        
def before_del_entity(session, eid):
    if not session.is_super_session:
        eschema = session.repo.schema[session.describe(eid)[0]]
        eschema.check_perm(session, 'delete', eid)


def before_add_relation(session, fromeid, rtype, toeid):
    if rtype in BEFORE_ADD_RELATIONS and not session.is_super_session:
        rschema = session.repo.schema[rtype]
        rschema.check_perm(session, 'add', fromeid, toeid)
        
def after_add_relation(session, fromeid, rtype, toeid):
    if not rtype in BEFORE_ADD_RELATIONS and not session.is_super_session:
        rschema = session.repo.schema[rtype]
        if rtype in ON_COMMIT_ADD_RELATIONS:
            CheckRelationPermissionOp(session, action='add', rschema=rschema,
                                      fromeid=fromeid, toeid=toeid)
        else:
            rschema.check_perm(session, 'add', fromeid, toeid)

def before_del_relation(session, fromeid, rtype, toeid):
    if not session.is_super_session:
        session.repo.schema[rtype].check_perm(session, 'delete', fromeid, toeid)

def register_security_hooks(hm):
    """register meta-data related hooks on the hooks manager"""
    hm.register_hook(after_add_entity, 'after_add_entity', '')
    hm.register_hook(after_update_entity, 'after_update_entity', '')
    hm.register_hook(before_del_entity, 'before_delete_entity', '')
    hm.register_hook(before_add_relation, 'before_add_relation', '')
    hm.register_hook(after_add_relation, 'after_add_relation', '')
    hm.register_hook(before_del_relation, 'before_delete_relation', '')