Przeglądaj źródła

add ACL documentation, add ACL check inside template files

Thomas Rabaix 14 lat temu
rodzic
commit
fb1ef76553

+ 0 - 122
Command/DumpActionRolesCommand.php

@@ -1,122 +0,0 @@
-<?php
-
-/*
- * This file is part of the Sonata package.
- *
- * (c) Thomas Rabaix <thomas.rabaix@sonata-project.org>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
-
-namespace Sonata\AdminBundle\Command;
-
-use Symfony\Bundle\FrameworkBundle\Command\Command;
-use Symfony\Component\Console\Input\InputArgument;
-use Symfony\Component\Console\Input\InputOption;
-use Symfony\Component\Console\Input\InputInterface;
-use Symfony\Component\Console\Output\OutputInterface;
-use Symfony\Component\Console\Output\Output;
-
-use Symfony\Component\Routing\RouteCollection;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\Config\Resource\FileResource;
-
-class DumpActionRolesCommand extends Command
-{
-    public function configure()
-    {
-        $this->setName('sonata:admin:dump-action-roles');
-        $this->setDescription('Dumps a set of access control rules for the classes');
-        $this->addOption('format', null, InputOption::VALUE_OPTIONAL, 'define the output format', 'yaml');
-        $this->addOption('prefix', null, InputOption::VALUE_OPTIONAL, 'define the admin route prefix', '/admin');
-        $this->setHelp(<<<EOF
-Dumps a role hierachy and a set of access control rules using a different role
-for each admin actions.
-EOF
-            );
-    }
-
-    public function execute(InputInterface $input, OutputInterface $output)
-    {
-        $infos = array();
-        foreach ($this->getAdminRoutesCollection($input->getOption('prefix'))->all() as $route) {
-            $compiledRoute = $route->compile();
-
-            $regex = str_replace(array("\n", ' '), '', $compiledRoute->getRegex());
-            if ($pos = strpos($regex, '/$')) {
-                $regex = substr($regex, 0, $pos).'/?$'.substr($regex, $pos + 2);
-            }
-
-            $defaults = $route->getDefaults();
-
-            $controllerInfos = explode(':', $defaults['_controller']);
-
-            $group = strtoupper(sprintf('ROLE_%s', str_replace(array('.','|'), '_', strtoupper($defaults['_sonata_admin']))));
-            if (!isset($infos[$group])) {
-                $infos[$group] = array();
-            }
-
-            $name = strtoupper(sprintf('ROLE_%s_%s',
-                str_replace(array('.','|'), '_', strtoupper($defaults['_sonata_admin'])),
-                $controllerInfos[2]
-            ));
-
-            $infos[$group][] = array(
-                'path' => substr($regex, 1, -2),
-                'roles' => $name
-            );
-        }
-
-        $this->dumpYaml($output, $infos);
-    }
-
-    public function dumpYaml(OutputInterface $output, array $infos)
-    {
-
-        $output->writeln('security:');
-        $output->writeln('    access_control:');
-        foreach ($infos as $groups) {
-            foreach ($groups as $group) {
-                $output->writeln(sprintf('        - { path: %s, roles: [%s], methods: null }', $group['path'], $group['roles']));
-            }
-        }
-
-        $output->writeln('');
-        $output->writeln('    role_hierarchy:');
-
-        $superAdmin = array();
-        foreach ($infos as $groupName => $groups) {
-            $roles = array();
-            foreach ($groups as $group) {
-                $roles[] = $group['roles'];
-            }
-            $output->writeln(sprintf('        %s: [%s] ', $groupName, implode(', ', $roles)));
-
-            $superAdmin[] = $groupName;
-        }
-
-        $output->writeln(sprintf('        ROLE_SONATA_ADMIN_ROOT: [%s] ', implode(', ', $superAdmin)));
-    }
-
-    public function getAdminRoutesCollection($prefix)
-    {
-        $pool = $this->container->get('sonata.admin.pool');
-        $collection = new RouteCollection;
-
-        foreach ($pool->getAdminServiceIds() as $id) {
-
-            $admin = $pool->getInstance($id);
-
-            foreach ($admin->getRoutes()->getElements() as $code => $route) {
-                $collection->add($route->getDefault('_sonata_name'), $route);
-            }
-
-            $reflection = new \ReflectionObject($admin);
-            $collection->addResource(new FileResource($reflection->getFileName()));
-        }
-
-        $collection->addPrefix($prefix);
-        return $collection;
-    }
-}

+ 0 - 1
Command/ListAdminCommand.php

@@ -29,7 +29,6 @@ class ListAdminCommand extends Command
 
     public function execute(InputInterface $input, OutputInterface $output)
     {
-
         $pool = $this->container->get('sonata.admin.pool');
 
         $output->writeln("<info>Admin services:</info>");

+ 2 - 2
Resources/doc/conf.py

@@ -92,7 +92,7 @@ pygments_style = 'sphinx'
 
 # The theme to use for HTML and HTML Help pages.  See the documentation for
 # a list of builtin themes.
-html_theme = 'default'
+html_theme = 'basic'
 
 # Theme options are theme-specific and customize the look and feel of a theme
 # further.  For a list of options available for each theme, see the
@@ -100,7 +100,7 @@ html_theme = 'default'
 #html_theme_options = {}
 
 # Add any paths that contain custom themes here, relative to this directory.
-#html_theme_path = []
+html_theme_path = ['/Users/thomas/Projects/']
 
 # The name for this set of Sphinx documents.  If None, it defaults to
 # "<project> v<release> documentation".

+ 1 - 0
Resources/doc/index.rst

@@ -24,6 +24,7 @@ Reference Guide
    reference/saving_hooks
    reference/routing
    reference/dashboard
+   reference/security
 
 Doctrine ORM
 ------------

+ 1 - 0
Resources/doc/reference/installation.rst

@@ -74,3 +74,4 @@ At this point you can access to the dashboard with the url:
     the above configuration and routing will actually be placed in those
     files, with the correct format (i.e. XML or PHP).
 
+The last important step is security, please refer to the dedicated section.

+ 126 - 0
Resources/doc/reference/security.rst

@@ -0,0 +1,126 @@
+Security
+========
+
+The current ``AdminBundle`` implementation uses ACL and ROLES to handle permissions.
+
+If you want an easy way to handle users, please use :
+
+ - https://github.com/FriendsOfSymfony/UserBundle : handle users and group stored from RDMS or MongoDB
+ - https://github.com/sonata-project/UserBundle : integrate the ``FriendsOfSymfony/UserBundle`` with
+   the ``AdminBundle``
+
+The security integration is a work in progress and have some knows issues :
+ - ACL permissions are immutables
+ - Only one PermissionMap can be defined
+
+
+Configuration
+-------------
+
+
+    - The following configuration defines :
+
+        - the ``FriendsOfSymfony/UserBundle`` as a security provider
+        - the login form for authentification
+        - the access control : resources with related required roles, the important part is the admin configuration
+        - the ``acl`` option enable the ACL.
+
+.. code-block:: yaml
+
+    parameters:
+        # ... other parameters
+        security.acl.permission.map.class: Sonata\AdminBundle\Security\Acl\Permission\AdminPermissionMap
+
+    security:
+        providers:
+            fos_userbundle:
+                id: fos_user.user_manager
+
+        firewalls:
+            main:
+                pattern:      .*
+                form-login:
+                    provider:       fos_userbundle
+                    login_path:     /login
+                    use_forward:    false
+                    check_path:     /login_check
+                    failure_path:   null
+                logout:       true
+                anonymous:    true
+
+        access_control:
+            # The WDT has to be allowed to anonymous users to avoid requiring the login with the AJAX request
+            - { path: ^/wdt/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/profiler/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+
+            # AsseticBundle paths used when using the controller for assets
+            - { path: ^/js/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/css/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+
+            # URL of FOSUserBundle which need to be available to anonymous users
+            - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY } # for the case of a failed login
+            - { path: ^/user/new$, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/user/check-confirmation-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/user/confirm/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/user/confirmed$, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/user/request-reset-password$, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/user/send-resetting-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/user/check-resetting-email$, role: IS_AUTHENTICATED_ANONYMOUSLY }
+            - { path: ^/user/reset-password/, role: IS_AUTHENTICATED_ANONYMOUSLY }
+
+            # Secured part of the site
+            # This config requires being logged for the whole site and having the admin role for the admin part.
+            # Change these rules to adapt them to your needs
+            - { path: ^/admin/, role: ROLE_ADMIN }
+            - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }
+
+
+        role_hierarchy:
+            ROLE_ADMIN:       ROLE_USER
+            ROLE_SUPERADMIN: [ROLE_ADMIN, ROLE_SONATA_ADMIN, ROLE_ALLOWED_TO_SWITCH]
+
+        acl:
+            connection: default
+
+- Install the ACL tables ``php app/console init:acl``
+
+- Create a new user :
+
+.. code-block::
+
+    # php app/console fos:user:create
+    Please choose a username:root
+    Please choose an email:root@domain.com
+    Please choose a password:root
+    Created user root
+
+
+- Promote an user as super admin :
+
+.. code-block::
+
+    # php app/console fos:user:promote root
+    User "root" has been promoted as a super administrator.
+
+If you have Admin classes, you can install the related CRUD ACL rules :
+
+.. code-block::
+
+    # php app/console sonata:admin:setup-acl
+    Starting ACL AdminBundle configuration
+    > install ACL for sonata.media.admin.media
+       - add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_EDIT, ACL: ["EDIT"]
+       - add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_LIST, ACL: ["LIST"]
+       - add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_CREATE, ACL: ["CREATE"]
+       - add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_DELETE, ACL: ["DELETE"]
+       - add role: ROLE_SONATA_MEDIA_ADMIN_MEDIA_OPERATOR, ACL: ["OPERATOR"]
+    ... skipped ...
+
+If you try to access to the admin class you should see the login form, just logon with the ``root`` user.
+
+Usage
+-----
+
+Everytime you create a new ``Admin`` class, you should create start the command ``php app/console sonata:admin:setup-acl``
+so the ACL database will be updated with the latest masks and roles informations.

+ 6 - 2
Resources/views/CRUD/action.html.twig

@@ -14,8 +14,12 @@ file that was distributed with this source code.
 {% block actions %}
     <div class="sonata-actions">
         <ul>
-            <li class="sonata-action-element"><a href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a></li>
-            <li class="sonata-action-element"><a href="{{ admin.generateUrl('list') }}">{% trans from 'SonataAdminBundle' %}link_action_list{% endtrans %}</a></li>
+            {% if admin.isGranted('CREATE')%}
+                <li class="sonata-action-element"><a href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a></li>
+            {% endif %}
+            {% if admin.isGranted('LIST')%}
+                <li class="sonata-action-element"><a href="{{ admin.generateUrl('list') }}">{% trans from 'SonataAdminBundle' %}link_action_list{% endtrans %}</a></li>
+            {% endif %}
         </ul>
     </div>
 {% endblock %}

+ 6 - 2
Resources/views/CRUD/base_edit.html.twig

@@ -14,8 +14,12 @@ file that was distributed with this source code.
 {% block actions %}
     <div class="sonata-actions">
         <ul>
-            <li class="sonata-action-element"><a href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a></li>
-            <li class="sonata-action-element"><a href="{{ admin.generateUrl('list') }}">{% trans from 'SonataAdminBundle' %}link_action_list{% endtrans %}</a></li>
+            {% if admin.isGranted('CREATE')%}
+                <li class="sonata-action-element"><a href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a></li>
+            {% endif %}
+            {% if admin.isGranted('LIST')%}
+                <li class="sonata-action-element"><a href="{{ admin.generateUrl('list') }}">{% trans from 'SonataAdminBundle' %}link_action_list{% endtrans %}</a></li>
+            {% endif %}
         </ul>
     </div>
 {% endblock %}

+ 3 - 1
Resources/views/CRUD/base_list.html.twig

@@ -14,7 +14,9 @@ file that was distributed with this source code.
 {% block actions %}
     <div class="sonata-actions">
         <ul>
-            <li class="sonata-action-element"><a href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a></li>
+            {% if admin.isGranted('CREATE')%}
+                <li class="sonata-action-element"><a href="{{ admin.generateUrl('create') }}">{% trans from 'SonataAdminBundle' %}link_action_create{% endtrans %}</a></li>
+            {% endif %}
         </ul>
     </div>
 {% endblock %}

+ 1 - 1
Resources/views/CRUD/base_list_field.html.twig

@@ -10,7 +10,7 @@ file that was distributed with this source code.
 #}
 
 <td class="sonata-ba-list-field sonata-ba-list-field-{{ field_description.type }}" objectId="{{ object.id }}">
-    {% if field_description.options.identifier is defined %}
+    {% if field_description.options.identifier is defined and admin.isGranted('EDIT') %}
         <a href="{{ admin.generateUrl('edit', {'id': object.id}) }}">
             {% block field %}{{ value }}{% endblock %}
         </a>

+ 3 - 1
Resources/views/CRUD/edit_orm_many_to_many.html.twig

@@ -21,7 +21,9 @@ file that was distributed with this source code.
             <a
                 href="{{ field_description.associationadmin.generateUrl('create') }}"
                 onclick="start_field_dialog_form_add_{{ field_element.vars.id }}(event)"
-                class="sonata-ba-action">
+                class="sonata-ba-action"
+                style="{% if not field_description.associationadmin.isGranted('CREATE')%}display:none{% endif %}"
+                >
                     <img
                         src="{{ asset('bundles/sonataadmin/famfamfam/add.png') }}"
                         alt="{% trans from 'SonataAdminBundle' %}btn_add{% endtrans %}"

+ 6 - 2
Resources/views/CRUD/edit_orm_many_to_one.html.twig

@@ -45,7 +45,9 @@ file that was distributed with this source code.
                 {% if field_description.options.edit == 'list' %}
                     <a  href="{{ field_description.associationadmin.generateUrl('list') }}"
                         onclick="start_field_dialog_form_list_{{ field_element.vars.id }}(event)"
-                        class="sonata-ba-action">
+                        class="sonata-ba-action"
+                        style="{% if not field_description.associationadmin.isGranted('LIST')%}display:none{% endif %}"
+                        >
                         <img src="{{ asset('bundles/sonataadmin/famfamfam/application_view_list.png') }}"
                              alt="{% trans from 'SonataAdminBundle' %}btn_add{% endtrans %}"
                         />
@@ -54,7 +56,9 @@ file that was distributed with this source code.
 
                 <a  href="{{ field_description.associationadmin.generateUrl('create') }}"
                     onclick="start_field_dialog_form_add_{{ field_element.vars.id }}(event)"
-                    class="sonata-ba-action">
+                    class="sonata-ba-action"
+                    style="{% if not field_description.associationadmin.isGranted('CREATE')%}display:none{% endif %}"
+                    >
                         <img src="{{ asset('bundles/sonataadmin/famfamfam/add.png') }}"
                             alt="{% trans from 'SonataAdminBundle' %}btn_add{% endtrans %}"
                         />

+ 6 - 2
Resources/views/CRUD/edit_orm_one_to_many.html.twig

@@ -70,7 +70,9 @@ file that was distributed with this source code.
                 <a
                     href="{{ field_description.associationadmin.generateUrl('create') }}"
                     onclick="start_field_retrieve_{{ field_element.vars.id }}(event)"
-                    class="sonata-ba-action">
+                    class="sonata-ba-action"
+                    style="{% if not field_description.associationadmin.isGranted('CREATE')%}display:none{% endif %}"
+                    >
                     <img
                         src="{{ asset('bundles/sonataadmin/famfamfam/add.png') }}"
                         alt="{% trans from 'SonataAdminBundle' %}btn_add{% endtrans %}"
@@ -121,7 +123,9 @@ file that was distributed with this source code.
                 <a
                     href="{{ field_description.associationadmin.generateUrl('create') }}"
                     onclick="start_field_dialog_form_add_{{ field_element.vars.id }}(event)"
-                    class="sonata-ba-action">
+                    class="sonata-ba-action"
+                    style="{% if not field_description.associationadmin.isGranted('CREATE')%}display:none{% endif %}"
+                    >
                     <img
                         src="{{ asset('bundles/sonataadmin/famfamfam/add.png') }}"
                         alt="{% trans from 'SonataAdminBundle' %}btn_add{% endtrans %}"

+ 6 - 2
Resources/views/CRUD/edit_orm_one_to_one.html.twig

@@ -44,7 +44,9 @@ file that was distributed with this source code.
                 {% if field_description.options.edit == 'list' %}
                     <a  href="{{ field_description.associationadmin.generateUrl('list') }}"
                         onclick="start_field_dialog_form_list_{{ field_element.vars.id }}(event)"
-                        class="sonata-ba-action">
+                        class="sonata-ba-action"
+                        style="{% if not field_description.associationadmin.isGranted('LIST')%}display:none{% endif %}"
+                        >
                             <img
                                 src="{{ asset('bundles/sonataadmin/famfamfam/application_view_list.png') }}"
                                 alt="{% trans from 'SonataAdminBundle' %}btn_add{% endtrans %}"
@@ -54,7 +56,9 @@ file that was distributed with this source code.
 
                 <a  href="{{ field_description.associationadmin.generateUrl('create') }}"
                     onclick="start_field_dialog_form_add_{{ field_element.vars.id }}(event)"
-                    class="sonata-ba-action">
+                    class="sonata-ba-action"
+                    style="{% if not field_description.associationadmin.isGranted('CREATE')%}display:none{% endif %}"
+                    >
                         <img
                             src="{{ asset('bundles/sonataadmin/famfamfam/add.png') }}"
                             alt="{% trans from 'SonataAdminBundle' %}btn_add{% endtrans %}"

+ 5 - 3
Resources/views/CRUD/list__action_delete.html.twig

@@ -1,3 +1,5 @@
-<a href="{{ admin.generateUrl('delete', {'id': object.id}) }}" class="delete_link" title="{% trans from 'SonataAdminBundle' %}action_delete{% endtrans %}">
-    <img src="{{ asset('bundles/sonataadmin/famfamfam/delete.png') }}" alt="{% trans from 'SonataAdminBundle' %}action_delete{% endtrans %}" />
-</a>
+{% if admin.isGranted('DELETE')%}
+    <a href="{{ admin.generateUrl('delete', {'id': object.id}) }}" class="delete_link" title="{% trans from 'SonataAdminBundle' %}action_delete{% endtrans %}">
+        <img src="{{ asset('bundles/sonataadmin/famfamfam/delete.png') }}" alt="{% trans from 'SonataAdminBundle' %}action_delete{% endtrans %}" />
+    </a>
+{% endif %}

+ 5 - 3
Resources/views/CRUD/list__action_edit.html.twig

@@ -1,3 +1,5 @@
-<a href="{{ admin.generateUrl('edit', {'id': object.id}) }}" class="edit_link" title="{% trans from 'SonataAdminBundle' %}action_edit{% endtrans %}">
-    <img src="{{ asset('bundles/sonataadmin/famfamfam/page_white_edit.png') }}" alt="{% trans from 'SonataAdminBundle' %}action_edit{% endtrans %}" />
-</a>
+{% if admin.isGranted('EDIT')%}
+    <a href="{{ admin.generateUrl('edit', {'id': object.id}) }}" class="edit_link" title="{% trans from 'SonataAdminBundle' %}action_edit{% endtrans %}">
+        <img src="{{ asset('bundles/sonataadmin/famfamfam/page_white_edit.png') }}" alt="{% trans from 'SonataAdminBundle' %}action_edit{% endtrans %}" />
+    </a>
+{% endif %}

+ 5 - 1
Resources/views/CRUD/list_orm_many_to_one.html.twig

@@ -13,6 +13,10 @@ file that was distributed with this source code.
 
 {% block field%}
     {% if value %}
-        <a href="{{ field_description.associationadmin.generateUrl('edit', {'id': value.id}) }}">{{ value }}</a>
+        {% if field_description.associationadmin.isGranted('EDIT')%}
+            <a href="{{ field_description.associationadmin.generateUrl('edit', {'id': value.id}) }}">{{ value }}</a>
+        {% else %}
+            {{ value }}
+        {% endif %}
     {% endif %}
 {% endblock %}

+ 5 - 1
Resources/views/CRUD/list_orm_one_to_one.html.twig

@@ -14,6 +14,10 @@ file that was distributed with this source code.
 {% block field%}
 
     {% if value.id %}
-        <a href="{{ field_description.associationadmin.generateUrl('edit', {'id': value.id}) }}">{{ value }}</a>
+        {% if field_description.associationadmin.isGranted('EDIT')%}
+            <a href="{{ field_description.associationadmin.generateUrl('edit', {'id': value.id}) }}">{{ value }}</a>
+        {% else %}
+            {{ value }}
+        {% endif %}
     {% endif %}
 {% endblock %}

+ 93 - 0
Security/Acl/Permission/AdminPermissionMap.php

@@ -0,0 +1,93 @@
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Sonata\AdminBundle\Security\Acl\Permission;
+
+use Symfony\Component\Security\Acl\Permission\PermissionMapInterface;
+
+/**
+ * This is basic permission map complements the masks which have been defined
+ * on the standard implementation of the MaskBuilder.
+ *
+ * @author Johannes M. Schmitt <schmittjoh@gmail.com>
+ * @author Thomas Rabaix <thomas.rabaix@gmail.com>
+ */
+class AdminPermissionMap implements PermissionMapInterface
+{
+    const PERMISSION_VIEW        = 'VIEW';
+    const PERMISSION_EDIT        = 'EDIT';
+    const PERMISSION_CREATE      = 'CREATE';
+    const PERMISSION_DELETE      = 'DELETE';
+    const PERMISSION_UNDELETE    = 'UNDELETE';
+    const PERMISSION_OPERATOR    = 'OPERATOR';
+    const PERMISSION_MASTER      = 'MASTER';
+    const PERMISSION_OWNER       = 'OWNER';
+
+    const PERMISSION_LIST        = 'LIST';
+
+    private $map = array(
+        self::PERMISSION_LIST => array(
+            MaskBuilder::MASK_LIST
+        ),
+
+        self::PERMISSION_VIEW => array(
+            MaskBuilder::MASK_VIEW,
+        ),
+
+        self::PERMISSION_EDIT => array(
+            MaskBuilder::MASK_EDIT,
+        ),
+
+        self::PERMISSION_CREATE => array(
+            MaskBuilder::MASK_CREATE,
+        ),
+
+        self::PERMISSION_DELETE => array(
+            MaskBuilder::MASK_DELETE,
+        ),
+
+        self::PERMISSION_UNDELETE => array(
+            MaskBuilder::MASK_UNDELETE,
+        ),
+
+        self::PERMISSION_OPERATOR => array(
+            MaskBuilder::MASK_OPERATOR,
+        ),
+
+        self::PERMISSION_MASTER => array(
+            MaskBuilder::MASK_MASTER,
+        ),
+
+        self::PERMISSION_OWNER => array(
+            MaskBuilder::MASK_OWNER,
+        ),
+    );
+
+    /**
+     * {@inheritDoc}
+     */
+    public function getMasks($permission, $object)
+    {
+        if (!isset($this->map[$permission])) {
+            return null;
+        }
+
+        return $this->map[$permission];
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public function contains($permission)
+    {
+        return isset($this->map[$permission]);
+    }
+}