Perforce Chronicle 2012.2/486814
API Documentation

User_IndexController Class Reference

Manages user operations (e.g. More...

List of all members.

Public Member Functions

 addAction ()
 Add a new user.
 deleteAction ()
 Delete user entry.
 editAction ()
 Edit an existing user entry.
 indexAction ()
 List all users.
 init ()
 Use management layout for all actions.
 loginAction ()
 Handle user logins.
 logoutAction ()
 Log the user out (clear identity).

Public Attributes

 $contexts
const LOGIN_FAILED_MAX_DELAY = 2000000
const LOGIN_FAILED_MIN_DELAY = 1000000

Protected Member Functions

 _authenticate ($login, $password)
 Authenticate the user.
 _getLoginForm ()
 Get the login form.
 _isSystemUser ($userId)
 Check if the given user is the system user.

Detailed Description

Manages user operations (e.g.

login/logout).

Copyright:
2011-2012 Perforce Software. All rights reserved
License:
Please see LICENSE.txt in top-level folder of this distribution.
Version:
2012.2/486814

Member Function Documentation

User_IndexController::_authenticate ( login,
password 
) [protected]

Authenticate the user.

Return true if success, otherwise false.

Parameters:
string$loginuser's id
string$passworduser's password
Returns:
Zend_Auth_Result The result of the authentication attempt.
    {
        // construct user instance to authenticate against.
        $user = new P4Cms_User;
        $user->setId($login)
             ->setPassword($password);

        // authenticate
        return Zend_Auth::getInstance()->authenticate($user);
    }
User_IndexController::_getLoginForm ( ) [protected]

Get the login form.

Returns:
User_Form_Login the login form.
    {
        // grab current acl.
        $acl = $this->acl->getAcl();

        $form = new User_Form_Login(
            array(
                'action' => $this->view->url(
                    array(
                        'module'        => 'user',
                        'controller'    => 'index',
                        'action'        => 'login'
                    )
                ),
                'acl'   => $acl
            )
        );

        // for context switched requests, prefix form ids with context
        // to ensure ids are unique if they appear twice on the page.
        $context = $this->contextSwitch->getCurrentContext();
        if ($context) {
            $form->setIdPrefix($context . "-");
        }

        return $form;
    }
User_IndexController::_isSystemUser ( userId) [protected]

Check if the given user is the system user.

Parameters:
string$userIdthe user id to compare against the system user id.
Returns:
boolean true if the given user is the system user; otherwise false.
    {
        return $userId == $this->getInvokeArg('bootstrap')->getResource('perforce')->getUser();
    }
User_IndexController::addAction ( )

Add a new user.

    {
        // enforce permissions
        $this->acl->check('users', 'add');

        $request    = $this->getRequest();
        $activeUser = P4Cms_User::fetchActive();
        $form       = new User_Form_Add;

        // deny if adding user is disabled
        if (!$activeUser->isAdministrator() && !P4_User::isAutoUserCreationEnabled()) {
            throw new P4Cms_AccessDeniedException(
                "You don't have permission to add users."
            );
        }

        // set up view
        $view               = $this->view;
        $view->form         = $form;
        $view->headTitle()->set('Add User');

        // prepare default roles
        $defaultRoles = P4Cms_Acl_Role::exists(P4Cms_Acl_Role::ROLE_MEMBER)
            ? array(P4Cms_Acl_Role::ROLE_MEMBER)
            : array();

        // set default roles if no post
        if (!$request->isPost()) {
            $request->setParam('roles', $defaultRoles);
        }

        // populate form from request
        $form->populate($request->getParams());

        // if posted, validate form and save user.
        if ($request->isPost()) {
            // if form is invalid, set response code and exit
            if (!$form->isValid($request->getParams())) {
                $this->getResponse()->setHttpResponseCode(400);
                $view->errors = $form->getMessages();
                return;
            }

            // create the user entry.
            $user = new P4Cms_User;
            $user->setValues($form->getValues())
                 ->save();

            // set roles
            //  - if active user is not administrator, use site storage adapter
            //  - if user has permission, take roles from request, otherwise use defaults
            $adapter = $activeUser->isAdministrator()
                ? $activeUser->getPersonalAdapter()
                : P4Cms_Site::fetchActive()->getStorageAdapter();
            $roles   = $form->getElement('roles') && $this->acl->isAllowed('users', 'manage-roles')
                ? $form->getValue('roles')
                : $defaultRoles;
            P4Cms_Acl_Role::setUserRoles($user, $roles, $adapter);

            // if active user is anonymous, log in as newly created user
            if (P4Cms_User::fetchActive()->isAnonymous()) {
                $result = $this->_authenticate($user->getId(), $form->getValue('password'));
                if ($result->isValid()) {
                    P4Cms_Notifications::add(
                        "You have been logged in as '{$user->getId()}'",
                        P4Cms_Notifications::SEVERITY_SUCCESS
                    );
                }
            }

            // set notification message
            $view->message = "User '{$user->getId()}' has been successfuly added.";

            // for traditional requests, add notification message and redirect
            if (!$this->contextSwitch->getCurrentContext()) {
                P4Cms_Notifications::add(
                    $view->message,
                    P4Cms_Notifications::SEVERITY_SUCCESS
                );
                $this->redirector->gotoUrl($request->getBaseUrl());
            }
        }
    }
User_IndexController::deleteAction ( )

Delete user entry.

User account can be removed only via post.

    {
        // deny if not accessed via post
        $request = $this->getRequest();
        if (!$request->isPost()) {
            throw new P4Cms_AccessDeniedException(
                "Deleting users is not permitted in this context."
            );
        }

        $activeUser       = P4Cms_User::fetchActive();
        $userId           = $request->getPost('id');
        $deleteActiveUser = $userId === $activeUser->getId();

        if (!$deleteActiveUser) {
            $this->acl->check('users', 'manage');
        }

        // deny if attempting to delete system user
        if ($this->_isSystemUser($userId)) {
            throw new P4Cms_AccessDeniedException(
                "You don't have permission to delete this user."
            );
        }

        // get user to delete
        $user = $deleteActiveUser
            ? $activeUser
            : P4Cms_User::fetch($userId);

        // deleted user should have the same adapter as active user personal adapter
        $user->setAdapter($activeUser->getPersonalAdapter());

        // deny if deleted user is the only administrator
        if ($user->isAdministrator()
             && count(P4Cms_Acl_Role::fetch(P4Cms_Acl_Role::ROLE_ADMINISTRATOR)->getUsers()) == 1
        ) {
            throw new P4Cms_AccessDeniedException(
                "The only administrator cannot be deleted."
            );
        }

        // remove user references from the roles that user is associated with
        // use personal adapter if user is admin, otherwise use site adapter.
        $adapter = $activeUser->isAdministrator()
                 ? $activeUser->getPersonalAdapter()
                 : P4Cms_Site::fetchActive()->getStorageAdapter();
        P4Cms_Acl_Role::setUserRoles($user, array(), $adapter);

        // do the actual delete
        $user->delete();

        // clear any cache entries related to this user
        P4Cms_Cache::clean('all', 'p4cms_user_' . md5($user->getId()));

        // add notification if active user was deleted or
        // if we are in traditional context
        $context = $this->contextSwitch->getCurrentContext();
        if (!$context || $deleteActiveUser) {
            P4Cms_Notifications::add(
                "User '$userId' has been deleted.",
                P4Cms_Notifications::SEVERITY_SUCCESS
            );
        }

        // redirect for traditional requests
        if (!$context) {
            $this->redirector->gotoUrl($request->getBaseUrl());
        }

        $this->view->userId           = $userId;
        $this->view->deleteActiveUser = $deleteActiveUser;
    }
User_IndexController::editAction ( )

Edit an existing user entry.

    {
        $request    = $this->getRequest();
        $activeUser = P4Cms_User::fetchActive();
        $userId     = $request->getParam('id', $activeUser->getId());

        // ensure user id is set in the request
        $request->setParam('id', $userId);

        // enforce permissions
        if ($userId !== $activeUser->getId()) {
            $this->acl->check('users', 'manage');
        }

        // deny if user id is null or attempted to edit system user
        if ($userId === null || $this->_isSystemUser($userId)) {
            throw new P4Cms_AccessDeniedException(
                "You don't have permission to edit this user."
            );
        }

        // determine whether old password input is neccessary when setting up new password
        $formOptions = array(
            'needOldPassword' => !$activeUser->isAdministrator()
                || ($activeUser->getId() === $userId)
        );

        // determine whether the user is the last administrator
        $admins = P4Cms_Acl_Role::fetch(P4Cms_Acl_Role::ROLE_ADMINISTRATOR)->getRealUsers();
        $formOptions['requireAdministrator'] = count($admins) == 1
            && in_array($userId, $admins);

        // redirect to page not found if user doesn't exist
        if (!P4Cms_User::exists($userId)) {
            return $this->_forward('page-not-found', 'index', 'error');
        }

        $user = P4Cms_User::fetch($userId);

        // set up view
        $form               = new User_Form_Edit($formOptions);
        $view               = $this->view;
        $view->form         = $form;
        $view->user         = $user;
        $view->headTitle()->set('Edit User');

        // set roles from storage if no post, or not permitted.
        if (!$request->isPost() || !$this->acl->isAllowed('users', 'manage-roles')) {
            $request->setParam('roles', $user->getRoles()->invoke('getId'));
        }

        // populate form from request if posted, otherwise from storage.
        $form->populate(
            $request->isPost()
            ? $request->getParams()
            : $request->getParams() + $user->getValues()
        );

        // if change password unchecked, disable 'password' group.
        if (!$form->getValue('changePassword')) {
            $group = $form->getDisplayGroup('passwords');
            $group->setAttrib('class', $group->getAttrib('class') . ' disabled');
        }

        // if posted, validate form and save user.
        if ($request->isPost()) {
            // if form is invalid, set response code and exit
            if (!$form->isValid($request->getParams())) {
                $this->getResponse()->setHttpResponseCode(400);
                $view->errors = $form->getMessages();
                return;
            }

            $user->setValues($form->getValues());

            // if current password given, must set password explicitly.
            if ($form->getValue('currentPassword')) {
                $user->setPassword(
                    $form->getValue('password'),
                    $form->getValue('currentPassword')
                );
            }

            $user->save();

            // if user has permission to manage roles, set roles from request
            if ($form->getElement('roles') && $this->acl->isAllowed('users', 'manage-roles')) {
                P4Cms_Acl_Role::setUserRoles($user, $form->getValue('roles'));
            }

            // if current user changed the password, re-authenticate to get updated ticket
            if ($activeUser->getId() === $userId && $form->getValue('changePassword')) {
                $result = $this->_authenticate($userId, $form->getValue('password'));
            }

            // set notification message
            $view->message = "User '{$user->getId()}' has been successfuly updated.";

            // clear any cache entries related to this user
            P4Cms_Cache::clean('all', 'p4cms_user_' . md5($user->getId()));

            // for traditional requests, add notification message and redirect
            if (!$this->contextSwitch->getCurrentContext()) {
                P4Cms_Notifications::add(
                    $view->message,
                    P4Cms_Notifications::SEVERITY_SUCCESS
                );
                $this->redirector->gotoUrl($request->getBaseUrl());
            }
        }
    }
User_IndexController::indexAction ( )

List all users.

p4cms.user.grid.actions Modify the passed menu (add/modify/delete items) to influence the actions shown on entries in the Manage Users grid. P4Cms_Navigation $actions A menu to hold grid actions.

p4cms.user.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 Users 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.

p4cms.user.grid.data Adjust the passed data (add properties, modify values, etc.) to influence the row values sent to the Manage Users grid. Zend_Dojo_Data $data The data to be filtered. Ui_View_Helper_DataGrid $helper The view helper that broadcast this topic.

p4cms.user.grid.populate Adjust the passed iterator (possibly based on values in the passed form) to filter which users will be shown on the Manage Users grid. P4Cms_Model_Iterator $users An Iterator of User_Model_User objects. P4Cms_Form_PubSubForm $form A form containing filter options.

p4cms.user.grid.render Make adjustments to the datagrid helper's options pre-render (e.g. change options to add columns) for the Manage Users grid. Ui_View_Helper_DataGrid $helper The view helper that broadcast this topic.

p4cms.user.grid.form Make arbitrary modifications to the Manage Users filters form. P4Cms_Form_PubSubForm $form The form that published this event.

p4cms.user.grid.form.subForms Return a Form (or array of Forms) to have them added to the Manage Users 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.

p4cms.user.grid.form.preValidate Allows subscribers to adjust the Manage Users 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.

p4cms.user.grid.form.validate Return false to indicate the Manage Users 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.

p4cms.user.grid.form.populate Allows subscribers to adjust the Manage Users 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.

    {
        // enforce permissions
        $this->acl->check('users', 'manage');

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

        // setup view.
        $view               = $this->view;
        $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->showAddLink  = $this->acl->isAllowed('users', 'add');
        $view->headTitle()->set('Manage Users');

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

        // collect the actions from interested parties
        $actions = new P4Cms_Navigation;
        P4Cms_PubSub::publish($gridNamespace . '.actions', $actions);
        $view->actions = $actions;

        // early exit for standard requests (ie. not json)
        if (!$this->contextSwitch->getCurrentContext()) {
            return;
        }

        // fetch users - allow third-parties to influence list
        $users = P4Cms_User::fetchAll();
        try {
            P4Cms_PubSub::publish($gridNamespace . '.populate', $users, $form);
        } catch (Exception $e) {
            P4Cms_Log::logException("Error building user list.", $e);
        }

        // prepare sorting options
        $sortKey    = $request->getParam('sort', 'id');
        $sortFlags  = array(
            P4Cms_Model_Iterator::SORT_NATURAL,
            P4Cms_Model_Iterator::SORT_NO_CASE
        );
        if (substr($sortKey, 0, 1) == '-') {
            $sortKey = substr($sortKey, 1);
            $sortFlags[] = P4Cms_Model_Iterator::SORT_DESCENDING;
        } else {
            $sortFlags[] = P4Cms_Model_Iterator::SORT_ASCENDING;
        }

        // apply sorting options.
        $users->sortBy($sortKey, $sortFlags);

        // add users to the view.
        $view->users = $users;
    }
User_IndexController::init ( )

Use management layout for all actions.

    {
        $this->_helper->layout->setLayout('manage-layout');
    }
User_IndexController::loginAction ( )

Handle user logins.

If not posted, presents form; otherwise, authenticates.

    {
        $request            = $this->getRequest();
        $form               = $this->_getLoginForm();
        $form->setAction($this->_helper->url('login'));

        // set up view
        $view               = $this->view;
        $view->form         = $form;
        $view->headTitle()->set('User Login');

        // if posted, validate form and authenticate user.
        if ($request->isPost()) {
            // if form is invalid, set response code and exit
            if (!$form->isValid($request->getPost())) {
                $this->getResponse()->setHttpResponseCode(400);
                return;
            }

            $login = $form->getValue('user');

            // silently clear login if it is does not look like
            // a valid p4 username or email address.
            $userValidator  = new P4_Validate_UserName;
            $emailValidator = new Zend_Validate_EmailAddress;
            if (!$userValidator->isValid($login) && !$emailValidator->isValid($login)) {
                $login = null;
            }

            // if login is an email address, lookup corresponding usernames.
            if (strpos($login, '@')) {
                $found = array(); // list with users matching the email address
                foreach (P4Cms_User::fetchAll() as $user) {
                    if ($user->getEmail() !== $login) {
                        continue;
                    }

                    $found[] = $user->getId();
                }

                $login = $found ?: null;
            }

            if (!is_array($login)) {
                $login = array($login);
            }

            // loop through all login candidates
            $errorMessage = 'Login failed. Invalid user or password.';
            foreach ($login as $loginCandidate) {

                // try to authenticate with given password
                $result = $this->_authenticate($loginCandidate, $form->getValue('password'));
                if (!$result->isValid()) {
                    // don't allow successful auth without priveleges, and report correct error
                    if ($result->getCode() === Zend_Auth_Result::FAILURE_IDENTITY_AMBIGUOUS) {
                        $errorMessage = 'You do not have permission to access this site.';
                        break;
                    }
                    continue;
                }

                // don't allow login as system user
                if ($this->_isSystemUser($loginCandidate)) {
                    Zend_Auth::getInstance()->clearIdentity();
                    $errorMessage = 'Login failed. Cannot login as the system user.';
                    break;
                }

                // auth data in session shouldn't influence page caching; add ourselves to the ignore list
                if (P4Cms_Cache::canCache('page')) {
                    P4Cms_Cache::getCache('page')->addIgnoredSessionVariable(
                        Zend_Auth::getInstance()->getStorage()->getNamespace()
                    );
                }

                // protect against session fixation
                Zend_Session::regenerateId();

                // login successful
                P4Cms_Notifications::add(
                    'Login successful.',
                    P4Cms_Notifications::SEVERITY_SUCCESS
                );

                // redirect for traditional contexts
                if (!$this->contextSwitch->getCurrentContext()) {
                    $this->redirector->gotoUrl($request->getBaseUrl());
                }

                return;
            }

            // login failed, add random 1-2 second delay.
            $delay = mt_rand(self::LOGIN_FAILED_MIN_DELAY, self::LOGIN_FAILED_MAX_DELAY);
            usleep($delay);

            // authentication failed - add error to form.
            if (!$this->contextSwitch->getCurrentContext()) {
                P4Cms_Notifications::add($errorMessage, P4Cms_Notifications::SEVERITY_ERROR);
            }

            $form->addError($errorMessage);
            $this->getResponse()->setHttpResponseCode(400);
        }
    }
User_IndexController::logoutAction ( )

Log the user out (clear identity).

    {
        Zend_Auth::getInstance()->clearIdentity();
        P4Cms_Notifications::add('Logout completed.', P4Cms_Notifications::SEVERITY_SUCCESS);
        $this->redirector->gotoUrl($this->getRequest()->getBaseUrl());
    }

Member Data Documentation

User_IndexController::$contexts
Initial value:
 array(
        'login'     => array('partial', 'json'),
        'add'       => array('partial', 'json'),
        'edit'      => array('partial', 'json'),
        'index'     => array('json'),
        'delete'    => array('json'),
    )

The documentation for this class was generated from the following file: