AclController.php #1

  • //
  • guest/
  • perforce_software/
  • chronicle/
  • main/
  • application/
  • user/
  • controllers/
  • AclController.php
  • View
  • Commits
  • Open Download .zip Download (16 KB)
<?php
/**
 * Manages user permissions.
 *
 * @copyright   2011 Perforce Software. All rights reserved.
 * @license     Please see LICENSE.txt in top-level folder of this distribution.
 * @version     <release>/<patch>
 */
class User_AclController extends Zend_Controller_Action
{
    public $contexts = array(
        'index'     => array('json'),
        'save'      => array('json')
    );

    /**
     * Set management layout as default layout for acl.
     */
    public function init()
    {
        $this->_helper->layout->setLayout('manage-layout');
    }

    /**
     * List all permissions.
     *
     * @publishes   p4cms.user.acl.roles
     *              Adjust the passed iterator (possibly based on values in the passed form) to
     *              filter which roles will be shown on the Manage Permissions grid.
     *              P4Cms_Model_Iterator        $roles      An iterator of P4Cms_Acl_Role objects.
     *              P4Cms_Form_PubSubForm       $form       A form containing filter options.
     *
     * @publishes   p4cms.user.acl.permissions
     *              Adjust the passed iterator (possibly based on values in the passed form) to
     *              filter which permissions will be shown on the Manage Permissions grid.
     *              P4Cms_Model_Iterator        $permissions    An iterator of P4Cms_Model objects
     *                                                          (representing each permission).
     *              P4Cms_Form_PubSubForm       $form           A form containing filter options.
     *
     * @publishes   p4cms.user.acl.grid.data.item
     *              Return the passed item after applying any modifications (add properties, change
     *              values, etc.) to influence the row values sent to the Manage Permissions grid.
     *              array                       $item       The item to potentially modify.
     *              mixed                       $model      The original object/array that was used
     *                                                      to make the item.
     *              Ui_View_Helper_DataGrid     $helper     The view helper that broadcast this
     *                                                      topic.
     *
     * @publishes   p4cms.user.acl.grid.data
     *              Adjust the passed data (add properties, modify values, etc.) to influence the
     *              row values sent to the Manage Permissions grid.
     *              Zend_Dojo_Data              $data       The data to be filtered.
     *              Ui_View_Helper_DataGrid     $helper     The view helper that broadcast this topic.
     *
     * @publishes   p4cms.user.acl.grid.render
     *              Make adjustments to the datagrid helper's options pre-render (i.e. change
     *              options) for the Manage Permissions grid.
     *              Ui_View_Helper_DataGrid     $helper     The view helper that broadcast this
     *                                                      topic.
     *
     * @publishes   p4cms.user.acl.grid.form
     *              Make arbitrary modifications to the Manage Permissions filters form.
     *              P4Cms_Form_PubSubForm       $form       The form that published this event.
     *
     * @publishes   p4cms.user.acl.grid.form.subForms
     *              Return a Form (or array of Forms) to have them added to the Manage Permissions
     *              filters form. The returned form(s) should have a 'name' set on them to allow
     *              them to be uniquely identified.
     *              P4Cms_Form_PubSubForm       $form       The form that published this event.
     *
     * @publishes   p4cms.user.acl.grid.form.preValidate
     *              Allows subscribers to adjust the Manage Permissions filters form prior to
     *              validation of the passed data. For example, modify element values based on
     *              related selections to permit proper validation.
     *              P4Cms_Form_PubSubForm       $form       The form that published this event.
     *              array                       $values     An associative array of form values.
     *
     * @publishes   p4cms.user.acl.grid.form.validate
     *              Return false to indicate the Manage Permissions filters form is invalid. Return
     *              true to indicate your custom checks were satisfied, so form validity should be
     *              unchanged.
     *              P4Cms_Form_PubSubForm       $form       The form that published this event.
     *              array                       $values     An associative array of form values.
     *
     * @publishes   p4cms.user.acl.grid.form.populate
     *              Allows subscribers to adjust the Manage Permissions filters form after it has
     *              been populated with the passed data.
     *              P4Cms_Form_PubSubForm       $form       The form that published this event.
     *              array                       $values     The values passed to the populate
     *                                                      method.
     */
    public function indexAction()
    {
        // enforce permissions
        $this->acl->check('users', 'manage-acl');

        // grab current acl.
        $acl = $this->acl->getAcl();

        // setup grid options form.
        $request        = $this->getRequest();
        $gridNamespace  = 'p4cms.user.acl.grid';
        $form           = new User_Form_AclGridOptions(
            array(
                'acl'       => $acl,
                'namespace' => $gridNamespace
            )
        );
        $form->populate($request->getParams());

        // setup view.
        $view               = $this->view;
        $view->acl          = $acl;
        $view->form         = $form;
        $view->pageSize     = $request->getParam('count', 100);
        $view->rowOffset    = $request->getParam('start', 0);
        $view->pageOffset   = round($view->rowOffset / $view->pageSize, 0) + 1;
        $view->headTitle()->set('Manage Permissions');

        // set DataGrid view helper namespace
        $helper = $view->dataGrid();
        $helper->setNamespace($gridNamespace);

        // prepare different data for different request contexts.
        if (!$this->contextSwitch->getCurrentContext()) {

            // load roles and rules for standard requests
            $roles = P4Cms_Acl_Role::fetchAll();
            $rules = $this->_getRules($acl);

            // allow third-parties to influence roles in acl
            P4Cms_PubSub::publish('p4cms.user.acl.roles', $roles, $form);

            $this->view->roles = $roles;
            $this->view->rules = $rules;

            $this->getHelper('helpUrl')->setUrl('permissions.manage.html');
        } else {

            // collect permissions for other (e.g. json) requests.
            $permissions = $this->_getPermissions($acl);

            // allow third-parties to influence permissions list
            P4Cms_PubSub::publish('p4cms.user.acl.permissions', $permissions, $form);

            // sort permissions by resource first and privilege second.
            $permissions->sortBy(
                array('resourceLabel', 'privilegeLabel'),
                array(P4Cms_Model_Iterator::SORT_NATURAL, P4Cms_Model_Iterator::SORT_NO_CASE)
            );

            $this->view->permissions = $permissions;
        }
    }

    /**
     * Save modified rules.
     */
    public function saveAction()
    {
        // enforce permissions
        $this->acl->check('users', 'manage-acl');

        // only accept post requests.
        $request = $this->getRequest();
        if (!$request->isPost()) {
            throw new P4Cms_AccessDeniedException(
                "You may only save ACL via HTTP post."
            );
        }

        // rules are expected as a multi-dimensional array
        // organized into roles that contain resources which
        // contain privileges:
        //
        //  array(
        //      <role> => array(
        //          <resource> => array(
        //              <privilege> => array('allowed' => true,  'disabled' => false),
        //              <privilege> => array('allowed' => false, 'disabled' => true)
        //          ),
        //          <resource> => ...
        //      ),
        //      <role> => ...
        //  )
        //
        $rules = $request->getPost('rules');

        // if context is json, rules must be decoded.
        if ($this->contextSwitch->getCurrentContext() == 'json') {
            $rules = Zend_Json::decode($rules);
        }

        // grab the active acl.
        $acl = $this->acl->getAcl();

        // iterate over roles, resources and privileges
        // set allow rule if privilege evaluates to true,
        // remove rule if it is false (ie. no explicit 'deny').
        foreach ((array) $rules as $role => $resources) {
            // resources must be an array.
            if (!is_array($resources)) {
                continue;
            }

            foreach ($resources as $resource => $privileges) {

                // skip invalid resources.
                if (!$acl->has($resource)) {
                    continue;
                }

                $resource = $acl->get($resource);

                // privileges must be an array.
                if (!is_array($privileges)) {
                    continue;
                }

                // set each privilege rule.
                foreach ($privileges as $privilege => $rule) {

                    // skip non-existant privileges.
                    if (!$resource->hasPrivilege($privilege)) {
                        continue;
                    }

                    $privilege = $resource->getPrivilege($privilege);

                    // skip disabled privileges.
                    if ($this->_isDisabledPrivilege($privilege, $acl->getRole($role))) {
                        continue;
                    }

                    // use a proxy for assertions to allow for assert
                    // classes that might not exist at all times.
                    $assert = $privilege->getOption('assertion');
                    if ($assert) {
                        $assert = new P4Cms_Acl_Assert_Proxy($assert);
                    }

                    // set the acl rule
                    $operation = isset($rule['allowed']) && $rule['allowed']
                        ? P4Cms_Acl::OP_ADD
                        : P4Cms_Acl::OP_REMOVE;
                    $this->acl->getAcl()->setRule(
                        $operation,
                        P4Cms_Acl::TYPE_ALLOW,
                        $role,
                        $resource,
                        $privilege->getId(),
                        $assert
                    );
                }
            }
        }

        // save the acl.
        $acl->save();

        // ACL changes can have quite an affect; clear caches
        P4Cms_Cache::clean();

        $this->redirector->gotoSimple('index');
    }

    /**
     * Reset acl resources and privileges to defaults.
     */
    public function resetAction()
    {
        // enforce permissions
        $this->acl->check('users', 'manage-acl');

        $acl = $this->acl->getAcl();

        // reset acl and re-install defaults.
        $acl->removeAll()
            ->installDefaults()
            ->save();

        // ACL changes can have quite an affect; clear caches
        P4Cms_Cache::clean();

        // notify user of reset.
        P4Cms_Notifications::add(
            'Permissions Reset',
            P4Cms_Notifications::SEVERITY_SUCCESS
        );

        $this->redirector->gotoSimple('index');
    }

    /**
     * Get all of the privilege rules organized by role and then resource.
     *
     * @param   P4Cms_Acl   $acl    the acl to read rules from.
     * @return  array       the list of all privilege rules grouped by role and then resource.
     */
    protected function _getRules(P4Cms_Acl $acl)
    {
        $rules = array();
        foreach ($acl->getRoles() as $role) {
            $rules[$role] = $this->_getRoleRules($role, $acl);
        }

        return $rules;
    }

    /**
     * Get all of the privilege rules for the given role, organized by resource.
     *
     * @param   string      $role   the role to get rules for.
     * @param   P4Cms_Acl   $acl    the acl to read rules from.
     * @return  array       list of privilege rules for the given role, grouped by resource.
     */
    protected function _getRoleRules($role, P4Cms_Acl $acl)
    {
        $rules = array();
        foreach ($acl->getResourceObjects() as $resource) {
            $rules[$resource->getId()] = array();
            foreach ($resource->getPrivileges() as $privilege) {

                // skip hidden privileges.
                if ($privilege->getOption('hidden')) {
                    continue;
                }

                // determine if role is allowed access to privilege.
                $allowed = $acl->isAllowed(
                    $role,
                    $resource->getId(),
                    $privilege->getId()
                );

                $rules[$resource->getId()][$privilege->getId()] = array(
                    'allowed'  => $allowed,
                    'disabled' => $this->_isDisabledPrivilege($privilege, $acl->getRole($role))
                );
            }
        }

        return $rules;
    }

    /**
     * Combine resources and privileges into one list of permissions.
     *
     * Each list entry contains:
     *  - type
     *  - resourceId
     *  - resourceLabel
     *  - privilegeId    (null for resources)
     *  - privilegeLabel (null for resources)
     *
     * @param   P4Cms_Acl   $acl        the acl to read permissions from.
     * @return  P4Cms_Model_Iterator    a iterator of resources and privileges.
     */
    protected function _getPermissions(P4Cms_Acl $acl)
    {
        $permissions = new P4Cms_Model_Iterator;
        foreach ($acl->getResourceObjects() as $resource) {

            // add the resource first.
            $permissions[] = new P4Cms_Model(
                array(
                    'type'              => 'resource',
                    'resourceId'        => $resource->getId(),
                    'resourceLabel'     => $resource->getLabel(),
                    'privilegeId'       => null,
                    'privilegeLabel'    => null,
                    'options'           => array()
                )
            );

            // add any associated privileges.
            foreach ($resource->getPrivileges() as $privilege) {

                // skip hidden privileges.
                if ($privilege->getOption('hidden')) {
                    continue;
                }

                $permissions[] = new P4Cms_Model(
                    array(
                        'type'              => 'privilege',
                        'resourceId'        => $resource->getId(),
                        'resourceLabel'     => $resource->getLabel(),
                        'privilegeId'       => $privilege->getId(),
                        'privilegeLabel'    => $privilege->getLabel(),
                        'options'           => $privilege->getOptions()
                    )
                );
            }

        }

        return $permissions;
    }

    /**
     * Check if the given privilege is disabled for the named role.
     * A privilege is disabled if it is locked or if it requires super
     * user access and the given role is not a super-user role.
     *
     * @param   P4Cms_Acl_Privilege     $privilege  the privilege to check.
     * @param   Zend_Acl_Role_Interface $role       the role to check for.
     */
    protected function _isDisabledPrivilege(P4Cms_Acl_Privilege $privilege, Zend_Acl_Role_Interface $role)
    {
        $needsSuper = $privilege->getOption('needsSuper');
        $locked     = $privilege->getOption('locked');
        if (is_array($locked)) {
            $locked = in_array($role->getRoleId(), $locked);
        }

        if ($locked || ($needsSuper && !P4Cms_Acl_Role::isSuper($role->getRoleId()))) {
            return true;
        }

        return false;
    }
}
# Change User Description Committed
#1 16170 perforce_software Move Chronicle files to follow new path scheme for branching.
//guest/perforce_software/chronicle/application/user/controllers/AclController.php
#1 8972 Matt Attaway Initial add of the Chronicle source code