public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); if ($request->isFormPost()) { $log = PhabricatorUserLog::initializeNewLog($viewer, $viewer->getPHID(), PhabricatorUserLog::ACTION_LOGOUT); $log->save(); // Destroy the user's session in the database so logout works even if // their cookies have some issues. We'll detect cookie issues when they // try to login again and tell them to clear any junk. $phsid = $request->getCookie(PhabricatorCookies::COOKIE_SESSION); if (strlen($phsid)) { $session = id(new PhabricatorAuthSessionQuery())->setViewer($viewer)->withSessionKeys(array($phsid))->executeOne(); if ($session) { $session->delete(); } } $request->clearCookie(PhabricatorCookies::COOKIE_SESSION); return id(new AphrontRedirectResponse())->setURI('/auth/loggedout/'); } if ($viewer->getPHID()) { $dialog = id(new AphrontDialogView())->setUser($viewer)->setTitle(pht('Log out of Phabricator?'))->appendChild(pht('Are you sure you want to log out?'))->addSubmitButton(pht('Logout'))->addCancelButton('/'); return id(new AphrontDialogResponse())->setDialog($dialog); } return id(new AphrontRedirectResponse())->setURI('/'); }
public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); // If the user already has a full session, just kick them out of here. $has_partial_session = $viewer->hasSession() && $viewer->getSession()->getIsPartial(); if (!$has_partial_session) { return id(new AphrontRedirectResponse())->setURI('/'); } $engine = new PhabricatorAuthSessionEngine(); // If this cookie is set, the user is headed into a high security area // after login (normally because of a password reset) so if they are // able to pass the checkpoint we just want to put their account directly // into high security mode, rather than prompt them again for the same // set of credentials. $jump_into_hisec = $request->getCookie(PhabricatorCookies::COOKIE_HISEC); try { $token = $engine->requireHighSecuritySession($viewer, $request, '/logout/', $jump_into_hisec); } catch (PhabricatorAuthHighSecurityRequiredException $ex) { $form = id(new PhabricatorAuthSessionEngine())->renderHighSecurityForm($ex->getFactors(), $ex->getFactorValidationResults(), $viewer, $request); return $this->newDialog()->setTitle(pht('Provide Multi-Factor Credentials'))->setShortTitle(pht('Multi-Factor Login'))->setWidth(AphrontDialogView::WIDTH_FORM)->addHiddenInput(AphrontRequest::TYPE_HISEC, true)->appendParagraph(pht('Welcome, %s. To complete the login process, provide your ' . 'multi-factor credentials.', phutil_tag('strong', array(), $viewer->getUsername())))->appendChild($form->buildLayoutView())->setSubmitURI($request->getPath())->addCancelButton($ex->getCancelURI())->addSubmitButton(pht('Continue')); } // Upgrade the partial session to a full session. $engine->upgradePartialSession($viewer); // TODO: It might be nice to add options like "bind this session to my IP" // here, even for accounts without multi-factor auth attached to them. $next = PhabricatorCookies::getNextURICookie($request); $request->clearCookie(PhabricatorCookies::COOKIE_NEXTURI); $request->clearCookie(PhabricatorCookies::COOKIE_HISEC); if (!PhabricatorEnv::isValidLocalURIForLink($next)) { $next = '/'; } return id(new AphrontRedirectResponse())->setURI($next); }
public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $failures = array(); if (!strlen($request->getStr('expect'))) { return $this->renderErrors(array(pht('Login validation is missing expected parameter ("%s").', 'phusr'))); } $expect_phusr = $request->getStr('expect'); $actual_phusr = $request->getCookie(PhabricatorCookies::COOKIE_USERNAME); if ($actual_phusr != $expect_phusr) { if ($actual_phusr) { $failures[] = pht("Attempted to set '%s' cookie to '%s', but your browser sent back " . "a cookie with the value '%s'. Clear your browser's cookies and " . "try again.", 'phusr', $expect_phusr, $actual_phusr); } else { $failures[] = pht("Attempted to set '%s' cookie to '%s', but your browser did not " . "accept the cookie. Check that cookies are enabled, clear them, " . "and try again.", 'phusr', $expect_phusr); } } if (!$failures) { if (!$viewer->getPHID()) { $failures[] = pht('Login cookie was set correctly, but your login session is not ' . 'valid. Try clearing cookies and logging in again.'); } } if ($failures) { return $this->renderErrors($failures); } $finish_uri = $this->getApplicationURI('finish/'); return id(new AphrontRedirectResponse())->setURI($finish_uri); }
public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $this->providerKey = $request->getURIData('pkey'); list($type, $domain) = explode(':', $this->providerKey, 2); // Check that this account link actually exists. We don't require the // provider to exist because we want users to be able to delete links to // dead accounts if they want. $account = id(new PhabricatorExternalAccount())->loadOneWhere('accountType = %s AND accountDomain = %s AND userPHID = %s', $type, $domain, $viewer->getPHID()); if (!$account) { return $this->renderNoAccountErrorDialog(); } // Check that the provider (if it exists) allows accounts to be unlinked. $provider_key = $this->providerKey; $provider = PhabricatorAuthProvider::getEnabledProviderByKey($provider_key); if ($provider) { if (!$provider->shouldAllowAccountUnlink()) { return $this->renderNotUnlinkableErrorDialog($provider); } } // Check that this account isn't the last account which can be used to // login. We prevent you from removing the last account. if ($account->isUsableForLogin()) { $other_accounts = id(new PhabricatorExternalAccount())->loadAllWhere('userPHID = %s', $viewer->getPHID()); $valid_accounts = 0; foreach ($other_accounts as $other_account) { if ($other_account->isUsableForLogin()) { $valid_accounts++; } } if ($valid_accounts < 2) { return $this->renderLastUsableAccountErrorDialog(); } } if ($request->isDialogFormPost()) { $account->delete(); id(new PhabricatorAuthSessionEngine())->terminateLoginSessions($viewer, $request->getCookie(PhabricatorCookies::COOKIE_SESSION)); return id(new AphrontRedirectResponse())->setURI($this->getDoneURI()); } return $this->renderConfirmDialog($account); }
public function processRequest(AphrontRequest $request) { $viewer = $request->getUser(); $accounts = id(new PhabricatorExternalAccountQuery())->setViewer($viewer)->withUserPHIDs(array($viewer->getPHID()))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->execute(); $identity_phids = mpull($accounts, 'getPHID'); $identity_phids[] = $viewer->getPHID(); $sessions = id(new PhabricatorAuthSessionQuery())->setViewer($viewer)->withIdentityPHIDs($identity_phids)->execute(); $handles = id(new PhabricatorHandleQuery())->setViewer($viewer)->withPHIDs($identity_phids)->execute(); $current_key = PhabricatorHash::digest($request->getCookie(PhabricatorCookies::COOKIE_SESSION)); $rows = array(); $rowc = array(); foreach ($sessions as $session) { $is_current = phutil_hashes_are_identical($session->getSessionKey(), $current_key); if ($is_current) { $rowc[] = 'highlighted'; $button = phutil_tag('a', array('class' => 'small grey button disabled'), pht('Current')); } else { $rowc[] = null; $button = javelin_tag('a', array('href' => '/auth/session/terminate/' . $session->getID() . '/', 'class' => 'small grey button', 'sigil' => 'workflow'), pht('Terminate')); } $hisec = $session->getHighSecurityUntil() - time(); $rows[] = array($handles[$session->getUserPHID()]->renderLink(), substr($session->getSessionKey(), 0, 6), $session->getType(), $hisec > 0 ? phutil_format_relative_time($hisec) : null, phabricator_datetime($session->getSessionStart(), $viewer), phabricator_date($session->getSessionExpires(), $viewer), $button); } $table = new AphrontTableView($rows); $table->setNoDataString(pht("You don't have any active sessions.")); $table->setRowClasses($rowc); $table->setHeaders(array(pht('Identity'), pht('Session'), pht('Type'), pht('HiSec'), pht('Created'), pht('Expires'), pht(''))); $table->setColumnClasses(array('wide', 'n', '', 'right', 'right', 'right', 'action')); $terminate_icon = id(new PHUIIconView())->setIconFont('fa-exclamation-triangle'); $terminate_button = id(new PHUIButtonView())->setText(pht('Terminate All Sessions'))->setHref('/auth/session/terminate/all/')->setTag('a')->setWorkflow(true)->setIcon($terminate_icon); $header = id(new PHUIHeaderView())->setHeader(pht('Active Login Sessions'))->addActionLink($terminate_button); $hisec = $viewer->getSession()->getHighSecurityUntil() - time(); if ($hisec > 0) { $hisec_icon = id(new PHUIIconView())->setIconFont('fa-lock'); $hisec_button = id(new PHUIButtonView())->setText(pht('Leave High Security'))->setHref('/auth/session/downgrade/')->setTag('a')->setWorkflow(true)->setIcon($hisec_icon); $header->addActionLink($hisec_button); } $panel = id(new PHUIObjectBoxView())->setHeader($header)->setTable($table); return $panel; }
public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $id = $request->getURIData('id'); $is_all = $id === 'all'; $query = id(new PhabricatorAuthSessionQuery())->setViewer($viewer)->withIdentityPHIDs(array($viewer->getPHID())); if (!$is_all) { $query->withIDs(array($id)); } $current_key = PhabricatorHash::digest($request->getCookie(PhabricatorCookies::COOKIE_SESSION)); $sessions = $query->execute(); foreach ($sessions as $key => $session) { $is_current = phutil_hashes_are_identical($session->getSessionKey(), $current_key); if ($is_current) { // Don't terminate the current login session. unset($sessions[$key]); } } $panel_uri = '/settings/panel/sessions/'; if (!$sessions) { return $this->newDialog()->setTitle(pht('No Matching Sessions'))->appendParagraph(pht('There are no matching sessions to terminate.'))->appendParagraph(pht('(You can not terminate your current login session. To ' . 'terminate it, log out.)'))->addCancelButton($panel_uri); } if ($request->isDialogFormPost()) { foreach ($sessions as $session) { $session->delete(); } return id(new AphrontRedirectResponse())->setURI($panel_uri); } if ($is_all) { $title = pht('Terminate Sessions?'); $short = pht('Terminate Sessions'); $body = pht('Really terminate all sessions? (Your current login session will ' . 'not be terminated.)'); } else { $title = pht('Terminate Session?'); $short = pht('Terminate Session'); $body = pht('Really terminate session %s?', phutil_tag('strong', array(), substr($session->getSessionKey(), 0, 6))); } return $this->newDialog()->setTitle($title)->setShortTitle($short)->appendParagraph($body)->addSubmitButton(pht('Terminate'))->addCancelButton($panel_uri); }
public function handleRequest(AphrontRequest $request) { $viewer = $request->getUser(); if ($viewer->isLoggedIn()) { // Kick the user home if they are already logged in. return id(new AphrontRedirectResponse())->setURI('/'); } if ($request->isAjax()) { return $this->processAjaxRequest(); } if ($request->isConduit()) { return $this->processConduitRequest(); } // If the user gets this far, they aren't logged in, so if they have a // user session token we can conclude that it's invalid: if it was valid, // they'd have been logged in above and never made it here. Try to clear // it and warn the user they may need to nuke their cookies. $session_token = $request->getCookie(PhabricatorCookies::COOKIE_SESSION); if (strlen($session_token)) { $kind = PhabricatorAuthSessionEngine::getSessionKindFromToken($session_token); switch ($kind) { case PhabricatorAuthSessionEngine::KIND_ANONYMOUS: // If this is an anonymous session. It's expected that they won't // be logged in, so we can just continue. break; default: // The session cookie is invalid, so clear it. $request->clearCookie(PhabricatorCookies::COOKIE_USERNAME); $request->clearCookie(PhabricatorCookies::COOKIE_SESSION); return $this->renderError(pht('Your login session is invalid. Try reloading the page and ' . 'logging in again. If that does not work, clear your browser ' . 'cookies.')); } } $providers = PhabricatorAuthProvider::getAllEnabledProviders(); foreach ($providers as $key => $provider) { if (!$provider->shouldAllowLogin()) { unset($providers[$key]); } } if (!$providers) { if ($this->isFirstTimeSetup()) { // If this is a fresh install, let the user register their admin // account. return id(new AphrontRedirectResponse())->setURI($this->getApplicationURI('/register/')); } return $this->renderError(pht('This Phabricator install is not configured with any enabled ' . 'authentication providers which can be used to log in. If you ' . 'have accidentally locked yourself out by disabling all providers, ' . 'you can use `%s` to recover access to an administrative account.', 'phabricator/bin/auth recover <username>')); } $next_uri = $request->getStr('next'); if (!strlen($next_uri)) { if ($this->getDelegatingController()) { // Only set a next URI from the request path if this controller was // delegated to, which happens when a user tries to view a page which // requires them to login. // If this controller handled the request directly, we're on the main // login page, and never want to redirect the user back here after they // login. $next_uri = (string) $this->getRequest()->getRequestURI(); } } if (!$request->isFormPost()) { if (strlen($next_uri)) { PhabricatorCookies::setNextURICookie($request, $next_uri); } PhabricatorCookies::setClientIDCookie($request); } if (!$request->getURIData('loggedout') && count($providers) == 1) { $auto_login_provider = head($providers); $auto_login_config = $auto_login_provider->getProviderConfig(); if ($auto_login_provider instanceof PhabricatorPhabricatorAuthProvider && $auto_login_config->getShouldAutoLogin()) { $auto_login_adapter = $provider->getAdapter(); $auto_login_adapter->setState($provider->getAuthCSRFCode($request)); return id(new AphrontRedirectResponse())->setIsExternal(true)->setURI($provider->getAdapter()->getAuthenticateURI()); } } $invite = $this->loadInvite(); $not_buttons = array(); $are_buttons = array(); $providers = msort($providers, 'getLoginOrder'); foreach ($providers as $provider) { if ($invite) { $form = $provider->buildInviteForm($this); } else { $form = $provider->buildLoginForm($this); } if ($provider->isLoginFormAButton()) { $are_buttons[] = $form; } else { $not_buttons[] = $form; } } $out = array(); $out[] = $not_buttons; if ($are_buttons) { require_celerity_resource('auth-css'); foreach ($are_buttons as $key => $button) { $are_buttons[$key] = phutil_tag('div', array('class' => 'phabricator-login-button mmb'), $button); } // If we only have one button, add a second pretend button so that we // always have two columns. This makes it easier to get the alignments // looking reasonable. if (count($are_buttons) == 1) { $are_buttons[] = null; } $button_columns = id(new AphrontMultiColumnView())->setFluidLayout(true); $are_buttons = array_chunk($are_buttons, ceil(count($are_buttons) / 2)); foreach ($are_buttons as $column) { $button_columns->addColumn($column); } $out[] = phutil_tag('div', array('class' => 'phabricator-login-buttons'), $button_columns); } $login_message = PhabricatorEnv::getEnvConfig('auth.login-message'); $login_message = phutil_safe_html($login_message); $invite_message = null; if ($invite) { $invite_message = $this->renderInviteHeader($invite); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Login')); $crumbs->setBorder(true); return $this->buildApplicationPage(array($crumbs, $login_message, $invite_message, $out), array('title' => pht('Login to Phabricator'))); }
public function getAuthCSRFCode(AphrontRequest $request) { $phcid = $request->getCookie(PhabricatorCookies::COOKIE_CLIENTID); if (!strlen($phcid)) { throw new Exception(pht('Your browser did not submit a "%s" cookie with client state ' . 'information in the request. Check that cookies are enabled. ' . 'If this problem persists, you may need to clear your cookies.', PhabricatorCookies::COOKIE_CLIENTID)); } return PhabricatorHash::digest($phcid); }
public function handleRequest(AphrontRequest $request) { $viewer = $request->getUser(); if ($viewer->isLoggedIn()) { // Kick the user home if they are already logged in. return id(new AphrontRedirectResponse())->setURI('/'); } if ($request->isAjax()) { return $this->processAjaxRequest(); } if ($request->isConduit()) { return $this->processConduitRequest(); } // If the user gets this far, they aren't logged in, so if they have a // user session token we can conclude that it's invalid: if it was valid, // they'd have been logged in above and never made it here. Try to clear // it and warn the user they may need to nuke their cookies. $session_token = $request->getCookie(PhabricatorCookies::COOKIE_SESSION); $did_clear = $request->getStr('cleared'); if (strlen($session_token)) { $kind = PhabricatorAuthSessionEngine::getSessionKindFromToken($session_token); switch ($kind) { case PhabricatorAuthSessionEngine::KIND_ANONYMOUS: // If this is an anonymous session. It's expected that they won't // be logged in, so we can just continue. break; default: // The session cookie is invalid, so try to clear it. $request->clearCookie(PhabricatorCookies::COOKIE_USERNAME); $request->clearCookie(PhabricatorCookies::COOKIE_SESSION); // We've previously tried to clear the cookie but we ended up back // here, so it didn't work. Hard fatal instead of trying again. if ($did_clear) { return $this->renderError(pht('Your login session is invalid, and clearing the session ' . 'cookie was unsuccessful. Try clearing your browser cookies.')); } $redirect_uri = $request->getRequestURI(); $redirect_uri->setQueryParam('cleared', 1); return id(new AphrontRedirectResponse())->setURI($redirect_uri); } } // If we just cleared the session cookie and it worked, clean up after // ourselves by redirecting to get rid of the "cleared" parameter. The // the workflow will continue normally. if ($did_clear) { $redirect_uri = $request->getRequestURI(); $redirect_uri->setQueryParam('cleared', null); return id(new AphrontRedirectResponse())->setURI($redirect_uri); } $providers = PhabricatorAuthProvider::getAllEnabledProviders(); foreach ($providers as $key => $provider) { if (!$provider->shouldAllowLogin()) { unset($providers[$key]); } } if (!$providers) { if ($this->isFirstTimeSetup()) { // If this is a fresh install, let the user register their admin // account. return id(new AphrontRedirectResponse())->setURI($this->getApplicationURI('/register/')); } return $this->renderError(pht('This Phabricator install is not configured with any enabled ' . 'authentication providers which can be used to log in. If you ' . 'have accidentally locked yourself out by disabling all providers, ' . 'you can use `%s` to recover access to an administrative account.', 'phabricator/bin/auth recover <username>')); } $next_uri = $request->getStr('next'); if (!strlen($next_uri)) { if ($this->getDelegatingController()) { // Only set a next URI from the request path if this controller was // delegated to, which happens when a user tries to view a page which // requires them to login. // If this controller handled the request directly, we're on the main // login page, and never want to redirect the user back here after they // login. $next_uri = (string) $this->getRequest()->getRequestURI(); } } if (!$request->isFormPost()) { if (strlen($next_uri)) { PhabricatorCookies::setNextURICookie($request, $next_uri); } PhabricatorCookies::setClientIDCookie($request); } $auto_response = $this->tryAutoLogin($providers); if ($auto_response) { return $auto_response; } $invite = $this->loadInvite(); $not_buttons = array(); $are_buttons = array(); $providers = msort($providers, 'getLoginOrder'); foreach ($providers as $provider) { if ($invite) { $form = $provider->buildInviteForm($this); } else { $form = $provider->buildLoginForm($this); } if ($provider->isLoginFormAButton()) { $are_buttons[] = $form; } else { $not_buttons[] = $form; } } $out = array(); $out[] = $not_buttons; if ($are_buttons) { require_celerity_resource('auth-css'); foreach ($are_buttons as $key => $button) { $are_buttons[$key] = phutil_tag('div', array('class' => 'phabricator-login-button mmb'), $button); } // If we only have one button, add a second pretend button so that we // always have two columns. This makes it easier to get the alignments // looking reasonable. if (count($are_buttons) == 1) { $are_buttons[] = null; } $button_columns = id(new AphrontMultiColumnView())->setFluidLayout(true); $are_buttons = array_chunk($are_buttons, ceil(count($are_buttons) / 2)); foreach ($are_buttons as $column) { $button_columns->addColumn($column); } $out[] = phutil_tag('div', array('class' => 'phabricator-login-buttons'), $button_columns); } $handlers = PhabricatorAuthLoginHandler::getAllHandlers(); $delegating_controller = $this->getDelegatingController(); $header = array(); foreach ($handlers as $handler) { $handler = clone $handler; $handler->setRequest($request); if ($delegating_controller) { $handler->setDelegatingController($delegating_controller); } $header[] = $handler->getAuthLoginHeaderContent(); } $invite_message = null; if ($invite) { $invite_message = $this->renderInviteHeader($invite); } $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Login')); $crumbs->setBorder(true); $title = pht('Login to Phabricator'); $view = array($header, $invite_message, $out); return $this->newPage()->setTitle($title)->setCrumbs($crumbs)->appendChild($view); }
private function renderPasswordLoginForm(AphrontRequest $request, $require_captcha = false, $captcha_valid = false) { $viewer = $request->getUser(); $dialog = id(new AphrontDialogView())->setSubmitURI($this->getLoginURI())->setUser($viewer)->setTitle(pht('Login to Phabricator'))->addSubmitButton(pht('Login')); if ($this->shouldAllowRegistration()) { $dialog->addCancelButton('/auth/register/', pht('Register New Account')); } $dialog->addFooter(phutil_tag('a', array('href' => '/login/email/'), pht('Forgot your password?'))); $v_user = nonempty($request->getStr('username'), $request->getCookie(PhabricatorCookies::COOKIE_USERNAME)); $e_user = null; $e_pass = null; $e_captcha = null; $errors = array(); if ($require_captcha && !$captcha_valid) { if (AphrontFormRecaptchaControl::hasCaptchaResponse($request)) { $e_captcha = pht('Invalid'); $errors[] = pht('CAPTCHA was not entered correctly.'); } else { $e_captcha = pht('Required'); $errors[] = pht('Too many login failures recently. You must ' . 'submit a CAPTCHA with your login request.'); } } else { if ($request->isHTTPPost()) { // NOTE: This is intentionally vague so as not to disclose whether a // given username or email is registered. $e_user = pht('Invalid'); $e_pass = pht('Invalid'); $errors[] = pht('Username or password are incorrect.'); } } if ($errors) { $errors = id(new PHUIInfoView())->setErrors($errors); } $form = id(new PHUIFormLayoutView())->setFullWidth(true)->appendChild($errors)->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Username or Email'))->setName('username')->setValue($v_user)->setError($e_user))->appendChild(id(new AphrontFormPasswordControl())->setLabel(pht('Password'))->setName('password')->setError($e_pass)); if ($require_captcha) { $form->appendChild(id(new AphrontFormRecaptchaControl())->setError($e_captcha)); } $dialog->appendChild($form); return $dialog; }
/** * Read the URI out of the Next URI cookie. * * @param AphrontRequest Request to examine. * @return string|null Next URI cookie's URI value. * * @task next */ public static function getNextURICookie(AphrontRequest $request) { $cookie_value = $request->getCookie(self::COOKIE_NEXTURI); list($set_at, $next_uri) = self::parseNextURICookie($cookie_value); return $next_uri; }