.. _topics-permissions:

========================
Working with permissions
========================

.. highlightlang:: html+django


Overview
========

Permission in merengue are strongly based on wonderful `django-permissions`_ application.

Main change (but important) is not using ``GenericForeignKey`` for granting
permissions to objects. We prefer use a ``ForeignKey`` to ``BaseContent`` for
performance reasons. Besides, in merengue all managed content are ``BaseContent``
instances.

.. _django-permissions: http://pypi.python.org/pypi/django-permissions

What is an permission?
----------------------

Permissions are granted to roles (and only to roles) in order to allow something to users or groups which have these roles.

What is an role?
----------------

Roles are used to grant permissions. Typical roles are Reader, Manager or Editor.

What is a local role?
---------------------

Local roles are roles which are assigned to users and groups for a specific content objects.

Users
-----

 * Users are actors which may need a permission to do something within the system.
 * Users can be member of several groups.
 * User can have several roles, directly or via a membership to a group (these are considered as global).
 * User can have local roles, directly or via a membership to a group. That is roles for a specific object.
 * Users have all roles of their groups - global and local ones.
 * Users have all permissions of their roles - global and local ones.

Groups
------

 * Groups combines users together.
 * Groups can have roles (these are considered as global).
 * Groups can have local roles, that is roles for a specific object.
 * Groups has all permissions of their roles - global and local ones.
 * Users of a Group have the group’s roles and permissions.


Builtin permissions on merengue
-------------------------------

A builtin permission will appears in the manage permission view of every content.

The builtin permissions in merengue are:

    View (``view``)
        A non published content will be visible for anonymous if this permission
        is activated for this content.

    Edit (``edit``)
        If a user is member of a role with this permission or for a content
        which has activated that permission for a user role, user will can edit
        the content.

    Delete (``delete``)
        If user has this permission in a content, user will be able to delete it.

    Can set as draft (``can_draft``)
        If user has this permission in a content, user will can to change the content status to draft.

    Can set as pending (``can_pending``)
        If user has this permission in a content, user will can to change the content status to pending.

    Can set as published (``can_published``)
        If user has this permission in a content, user will can to change the content status to published.

Global permissions on merengue
-------------------------------

    Manage section (``manage_section``)
      If user has this permission, user will be able to edit, delete and add sections.

    Manage link portal (``manage_link``)
      If user has this permission, user will be able to edit, delete and add portal links.

    Manage menu portal (``manage_menu``)
      If user has this permission, user will be able to edit, delete and add portal menus.

    Manage multimedia (``manage_multimedia``)
      If user has this permission, user will be able to edit, delete and add any multimedia content.

    Manage plugin content (``manage_plugin_content``)
      If user has this permission, user will be able to manage all contents from installed plugins.

    Manage category (``manage_category``)
      If user has this permission, user will be able to edit, delete and add categories.

    Manage block (``manage_block``)
      If user has this permission, user will be able to move or add blocks in public views.

    Manage site (``manage_site``)
      If user has this permission, user is like the webmaster, and he will be able to manage plugins, themes, registered items and save/restore configuration.

    Manage user (``manage_user``)
      If user has this permission, user will be able to manage groups, users, roles and permissions.


Admin interface
===============

You can access to the permissions configuration clicking groups, users, roles or permissions in the main admin page in "User Management" portlet.

Creating a role and assigning user to role
------------------------------------------

To create a role, first you should click in "Roles" in the main admin page or go to the url http://localhost:8000/admin/perms/role/, then you click in "Add role" button. You fill the form with a name and mark the permissions for this role.

.. image:: _images/add_role.png


Finally, you must go a users admin page clicking "Users" in the main admin page or go to the url http://localhost:8000/admin/auth/user/, choose a user, add previously created role and save the form.

.. image:: _images/assign_role_to_user.png

Assigning role to group
-----------------------

This step is same as above, except that the user is assigned in the group admin view. You must go a groups admin page clicking "Groups" in the main admin page or go to the url http://localhost:8000/admin/auth/group/, choose a group, add previously created role and save the form.

Granting a permission to all contents
-------------------------------------

To guarantee a permission to all contents, you must assign a builin permission to a role. For example, if you want give edit permission for all contents to "editor" role, you must be assign the "edit" permission in role admin page.

.. image:: _images/assign_edit_permission.png



Granting a permission to a content
----------------------------------

To guarantee a permission to a content, you have to go content admin page and click in "Permissions" button. Note this button is only shown if content type is basecontent.

The content permission admin page show a table with permissions and roles availables, you can assign permission to current content for a given role.

.. image:: _images/assign_permission_content.png


Local roles
-----------

To create a local role for a content, you have to go content admin page and click in "Permissions" button.
With the form shown in the image, you can assign a local role a user or a group for current content. For example, you want that a user or a group has "editor" role only for "welcome" page. You just have to fill the user or group and role fields and click in "create" button. The user and group fields will be autocompleted.


.. image:: _images/add_local_role.png

When you've created local roles, you can modify assigning new roles.

.. image:: _images/edit_local_role.png

Manage all permissions
----------------------

There is a admin view to edit all builtin and global permissions for each role. To access to this view, you have to click on "Permissions" in the main admin page or go to the url http://localhost:8000/admin/perms/objectpermission/

.. image:: _images/global_permissions.png


Using the python API
====================

Creating a custom permission
----------------------------

.. code-block:: python

    >>> from merengue.perms.utils import register_permission
    >>> register_permission('Vote', 'vote')
    <Permission: Vote (vote)>
    >>> from merengue.perms.utils import register_permission
    >>> register_permission('Vote', 'vote')

You can create a custom permission available only for some models (inherited
from ``BaseContent``):

.. code-block:: python

    >>> from merengue.section.models import Document
    >>> from plugins.news.models import NewsItem
    >>> register_permission('My permission', 'myperm', 
                            for_models=[Document, NewsItem])
    <Permission: My permission (myperm)>

Creating a role and assigning user to role
------------------------------------------

.. code-block:: python

    >>> staff_user = User.objects.create(username='roque')
    >>> plain_user = User.objects.create(username='other')
    >>> from merengue.perms.utils import register_role
    >>> editor = register_role('Editor')
    >>> editor.add_principal(staff_user) # add user to role 'Editor'

Granting a permission to a content
----------------------------------

.. code-block:: python

    >>> from merengue.base.models import BaseContent
    >>> content = BaseContent.objects.create(slug='mycontent', name_en='My content')
    >>> from merengue.perms.utils import grant_permission
    >>> grant_permission(editor, 'vote', content)

Granting a permission to all contents
-------------------------------------

.. code-block:: python

    >>> grant_permission(editor, 'vote')

Checking permissions
--------------------

.. code-block:: python

    >>> from merengue.perms.utils import has_permission
    >>> has_permission(content, staff_user, 'vote')
    True

Removing a permission to a content
----------------------------------

.. code-block:: python

    >>> from merengue.perms.utils import remove_permission
    >>> remove_permission(editor, 'vote', content)

Removing a permission to all contents
-------------------------------------

.. code-block:: python

    >>> remove_permission(editor, 'vote')


Local roles
-----------

You can assign a role to an user or a group, globally (for all contents) or locally (to one content).

.. code-block:: python

    >>> othercontent = BaseContent.objects.create(slug='othercontent', name_en='Other content')
    >>> grant_permission(editor, 'vote', othercontent) # like "content"
    >>> from merengue.perms.utils import add_local_role
    >>> add_local_role(othercontent, plain_user, editor)
    # plain_user will be editor only in othercontent
    >>> has_permission(content, plain_user, 'vote')
    False
    >>> has_permission(othercontent, plain_user, 'vote')
    True # because is local editor for that content
    >>> has_permission(othercontent, staff_user, 'vote')
    True # because is a global editor (for all contents)


Permissions in plugins
======================

You can create permissions for your plugins defining the function get_perms in the config.py file. These permissions can be globals or related to models and content types.

To define specific permissions, for example, to edit news:

.. code-block:: python

    from merengue.pluggable import Plugin
    from plugins.news.models import NewsItem

    class PluginConfig(Plugin):

        def get_perms(self):
            return (
                ('edit_newsitem', _('Edit news items'), [NewsItem, ]),)


To define global permissions to vote any such content:

.. code-block:: python

    from merengue.pluggable import Plugin

    class PluginConfig(Plugin):

        def get_perms(self):
            return (
                ('vote', _('Vote content')),)

