# HG changeset patch # User Sylvain Thénault # Date 1315921206 -7200 # Node ID 40a49f4350a5fc254c7e44938bc043164a8f7d62 # Parent e95cfd5eca610ae80988fa927f641b8fb79db234 backout 7780:a1d5365fefc1 diff -r e95cfd5eca61 -r 40a49f4350a5 __pkginfo__.py --- a/__pkginfo__.py Tue Sep 13 14:54:00 2011 +0200 +++ b/__pkginfo__.py Tue Sep 13 15:40:06 2011 +0200 @@ -22,7 +22,7 @@ modname = distname = "cubicweb" -numversion = (3, 14, 0) +numversion = (3, 13, 5) version = '.'.join(str(num) for num in numversion) description = "a repository of entities / relations for knowledge management" diff -r e95cfd5eca61 -r 40a49f4350a5 devtools/testlib.py --- a/devtools/testlib.py Tue Sep 13 14:54:00 2011 +0200 +++ b/devtools/testlib.py Tue Sep 13 15:40:06 2011 +0200 @@ -387,6 +387,31 @@ req.cnx.commit() return user + @iclassmethod # XXX turn into a class method + def grant_permission(self, session, entity, group, pname=None, plabel=None): + """insert a permission on an entity. Will have to commit the main + connection to be considered + """ + if not isinstance(session, Session): + warn('[3.12] grant_permission arguments are now (session, entity, group, pname[, plabel])', + DeprecationWarning, stacklevel=2) + plabel = pname + pname = group + group = entity + entity = session + assert not isinstance(self, type) + session = self.session + pname = unicode(pname) + plabel = plabel and unicode(plabel) or unicode(group) + e = getattr(entity, 'eid', entity) + with security_enabled(session, False, False): + peid = session.execute( + 'INSERT CWPermission X: X name %(pname)s, X label %(plabel)s,' + 'X require_group G, E require_permission X ' + 'WHERE G name %(group)s, E eid %(e)s', + locals())[0][0] + return peid + def login(self, login, **kwargs): """return a connection for the given login/password""" if login == self.admlogin: diff -r e95cfd5eca61 -r 40a49f4350a5 doc/book/en/devrepo/datamodel/baseschema.rst --- a/doc/book/en/devrepo/datamodel/baseschema.rst Tue Sep 13 14:54:00 2011 +0200 +++ b/doc/book/en/devrepo/datamodel/baseschema.rst Tue Sep 13 15:40:06 2011 +0200 @@ -19,6 +19,7 @@ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * _`CWUser`, system users * _`CWGroup`, users groups +* _`CWPermission`, used to configure the security of the instance Entity types used to manage workflows ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ diff -r e95cfd5eca61 -r 40a49f4350a5 doc/book/en/devrepo/datamodel/definition.rst --- a/doc/book/en/devrepo/datamodel/definition.rst Tue Sep 13 14:54:00 2011 +0200 +++ b/doc/book/en/devrepo/datamodel/definition.rst Tue Sep 13 15:40:06 2011 +0200 @@ -646,7 +646,68 @@ RelationType declaration which offers some advantages in the context of reusable cubes. - +Definition of permissions +~~~~~~~~~~~~~~~~~~~~~~~~~~ +The entity type `CWPermission` from the standard library +allows to build very complex and dynamic security architectures. The schema of +this entity type is as follow: + +.. sourcecode:: python + + class CWPermission(EntityType): + """entity type that may be used to construct some advanced security configuration + """ + name = String(required=True, indexed=True, internationalizable=True, maxsize=100) + require_group = SubjectRelation('CWGroup', cardinality='+*', + description=_('groups to which the permission is granted')) + require_state = SubjectRelation('State', + description=_("entity's state in which the permission is applicable")) + # can be used on any entity + require_permission = ObjectRelation('**', cardinality='*1', composite='subject', + description=_("link a permission to the entity. This " + "permission should be used in the security " + "definition of the entity's type to be useful.")) + + +Example of configuration: + +.. sourcecode:: python + + class Version(EntityType): + """a version is defining the content of a particular project's release""" + + __permissions__ = {'read': ('managers', 'users', 'guests',), + 'update': ('managers', 'logilab', 'owners',), + 'delete': ('managers', ), + 'add': ('managers', 'logilab', + ERQLExpression('X version_of PROJ, U in_group G,' + 'PROJ require_permission P, P name "add_version",' + 'P require_group G'),)} + + + class version_of(RelationType): + """link a version to its project. A version is necessarily linked to one and only one project. + """ + __permissions__ = {'read': ('managers', 'users', 'guests',), + 'delete': ('managers', ), + 'add': ('managers', 'logilab', + RRQLExpression('O require_permission P, P name "add_version",' + 'U in_group G, P require_group G'),) + } + inlined = True + + +This configuration indicates that an entity `CWPermission` named +"add_version" can be associated to a project and provides rights to create +new versions on this project to specific groups. It is important to notice that: + +* in such case, we have to protect both the entity type "Version" and the relation + associating a version to a project ("version_of") + +* because of the genericity of the entity type `CWPermission`, we have to execute + a unification with the groups and/or the states if necessary in the expression + ("U in_group G, P require_group G" in the above example) + Handling schema changes diff -r e95cfd5eca61 -r 40a49f4350a5 entities/authobjs.py --- a/entities/authobjs.py Tue Sep 13 14:54:00 2011 +0200 +++ b/entities/authobjs.py Tue Sep 13 15:40:06 2011 +0200 @@ -29,6 +29,22 @@ fetch_attrs, fetch_order = fetch_config(['name']) fetch_unrelated_order = fetch_order + def grant_permission(self, entity, pname, plabel=None): + """grant local `pname` permission on `entity` to this group using + :class:`CWPermission`. + + If a similar permission already exists, add the group to it, else create + a new one. + """ + if not self._cw.execute( + 'SET X require_group G WHERE E eid %(e)s, G eid %(g)s, ' + 'E require_permission X, X name %(name)s, X label %(label)s', + {'e': entity.eid, 'g': self.eid, + 'name': pname, 'label': plabel}): + self._cw.create_entity('CWPermission', name=pname, label=plabel, + require_group=self, + reverse_require_permission=entity) + class CWUser(AnyEntity): __regid__ = 'CWUser' @@ -123,6 +139,18 @@ return False owns = cached(owns, keyarg=1) + def has_permission(self, pname, contexteid=None): + rql = 'Any P WHERE P is CWPermission, U eid %(u)s, U in_group G, '\ + 'P name %(pname)s, P require_group G' + kwargs = {'pname': pname, 'u': self.eid} + if contexteid is not None: + rql += ', X require_permission P, X eid %(x)s' + kwargs['x'] = contexteid + try: + return self._cw.execute(rql, kwargs) + except Unauthorized: + return False + # presentation utilities ################################################## def name(self): diff -r e95cfd5eca61 -r 40a49f4350a5 entities/schemaobjs.py --- a/entities/schemaobjs.py Tue Sep 13 14:54:00 2011 +0200 +++ b/entities/schemaobjs.py Tue Sep 13 15:40:06 2011 +0200 @@ -176,3 +176,13 @@ def check_expression(self, *args, **kwargs): return self._rqlexpr().check(*args, **kwargs) + + +class CWPermission(AnyEntity): + __regid__ = 'CWPermission' + fetch_attrs, fetch_order = fetch_config(['name', 'label']) + + def dc_title(self): + if self.label: + return '%s (%s)' % (self._cw._(self.name), self.label) + return self._cw._(self.name) diff -r e95cfd5eca61 -r 40a49f4350a5 misc/migration/bootstrapmigration_repository.py --- a/misc/migration/bootstrapmigration_repository.py Tue Sep 13 14:54:00 2011 +0200 +++ b/misc/migration/bootstrapmigration_repository.py Tue Sep 13 15:40:06 2011 +0200 @@ -41,15 +41,6 @@ 'FROM cw_CWSource, cw_source_relation ' 'WHERE entities.eid=cw_source_relation.eid_from AND cw_source_relation.eid_to=cw_CWSource.cw_eid') -if applcubicwebversion <= (3, 14, 0) and cubicwebversion >= (3, 14, 0): - if 'require_permission' in schema and not 'localperms'in repo.config.cubes(): - from cubicweb import ExecutionError - try: - add_cube('localperms', update_database=False) - except ImportError: - raise ExecutionError('In cubicweb 3.14, CWPermission and related stuff ' - 'has been moved to cube localperms. Install it first.') - if applcubicwebversion == (3, 6, 0) and cubicwebversion >= (3, 6, 0): CSTRMAP = dict(rql('Any T, X WHERE X is CWConstraintType, X name T', ask_confirm=False)) diff -r e95cfd5eca61 -r 40a49f4350a5 misc/migration/postcreate.py --- a/misc/migration/postcreate.py Tue Sep 13 14:54:00 2011 +0200 +++ b/misc/migration/postcreate.py Tue Sep 13 15:40:06 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -69,3 +69,10 @@ if value != default: rql('INSERT CWProperty X: X pkey %(k)s, X value %(v)s', {'k': key, 'v': value}) + +# add PERM_USE_TEMPLATE_FORMAT permission +from cubicweb.schema import PERM_USE_TEMPLATE_FORMAT +usetmplperm = create_entity('CWPermission', name=PERM_USE_TEMPLATE_FORMAT, + label=_('use template languages')) +rql('SET X require_group G WHERE G name "managers", X eid %(x)s', + {'x': usetmplperm.eid}) diff -r e95cfd5eca61 -r 40a49f4350a5 schema.py --- a/schema.py Tue Sep 13 14:54:00 2011 +0200 +++ b/schema.py Tue Sep 13 15:40:06 2011 +0200 @@ -59,11 +59,12 @@ 'from_state', 'to_state', 'condition', 'subworkflow', 'subworkflow_state', 'subworkflow_exit', )) -SYSTEM_RTYPES = set(('in_group', 'require_group', +SYSTEM_RTYPES = set(('in_group', 'require_group', 'require_permission', # cwproperty 'for_user', )) | WORKFLOW_RTYPES NO_I18NCONTEXT = META_RTYPES | WORKFLOW_RTYPES +NO_I18NCONTEXT.add('require_permission') SKIP_COMPOSITE_RELS = [('cw_source', 'subject')] @@ -84,7 +85,7 @@ 'WorkflowTransition', 'BaseTransition', 'SubWorkflowExitPoint')) -INTERNAL_TYPES = set(('CWProperty', 'CWCache', 'ExternalUri', +INTERNAL_TYPES = set(('CWProperty', 'CWPermission', 'CWCache', 'ExternalUri', 'CWSource', 'CWSourceHostConfig', 'CWSourceSchemaConfig')) @@ -1173,7 +1174,7 @@ # _() is just there to add messages to the catalog, don't care about actual # translation -MAY_USE_TEMPLATE_FORMAT = set(('managers',)) +PERM_USE_TEMPLATE_FORMAT = _('use_template_format') NEED_PERM_FORMATS = [_('text/cubicweb-page-template')] @monkeypatch(FormatConstraint) @@ -1188,9 +1189,9 @@ # cw is a server session hasperm = not cw.write_security or \ not cw.is_hook_category_activated('integrity') or \ - cw.user.matching_groups(MAY_USE_TEMPLATE_FORMAT) + cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT) else: - hasperm = cw.user.matching_groups(MAY_USE_TEMPLATE_FORMAT) + hasperm = cw.user.has_permission(PERM_USE_TEMPLATE_FORMAT) if hasperm: return self.regular_formats + tuple(NEED_PERM_FORMATS) return self.regular_formats diff -r e95cfd5eca61 -r 40a49f4350a5 schemas/__init__.py --- a/schemas/__init__.py Tue Sep 13 14:54:00 2011 +0200 +++ b/schemas/__init__.py Tue Sep 13 15:40:06 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -15,10 +15,12 @@ # # You should have received a copy of the GNU Lesser General Public License along # with CubicWeb. If not, see . -"""some constants and classes to define schema permissions""" +"""some utilities to define schema permissions +""" __docformat__ = "restructuredtext en" +from rql.utils import quote from cubicweb.schema import RO_REL_PERMS, RO_ATTR_PERMS, \ PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, \ ERQLExpression, RRQLExpression @@ -33,19 +35,59 @@ # execute, readable by anyone HOOKS_RTYPE_PERMS = RO_REL_PERMS # XXX deprecates +def _perm(names): + if isinstance(names, (list, tuple)): + if len(names) == 1: + names = quote(names[0]) + else: + names = 'IN (%s)' % (','.join(quote(name) for name in names)) + else: + names = quote(names) + #return u' require_permission P, P name %s, U in_group G, P require_group G' % names + return u' require_permission P, P name %s, U has_group_permission P' % names -from logilab.common.modutils import LazyObject -from logilab.common.deprecation import deprecated -class MyLazyObject(LazyObject): + +def xperm(*names): + return 'X' + _perm(names) + +def xexpr(*names): + return ERQLExpression(xperm(*names)) + +def xrexpr(relation, *names): + return ERQLExpression('X %s Y, Y %s' % (relation, _perm(names))) + +def xorexpr(relation, etype, *names): + return ERQLExpression('Y %s X, X is %s, Y %s' % (relation, etype, _perm(names))) + + +def sexpr(*names): + return RRQLExpression('S' + _perm(names), 'S') - def _getobj(self): - try: - return super(MyLazyObject, self)._getobj() - except ImportError: - raise ImportError('In cubicweb 3.14, function %s has been moved to ' - 'cube localperms. Install it first.' % self.obj) +def restricted_sexpr(restriction, *names): + rql = '%s, %s' % (restriction, 'S' + _perm(names)) + return RRQLExpression(rql, 'S') + +def restricted_oexpr(restriction, *names): + rql = '%s, %s' % (restriction, 'O' + _perm(names)) + return RRQLExpression(rql, 'O') + +def oexpr(*names): + return RRQLExpression('O' + _perm(names), 'O') + -for name in ('xperm', 'xexpr', 'xrexpr', 'xorexpr', 'sexpr', 'restricted_sexpr', - 'restricted_oexpr', 'oexpr', 'relxperm', 'relxexpr', '_perm'): - msg = '[3.14] import %s from cubes.localperms' % name - globals()[name] = deprecated(msg)(MyLazyObject('cubes.localperms', name)) +# def supdate_perm(): +# return RRQLExpression('U has_update_permission S', 'S') + +# def oupdate_perm(): +# return RRQLExpression('U has_update_permission O', 'O') + +def relxperm(rel, role, *names): + assert role in ('subject', 'object') + if role == 'subject': + zxrel = ', X %s Z' % rel + else: + zxrel = ', Z %s X' % rel + return 'Z' + _perm(names) + zxrel + +def relxexpr(rel, role, *names): + return ERQLExpression(relxperm(rel, role, *names)) diff -r e95cfd5eca61 -r 40a49f4350a5 schemas/base.py --- a/schemas/base.py Tue Sep 13 14:54:00 2011 +0200 +++ b/schemas/base.py Tue Sep 13 15:40:06 2011 +0200 @@ -180,6 +180,31 @@ cardinality = '?*' +class CWPermission(EntityType): + """entity type that may be used to construct some advanced security configuration + """ + __permissions__ = PUB_SYSTEM_ENTITY_PERMS + + name = String(required=True, indexed=True, internationalizable=True, maxsize=100, + description=_('name or identifier of the permission')) + label = String(required=True, internationalizable=True, maxsize=100, + description=_('distinct label to distinguate between other ' + 'permission entity of the same name')) + require_group = SubjectRelation('CWGroup', + description=_('groups to which the permission is granted')) + +# explicitly add X require_permission CWPermission for each entity that should have +# configurable security +class require_permission(RelationType): + """link a permission to the entity. This permission should be used in the + security definition of the entity's type to be useful. + """ + __permissions__ = PUB_SYSTEM_REL_PERMS + +class require_group(RelationType): + """used to grant a permission to a group""" + __permissions__ = PUB_SYSTEM_REL_PERMS + class ExternalUri(EntityType): """a URI representing an object in external data store""" @@ -357,5 +382,3 @@ 'add': ('managers', RRQLExpression('U has_update_permission S'),), 'delete': ('managers', RRQLExpression('U has_update_permission S'),), } - - diff -r e95cfd5eca61 -r 40a49f4350a5 schemas/workflow.py --- a/schemas/workflow.py Tue Sep 13 14:54:00 2011 +0200 +++ b/schemas/workflow.py Tue Sep 13 15:40:06 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -21,15 +21,14 @@ __docformat__ = "restructuredtext en" _ = unicode -from yams.buildobjs import (EntityType, RelationType, RelationDefinition, - SubjectRelation, +from yams.buildobjs import (EntityType, RelationType, SubjectRelation, RichString, String, Int) from cubicweb.schema import RQLConstraint, RQLUniqueConstraint -from cubicweb.schemas import (PUB_SYSTEM_ENTITY_PERMS, PUB_SYSTEM_REL_PERMS, - RO_REL_PERMS) +from cubicweb.schemas import (META_ETYPE_PERMS, META_RTYPE_PERMS, + HOOKS_RTYPE_PERMS) class Workflow(EntityType): - __permissions__ = PUB_SYSTEM_ENTITY_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, maxsize=256) @@ -48,7 +47,7 @@ class default_workflow(RelationType): """default workflow for an entity type""" - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS subject = 'CWEType' object = 'Workflow' @@ -61,7 +60,7 @@ """used to associate simple states to an entity type and/or to define workflows """ - __permissions__ = PUB_SYSTEM_ENTITY_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, maxsize=256, @@ -84,7 +83,7 @@ class BaseTransition(EntityType): """abstract base class for transitions""" - __permissions__ = PUB_SYSTEM_ENTITY_PERMS + __permissions__ = META_ETYPE_PERMS name = String(required=True, indexed=True, internationalizable=True, maxsize=256, @@ -92,34 +91,22 @@ _('workflow already have a transition of that name'))]) type = String(vocabulary=(_('normal'), _('auto')), default='normal') description = RichString(description=_('semantic description of this transition')) + condition = SubjectRelation('RQLExpression', cardinality='*?', composite='subject', + description=_('a RQL expression which should return some results, ' + 'else the transition won\'t be available. ' + 'This query may use X and U variables ' + 'that will respectivly represents ' + 'the current entity and the current user')) + require_group = SubjectRelation('CWGroup', cardinality='**', + description=_('group in which a user should be to be ' + 'allowed to pass this transition')) transition_of = SubjectRelation('Workflow', cardinality='1*', composite='object', description=_('workflow to which this transition belongs'), constraints=[RQLUniqueConstraint('S name N, Y transition_of O, Y name N', 'Y', _('workflow already have a transition of that name'))]) -class require_group(RelationDefinition): - """group in which a user should be to be allowed to pass this transition""" - __permissions__ = PUB_SYSTEM_REL_PERMS - subject = 'BaseTransition' - object = 'CWGroup' - - -class condition(RelationDefinition): - """a RQL expression which should return some results, else the transition - won't be available. - - This query may use X and U variables that will respectivly represents the - current entity and the current user. - """ - __permissions__ = PUB_SYSTEM_REL_PERMS - subject = 'BaseTransition' - object = 'RQLExpression' - cardinality = '*?' - composite = 'subject' - - class Transition(BaseTransition): """use to define a transition from one or multiple states to a destination states in workflow's definitions. Transition without destination state will @@ -190,11 +177,11 @@ # get actor and date time using owned_by and creation_date class from_state(RelationType): - __permissions__ = RO_REL_PERMS.copy() + __permissions__ = HOOKS_RTYPE_PERMS.copy() inlined = True class to_state(RelationType): - __permissions__ = RO_REL_PERMS.copy() + __permissions__ = HOOKS_RTYPE_PERMS.copy() inlined = True class by_transition(RelationType): @@ -209,52 +196,60 @@ class workflow_of(RelationType): """link a workflow to one or more entity type""" - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS class state_of(RelationType): """link a state to one or more workflow""" - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class transition_of(RelationType): """link a transition to one or more workflow""" - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class destination_state(RelationType): """destination state of a transition""" - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class allowed_transition(RelationType): """allowed transitions from this state""" - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS class initial_state(RelationType): """indicate which state should be used by default when an entity using states is created """ - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class subworkflow(RelationType): - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True class exit_point(RelationType): - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS class subworkflow_state(RelationType): - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS inlined = True +class condition(RelationType): + __permissions__ = META_RTYPE_PERMS + +# already defined in base.py +# class require_group(RelationType): +# __permissions__ = META_RTYPE_PERMS + + # "abstract" relations, set by WorkflowableEntityType ########################## class custom_workflow(RelationType): """allow to set a specific workflow for an entity""" - __permissions__ = PUB_SYSTEM_REL_PERMS + __permissions__ = META_RTYPE_PERMS cardinality = '?*' constraints = [RQLConstraint('S is ET, O workflow_of ET', @@ -280,7 +275,7 @@ class in_state(RelationType): """indicate the current state of an entity""" - __permissions__ = RO_REL_PERMS + __permissions__ = HOOKS_RTYPE_PERMS # not inlined intentionnaly since when using ldap sources, user'state # has to be stored outside the CWUser table diff -r e95cfd5eca61 -r 40a49f4350a5 server/migractions.py --- a/server/migractions.py Tue Sep 13 14:54:00 2011 +0200 +++ b/server/migractions.py Tue Sep 13 15:40:06 2011 +0200 @@ -117,15 +117,7 @@ # which is called on regular start repo.hm.call_hooks('server_maintenance', repo=repo) if not schema and not getattr(config, 'quick_start', False): - insert_lperms = self.repo.get_versions()['cubicweb'] < (3, 14, 0) and 'localperms' in config.available_cubes() - if insert_lperms: - cubes = config._cubes - config._cubes += ('localperms',) - try: - schema = config.load_schema(expand_cubes=True) - finally: - if insert_lperms: - config._cubes = cubes + schema = config.load_schema(expand_cubes=True) self.fs_schema = schema self._synchronized = set() diff -r e95cfd5eca61 -r 40a49f4350a5 server/repository.py --- a/server/repository.py Tue Sep 13 14:54:00 2011 +0200 +++ b/server/repository.py Tue Sep 13 15:40:06 2011 +0200 @@ -60,7 +60,8 @@ security_enabled from cubicweb.server.ssplanner import EditedEntity -NO_CACHE_RELATIONS = set( [('owned_by', 'object'), +NO_CACHE_RELATIONS = set( [('require_permission', 'object'), + ('owned_by', 'object'), ('created_by', 'object'), ('cw_source', 'object'), ]) diff -r e95cfd5eca61 -r 40a49f4350a5 server/session.py --- a/server/session.py Tue Sep 13 14:54:00 2011 +0200 +++ b/server/session.py Tue Sep 13 15:40:06 2011 +0200 @@ -1319,6 +1319,9 @@ def owns(self, eid): return True + def has_permission(self, pname, contexteid=None): + return True + def property_value(self, key): if key == 'ui.language': return 'en' diff -r e95cfd5eca61 -r 40a49f4350a5 server/test/data/bootstrap_cubes --- a/server/test/data/bootstrap_cubes Tue Sep 13 14:54:00 2011 +0200 +++ b/server/test/data/bootstrap_cubes Tue Sep 13 15:40:06 2011 +0200 @@ -1,1 +1,1 @@ -card,comment,folder,tag,basket,email,file,localperms +card,comment,folder,tag,basket,email,file diff -r e95cfd5eca61 -r 40a49f4350a5 test/data/bootstrap_cubes --- a/test/data/bootstrap_cubes Tue Sep 13 14:54:00 2011 +0200 +++ b/test/data/bootstrap_cubes Tue Sep 13 15:40:06 2011 +0200 @@ -1,1 +1,1 @@ -card, file, tag, localperms +card, file, tag diff -r e95cfd5eca61 -r 40a49f4350a5 web/views/actions.py --- a/web/views/actions.py Tue Sep 13 14:54:00 2011 +0200 +++ b/web/views/actions.py Tue Sep 13 15:40:06 2011 +0200 @@ -182,6 +182,15 @@ category = 'moreactions' order = 15 + @classmethod + def __registered__(cls, reg): + if 'require_permission' in reg.schema: + cls.__select__ = (one_line_rset() & non_final_entity() & + (match_user_groups('managers') + | relation_possible('require_permission', 'subject', 'CWPermission', + action='add'))) + return super(ManagePermissionsAction, cls).__registered__(reg) + def url(self): return self.cw_rset.get_entity(self.cw_row or 0, self.cw_col or 0).absolute_url(vid='security') @@ -427,6 +436,7 @@ ## default actions ui configuration ########################################### addmenu = uicfg.actionbox_appearsin_addmenu +addmenu.tag_subject_of(('*', 'require_permission', '*'), False) addmenu.tag_object_of(('*', 'relation_type', 'CWRType'), True) addmenu.tag_object_of(('*', 'from_entity', 'CWEType'), False) addmenu.tag_object_of(('*', 'to_entity', 'CWEType'), False) diff -r e95cfd5eca61 -r 40a49f4350a5 web/views/autoform.py --- a/web/views/autoform.py Tue Sep 13 14:54:00 2011 +0200 +++ b/web/views/autoform.py Tue Sep 13 15:40:06 2011 +0200 @@ -920,6 +920,7 @@ 'owned_by', 'created_by', 'cw_source'): _AFS.tag_subject_of(('*', rtype, '*'), 'main', 'metadata') +_AFS.tag_subject_of(('*', 'require_permission', '*'), 'main', 'hidden') _AFS.tag_subject_of(('*', 'by_transition', '*'), 'main', 'attributes') _AFS.tag_subject_of(('*', 'by_transition', '*'), 'muledit', 'attributes') _AFS.tag_object_of(('*', 'by_transition', '*'), 'main', 'hidden') @@ -928,6 +929,8 @@ _AFS.tag_subject_of(('*', 'wf_info_for', '*'), 'main', 'attributes') _AFS.tag_subject_of(('*', 'wf_info_for', '*'), 'muledit', 'attributes') _AFS.tag_object_of(('*', 'wf_info_for', '*'), 'main', 'hidden') +_AFS.tag_subject_of(('CWPermission', 'require_group', '*'), 'main', 'attributes') +_AFS.tag_subject_of(('CWPermission', 'require_group', '*'), 'muledit', 'attributes') _AFS.tag_attribute(('CWEType', 'final'), 'main', 'hidden') _AFS.tag_attribute(('CWRType', 'final'), 'main', 'hidden') _AFS.tag_attribute(('CWUser', 'firstname'), 'main', 'attributes') diff -r e95cfd5eca61 -r 40a49f4350a5 web/views/management.py --- a/web/views/management.py Tue Sep 13 14:54:00 2011 +0200 +++ b/web/views/management.py Tue Sep 13 15:40:06 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -45,9 +45,10 @@ self.w(u'
%s
' % self._cw._('validating...')) super(SecurityManagementView, self).call() - def entity_call(self, entity): + def cell_call(self, row, col): self._cw.add_js('cubicweb.edition.js') self._cw.add_css('cubicweb.acl.css') + entity = self.cw_rset.get_entity(row, col) w = self.w _ = self._cw._ w(u'

%s %s

' @@ -64,6 +65,13 @@ self.owned_by_edit_form(entity) else: self.owned_by_information(entity) + # cwpermissions + if 'require_permission' in entity.e_schema.subject_relations(): + w('

%s

' % _('permissions for this entity')) + reqpermschema = self._cw.vreg.schema.rschema('require_permission') + self.require_permission_information(entity, reqpermschema) + if reqpermschema.has_perm(self._cw, 'add', fromeid=entity.eid): + self.require_permission_edit_form(entity) def owned_by_edit_form(self, entity): self.w('

%s

' % self._cw._('ownership')) @@ -89,6 +97,65 @@ # else we don't know if this is because entity has no owner or becayse # user as no access to owner users entities + def require_permission_information(self, entity, reqpermschema): + if entity.require_permission: + w = self.w + _ = self._cw._ + if reqpermschema.has_perm(self._cw, 'delete', fromeid=entity.eid): + delurl = self._cw.build_url('edit', __redirectvid='security', + __redirectpath=entity.rest_path()) + delurl = delurl.replace('%', '%%') + # don't give __delete value to build_url else it will be urlquoted + # and this will replace %s by %25s + delurl += '&__delete=%s:require_permission:%%s' % entity.eid + dellinktempl = u'[-] ' % ( + xml_escape(delurl), _('delete this permission')) + else: + dellinktempl = None + w(u'') + w(u'' % (_("permission"), + _('granted to groups'))) + for cwperm in entity.require_permission: + w(u'') + if dellinktempl: + w(u'' % (dellinktempl % cwperm.eid, + cwperm.view('oneline'))) + else: + w(u'' % cwperm.view('oneline')) + w(u'' % self._cw.view('csv', cwperm.related('require_group'), 'null')) + w(u'\n') + w(u'
%s%s
%s%s%s%s
') + else: + self.w(self._cw._('no associated permissions')) + + def require_permission_edit_form(self, entity): + newperm = self._cw.vreg['etypes'].etype_class('CWPermission')(self._cw) + newperm.eid = self._cw.varmaker.next() + self.w(u'

%s

' % self._cw._('add a new permission')) + form = self._cw.vreg['forms'].select('base', self._cw, entity=newperm, + form_buttons=[wdgs.SubmitButton()], + domid='reqperm%s' % entity.eid, + __redirectvid='security', + __redirectpath=entity.rest_path()) + form.add_hidden('require_permission', entity.eid, role='object', + eidparam=True) + permnames = getattr(entity, '__permissions__', None) + cwpermschema = newperm.e_schema + if permnames is not None: + field = guess_field(cwpermschema, self._cw.vreg.schema.rschema('name'), + widget=wdgs.Select({'size': 1}), + choices=permnames) + else: + field = guess_field(cwpermschema, self._cw.vreg.schema.rschema('name')) + form.append_field(field) + field = guess_field(cwpermschema, self._cw.vreg.schema.rschema('label')) + form.append_field(field) + field = guess_field(cwpermschema, self._cw.vreg.schema.rschema('require_group')) + form.append_field(field) + renderer = self._cw.vreg['formrenderers'].select( + 'htable', self._cw, rset=None, display_progress_div=False) + form.render(w=self.w, renderer=renderer) + class ErrorView(AnyRsetView): """default view when no result has been found""" diff -r e95cfd5eca61 -r 40a49f4350a5 web/views/primary.py --- a/web/views/primary.py Tue Sep 13 14:54:00 2011 +0200 +++ b/web/views/primary.py Tue Sep 13 15:40:06 2011 +0200 @@ -1,4 +1,4 @@ -# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. +# copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved. # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr # # This file is part of CubicWeb. @@ -396,3 +396,5 @@ for rtype in META_RTYPES: _pvs.tag_subject_of(('*', rtype, '*'), 'hidden') _pvs.tag_object_of(('*', rtype, '*'), 'hidden') +_pvs.tag_subject_of(('*', 'require_permission', '*'), 'hidden') +_pvs.tag_object_of(('*', 'require_permission', '*'), 'hidden') diff -r e95cfd5eca61 -r 40a49f4350a5 web/views/schema.py --- a/web/views/schema.py Tue Sep 13 14:54:00 2011 +0200 +++ b/web/views/schema.py Tue Sep 13 15:40:06 2011 +0200 @@ -676,6 +676,14 @@ def parent_entity(self): return self.entity.expression_of +class CWPermissionIBreadCrumbsAdapter(ibreadcrumbs.IBreadCrumbsAdapter): + __select__ = is_instance('CWPermission') + def parent_entity(self): + # XXX useless with permission propagation + permissionof = getattr(self.entity, 'reverse_require_permission', ()) + if len(permissionof) == 1: + return permissionof[0] + # misc: facets, actions ########################################################