doc/book/en/B0015-define-permissions.en.txt
author Nicolas Chauvat <nicolas.chauvat@logilab.fr>
Wed, 07 Oct 2009 10:33:24 +0200
branchstable
changeset 3581 669854258b90
parent 3556 ca16cb416294
permissions -rw-r--r--
[doc] various fixes

.. -*- coding: utf-8 -*-

The security model
------------------

The security model of `cubicWeb` is based on `Access Control List`.
The main principles are:

* users and groups of users
* a user belongs to at least one group of user
* permissions (read, update, create, delete)
* permissions are assigned to groups (and not to users)

For *CubicWeb* in particular:

* we associate rights at the enttities/relations schema level
* for each entity, we distinguish four kind of permissions: read,
  add, update and delete
* for each relation, we distinguish three king of permissions: read,
  add and delete (we can not modify a relation)
* the basic groups are: Administrators, Users and Guests
* by default, users belongs to the group Users
* there is a virtual group called `Owners users` to which we
  can associate only deletion and update permissions
* we can not add users to the `Owners users` group, they are
  implicetely added to it according to the context of the objects
  they own
* the permissions of this group are only be checked on update/deletion
  actions if all the other groups the user belongs does not provide
  those permissions


Permissions definition
``````````````````````

Setting permissions is done with the attribute `permissions` of entities and
relation types. It defines a dictionary where the keys are the access types
(action), and the values are the authorized groups or expressions.

For an entity type, the possible actions are `read`, `add`, `update` and
`delete`.

For a relation type, the possible actions are `read`, `add`, and `delete`.

For each access type, a tuple indicates the name of the authorized groups and/or
one or multiple RQL expressions to satisfy to grant access. The access is
provided once the user is in the listed groups or one of the RQL condition is
satisfied.

The standard groups are :

* `guests`

* `users`

* `managers`

* `owners` : virtual group corresponding to the entity's owner.
  This can only be used for the actions `update` and `delete` of an entity
  type.

It is also possible to use specific groups if they are defined in the precreate
of the cube (``migration/precreate.py``).


Use of RQL expression for writing rights
````````````````````````````````````````

It is possible to define RQL expression to provide update permission
(`add`, `delete` and `update`) on relation and entity types.

RQL expression for entity type permission :

* you have to use the class `ERQLExpression`

* the used expression corresponds to the WHERE statement of an RQL query

* in this expression, the variables X and U are pre-defined references
  respectively on the current entity (on which the action is verified) and
  on the user who send the request

* it is possible to use, in this expression, a special relation
  "has_<ACTION>_permission" where the subject is the user and the
  object is a any variable, meaning that the user needs to have
  permission to execute the action <ACTION> on the entities related
  to this variable

For RQL expressions on a relation type, the principles are the same except
for the following :

* you have to use the class `RRQLExpression` in the case of a non-final relation

* in the expression, the variables S, O and U are pre-defined references
  to respectively the subject and the object of the current relation (on
  which the action is being verified) and the user who executed the query

* we can also define rights on attributes of an entity (non-final
  relation), knowing that :

  - to define RQL expression, we have to use the class
    `ERQLExpression` in which X represents the entity the attribute
    belongs to

  - the permissions `add` and `delete` are equivalent. Only `add`/`read`
    are actually taken in consideration.

In addition to that the entity type `EPermission` from the standard library
allows to build very complex and dynamic security architecture. The schema of
this entity type is as follow : ::

    class CWPermission(EntityType):
	"""entity type that may be used to construct some advanced security configuration
	"""
        permissions = META_ETYPE_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 = {
            'read':   ('managers', 'users', 'guests'),
            'add':    ('managers',),
            'delete': ('managers',),
            }

    class require_group(RelationType):
        """used to grant a permission to a group"""
        permissions = {
            'read':   ('managers', 'users', 'guests'),
            'add':    ('managers',),
            'delete': ('managers',),
            }


Example of configuration ::


    ...

    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)

Use of RQL expression for reading rights
````````````````````````````````````````

The principles are the same but with the following restrictions :

* we can not use `RRQLExpression` on relation types for reading

* special relations "has_<ACTION>_permission" can not be used


Note on the use of RQL expression for `add` permission
``````````````````````````````````````````````````````
Potentially, the use of an RQL expression to add an entity or a relation
can cause problems for the user interface, because if the expression uses
the entity or the relation to create, then we are not able to verify the
permissions before we actually add the entity (please note that this is
not a problem for the RQL server at all, because the permissions checks are
done after the creation). In such case, the permission check methods
(check_perm, has_perm) can indicate that the user is not allowed to create
this entity but can obtain the permission.

To compensate this problem, it is usually necessary, for such case,
to use an action that reflects the schema permissions but which enables
to check properly the permissions so that it would show up if necessary.