[hooks/security] Streamline attributes default permission check. stable
authorAurelien Campeas <aurelien.campeas@logilab.fr>
Wed, 03 Jul 2013 14:16:21 +0200
branchstable
changeset 9129 6c4ae3a06619
parent 9128 d988eec2d5d3
child 9130 0f1504a9fb51
[hooks/security] Streamline attributes default permission check. The current default permission on attributes delegates the check to the entity permission update policy. Since this is already checked it can be skipped. The equality comparison will work, even with a deserialized schema, because the default update perm is:: ('managers', ERQLExpression(Any X WHERE U has_update_permission X, X eid %(x)s, U eid %(u)s)) which will always be deserialized in this order (groups first). However this is a slight semantic change: entity type level 'update' permissions can now be effectively used to encode update-time rules if the default attribute permissions are used (before this change, the 'update' rules at entity type level were fired at creation time). Closes #2930861.
hooks/security.py
--- a/hooks/security.py	Wed Jun 26 14:22:22 2013 +0200
+++ b/hooks/security.py	Wed Jul 03 14:16:21 2013 +0200
@@ -23,10 +23,13 @@
 
 from logilab.common.registry import objectify_predicate
 
+from yams import buildobjs
+
 from cubicweb import Unauthorized
 from cubicweb.server import BEFORE_ADD_RELATIONS, ON_COMMIT_ADD_RELATIONS, hook
 
 
+_DEFAULT_UPDATE_ATTRPERM = buildobjs.DEFAULT_ATTRPERMS['update']
 def check_entity_attributes(session, entity, editedattrs=None, creation=False):
     eid = entity.eid
     eschema = entity.e_schema
@@ -39,9 +42,26 @@
         if attr in dontcheck:
             continue
         rdef = eschema.rdef(attr)
-        if rdef.final: # non final relation are checked by other hooks
-            # add/delete should be equivalent (XXX: unify them into 'update' ?)
-            if creation and not rdef.permissions.get('update'):
+        if rdef.final: # non final relation are checked by standard hooks
+            # attributes only have a specific 'update' permission
+            updateperm = rdef.permissions.get('update')
+            # comparison below works because the default update perm is:
+            #
+            #  ('managers', ERQLExpression(Any X WHERE U has_update_permission X, X eid %(x)s, U eid %(u)s))
+            #
+            # is deserialized in this order (groups first), and ERQLExpression
+            # implements comparison by expression.
+            if updateperm == _DEFAULT_UPDATE_ATTRPERM:
+                # The default update permission is to delegate to the entity
+                # update permission. This is an historical artefact but it is
+                # costly (in general). Hence we take this permission object as a
+                # marker saying "no specific" update permissions for this
+                # attribute. Thus we just do nothing.
+                continue
+            if creation and updateperm == ():
+                # That actually means an immutable attribute.  We make an
+                # _exception_ to the `check attr update perms at entity create &
+                # update time` rule for this case.
                 continue
             rdef.check_perm(session, 'update', eid=eid)