/** * Display the output of the login block. * * @param mixed[] $blockInfo { * @type string $title the title of the block * @type int $bid the id of the block * @type string $content the seralized block content array * } * * @return string The rendered blockoutput. */ 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 AuthenticationMethodListHelper($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->isMethod('GET')) { $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('Block/login.tpl'); } $renderedOutput = BlockUtil::themeBlock($blockInfo); } } return $renderedOutput; }
/** * @Route("/login", options={"zkNoBundlePrefix"=1}) * @Method({"GET", "POST"}) * * 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. * * @param Request $request * * Parameters passed via FORWARD: * -------------------------------------- * 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 firstmethodisdefault If to display first of authentication methods as preselected in login form, when more then one are specified (default is true). * 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.) * boolean from_password_change Always true. * * 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: users * Variable: User_login * Type: array * Contents: An array containing the information passed in via the GET or POST variables, and additionally, 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|Response True on successful authentication and login, the rendered output of the appropriate * template to display the log-in form. * * @throws FatalErrorException Thrown if no arguments are provided or * if an invalid authentication method is provided * @throws AccessDeniedException Thrown if no arguments are found in either GET or POST * @throws NotFoundHttpException Thrown if the user account couldn't be found or * if the user credentials aren't valid */ public function loginAction(Request $request) { // we shouldn't get here if logged in already.... if (UserUtil::isLoggedIn()) { return new RedirectResponse($this->get('router')->generate('zikulausersmodule_user_index', array(), RouterInterface::ABSOLUTE_URL)); } // set default value of variables $loggedIn = false; $isFunctionCall = false; $isReentry = false; $firstmethodisdefault = true; $fromPasswordChange = $request->get('from_password_change', false); $authenticationInfo = array(); $selectedAuthenticationMethod = array(); $rememberMe = false; $returnPage = $request->query->get('returnpage', ''); $eventType = false; if (!$fromPasswordChange) { // Get return page parameter. First try to get it from args and POST. $returnPage = $request->request->get('returnpage', ''); if (empty($returnPage)) { // Check if returnurl was set instead of returnpage $returnPage = $request->request->get('returnurl', ''); if (empty($returnPage)) { // Still no return page. Try to get it from query. $returnPage = urldecode($request->query->get('returnpage', $request->query->get('returnurl', ''))); } } if ($request->isMethod('POST')) { // 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 = (array) $request->request->get('authentication_info', array()); $selectedAuthenticationMethod = (array) $request->request->get('authentication_method', array()); $rememberMe = (bool) $request->request->get('rememberme', false); $eventType = $request->request->get('event_type', false); } elseif ($request->isMethod('GET')) { $reentry = false; $reentrantTokenReceived = $request->query->get('reentranttoken', ''); $sessionVars = $request->getSession()->get('User_login', array(), UsersConstant::SESSION_VAR_NAMESPACE); $request->getSession()->remove('User_login', UsersConstant::SESSION_VAR_NAMESPACE); $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'] : $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; $eventType = 'login_screen'; $user = array(); $this->getDispatcher()->dispatch('module.users.ui.login.started', new GenericEvent()); } } } 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. $request->getSession()->remove('User_login', UsersConstant::SESSION_VAR_NAMESPACE); $authenticationMethodList = new AuthenticationMethodListHelper($this); if ($request->isMethod('POST') || $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 \InvalidArgumentException($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); $request->getSession()->set('User_login', $sessionVars, UsersConstant::SESSION_VAR_NAMESPACE); // 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 = $this->get('router')->generate('zikulausersmodule_user_login', array('reentranttoken' => $reentrantToken), RouterInterface::ABSOLUTE_URL); // 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. try { $user = UserUtil::authenticateUserUsing($selectedAuthenticationMethod, $authenticationInfo, $reentrantUrl, true); } catch (AccessDeniedException $e) { $request->getSession()->getFlashBag()->set('error', $e->getMessage()); } // 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 ValidationProviders(); if ($eventType) { $event = new GenericEvent($user, array(), $validators); $validators = $this->getDispatcher()->dispatch("module.users.ui.validate_edit.{$eventType}", $event)->getData(); $hook = new ValidationHook($validators); $this->dispatchHooks("users.ui_hooks.{$eventType}.validate_edit", $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 GenericEvent($user, array()); $this->getDispatcher()->dispatch("module.users.ui.process_edit.{$eventType}", $event); $hook = new ProcessHook($user['uid']); $this->dispatchHooks("users.ui_hooks.{$eventType}.process_edit", $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 (!$request->getSession()->hasMessages(Zikula_Session::MESSAGE_ERROR)) { $request->getSession()->getFlashBag()->add('error', $this->__('Your log-in request was not completed.')); } $eventArgs = array('authentication_method' => $selectedAuthenticationMethod, 'authentication_info' => $authenticationInfo, 'redirecturl' => ''); $failedEvent = new GenericEvent($user, $eventArgs); $failedEvent = $this->getDispatcher()->dispatch('module.users.ui.login.failed', $failedEvent); $redirectUrl = $failedEvent->hasArgument('redirecturl') ? $failedEvent->getArgument('redirecturl') : ''; if (!empty($redirectUrl)) { return new RedirectResponse($redirectUrl); } } } else { if (!$request->getSession()->hasMessages(Zikula_Session::MESSAGE_ERROR)) { $request->getSession()->getFlashBag()->add('error', $this->__('Your log-in request was not completed.')); } $eventArgs = array('authentication_method' => $selectedAuthenticationMethod, 'authentication_info' => $authenticationInfo, 'redirecturl' => ''); $failedEvent = new GenericEvent($user, $eventArgs); $failedEvent = $this->getDispatcher()->dispatch('module.users.ui.login.failed', $failedEvent); $redirectUrl = $failedEvent->hasArgument('redirecturl') ? $failedEvent->getArgument('redirecturl') : ''; if (!empty($redirectUrl)) { return new RedirectResponse($redirectUrl); } } } else { // The user with the given credentials does not exist. // Check if we shall redirect to the account registration screen if a user with the given // credentials does not exist.. if (ModUtil::apiFunc($selectedAuthenticationMethod['modname'], 'Authentication', 'redirectToRegistrationOnLoginError', array('authentication_method' => $selectedAuthenticationMethod, 'authentication_info' => $authenticationInfo))) { // We shall redirect to the account registration screen, but first we need to check // if the user can be created with the given credentials or if another error has // occured. if (ModUtil::apiFunc($selectedAuthenticationMethod['modname'], 'Authentication', 'checkPassword', array('authentication_method' => $selectedAuthenticationMethod, 'authentication_info' => $authenticationInfo, 'reentrant_url' => $reentrantUrl))) { // Redirect to account registration screen. Clear error messages and re-save session // vars for registration. $request->getSession()->clearMessages(Zikula_Session::MESSAGE_ERROR); $request->getSession()->set('User_register', $sessionVars, UsersConstant::SESSION_VAR_NAMESPACE); return new RedirectResponse($this->get('router')->generate('zikulausersmodule_user_register', array('reentranttoken' => $reentrantTokenReceived), RouterInterface::ABSOLUTE_URL)); } } if (!$request->getSession()->hasMessages(Zikula_Session::MESSAGE_ERROR)) { throw new NotFoundHttpException($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 GenericEvent(null, $eventArgs); $failedEvent = $this->getDispatcher()->dispatch('module.users.ui.login.failed', $failedEvent); $redirectUrl = $failedEvent->hasArgument('redirecturl') ? $failedEvent->getArgument('redirecturl') : ''; if (!empty($redirectUrl)) { return new RedirectResponse(System::normalizeUrl($redirectUrl)); } } // 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). $request->getSession()->remove('User_login', UsersConstant::SESSION_VAR_NAMESPACE); } else { if (!$request->getSession()->hasMessages(Zikula_Session::MESSAGE_ERROR)) { throw new NotFoundHttpException($this->__('The credentials you entered were not valid. Please reenter the requested information and try again.')); } } } else { if ($authenticationMethodList->countEnabledForAuthentication() <= 1) { $request->getSession()->getFlashBag()->add('error', $this->__('The selected log-in method is not currently available. Please contact the site administrator for assistance.')); } else { $request->getSession()->getFlashBag()->add('error', $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 \InvalidArgumentException($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)) && ($firstmethodisdefault || $authenticationMethodList->countEnabledForAuthentication() <= 1)) { /* @var AuthenticationMethodHelper $authenticationMethod */ $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 new Response($this->view->assign($templateArgs)->fetch('User/login.tpl')); } else { $eventArgs = array('authentication_method' => $selectedAuthenticationMethod, 'redirecturl' => $returnPage); if (isset($isFirstLogin)) { $eventArgs['is_first_login'] = $isFirstLogin; } $event = new GenericEvent($user, $eventArgs); $event = $this->getDispatcher()->dispatch('module.users.ui.login.succeeded', $event); $returnPage = $event->hasArgument('redirecturl') ? $event->getArgument('redirecturl') : $returnPage; if (empty($returnPage)) { $returnPage = System::getHomepageUrl(); } // A successful login. if ($this->getVar(UsersConstant::MODVAR_LOGIN_WCAG_COMPLIANT, 1) == 1) { // WCAG compliant login return new RedirectResponse(System::normalizeUrl($returnPage)); } else { // meta refresh $this->printRedirectPage($this->__('You are being logged-in. Please wait...'), $returnPage); return true; } } }