[PATCH 1 of 4 saem v4] [security] Fix permissions of the use_profile relation

Sylvain Thenault sylvain.thenault at logilab.fr
Wed May 10 10:13:26 CEST 2017


# HG changeset patch
# User Sylvain Thénault <sylvain.thenault at logilab.fr>
# Date 1492789034 -7200
#      Fri Apr 21 17:37:14 2017 +0200
# Node ID 9b50a1f441345fdfbb58be45a045d91b9543dde6
# Parent  4d82b62b0e74e4d39153fd0ba395926139ef032e
# Available At http://hg.logilab.org/review/cubes/saem_ref
#              hg pull http://hg.logilab.org/review/cubes/saem_ref -r 9b50a1f44134
[security] Fix permissions of  the use_profile relation

They should be editable depending on the permission of the associated
organization unit.

Security is set using a decorator that will be reused in later changeset.

Also, add use_profile relation to the structure that control copy, as it should
not be copied.

diff --git a/cubicweb_saem_ref/migration/0.15.3_Any.py b/cubicweb_saem_ref/migration/0.15.3_Any.py
--- a/cubicweb_saem_ref/migration/0.15.3_Any.py
+++ b/cubicweb_saem_ref/migration/0.15.3_Any.py
@@ -1,4 +1,5 @@
-for etype in ('ChronologicalRelation', 'HierarchicalRelation', 'AssociationRelation'):
+for etype in ('ChronologicalRelation', 'HierarchicalRelation', 'AssociationRelation',
+              'use_profile'):
     sync_schema_props_perms(etype)
 
 sync_schema_props_perms('ConceptScheme')
diff --git a/cubicweb_saem_ref/schema.py b/cubicweb_saem_ref/schema.py
--- a/cubicweb_saem_ref/schema.py
+++ b/cubicweb_saem_ref/schema.py
@@ -48,11 +48,11 @@ def publication_permissions(cls, groups=
                        'X in_state ST, ST name "draft"'.format(groups)),
     )
     return cls
 
 
-def authority_permissions(cls):
+def authority_permissions_etype(cls):
     """Set __permissions__ of `cls` entity type class to ensure user can
     create/update/delete provided its authority is the same as the entity's
     authority.
     """
     cls.__permissions__ = {
@@ -62,10 +62,23 @@ def authority_permissions(cls):
         'delete': ('managers', ERQLExpression('U authority A, X authority A')),
     }
     return cls
 
 
+def authority_permissions_rdef(cls):
+    """Set __permissions__ of `cls` relation definition class to ensure user can
+    create/delete provided its authority is the same as the relation subject's
+    authority.
+    """
+    cls.__permissions__ = {
+        'read': ('managers', 'users', 'guests'),
+        'add': ('managers', RRQLExpression('U authority A, S authority A')),
+        'delete': ('managers', RRQLExpression('U authority A, S authority A')),
+    }
+    return cls
+
+
 def groups_permissions(cls):
     """Set __permissions__ of `cls` entity type class preventing modification
     when user is not in managers or users group.
     """
     cls.__permissions__ = cls.__permissions__.copy()
@@ -130,17 +143,17 @@ class Organization(EntityType):
         'delete': ('managers', ),
     }
     name = String(required=True, fulltextindexed=True, unique=True)
 
 
- at authority_permissions
+ at authority_permissions_etype
 class OrganizationUnit(WorkflowableEntityType):
     __unique_together__ = [('name', 'authority')]
     name = String(required=True, fulltextindexed=True)
 
 
- at authority_permissions
+ at authority_permissions_etype
 class Agent(WorkflowableEntityType):
     __unique_together__ = [('name', 'authority')]
     name = String(required=True, fulltextindexed=True)
 
 
@@ -445,10 +458,11 @@ publication_permissions(SEDAArchiveTrans
 
 
 simplified_profile.default = True
 
 
+ at authority_permissions_rdef
 class use_profile(RelationDefinition):
     subject = 'OrganizationUnit'
     object = 'SEDAArchiveTransfer'
     cardinality = '**'
     constraints = [RQLConstraint('S archival_role R, R name "deposit"'),
diff --git a/cubicweb_saem_ref/site_cubicweb.py b/cubicweb_saem_ref/site_cubicweb.py
--- a/cubicweb_saem_ref/site_cubicweb.py
+++ b/cubicweb_saem_ref/site_cubicweb.py
@@ -156,13 +156,14 @@ def get_store(cnx):
 # causes problem because it belongs to several graphs with different compound
 # implementation (using "container" relation or not)
 seda.GRAPH_SKIP_ETYPES.add('Activity')
 # also, the new_version_of relation should not be considered as part of the
 # graph (as for e.g. container or clone_of)
-seda.GRAPH_SKIP_RTYPES.add('new_version_of')
-Entity.cw_skip_copy_for.append(('new_version_of', 'subject'))
-Entity.cw_skip_copy_for.append(('new_version_of', 'object'))
+for rtype in ('new_version_of', 'use_profile'):
+    seda.GRAPH_SKIP_RTYPES.add(rtype)
+    Entity.cw_skip_copy_for.append((rtype, 'subject'))
+    Entity.cw_skip_copy_for.append((rtype, 'object'))
 
 
 ####################################################################################################
 # temporary monkey-patches #########################################################################
 ####################################################################################################
diff --git a/test/test_security.py b/test/test_security.py
--- a/test/test_security.py
+++ b/test/test_security.py
@@ -157,34 +157,43 @@ class NonManagerUserTC(CubicWebTC):
     def test_create_update_organizationunit_in_own_organization(self):
         with self.admin_access.cnx() as cnx:
             other_authority = testutils.authority_with_naa(cnx, name=u'other authority')
             other_unit = testutils.organization_unit(
                 cnx, u'arch', archival_roles=[u'archival'], authority=other_authority)
+            profile = testutils.setup_profile(cnx)
             cnx.commit()
+            profile.cw_adapt_to('IWorkflowable').fire_transition('publish')
+            cnx.commit()
+
             other_authority_eid = other_authority.eid
             other_unit_eid = other_unit.eid
+            profile_eid = profile.eid
 
         with self.new_access(self.login).cnx() as cnx:
+            roles = (u'archival', u'deposit')
             unit = testutils.organization_unit(
-                cnx, u'arch', archival_roles=[u'archival'], authority=self.authority_eid)
+                cnx, u'arch', archival_roles=roles, authority=self.authority_eid)
             cnx.commit()
-            unit.cw_set(name=u'archi')
+            unit.cw_set(name=u'archi',
+                        use_profile=profile_eid)
             cnx.commit()
             unit.cw_delete()
             cnx.commit()
             arecord = testutils.authority_record(cnx, name=u'arch', kind=u'authority')
             unit.cw_set(authority_record=arecord)
             cnx.commit()
 
             with self.assertUnauthorized(cnx):
                 testutils.organization_unit(
-                    cnx, u'other arch', archival_roles=[u'archival'], authority=other_authority_eid)
+                    cnx, u'other arch', archival_roles=roles, authority=other_authority_eid)
 
             other_unit = cnx.entity_from_eid(other_unit_eid)
             with self.assertUnauthorized(cnx):
                 other_unit.cw_set(name=u'archi')
             with self.assertUnauthorized(cnx):
+                other_unit.cw_set(use_profile=profile_eid)
+            with self.assertUnauthorized(cnx):
                 other_unit.cw_delete()
             with self.assertUnauthorized(cnx):
                 other_arecord = testutils.authority_record(cnx, name=u'other arch',
                                                            kind=u'authority')
                 other_unit.cw_set(authority_record=other_arecord)
diff --git a/test/unittest_entities_container.py b/test/unittest_entities_container.py
--- a/test/unittest_entities_container.py
+++ b/test/unittest_entities_container.py
@@ -82,25 +82,26 @@ class TreeTC(CubicWebTC):
             transfer = testutils.setup_profile(cnx)
             unit, unit_alt, unit_alt_seq = testutils.create_archive_unit(transfer)
             testutils.create_data_object(transfer)
             cnx.commit()
             transfer.cw_adapt_to('IWorkflowable').fire_transition('publish')
-            agent = testutils.organization_unit(cnx, u'bob', archival_roles=['deposit'],
-                                                use_profile=transfer)
+            testutils.organization_unit(cnx, u'bob', archival_roles=['deposit'],
+                                        use_profile=transfer)
             cnx.commit()
 
             clone = testutils.setup_profile(cnx, title=u'Clone', new_version_of=transfer)
             cnx.commit()
 
             # ark and cwuri should not have been copied
             self.assertNotEqual(clone.ark, transfer.ark)
             self.assertNotEqual(clone.cwuri, transfer.cwuri)
 
-            # Everything else should have been copied (but other parts of the test live in the seda
+            # Everything else beside some explicitly skipped relations should
+            # have been copied (but other parts of the test live in the seda
             # cube)
-            self.assertEqual(clone.reverse_use_profile[0].eid,
-                             agent.eid)
+            self.assertEqual(clone.ark_naa[0].eid, transfer.ark_naa[0].eid)
+            self.assertEqual(len(clone.reverse_use_profile), 0)
 
 
 if __name__ == '__main__':
     import unittest
     unittest.main()


More information about the saem-devel mailing list