[PATCH 1 of 1 sherpa V4] [schema] Implement security for top level types

Philippe Pepiot philippe.pepiot at logilab.fr
Thu Mar 9 17:33:37 CET 2017


On 03/09/2017 10:22 AM, Sylvain Thenault wrote:
> # HG changeset patch
> # User Philippe Pepiot <philippe.pepiot at logilab.fr>


I don't remember I wrote this code :-)

LGTM, applied with modified author.

> # Date 1489050349 -3600
> #      Thu Mar 09 10:05:49 2017 +0100
> # Node ID f3e7fa997f268908dbfdc2486097f21f90fa14b2
> # Parent  bf3fa11e713283497dbc332a719e579b5205b336
> [schema] Implement security for top level types
>
> (ArchiveTransfer, ArchiveUnit, AuthorityRecord and ConceptScheme).
>
> For now, we only need to setup ConceptScheme compound security since it's not done
> yet by the skos cube, while it is in eac and seda cubes.
>
> We need to follow a change in the seda cube to get tests to green.
>
> Closes #16684441
>
> diff --git a/cubicweb_sherpa/migration/0.8.0_Any.py b/cubicweb_sherpa/migration/0.8.0_Any.py
> new file mode 100644
> --- /dev/null
> +++ b/cubicweb_sherpa/migration/0.8.0_Any.py
> @@ -0,0 +1,13 @@
> +from cubicweb_compound import utils, CompositeGraph
> +
> +graph = CompositeGraph(schema)
> +structure = graph.parent_structure('ConceptScheme')
> +
> +optionals = utils.optional_relations(schema, structure)
> +for child, relations in structure.iteritems():
> +    sync_schema_props_perms(child, syncprops=False)
> +
> +for rdef, parent_role in utils.mandatory_rdefs(schema, structure):
> +    sync_schema_props_perms((rdef.subject, rdef.rtype, rdef.object), syncprops=False)
> +
> +sync_schema_props_perms('SEDAArchiveUnit', syncprops=False)
> diff --git a/cubicweb_sherpa/schema.py b/cubicweb_sherpa/schema.py
> new file mode 100644
> --- /dev/null
> +++ b/cubicweb_sherpa/schema.py
> @@ -0,0 +1,40 @@
> +# copyright 2017 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
> +# contact http://www.logilab.fr -- mailto:contact at logilab.fr
> +#
> +# This program 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.
> +#
> +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
> +
> +from cubicweb.schema import ERQLExpression
> +
> +from cubes.skos.schema import ConceptScheme
> +
> +ConceptScheme.__permissions__ = {
> +    'read': ('managers', 'users', 'guests'),
> +    'add': ('managers',),
> +    'update': ('managers',),
> +    'delete': ('managers',),
> +}
> +
> +
> +def post_build_callback(schema):
> +    from cubicweb_compound import CompositeGraph, utils
> +    graph = CompositeGraph(schema)
> +    utils.graph_set_etypes_update_permissions(schema, graph, 'ConceptScheme')
> +    utils.graph_set_write_rdefs_permissions(schema, graph, 'ConceptScheme')
> +
> +    for action in ('update', 'delete'):
> +        schema['SEDAArchiveUnit'].set_action_permissions(
> +            action, ('managers',
> +                     ERQLExpression('U has_{action}_permission C, '
> +                                    'X container C'.format(action=action)),
> +                     ERQLExpression('NOT EXISTS(X container C), X owned_by U')))
> diff --git a/dev-requirements.txt b/dev-requirements.txt
> --- a/dev-requirements.txt
> +++ b/dev-requirements.txt
> @@ -1,1 +1,3 @@
>  pytest
> +http://hg.logilab.org/review/cubes/compound/archive/tip.tar.bz2#egg=cubicweb-compound
> +http://hg.logilab.org/review/cubes/seda/archive/7526210d26c9.tar.bz2#egg=cubicweb-seda
> diff --git a/test/test_security.py b/test/test_security.py
> new file mode 100644
> --- /dev/null
> +++ b/test/test_security.py
> @@ -0,0 +1,116 @@
> +# copyright 2017 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
> +# contact http://www.logilab.fr -- mailto:contact at logilab.fr
> +#
> +# This program 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.
> +#
> +# This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
> +"""cubicweb-sherpa security tests"""
> +
> +from contextlib import contextmanager
> +
> +from cubicweb import Unauthorized
> +from cubicweb.devtools.testlib import CubicWebTC
> +
> +
> +class SecurityTC(CubicWebTC):
> +
> +    @contextmanager
> +    def assertUnauthorized(self, cnx):
> +        with self.assertRaises(Unauthorized) as cm:
> +            yield cm
> +            cnx.commit()
> +        cnx.rollback()
> +
> +    def setUp(self):
> +        super(SecurityTC, self).setUp()
> +        with self.admin_access.cnx() as cnx:
> +            self.create_user(cnx, 'bob')
> +            self.create_user(cnx, 'alice')
> +
> +    def assertOwnershipBasedAccess(self, etype, to_test_attribute):
> +        # alice can read bob entity, but not update nor delete
> +        with self.new_access('alice').cnx() as cnx:
> +            entity = cnx.find(etype).one()
> +            with self.assertUnauthorized(cnx):
> +                entity.cw_set(**{to_test_attribute: u'abcd'})
> +                cnx.commit()
> +            with self.assertUnauthorized(cnx):
> +                entity.cw_delete()
> +                cnx.commit()
> +        # unless explicitly authorized using owned_by relation
> +        with self.new_access('bob').cnx() as cnx:
> +            entity = cnx.find(etype).one()
> +            entity.cw_set(owned_by=cnx.find('CWUser', login='alice').one())
> +            cnx.commit()
> +        with self.new_access('alice').cnx() as cnx:
> +            entity = cnx.find(etype).one()
> +            entity.cw_set(**{to_test_attribute: u'abcd'})
> +            cnx.commit()
> +            entity.cw_delete()
> +            cnx.commit()
> +
> +    def test_add_read_update_delete_record(self):
> +        with self.new_access('bob').cnx() as cnx:
> +            agent_kind = cnx.find('AgentKind', name=u'person').one()
> +            record = cnx.create_entity('AuthorityRecord', agent_kind=agent_kind)
> +            cnx.create_entity('NameEntry', parts=u'bob notice', form_variant=u'authorized',
> +                              name_entry_for=record)
> +            cnx.commit()
> +
> +        self.assertOwnershipBasedAccess('AuthorityRecord', 'isni')
> +
> +    def test_add_read_update_delete_transfer(self):
> +        with self.new_access('bob').cnx() as cnx:
> +            cnx.create_entity('SEDAArchiveTransfer', title=u'goldorak go')
> +            cnx.commit()
> +
> +        self.assertOwnershipBasedAccess('SEDAArchiveTransfer', 'title')
> +
> +    def test_add_read_update_delete_unit(self):
> +        with self.new_access('bob').cnx() as cnx:
> +            au = cnx.create_entity('SEDAArchiveUnit', user_annotation=u'title')
> +            alt = cnx.create_entity('SEDAAltArchiveUnitArchiveUnitRefId',
> +                                    reverse_seda_alt_archive_unit_archive_unit_ref_id=au)
> +            last = cnx.create_entity(
> +                'SEDASeqAltArchiveUnitArchiveUnitRefIdManagement',
> +                reverse_seda_seq_alt_archive_unit_archive_unit_ref_id_management=alt)
> +            cnx.create_entity('SEDATitle', seda_title=last)
> +            cnx.commit()
> +
> +        self.assertOwnershipBasedAccess('SEDAArchiveUnit', 'user_annotation')
> +
> +    def test_add_read_update_delete_schemes(self):
> +        with self.new_access('bob').cnx() as cnx:
> +            with self.assertUnauthorized(cnx):
> +                cnx.create_entity('ConceptScheme', title=u'goldorak')
> +
> +        with self.admin_access.cnx() as cnx:
> +            scheme = cnx.create_entity('ConceptScheme', title=u'goldorak')
> +            concept = scheme.add_concept(u'fulguropoint')
> +            cnx.commit()
> +
> +        with self.new_access('bob').cnx() as cnx:
> +            scheme = cnx.find('ConceptScheme').one()
> +            with self.assertUnauthorized(cnx):
> +                scheme.cw_set(title=u'hop')
> +            with self.assertUnauthorized(cnx):
> +                scheme.add_concept(u'fulguropoint')
> +            concept = cnx.find('Concept').one()
> +            with self.assertUnauthorized(cnx):
> +                concept.cw_delete()
> +            with self.assertUnauthorized(cnx):
> +                scheme.cw_delete()
> +
> +
> +if __name__ == '__main__':
> +    import unittest
> +    unittest.main()
>



More information about the saem-devel mailing list