--- 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"
--- 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:
--- 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
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- 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
--- 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):
--- 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)
--- 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))
--- 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})
--- 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
--- 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 <http://www.gnu.org/licenses/>.
-"""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))
--- 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'),),
}
-
-
--- 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
--- 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()
--- 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'),
])
--- 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'
--- 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
--- 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
--- 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)
--- 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')
--- 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'<div id="progress">%s</div>' % 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'<h1><span class="etype">%s</span> <a href="%s">%s</a></h1>'
@@ -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('<h3>%s</h3>' % _('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('<h3>%s</h3>' % 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'[<a href="%s" title="%s">-</a>] ' % (
+ xml_escape(delurl), _('delete this permission'))
+ else:
+ dellinktempl = None
+ w(u'<table class="schemaInfo">')
+ w(u'<tr><th>%s</th><th>%s</th></tr>' % (_("permission"),
+ _('granted to groups')))
+ for cwperm in entity.require_permission:
+ w(u'<tr>')
+ if dellinktempl:
+ w(u'<td>%s%s</td>' % (dellinktempl % cwperm.eid,
+ cwperm.view('oneline')))
+ else:
+ w(u'<td>%s</td>' % cwperm.view('oneline'))
+ w(u'<td>%s</td>' % self._cw.view('csv', cwperm.related('require_group'), 'null'))
+ w(u'</tr>\n')
+ w(u'</table>')
+ 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'<p>%s</p>' % 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"""
--- 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')
--- 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 ########################################################