.. _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.

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


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.
   
    Manage review tasks (``manage_review``)
      If user has this permission, user will be able to view, add, assign and mark as done the review tasks.
 
   Moderate forum (``moderate_forum``)
      If user has this permission, user will be able to create forum, threads, create comments, replay, publish, censure, delete and edit.

   Submit redirects (``submit_redirects``)
      If user has this permission, user will be able to submit redirects in forums.

   Create news thread (``create_new_thread``)
      If user has this permission, user will be able to create news thread in forums.

   View all stats  (``view_all_stats``)
      If user has this permission, user will be able to see all stats of elements create in the portal.

   View my stats  (``view_my_stats``)
      If user has this permission, user will be able to see the stats of elements create by the same.

   Manage redirects  (``manage_redirects``)
      If user has this permission, user will be able to manage redirects.

   Cache invalidation  (``cache_invalidation``)
      If user has this permission, user will be able to invalidate the cache site.



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 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 see the permissions in 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. The best way to change the permission in an object is changing the permissions for the content state, in the workflow system (see :ref:`permissions and workflows <topics-permissions-workflows>`), because if the content state changes, the permissions you define in this form are cleared and reset to the permissions defined for the new state.

.. 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

Permission inheritance
----------------------

Permissions in Merengue can be inherited if the ``ACQUIRE_SECTION_ROLES`` setting is defined as ``True``. If so, every user who is accessing to an object will acquire the roles of the content' section. So, if you set the ``Reviewer`` local role to an user in a section, he will be also ``Reviewer`` in all the section contents (news items, documents, events, etc.). Of course he will acquire also all the permissions of the ``Reviewer`` role.

.. _topics-permissions-workflows:

Permissions and workflows
-------------------------

Usually, the permissions in the content are defined by its workflow. So, a content in ``draft`` status with the ``basic workflow`` workflow will have the permissions defined in the ``draft`` status inside the workflow admin view.

This is an important fact because you have to think about workflow and statuses and not thinking about the content itself.

So if for example you want to change the content permission to be visible only for the ``Reviewer`` role, the best task to do is create a new ``visible by reviewer`` status, define the permission for this status and create the transitions to make sure you can pass your content to this transition. So, doing that you will gain that if you have the same need in another content, you only have to put the content in this status.


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')),)

