[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/auth/controller/ -> PhabricatorAuthController.php (source)

   1  <?php
   2  
   3  abstract class PhabricatorAuthController extends PhabricatorController {
   4  
   5    public function buildStandardPageResponse($view, array $data) {
   6      $page = $this->buildStandardPageView();
   7  
   8      $page->setApplicationName(pht('Login'));
   9      $page->setBaseURI('/login/');
  10      $page->setTitle(idx($data, 'title'));
  11      $page->appendChild($view);
  12  
  13      $response = new AphrontWebpageResponse();
  14      return $response->setContent($page->render());
  15    }
  16  
  17    protected function renderErrorPage($title, array $messages) {
  18      $view = new AphrontErrorView();
  19      $view->setTitle($title);
  20      $view->setErrors($messages);
  21  
  22      return $this->buildApplicationPage(
  23        $view,
  24        array(
  25          'title' => $title,
  26        ));
  27  
  28    }
  29  
  30    /**
  31     * Returns true if this install is newly setup (i.e., there are no user
  32     * accounts yet). In this case, we enter a special mode to permit creation
  33     * of the first account form the web UI.
  34     */
  35    protected function isFirstTimeSetup() {
  36      // If there are any auth providers, this isn't first time setup, even if
  37      // we don't have accounts.
  38      if (PhabricatorAuthProvider::getAllEnabledProviders()) {
  39        return false;
  40      }
  41  
  42      // Otherwise, check if there are any user accounts. If not, we're in first
  43      // time setup.
  44      $any_users = id(new PhabricatorPeopleQuery())
  45        ->setViewer(PhabricatorUser::getOmnipotentUser())
  46        ->setLimit(1)
  47        ->execute();
  48  
  49      return !$any_users;
  50    }
  51  
  52  
  53    /**
  54     * Log a user into a web session and return an @{class:AphrontResponse} which
  55     * corresponds to continuing the login process.
  56     *
  57     * Normally, this is a redirect to the validation controller which makes sure
  58     * the user's cookies are set. However, event listeners can intercept this
  59     * event and do something else if they prefer.
  60     *
  61     * @param   PhabricatorUser   User to log the viewer in as.
  62     * @return  AphrontResponse   Response which continues the login process.
  63     */
  64    protected function loginUser(PhabricatorUser $user) {
  65  
  66      $response = $this->buildLoginValidateResponse($user);
  67      $session_type = PhabricatorAuthSession::TYPE_WEB;
  68  
  69      $event_type = PhabricatorEventType::TYPE_AUTH_WILLLOGINUSER;
  70      $event_data = array(
  71        'user'        => $user,
  72        'type'        => $session_type,
  73        'response'    => $response,
  74        'shouldLogin' => true,
  75      );
  76  
  77      $event = id(new PhabricatorEvent($event_type, $event_data))
  78        ->setUser($user);
  79      PhutilEventEngine::dispatchEvent($event);
  80  
  81      $should_login = $event->getValue('shouldLogin');
  82      if ($should_login) {
  83        $session_key = id(new PhabricatorAuthSessionEngine())
  84          ->establishSession($session_type, $user->getPHID(), $partial = true);
  85  
  86        // NOTE: We allow disabled users to login and roadblock them later, so
  87        // there's no check for users being disabled here.
  88  
  89        $request = $this->getRequest();
  90        $request->setCookie(
  91          PhabricatorCookies::COOKIE_USERNAME,
  92          $user->getUsername());
  93        $request->setCookie(
  94          PhabricatorCookies::COOKIE_SESSION,
  95          $session_key);
  96  
  97        $this->clearRegistrationCookies();
  98      }
  99  
 100      return $event->getValue('response');
 101    }
 102  
 103    protected function clearRegistrationCookies() {
 104      $request = $this->getRequest();
 105  
 106      // Clear the registration key.
 107      $request->clearCookie(PhabricatorCookies::COOKIE_REGISTRATION);
 108  
 109      // Clear the client ID / OAuth state key.
 110      $request->clearCookie(PhabricatorCookies::COOKIE_CLIENTID);
 111    }
 112  
 113    private function buildLoginValidateResponse(PhabricatorUser $user) {
 114      $validate_uri = new PhutilURI($this->getApplicationURI('validate/'));
 115      $validate_uri->setQueryParam('expect', $user->getUsername());
 116  
 117      return id(new AphrontRedirectResponse())->setURI((string)$validate_uri);
 118    }
 119  
 120    protected function renderError($message) {
 121      return $this->renderErrorPage(
 122        pht('Authentication Error'),
 123        array(
 124          $message,
 125        ));
 126    }
 127  
 128    protected function loadAccountForRegistrationOrLinking($account_key) {
 129      $request = $this->getRequest();
 130      $viewer = $request->getUser();
 131  
 132      $account = null;
 133      $provider = null;
 134      $response = null;
 135  
 136      if (!$account_key) {
 137        $response = $this->renderError(
 138          pht('Request did not include account key.'));
 139        return array($account, $provider, $response);
 140      }
 141  
 142      // NOTE: We're using the omnipotent user because the actual user may not
 143      // be logged in yet, and because we want to tailor an error message to
 144      // distinguish between "not usable" and "does not exist". We do explicit
 145      // checks later on to make sure this account is valid for the intended
 146      // operation. This requires edit permission for completeness and consistency
 147      // but it won't actually be meaningfully checked because we're using the
 148      // ominpotent user.
 149  
 150      $account = id(new PhabricatorExternalAccountQuery())
 151        ->setViewer(PhabricatorUser::getOmnipotentUser())
 152        ->withAccountSecrets(array($account_key))
 153        ->needImages(true)
 154        ->requireCapabilities(
 155          array(
 156            PhabricatorPolicyCapability::CAN_VIEW,
 157            PhabricatorPolicyCapability::CAN_EDIT,
 158          ))
 159        ->executeOne();
 160  
 161      if (!$account) {
 162        $response = $this->renderError(pht('No valid linkable account.'));
 163        return array($account, $provider, $response);
 164      }
 165  
 166      if ($account->getUserPHID()) {
 167        if ($account->getUserPHID() != $viewer->getPHID()) {
 168          $response = $this->renderError(
 169            pht(
 170              'The account you are attempting to register or link is already '.
 171              'linked to another user.'));
 172        } else {
 173          $response = $this->renderError(
 174            pht(
 175              'The account you are attempting to link is already linked '.
 176              'to your account.'));
 177        }
 178        return array($account, $provider, $response);
 179      }
 180  
 181      $registration_key = $request->getCookie(
 182        PhabricatorCookies::COOKIE_REGISTRATION);
 183  
 184      // NOTE: This registration key check is not strictly necessary, because
 185      // we're only creating new accounts, not linking existing accounts. It
 186      // might be more hassle than it is worth, especially for email.
 187      //
 188      // The attack this prevents is getting to the registration screen, then
 189      // copy/pasting the URL and getting someone else to click it and complete
 190      // the process. They end up with an account bound to credentials you
 191      // control. This doesn't really let you do anything meaningful, though,
 192      // since you could have simply completed the process yourself.
 193  
 194      if (!$registration_key) {
 195        $response = $this->renderError(
 196          pht(
 197            'Your browser did not submit a registration key with the request. '.
 198            'You must use the same browser to begin and complete registration. '.
 199            'Check that cookies are enabled and try again.'));
 200        return array($account, $provider, $response);
 201      }
 202  
 203      // We store the digest of the key rather than the key itself to prevent a
 204      // theoretical attacker with read-only access to the database from
 205      // hijacking registration sessions.
 206  
 207      $actual = $account->getProperty('registrationKey');
 208      $expect = PhabricatorHash::digest($registration_key);
 209      if ($actual !== $expect) {
 210        $response = $this->renderError(
 211          pht(
 212            'Your browser submitted a different registration key than the one '.
 213            'associated with this account. You may need to clear your cookies.'));
 214        return array($account, $provider, $response);
 215      }
 216  
 217      $other_account = id(new PhabricatorExternalAccount())->loadAllWhere(
 218        'accountType = %s AND accountDomain = %s AND accountID = %s
 219          AND id != %d',
 220        $account->getAccountType(),
 221        $account->getAccountDomain(),
 222        $account->getAccountID(),
 223        $account->getID());
 224  
 225      if ($other_account) {
 226        $response = $this->renderError(
 227          pht(
 228            'The account you are attempting to register with already belongs '.
 229            'to another user.'));
 230        return array($account, $provider, $response);
 231      }
 232  
 233      $provider = PhabricatorAuthProvider::getEnabledProviderByKey(
 234        $account->getProviderKey());
 235  
 236      if (!$provider) {
 237        $response = $this->renderError(
 238          pht(
 239            'The account you are attempting to register with uses a nonexistent '.
 240            'or disabled authentication provider (with key "%s"). An '.
 241            'administrator may have recently disabled this provider.',
 242            $account->getProviderKey()));
 243        return array($account, $provider, $response);
 244      }
 245  
 246      return array($account, $provider, null);
 247    }
 248  
 249  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1