Skip to content

Dev.Module Authorization

taiwen edited this page Mar 14, 2013 · 1 revision

Contents

  • Introduction
  • Creating configuration files
  • Usage of Pi\Acl\Acl class
  • A Simple Case To Use Authorization in Module

Introduction

Authorization or Permission management is an important section of website. It mainly contains user roles definition, resources definition, and authorization distribution. In this user guide, we will introduce the following content:

  1. Module configuration creation for defining roles, resources and permission.
  2. Using Pi\Acl\Acl to add rules and check permission.

Creating configuration files

Pi use acl.php and page.php to define roles, resources and rules. Therefore users must first create the configuration files of module in the config folder.

config\acl.php

The array structure of acl.php must be like the following:

return array(
    // Definition roles
    'roles'     => array(
        // Case 1
        'roleName'  => array(
            'title'     => 'Title',
            'parents'   => array('parent'),
        ),
        // Case 2
        'admin'     => array(
            'title'     => __('Administrator'),
            'section'   => 'admin',
        ),
        // Case 3
        'staff'     => array(
            'title'     => __('Staff'),
            'section'   => 'admin',
        ),
        // Case 4
        'manager'   => array(
            'title'     => __('Manager'),
            'section'   => 'admin',
            'parents'   => array('admin', 'staff'),
        ),
        ...
    ),
    // Definition resources
    'resources' => array(
        ...
    ),
);

The above acl configuration allowed users to define roles and module resources, let's take a look at roles definition first.

  • The roles field tells application the array is used to add roles to application, in the four case
  • the roleName, admin, staff and manager are the role name. And the title field indicate the role tile.

Note: The difference between role name and role title is that: role name is used to distinguish different roles, so it must be unique; and the role title can be the same.

  • section field - this value is used to decide which section a role is belonged to. It can be admin, front, and it can be ignored if the role is in front section. Refer to Case 1.

  • parents field - this value indicates the inherited relationship between different roles. Its parents are passed by an array, if the array contains only one data, it called single inheritance, or else, called multi-inheritance. Refer to Case 2, 3, 4.

Note: In Pi, a child role will inherit the allow permission of its parent roles, which is said that if a parent allow a resource, the child will can not deny the resource in split of child choose the deny permission.

Definition resources

What is a resource, in Pi, a resources can be a page (it also known as controller/action), or a string.

The following codes is the resources part of the acl.php:

'resources'     => array(
    // Front resources
    'front'     => array(
        'category'      => array(
            'module'        => 'system',
            'title'         => __('Category'),
            'parent'        => 'parentCategory',
            'access'        => array(
                'guest'     => 0,
                'member'    => 1,
            ),
            'privileges'    => array(
                'read'      => array(
                    'title' => 'Read articles',
                ),
                'post'      => array(
                    'title' => 'Post articles',
                    'access' => array(
                        'guest'     => 0,
                    ),
                ),
                'delete'    => array(
                    'title' => 'Post articles',
                    'access' => array(
                        'guest'     => 0,
                        'member'    => 0,
                    ),
                ),
            ),
        ),
        ...
    ),
    'admin'     => array(
        ...
    ),
),

The code above will insert a system type resource into the acl_resource table.

In the array, the front and admin fields indicate the resource section. And the category field indicates the resource name.

  • module field - indicates the resource module, it can be ignored, therefore the current module name will be used.

  • title field - resource title.

  • parent field - this field indicates the current resource's parent, it will insert current resource as a child of its parent resource. The value of this field can be string or array.

    The value must be exist resource name if it is string:

    'parent' => 'categoryName',
    

    The value can contain resource information if it is an array:

    'parent' => array(
        'section'    => 'admin',
        'module'     => 'system',
    ),
    
  • access field - this field defines the rules between roles and resources, the guest, member is the role name, and the value 0 and 1 define whether the role has permission to access this resource, it will be allowed if this value is set to 1.

  • privileges field - NOT ACHIEVE YET!

Note: actually, the access field and privileges can not define at the same time in the same resource.

config/page.php

The page.php configuration file is used to add pages and page type resources of a module. It will define the page's controller and action, and its parent resource user defined in the acl.php.

return array(
    // front pages
    'front'     => array(
        ...
    ),
    'admin'     => array(
        // Case 1
        array(
            'title'       => __('All articles'),
            'module'      => 'article',
            'controller'  => 'article',
            'action'      => 'published',
            'cache_ttl'   => 60,
            'cache_level' => 'role',
            'block'       => 0,
            'permission'  => array(
                'access'        => array(
                    'manager'   => 1,
                    'staff'     => 0,
                ),
            ),
        ),
        // Case 2
        array(
            'controller'    => 'dashboard',
            'permission'    => array(
                'parent'    => 'category',
            ),
        ),
    ),

    // Exception of admin pages to skip ACL check
    'exception'  => array(
        array(
            'controller'   => 'ajax',
        ),
        array(
            'controller'   => 'upload',
            'action'       => 'upload',
        ),
    ),
);

In the codes, we introduce two cases, the first one define a page with cache and permissions, and the second one define a page and the permission of which is defined in acl.php.

The declare of the field can be known in the module configuration section of this document.

If user want to define a page resource, case 2 is recommended, in the array:

  • controller field - indicates the module controller.
  • parent field - indicates the parent resource defined in the acl.php and its value is the resource name.

Some controller or action need to skip acl check, then we can use exception field to realize it.

So far, we have defined all the resources, but we have extra thing to do to make it more perfect.

Make navigation hide the denied resource

In the navigation.php configuration, we need to add extra code to help application hide or display menu according to the permission.

config/navigation.php

return array(
    ...
    'admin'     => array(
        'article'   => array(
            ...
            'resource'  => array(
                'resource'  => 'article',
            ),
        ),
    ),
);

In the code, we add a resource array, and assign the resource name article to the resource field. Then the application will check whether current user have permission to access the resource.

Add installing configuration

The last step is to tell the application that we have a acl and page configuration to install, this will be achieve by adding fields in module.php.

config/module.php

return array(
    ...
    'acl'   => 'acl.php',
    'page'  => 'page.php',
);

Note: the acl.php must be defined before page.php, or else permission will not take effect after installs.

Usage of Pi\Acl\Acl class

Pi\Acl\Acl class is used to manage authentication, it achieve tasks such as:

  1. Loading user roles;
  2. Getting resources;
  3. Adding authentication rules;
  4. Checking whether a role has permission to access resources.

Methods provided by this class are list as follows:

RowGateway getModel(string $modelName);
Acl setSection(string $section);
string getSection();
Acl setDefault(bool $default);
bool getDefault();
Acl setModule(string $module);
string getModule();
Acl setRole(string $role);
string getRole();
bool addRule(bool|int $allowed, string $role, string $section, string $module, string|int $resource, string $privilege = null);
bool removeRule(string $role, string $section, string $module, string|int $resource, $string $privilege = null);
bool setRule(bool|int $allowed, string $role, string $section, string $module, string|int $resource, string $privilege);
bool isAllowed(string $role, string|array|object $resource, string $privilege = null);
bool checkAccess(string|array|object $resource, string $privilege = null);
array getResources(array|Where $where = null, bool $allowed = true);
array loadRoles(string $role = null);
array loadResources(string|array|Node $resource);
  • Construction

    The construction of Acl needs two parameters.

    Parameters

    $section: set the resource section, such as: front, admin.

    $default: set the default permission, when a rule is not specified.

    Examples

    $aclHandler = new \Pi\Acl\Acl; $aclHandler = new \Pi\Acl\Acl('admin'); $aclHandler = new \Pi\Acl\Acl('admin', true);

  • getModel

    Gets a acl model and set section/module if applicable. That is said, users should set section and module before calling getModel method if he/she when to set section and module.

    Parameters

    $modelName: acl model name, can be edge, inherit, privilege, resource, role and rule.

    Examples

    $model = $aclHandler->getModel('role'); $model->getAncestors('manager');

  • setSection($section) and getSection()

    Resources section operation methods, used to set section for resources and get current application section.

    Parameters

    $section: section name, can be admin and front.

    Examples

    echo $aclHandler->setSection('admin')->getSection();

    Output:

    'admin'

  • setDefault($default) and getDefault()

    Default permission operation.

    Parameters

    $default: default permission value.

    Examples

    echo $aclHandler->setDefault(true)->getDefault();

    Output:

    true

  • setModule($module) and getModule()

    Setting resource module and getting current resource module.

    Parameters

    $module: module name.

    Examples

    echo $aclHandler->setModule('demo')->getModule();

    Output:

    'demo'

  • setRole($role) and getRole()

    Role operation, the getRole method will load from current authenticated user if role is not set.

    Parameters

    $role: role name.

    Examples

    Suppose current authentication user is admin:

    // Set current role $aclHandler->setRole('manager'); echo $aclHandler->getRole(); // Not set current role echo $aclHandler->getRole();

    Output:

    'manager' 'admin'

  • addRule()

    This method is used to add a rule to database for later use, this method will add a new record into database whatever it is exists.

    Parameters

    $allowed: whether to allow role to access resources, indicate allowed if set to true.

    $role: role name.

    $section: resource section.

    $module: the name of module resources belong to.

    $resource: resource name.

    $privilege: resource privilege, can be ignored.

    Examples

    $aclHandler->setRule(true, 'manager', 'admin', 'demo', $resourceName);

  • removeRule

    Remove a exist rule from database.

    Parameters

    $role: role name.

    $section: resource section.

    $module: the name of module resources belong to.

    $resource: resource name.

    $privilege: resource privilege, can be ignored.

    Examples

    $aclHandler->removeRule('manager', 'admin', 'demo', $resourceName);

  • setRule

    This method is also used to add rule, but the difference is that it will update the record if the data is exists.

    Parameters

    $allowed: whether to allow role to access resources, indicate allowed if set to true.

    $role: role name.

    $section: resource section.

    $module: the name of module resources belong to.

    $resource: resource name.

    $privilege: resource privilege, can be ignored.

    Examples

    $aclHandler->setRule(true, 'manager', 'admin', 'demo', $resourceName);

  • isAllowed

    This method is used to check access to a resource by given role parameter. It will return true if the resource is allowed to access.

    Parameters

    $role: role name.

    $resource: this parameter can be a string indicate the resource name, or an array contains resource data. The resource type can refer to loadResources method.

    $privilege: resource privilege, can be ignored.

    Examples

    $aclHandler->isAllowed('manager', array( 'module' => 'system', 'controller' => 'config', )); $aclHandler->isAllowed('manager', 'config');

  • checkAccess

    This method is used to access a resource privilege for a given role, if the role is not set, current authentication role will be used.

    Parameters

    $resource: resource name, as same as isAllowed method.

    $privilege: privilege name.

    // use given role $aclHandler->setRole('manager')->checkAccess(array( 'name' => 'config', )); // use current role $aclHandler->checkAccess(5);

  • getResources

    This method is used to get resources to which a group of roles by given conditions.

    Parameters

    $where: the given conditions.

    $allowed: whether to choose allowed resources.

    Examples

    $aclHandler->getResources(array( 'resource' => 'system', 'module' => 'system', ), false);

  • loadRoles

    This method is used to load ancestors of a role from database.

    Parameters

    $role: role name.

    Examples

    $aclHandler->loadRoles('manager');

    Output:

    array( 0 => 'admin', 1 => 'staff', )

  • loadResources

    This method is used to load ancestors of a resource from database.

    Parameters

    $resource

    The resource parameter can be string which is the resource name or id, or an array contain resource data.

    Examples

  • Case 1: load a page resource (which describes by controller/action) by array, it will return two resource ids which is a system resource id and a page resource id, and the page resource is the child of system resource.

    Note: the module field is needed.

    $resource = array( 'module' => 'system', 'controller' => 'config', ); // Alternate $resource = array( 'type' => 'system', // this field can be ignored 'name' => 'config', ); $aclHandler->loadResources($resource);

    Output:

    array( 0 => 5, // system resource id 1 => 26, // page resource id )

  • Case 2: load only a system resource, note that do not set 'module' field.

    $resource = array( 'type' => 'system', // this field can be ignored 'name' => 'config', ); $aclHandler->loadResources($resource);

    Output:

    array( 0 => 5, // system resource id )

  • Case 3: load a resource by digit

    $aclHandler->loadResources('3'); $aclHandler->loadResources(5);

    Output:

    array( 0 => 3, )

    array( 0 => 4, )

  • Case 4: load a resource by string name

    $aclHandler->loadResources('config');

    Output:

    array( 0 => 4, )

A Simple Case To Use Authentication in Module

Now we will introduce how to create module authentication by a case, this case will achieve the following tasks:

  1. Adding a module role and its child to application;
  2. Adding a page resource (also a navigation menu) and a system resource by configuration;
  3. Allowing the created roles to access the resource;
  4. Enabling show the view menu when the user has authentication to access the resource;
  5. Adding a page resource and a system resource in module;
  6. Checking whether the role has authentication to access the resources.

Supposing our module named article.

TASK 1, TASK 2 and TASK 3

module/article/config/acl.php

return array(
    // Creating roles
    'roles'      => array(
        // Admin role inherit from manager for article module
        'contributor'   => array(
            'title'     => __('Contributor'),
            'section'   => 'admin',
            'parents'   => array('staff'),
        ),
        // Temporary editor inherit from contributor
        'temporary'     => array(
            'title'     => __('Temporary editor'),
            'section'   => 'admin',
            'parents'   => array('contributor'),
        ),
    ),

    // Creating resources
    'resources'  => array(
        'admin'          => array(
            // Creating a system resource
            'editor'     => array(
                'module'      => 'article',
                'title'       => __('Editor'),
                'access'      => array(
                    'temporary'   => 1,
                ),
            ),
            // Page resource, defined in page.php
            // This is also the navigation menu of article module
            'article'    => array(
                'module'      => 'article',
                'title'       => __('All article management'),
                'access'      => array(
                    'contributor'  => 1,
                ),
            ),
        ),
    ),
);

module/article/config/page.php

return array(
    'admin'   => array(
        // Indicating the article resource is a page resource
        array(
            'controller'   => 'article',
            'permission'   => array(
                'parent'       => 'article',
            ),
        ),
    ),

    // Exception of admin pages to skip ACL check
    'exception'  => array(
        array(
            'controller'   => 'ajax',
        ),
    ),
);

In the above case, we define two role and two resources, and the contributor role is defined as child role of system role staff, by defining in acl.php to indicate the article resource is a page resource, then the access field is used to tell the application contributor role is allow to access the article resource.

In module, we may have some ajax action, this action is always allowed to access, so we use exception field to tell the application this action do not need acl check.

TASK 4

module/article/config/navigation.php

return array(
    'item'  => array(      
        'admin'   => array(
            'article'   => array(
                'label'         => 'All articles',
                'route'         => 'admin',
                'controller'    => 'article',
                'resource'      => array(
                    'resource'  => 'article',
                ),
                'pages' => array(
                    ...
                ),
            ),
        ),
    ),   
);

In this case, we define a resource field, and assign resource name article to it to tell the application check the authentication of this resource, and decide whether to hidden the navigation menu.

Add configuration file to the module.php

module/article/config/module.php

return array(
    ...
    'acl'   => 'acl.php',
    'page'  => 'page.php',
);

TASK 5 and TASK 6

module/article/src/Controller/Admin/IndexController.php

...
use Pi\Acl\Acl;

class IndexController extends ActionController
{
    public function indexAction()
    {
        ...
        $acl = new Acl('admin');
        // Allow system role to access system resource
        $acl->setRule(true, 'contributor', 'admin', $this->getModule(), 'edit');
        // Allow system role to access page resource
        $acl->setRule(true, 'contributor', 'admin', $this->getModule(), array(
            'module'       => $this->getModule(),
            'controller'   => 'index',
            'action'       => 'perm'
        ));
        // Allow dynmic role to access system resource
        $acl->setRule(true, 'editor', 'admin', $this->getModule(), 'delete');
        ...
    }

    public function permAction()
    {
        ...
        $acl = new Acl('admin');
        $acl->setModule($this->getModule());
        // Checking page resource
        if ($acl->checkAccess(array(
            'module'       => $this->getModule(),
            'controller'   => 'index',
            'action'       => 'perm'
        ))) {
            return $this->jumpToDenied('__denied__');
        }
        // Checking system resource
        $rules['edit'] = $acl->checkAccess('edit');
        // Checking resource for dynmic role
        $rules['delete'] = $acl->->isAllowed('editor', 'delete');

        $this->view()->assign('rules', $rules);
        ...
    }
}

module/article/template/admin/index-perm.phtml

<?php if ($rule['edit']) { ?>
    <div><a href="#"><?php echo __('Edit') ?></a></div>
<?php } ?>

<?php if ($rule['delete']) { ?>
    <div><a href="#"><?php echo __('Delete') ?></a></div>
<?php } ?>