/**
  * Returns true if this install is newly setup (i.e., there are no user
  * accounts yet). In this case, we enter a special mode to permit creation
  * of the first account form the web UI.
  */
 protected function isFirstTimeSetup()
 {
     // If there are any auth providers, this isn't first time setup, even if
     // we don't have accounts.
     if (PhabricatorAuthProvider::getAllEnabledProviders()) {
         return false;
     }
     // Otherwise, check if there are any user accounts. If not, we're in first
     // time setup.
     $any_users = id(new PhabricatorPeopleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->setLimit(1)->execute();
     return !$any_users;
 }
 public function processRequest(AphrontRequest $request)
 {
     $viewer = $request->getUser();
     $providers = PhabricatorAuthProvider::getAllProviders();
     $accounts = id(new PhabricatorExternalAccountQuery())->setViewer($viewer)->withUserPHIDs(array($viewer->getPHID()))->needImages(true)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->execute();
     $linked_head = id(new PHUIHeaderView())->setHeader(pht('Linked Accounts and Authentication'));
     $linked = id(new PHUIObjectItemListView())->setUser($viewer)->setFlush(true)->setNoDataString(pht('You have no linked accounts.'));
     $login_accounts = 0;
     foreach ($accounts as $account) {
         if ($account->isUsableForLogin()) {
             $login_accounts++;
         }
     }
     foreach ($accounts as $account) {
         $item = id(new PHUIObjectItemView());
         $provider = idx($providers, $account->getProviderKey());
         if ($provider) {
             $item->setHeader($provider->getProviderName());
             $can_unlink = $provider->shouldAllowAccountUnlink();
             if (!$can_unlink) {
                 $item->addAttribute(pht('Permanently Linked'));
             }
         } else {
             $item->setHeader(pht('Unknown Account ("%s")', $account->getProviderKey()));
             $can_unlink = true;
         }
         $can_login = $account->isUsableForLogin();
         if (!$can_login) {
             $item->addAttribute(pht('Disabled (an administrator has disabled login for this ' . 'account provider).'));
         }
         $can_unlink = $can_unlink && (!$can_login || $login_accounts > 1);
         $can_refresh = $provider && $provider->shouldAllowAccountRefresh();
         if ($can_refresh) {
             $item->addAction(id(new PHUIListItemView())->setIcon('fa-refresh')->setHref('/auth/refresh/' . $account->getProviderKey() . '/'));
         }
         $item->addAction(id(new PHUIListItemView())->setIcon('fa-times')->setWorkflow(true)->setDisabled(!$can_unlink)->setHref('/auth/unlink/' . $account->getProviderKey() . '/'));
         if ($provider) {
             $provider->willRenderLinkedAccount($viewer, $item, $account);
         }
         $linked->addItem($item);
     }
     $linkable_head = id(new PHUIHeaderView())->setHeader(pht('Add External Account'));
     $linkable = id(new PHUIObjectItemListView())->setUser($viewer)->setFlush(true)->setNoDataString(pht('Your account is linked with all available providers.'));
     $accounts = mpull($accounts, null, 'getProviderKey');
     $providers = PhabricatorAuthProvider::getAllEnabledProviders();
     $providers = msort($providers, 'getProviderName');
     foreach ($providers as $key => $provider) {
         if (isset($accounts[$key])) {
             continue;
         }
         if (!$provider->shouldAllowAccountLink()) {
             continue;
         }
         $link_uri = '/auth/link/' . $provider->getProviderKey() . '/';
         $item = id(new PHUIObjectItemView());
         $item->setHeader($provider->getProviderName());
         $item->setHref($link_uri);
         $item->addAction(id(new PHUIListItemView())->setIcon('fa-link')->setHref($link_uri));
         $linkable->addItem($item);
     }
     $linked_box = id(new PHUIObjectBoxView())->setHeader($linked_head)->setObjectList($linked);
     $linkable_box = id(new PHUIObjectBoxView())->setHeader($linkable_head)->setObjectList($linkable);
     return array($linked_box, $linkable_box);
 }
 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')));
 }
 private function loadDefaultAccount()
 {
     $providers = PhabricatorAuthProvider::getAllEnabledProviders();
     $account = null;
     $provider = null;
     $response = null;
     foreach ($providers as $key => $candidate_provider) {
         if (!$candidate_provider->shouldAllowRegistration()) {
             unset($providers[$key]);
             continue;
         }
         if (!$candidate_provider->isDefaultRegistrationProvider()) {
             unset($providers[$key]);
         }
     }
     if (!$providers) {
         $response = $this->renderError(pht('There are no configured default registration providers.'));
         return array($account, $provider, $response);
     } else {
         if (count($providers) > 1) {
             $response = $this->renderError(pht('There are too many configured default registration providers.'));
             return array($account, $provider, $response);
         }
     }
     $provider = head($providers);
     $account = $provider->getDefaultExternalAccount();
     return array($account, $provider, $response);
 }
 public function execute(PhutilArgumentParser $args)
 {
     $console = PhutilConsole::getConsole();
     $viewer = $this->getViewer();
     $query = id(new PhabricatorExternalAccountQuery())->setViewer($viewer)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT));
     $username = $args->getArg('user');
     if (strlen($username)) {
         $user = id(new PhabricatorPeopleQuery())->setViewer($viewer)->withUsernames(array($username))->executeOne();
         if ($user) {
             $query->withUserPHIDs(array($user->getPHID()));
         } else {
             throw new PhutilArgumentUsageException(pht('No such user "%s"!', $username));
         }
     }
     $type = $args->getArg('type');
     if (strlen($type)) {
         $query->withAccountTypes(array($type));
     }
     $domain = $args->getArg('domain');
     if (strlen($domain)) {
         $query->withAccountDomains(array($domain));
     }
     $accounts = $query->execute();
     if (!$accounts) {
         throw new PhutilArgumentUsageException(pht('No accounts match the arguments!'));
     } else {
         $console->writeOut("%s\n", pht('Found %s account(s) to refresh.', phutil_count($accounts)));
     }
     $providers = PhabricatorAuthProvider::getAllEnabledProviders();
     foreach ($accounts as $account) {
         $console->writeOut("%s\n", pht('Refreshing account #%d (%s/%s).', $account->getID(), $account->getAccountType(), $account->getAccountDomain()));
         $key = $account->getProviderKey();
         if (empty($providers[$key])) {
             $console->writeOut("> %s\n", pht('Skipping, provider is not enabled or does not exist.'));
             continue;
         }
         $provider = $providers[$key];
         if (!$provider instanceof PhabricatorOAuth2AuthProvider) {
             $console->writeOut("> %s\n", pht('Skipping, provider is not an OAuth2 provider.'));
             continue;
         }
         $adapter = $provider->getAdapter();
         if (!$adapter->supportsTokenRefresh()) {
             $console->writeOut("> %s\n", pht('Skipping, provider does not support token refresh.'));
             continue;
         }
         $refresh_token = $account->getProperty('oauth.token.refresh');
         if (!$refresh_token) {
             $console->writeOut("> %s\n", pht('Skipping, provider has no stored refresh token.'));
             continue;
         }
         $console->writeOut("+ %s\n", pht('Refreshing token, current token expires in %s seconds.', new PhutilNumber($account->getProperty('oauth.token.access.expires') - time())));
         $token = $provider->getOAuthAccessToken($account, $force_refresh = true);
         if (!$token) {
             $console->writeOut("* %s\n", pht('Unable to refresh token!'));
             continue;
         }
         $console->writeOut("+ %s\n", pht('Refreshed token, new token expires in %s seconds.', new PhutilNumber($account->getProperty('oauth.token.access.expires') - time())));
     }
     $console->writeOut("%s\n", pht('Done.'));
     return 0;
 }
 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);
 }