Example #1
0
    /**
     * Display the output of the login block.
     *
     * @param array $blockInfo A blockinfo structure.
     *
     * @return string The output.
     */
    public function display($blockInfo)
    {
        $renderedOutput = '';

        if (SecurityUtil::checkPermission('Loginblock::', $blockInfo['title'].'::', ACCESS_READ)) {
            if (!UserUtil::isLoggedIn()) {
                if (empty($blockInfo['title'])) {
                    $blockInfo['title'] = DataUtil::formatForDisplay('Login');
                }

                $authenticationMethodList = new Users_Helper_AuthenticationMethodList($this);

                if ($authenticationMethodList->countEnabledForAuthentication() > 1) {
                    $selectedAuthenticationMethod = $this->request->request->get('authentication_method', false);
                } else {
                    // There is only one (or there is none), so auto-select it.
                    $authenticationMethod = $authenticationMethodList->getAuthenticationMethodForDefault();
                    $selectedAuthenticationMethod = array(
                        'modname'   => $authenticationMethod->modname,
                        'method'    => $authenticationMethod->method,
                    );
                }

                // TODO - The order and availability should be set by block configuration
                $authenticationMethodDisplayOrder = array();
                foreach ($authenticationMethodList as $authenticationMethod) {
                    if ($authenticationMethod->isEnabledForAuthentication()) {
                        $authenticationMethodDisplayOrder[] = array(
                            'modname'   => $authenticationMethod->modname,
                            'method'    => $authenticationMethod->method,
                        );
                    }
                }

                $this->view->assign('authentication_method_display_order', $authenticationMethodDisplayOrder)
                           ->assign('selected_authentication_method', $selectedAuthenticationMethod);

                // If the current page was reached via a POST or FILES then we don't want to return here.
                // Only return if the current page was reached via a regular GET
                if ($this->request->isGet()) {
                    $this->view->assign('returnpage', System::getCurrentUri());
                } else {
                    $this->view->assign('returnpage', '');
                }

                $tplName = mb_strtolower("users_block_login_{$blockInfo['position']}.tpl");
                if ($this->view->template_exists($tplName)) {
                    $blockInfo['content'] = $this->view->fetch($tplName);
                } else {
                    $blockInfo['content'] = $this->view->fetch('users_block_login.tpl');
                }

                $renderedOutput = BlockUtil::themeBlock($blockInfo);
            }
        }

        return $renderedOutput;
    }
Example #2
0
    /**
     * Display the login form, or process a user log-in request.
     *
     * This displays the main log-in screen to the user, allowing him to select a method of authenticating himself
     * to the system (if more than one authentication method is available), and to provide his credentials in
     * order to log into the site.
     *
     * Upon submitting his credentials (either through the log-in form mentioned above, or through another form
     * such as the log-in block, this processes the credentials as a log-in request.
     *
     * If the user is already logged in, then he is redirected the main Users module page.
     *
     * Parameters passed via the $args array:
     * --------------------------------------
     * array   authentication_info   An array containing the authentication information entered by the user.
     * array   authentication_method An array containing two elements: 'modname', the authentication module name, and 'method', the
     *                                      selected authentication method as defined by the module.
     * boolean rememberme            True if the user should remain logged in at that computer for future visits; otherwise false.
     * string  returnpage            The URL of the page to return to if the log-in attempt is successful. (This URL must not be urlencoded.)
     *
     * Parameters passed via GET:
     * --------------------------
     * string returnpage The urlencoded URL of the page to return to if the log-in attempt is successful.
     *
     * Parameters passed via POST:
     * ---------------------------
     * array   authentication_info   An array containing the authentication information entered by the user.
     * array   authentication_method An array containing two elements: 'modname', the authentication module name, and 'method', the
     *                                      selected authentication method as defined by the module.
     * boolean rememberme            True if the user should remain logged in at that computer for future visits; otherwise false.
     * string  returnpage            The URL of the page to return to if the log-in attempt is successful. (This URL must not be urlencoded.)
     *
     * Parameters passed via SESSION:
     * ------------------------------
     * Namespace: Zikula_Users
     * Variable:  Users_Controller_User_login
     * Type:      array
     * Contents:  An array containing the information passed in via the $args array or the GET or POST variables, and additionaly, the
     *                  element 'user_obj'if the user record has been loaded. (The returnpage element must not be urlencoded when stored
     *                  on the session.)
     *
     * @return boolean|string True on successful authentication and login, the rendered output of the appropriate
     *                        template to display the log-in form.
     *
     * @throws Zikula_Exception_Redirect If the user is already logged in, or upon successful login with the redirect
     *                                   option set to send the user to the appropriate page, or...
     */
    public function login($args)
    {
        // we shouldn't get here if logged in already....
        $this->redirectIf(UserUtil::isLoggedIn(), ModUtil::url($this->name, 'user', 'main'));

        $loggedIn = false;
        $isFunctionCall = false;
        $isReentry = false;

        // Need to check for $args first, since isPost() and isGet() will have been set on the original call
        if (isset($args) && is_array($args) && !empty($args)) {
            // We are coming in or back (reentering) from someplace else via a direct call to this function. It is likely that
            // we are coming back from a user.login.veto event handler that redirected the user to a page where he had to provide
            // more information.
            $authenticationInfo = isset($args['authentication_info']) ? $args['authentication_info'] : array();
            $selectedAuthenticationMethod = isset($args['authentication_method']) ? $args['authentication_method'] : array();
            $rememberMe         = isset($args['rememberme']) ? $args['rememberme'] : false;
            $returnPage         = isset($args['returnpage']) ? $args['returnpage'] : $this->request->query->get('returnpage', '');
            $eventType          = isset($args['event_type']) ? $args['event_type'] : false;

            $isFunctionCall = true;
        } elseif (isset($args) && !is_array($args)) {
            // Coming from a function call, but bad $args
            throw new Zikula_Exception_Fatal(LogUtil::getErrorMsgArgs());
        } elseif ($this->request->isPost()) {
            // We got here from a POST, either from the login, the login block, or some reasonable facsimile thereof.
            if (System::getVar('anonymoussessions', false)) {
                $this->checkCsrfToken();
            }

            $authenticationInfo = $this->request->request->get('authentication_info', array());
            $selectedAuthenticationMethod = $this->request->request->get('authentication_method', array());
            $rememberMe         = $this->request->request->get('rememberme', false);
            $returnPage         = $this->request->request->get('returnpage', urldecode($this->request->query->get('returnpage', '')));
            if (empty($returnPage)) {
                // Check if returnurl was set instead of returnpage
                $returnPage     = $this->request->request->get('returnurl', urldecode($this->request->query->get('returnurl', '')));
            }
            $eventType          = $this->request->request->get('event_type', false);
        } elseif ($this->request->isGet()) {
            $reentry = false;
            $reentrantTokenReceived = $this->request->query->get('reentranttoken', '');

            $sessionVars = $this->request->getSession()->get('Users_Controller_User_login', array(), 'Zikula_Users');
            $this->request->getSession()->del('Users_Controller_User_login', 'Zikula_Users');

            $reentrantToken = isset($sessionVars['reentranttoken']) ? $sessionVars['reentranttoken'] : false;

            if (!empty($reentrantTokenReceived) && ($reentrantTokenReceived == $reentrantToken)) {
                // We are coming back (reentering) from someplace else. It is likely that we are coming back from an external
                // authentication process initiated by an authentication module such as OpenID.
                $authenticationInfo = isset($sessionVars['authentication_info']) ? $sessionVars['authentication_info'] : array();
                $selectedAuthenticationMethod = isset($sessionVars['authentication_method']) ? $sessionVars['authentication_method'] : array();
                $rememberMe         = isset($sessionVars['rememberme']) ? $sessionVars['rememberme'] : false;
                $returnPage         = isset($sessionVars['returnpage']) ? $sessionVars['returnpage'] : $this->request->query->get('returnpage', '');
                $eventType          = isset($sessionVars['event_type']) ? $sessionVars['event_type'] : false;
                $user               = isset($sessionVars['user_obj']) ? $sessionVars['user_obj'] : null;

                $isReentry = true;
            } else {
                $authenticationInfo = array();
                $selectedAuthenticationMethod = array();
                $rememberMe         = false;
                $returnPage         = urldecode($this->request->query->get('returnpage', $this->request->query->get('returnurl', '')));
                $eventType          = 'login_screen';
                $user               = array();

                $event = new Zikula_Event('module.users.ui.login.started');
                $this->eventManager->notify($event);
            }
        } else {
            throw new Zikula_Exception_Forbidden();
        }

        if (!isset($reentrantToken)) {
            $reentrantToken = substr(SecurityUtil::generateCsrfToken(), 0, 10);
        }

        // Any authentication information for use in this pass through login is gathered, so ensure any session variable
        // is cleared, even if we are coming in through a post or a function call that didn't gather info from the session.
        $this->request->getSession()->del('Users_Controller_User_login', 'Zikula_Users');

        $authenticationMethodList = new Users_Helper_AuthenticationMethodList($this);

        if ($this->request->isPost() || $isFunctionCall || $isReentry) {
            // A form submission, or a simulated submission as a function call.
            if (isset($authenticationInfo) && is_array($authenticationInfo) && !empty($authenticationInfo)) {
                if (!isset($selectedAuthenticationMethod) || !is_array($selectedAuthenticationMethod) || empty($selectedAuthenticationMethod)
                        || !isset($selectedAuthenticationMethod['modname']) || empty($selectedAuthenticationMethod['modname'])
                        || !isset($selectedAuthenticationMethod['method']) || empty($selectedAuthenticationMethod['method'])
                        ) {
                    throw new Zikula_Exception_Fatal($this->__('Error! Invalid authentication method information.'));
                }

                if (ModUtil::available($selectedAuthenticationMethod['modname'])
                        && ModUtil::apiFunc($selectedAuthenticationMethod['modname'], 'authentication', 'isEnabledForAuthentication', $selectedAuthenticationMethod)
                        ) {
                    // The authentication method is reasonably valid, moving on to validate the user-entered credentials
                    $validateAuthenticationInfoArgs = array(
                        'authenticationMethod'  => $selectedAuthenticationMethod,
                        'authenticationInfo'    => $authenticationInfo,
                    );

                    if (ModUtil::func($selectedAuthenticationMethod['modname'], 'authentication', 'validateAuthenticationInformation', $validateAuthenticationInfoArgs)) {
                        // The authentication method and the authentication information have been validated at the UI level.
                        //
                        // Moving on to the actual authentication process. Save the submitted information in case the authentication
                        // method is external and reentrant.
                        //
                        // We're using sessions here, even though anonymous sessions might be turned off for anonymous users.
                        // If the user is trying to log in, then he's going to get a session if he's successful,
                        // so using sessions on the anonymous user just before logging in should be ok.
                        SessionUtil::requireSession();
                        $sessionVars = array(
                            'event_type'            => $eventType,
                            'returnpage'            => $returnPage,
                            'authentication_info'   => $authenticationInfo,
                            'authentication_method' => $selectedAuthenticationMethod,
                            'rememberme'            => $rememberMe,
                            'reentranttoken'        => $reentrantToken,
                        );
                        $this->request->getSession()->set('Users_Controller_User_login', $sessionVars, 'Zikula_Users');

                        // The authentication method selected might be reentrant (it might send the user out to an external web site
                        // for authentication, and then send us back to finish the job). We need to tell the external system to where
                        // we would like to return.
                        $reentrantUrl = ModUtil::url($this->name, 'user', 'login', array('reentranttoken' => $reentrantToken), null, null, true, true);

                        // There may be hook providers that need to be validated, so we cannot yet log in. The hook providers will
                        // need a user object to make sure they know who they're dealing with. Authenticate (so we are sure that
                        // the user is who he says he is) and get a user.
                        //
                        // The chosen authentication method might be reentrant, and this is the point were the user might be directed
                        // outside the Zikula system for external authentication.
                        $user = UserUtil::authenticateUserUsing($selectedAuthenticationMethod, $authenticationInfo, $reentrantUrl, true);

                        // If we have gotten to this point in the same call to login(), then the authentication method was not external
                        // and reentrant, so we should not need the session variable any more. If it is external and reentrant, and the
                        // user was required to exit the Zikula system for authentication on the external system, then we will not get
                        // to this point until the reentrant call back to login() (at which point the variable should, again, not be needed
                        // anymore).
                        $this->request->getSession()->del('Users_Controller_User_login', 'Zikula_Users');

                        // Did we get a good user? If so, then we can proceed to hook validation.
                        if (isset($user) && $user && is_array($user) && isset($user['uid']) && is_numeric($user['uid'])) {
                            $validators = new Zikula_Hook_ValidationProviders();
                            if ($eventType) {
                                $event = new Zikula_Event("module.users.ui.validate_edit.{$eventType}", $user, array(), $validators);
                                $validators  = $this->eventManager->notify($event)->getData();

                                $hook = new Zikula_ValidationHook("users.ui_hooks.{$eventType}.validate_edit", $validators);
                                $this->notifyHooks($hook);
                                $validators = $hook->getValidators();
                            }

                            if (!$validators->hasErrors()) {
                                // Process the edit hooks BEFORE we log in, so that any changes to the user record are recorded before we re-check
                                // the user's ability to log in. If we don't do this, then user.login.veto might trap and cancel the login attempt again.
                                if ($eventType) {
                                    $event = new Zikula_Event("module.users.ui.process_edit.{$eventType}", $user, array());
                                    $this->eventManager->notify($event);

                                    $hook = new Zikula_ProcessHook("users.ui_hooks.{$eventType}.process_edit", $user['uid']);
                                    $this->notifyHooks($hook);
                                }

                                if (!isset($user['lastlogin']) || empty($user['lastlogin']) || ($user['lastlogin'] == '1970-01-01 00:00:00')) {
                                    $isFirstLogin = true;
                                } else {
                                    $isFirstLogin = false;
                                }

                                // Because we are passing a $user and setting checkPassword false, this call back into the authentication
                                // chain should not trigger an external re-authentication, so it should not need preparation for reentry.
                                $loggedIn = UserUtil::loginUsing($selectedAuthenticationMethod, $authenticationInfo, $rememberMe, $reentrantUrl, false, $user);

                                if (!$loggedIn) {
                                    // Because the user was preauthentication, this should never happen, but just in case...

                                    if (!$this->request->getSession()->hasMessages(Zikula_Session::MESSAGE_ERROR)) {
                                        $this->registerError($this->__('Your log-in request was not completed.'));
                                    }

                                    $eventArgs = array(
                                        'authentication_method' => $selectedAuthenticationMethod,
                                        'authentication_info'   => $authenticationInfo,
                                        'redirecturl'           => '',
                                    );
                                    $failedEvent = new Zikula_Event('module.users.ui.login.failed', $user, $eventArgs);
                                    $failedEvent = $this->eventManager->notify($failedEvent);

                                    $redirectUrl = $failedEvent->hasArg('redirecturl') ? $failedEvent->getArg('redirecturl') : '';
                                    if (!empty($redirectUrl)) {
                                        $this->redirect($redirectUrl);
                                    }
                                }
                            } else {
                                if (!$this->request->getSession()->hasMessages(Zikula_Session::MESSAGE_ERROR)) {
                                    $this->registerError($this->__('Your log-in request was not completed.'));
                                }

                                $eventArgs = array(
                                    'authentication_method' => $selectedAuthenticationMethod,
                                    'authentication_info'   => $authenticationInfo,
                                    'redirecturl'           => '',
                                );
                                $failedEvent = new Zikula_Event('module.users.ui.login.failed', $user, $eventArgs);
                                $failedEvent = $this->eventManager->notify($failedEvent);

                                $redirectUrl = $failedEvent->hasArg('redirecturl') ? $failedEvent->getArg('redirecturl') : '';
                                if (!empty($redirectUrl)) {
                                    $this->redirect($redirectUrl);
                                }
                            }
                        } else {
                            if (!$this->request->getSession()->hasMessages(Zikula_Session::MESSAGE_ERROR)) {
                                $this->registerError($this->__('There is no user account matching that information, or the password you gave does not match the password on file for that account.'));
                            }

                            $eventArgs = array(
                                'authentication_method' => $selectedAuthenticationMethod,
                                'authentication_info'   => $authenticationInfo,
                                'redirecturl'           => '',
                            );
                            $failedEvent = new Zikula_Event('module.users.ui.login.failed', null, $eventArgs);
                            $failedEvent = $this->eventManager->notify($failedEvent);

                            $redirectUrl = $failedEvent->hasArg('redirecturl') ? $failedEvent->getArg('redirecturl') : '';
                            if (!empty($redirectUrl)) {
                                $this->redirect($redirectUrl);
                            }
                        }
                    } else {
                        if (!$this->request->getSession()->hasMessages(Zikula_Session::MESSAGE_ERROR)) {
                            $this->registerError($this->__('The credentials you entered were not valid. Please reenter the requested information and try again.'));
                        }
                    }
                } else {
                    if ($authenticationMethodList->countEnabledForAuthentication() <= 1) {
                        $this->registerError($this->__('The selected log-in method is not currently available. Please contact the site administrator for assistance.'));
                    } else {
                        $this->registerError($this->__('The selected log-in method is not currently available. Please choose another or contact the site administrator for assistance.'));
                    }
                }
            } elseif (isset($authenticationInfo) && (!is_array($authenticationInfo))) {
                throw new Zikula_Exception_Fatal($this->__('Error! Invalid authentication information received.'));
            }
        }

        if (!$loggedIn) {
            // Either a GET request type to initially display the login form, or a failed login attempt
            // which means the login form should be displayed anyway.
            if ((!isset($selectedAuthenticationMethod) || empty($selectedAuthenticationMethod))
                    && ($authenticationMethodList->countEnabledForAuthentication() <= 1)
                    ) {
                $authenticationMethod = $authenticationMethodList->getAuthenticationMethodForDefault();
                $selectedAuthenticationMethod = array(
                    'modname'   => $authenticationMethod->modname,
                    'method'    => $authenticationMethod->method,
                );
            }

            // TODO - The order and availability should be set by configuration
            $authenticationMethodDisplayOrder = array();
            foreach ($authenticationMethodList as $authenticationMethod) {
                if ($authenticationMethod->isEnabledForAuthentication()) {
                    $authenticationMethodDisplayOrder[] = array(
                        'modname'   => $authenticationMethod->modname,
                        'method'    => $authenticationMethod->method,
                    );
                }
            }

            $templateArgs = array(
                'returnpage'                            => isset($returnPage) ? $returnPage : '',
                'authentication_info'                   => isset($authenticationInfo) ? $authenticationInfo : array(),
                'selected_authentication_method'        => $selectedAuthenticationMethod,
                'authentication_method_display_order'   => $authenticationMethodDisplayOrder,
                'user_obj'                              => isset($user) ? $user : array(),
            );

            return $this->view->assign($templateArgs)
                    ->fetch('users_user_login.tpl');
        } else {
            $eventArgs = array(
                'authentication_method' => $selectedAuthenticationMethod,
                'redirecturl'           => $returnPage,
            );

            if (isset($isFirstLogin)) {
                $eventArgs['is_first_login'] = $isFirstLogin;
            }

            $event = new Zikula_Event('module.users.ui.login.succeeded', $user, $eventArgs);
            $event = $this->eventManager->notify($event);

            $returnPage = $event->hasArg('redirecturl') ? $event->getArg('redirecturl') : $returnPage;

            if (empty($returnPage)) {
                $returnPage = System::getHomepageUrl();
            }

            // A successful login.
            if ($this->getVar(Users_Constant::MODVAR_LOGIN_WCAG_COMPLIANT, 1) == 1) {
                // WCAG compliant login
                $this->redirect($returnPage);
            } else {
                // meta refresh
                $this->printRedirectPage($this->__('You are being logged-in. Please wait...'), $returnPage);

                return true;
            }
        }
    }