[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/auth/provider/ -> PhabricatorPasswordAuthProvider.php (source)

   1  <?php
   2  
   3  final class PhabricatorPasswordAuthProvider extends PhabricatorAuthProvider {
   4  
   5    private $adapter;
   6  
   7    public function getProviderName() {
   8      return pht('Username/Password');
   9    }
  10  
  11    public function getConfigurationHelp() {
  12      return pht(
  13        "(WARNING) Examine the table below for information on how password ".
  14        "hashes will be stored in the database.\n\n".
  15        "(NOTE) You can select a minimum password length by setting ".
  16        "`account.minimum-password-length` in configuration.");
  17    }
  18  
  19    public function renderConfigurationFooter() {
  20      $hashers = PhabricatorPasswordHasher::getAllHashers();
  21      $hashers = msort($hashers, 'getStrength');
  22      $hashers = array_reverse($hashers);
  23  
  24      $yes = phutil_tag(
  25        'strong',
  26        array(
  27          'style' => 'color: #009900',
  28        ),
  29        pht('Yes'));
  30  
  31      $no = phutil_tag(
  32        'strong',
  33        array(
  34          'style' => 'color: #990000',
  35        ),
  36        pht('Not Installed'));
  37  
  38      $best_hasher_name = null;
  39      try {
  40        $best_hasher = PhabricatorPasswordHasher::getBestHasher();
  41        $best_hasher_name = $best_hasher->getHashName();
  42      } catch (PhabricatorPasswordHasherUnavailableException $ex) {
  43        // There are no suitable hashers. The user might be able to enable some,
  44        // so we don't want to fatal here. We'll fatal when users try to actually
  45        // use this stuff if it isn't fixed before then. Until then, we just
  46        // don't highlight a row. In practice, at least one hasher should always
  47        // be available.
  48      }
  49  
  50      $rows = array();
  51      $rowc = array();
  52      foreach ($hashers as $hasher) {
  53        $is_installed = $hasher->canHashPasswords();
  54  
  55        $rows[] = array(
  56          $hasher->getHumanReadableName(),
  57          $hasher->getHashName(),
  58          $hasher->getHumanReadableStrength(),
  59          ($is_installed ? $yes : $no),
  60          ($is_installed ? null : $hasher->getInstallInstructions()),
  61        );
  62        $rowc[] = ($best_hasher_name == $hasher->getHashName())
  63          ? 'highlighted'
  64          : null;
  65      }
  66  
  67      $table = new AphrontTableView($rows);
  68      $table->setRowClasses($rowc);
  69      $table->setHeaders(
  70        array(
  71          pht('Algorithm'),
  72          pht('Name'),
  73          pht('Strength'),
  74          pht('Installed'),
  75          pht('Install Instructions'),
  76        ));
  77  
  78      $table->setColumnClasses(
  79        array(
  80          '',
  81          '',
  82          '',
  83          '',
  84          'wide',
  85        ));
  86  
  87      $header = id(new PHUIHeaderView())
  88        ->setHeader(pht('Password Hash Algorithms'))
  89        ->setSubheader(
  90          pht(
  91            'Stronger algorithms are listed first. The highlighted algorithm '.
  92            'will be used when storing new hashes. Older hashes will be '.
  93            'upgraded to the best algorithm over time.'));
  94  
  95      return id(new PHUIObjectBoxView())
  96        ->setHeader($header)
  97        ->appendChild($table);
  98    }
  99  
 100    public function getDescriptionForCreate() {
 101      return pht(
 102        'Allow users to login or register using a username and password.');
 103    }
 104  
 105    public function getAdapter() {
 106      if (!$this->adapter) {
 107        $adapter = new PhutilEmptyAuthAdapter();
 108        $adapter->setAdapterType('password');
 109        $adapter->setAdapterDomain('self');
 110        $this->adapter = $adapter;
 111      }
 112      return $this->adapter;
 113    }
 114  
 115    public function getLoginOrder() {
 116      // Make sure username/password appears first if it is enabled.
 117      return '100-'.$this->getProviderName();
 118    }
 119  
 120    public function shouldAllowAccountLink() {
 121      return false;
 122    }
 123  
 124    public function shouldAllowAccountUnlink() {
 125      return false;
 126    }
 127  
 128    public function isDefaultRegistrationProvider() {
 129      return true;
 130    }
 131  
 132    public function buildLoginForm(
 133      PhabricatorAuthStartController $controller) {
 134      $request = $controller->getRequest();
 135      return $this->renderPasswordLoginForm($request);
 136    }
 137  
 138    public function buildLinkForm(
 139      PhabricatorAuthLinkController $controller) {
 140      throw new Exception("Password providers can't be linked.");
 141    }
 142  
 143    private function renderPasswordLoginForm(
 144      AphrontRequest $request,
 145      $require_captcha = false,
 146      $captcha_valid = false) {
 147  
 148      $viewer = $request->getUser();
 149  
 150      $dialog = id(new AphrontDialogView())
 151        ->setSubmitURI($this->getLoginURI())
 152        ->setUser($viewer)
 153        ->setTitle(pht('Login to Phabricator'))
 154        ->addSubmitButton(pht('Login'));
 155  
 156      if ($this->shouldAllowRegistration()) {
 157        $dialog->addCancelButton(
 158          '/auth/register/',
 159          pht('Register New Account'));
 160      }
 161  
 162      $dialog->addFooter(
 163        phutil_tag(
 164          'a',
 165          array(
 166            'href' => '/login/email/',
 167          ),
 168          pht('Forgot your password?')));
 169  
 170      $v_user = nonempty(
 171        $request->getStr('username'),
 172        $request->getCookie(PhabricatorCookies::COOKIE_USERNAME));
 173  
 174      $e_user = null;
 175      $e_pass = null;
 176      $e_captcha = null;
 177  
 178      $errors = array();
 179      if ($require_captcha && !$captcha_valid) {
 180        if (AphrontFormRecaptchaControl::hasCaptchaResponse($request)) {
 181          $e_captcha = pht('Invalid');
 182          $errors[] = pht('CAPTCHA was not entered correctly.');
 183        } else {
 184          $e_captcha = pht('Required');
 185          $errors[] = pht('Too many login failures recently. You must '.
 186                      'submit a CAPTCHA with your login request.');
 187        }
 188      } else if ($request->isHTTPPost()) {
 189        // NOTE: This is intentionally vague so as not to disclose whether a
 190        // given username or email is registered.
 191        $e_user = pht('Invalid');
 192        $e_pass = pht('Invalid');
 193        $errors[] = pht('Username or password are incorrect.');
 194      }
 195  
 196      if ($errors) {
 197        $errors = id(new AphrontErrorView())->setErrors($errors);
 198      }
 199  
 200      $form = id(new PHUIFormLayoutView())
 201        ->setFullWidth(true)
 202        ->appendChild($errors)
 203        ->appendChild(
 204          id(new AphrontFormTextControl())
 205            ->setLabel('Username or Email')
 206            ->setName('username')
 207            ->setValue($v_user)
 208            ->setError($e_user))
 209        ->appendChild(
 210          id(new AphrontFormPasswordControl())
 211            ->setLabel('Password')
 212            ->setName('password')
 213            ->setError($e_pass));
 214  
 215      if ($require_captcha) {
 216          $form->appendChild(
 217            id(new AphrontFormRecaptchaControl())
 218              ->setError($e_captcha));
 219      }
 220  
 221      $dialog->appendChild($form);
 222  
 223      return $dialog;
 224    }
 225  
 226    public function processLoginRequest(
 227      PhabricatorAuthLoginController $controller) {
 228  
 229      $request = $controller->getRequest();
 230      $viewer = $request->getUser();
 231  
 232      $require_captcha = false;
 233      $captcha_valid = false;
 234      if (AphrontFormRecaptchaControl::isRecaptchaEnabled()) {
 235        $failed_attempts = PhabricatorUserLog::loadRecentEventsFromThisIP(
 236          PhabricatorUserLog::ACTION_LOGIN_FAILURE,
 237          60 * 15);
 238        if (count($failed_attempts) > 5) {
 239          $require_captcha = true;
 240          $captcha_valid = AphrontFormRecaptchaControl::processCaptcha($request);
 241        }
 242      }
 243  
 244      $response = null;
 245      $account = null;
 246      $log_user = null;
 247  
 248      if ($request->isFormPost()) {
 249        if (!$require_captcha || $captcha_valid) {
 250          $username_or_email = $request->getStr('username');
 251          if (strlen($username_or_email)) {
 252            $user = id(new PhabricatorUser())->loadOneWhere(
 253              'username = %s',
 254              $username_or_email);
 255  
 256            if (!$user) {
 257              $user = PhabricatorUser::loadOneWithEmailAddress(
 258                $username_or_email);
 259            }
 260  
 261            if ($user) {
 262              $envelope = new PhutilOpaqueEnvelope($request->getStr('password'));
 263              if ($user->comparePassword($envelope)) {
 264                $account = $this->loadOrCreateAccount($user->getPHID());
 265                $log_user = $user;
 266  
 267                // If the user's password is stored using a less-than-optimal
 268                // hash, upgrade them to the strongest available hash.
 269  
 270                $hash_envelope = new PhutilOpaqueEnvelope(
 271                  $user->getPasswordHash());
 272                if (PhabricatorPasswordHasher::canUpgradeHash($hash_envelope)) {
 273                  $user->setPassword($envelope);
 274  
 275                  $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
 276                    $user->save();
 277                  unset($unguarded);
 278                }
 279              }
 280            }
 281          }
 282        }
 283      }
 284  
 285      if (!$account) {
 286        if ($request->isFormPost()) {
 287          $log = PhabricatorUserLog::initializeNewLog(
 288            null,
 289            $log_user ? $log_user->getPHID() : null,
 290            PhabricatorUserLog::ACTION_LOGIN_FAILURE);
 291          $log->save();
 292        }
 293  
 294        $request->clearCookie(PhabricatorCookies::COOKIE_USERNAME);
 295  
 296        $response = $controller->buildProviderPageResponse(
 297          $this,
 298          $this->renderPasswordLoginForm(
 299            $request,
 300            $require_captcha,
 301            $captcha_valid));
 302      }
 303  
 304      return array($account, $response);
 305    }
 306  
 307    public function shouldRequireRegistrationPassword() {
 308      return true;
 309    }
 310  
 311    public function getDefaultExternalAccount() {
 312      $adapter = $this->getAdapter();
 313  
 314      return id(new PhabricatorExternalAccount())
 315        ->setAccountType($adapter->getAdapterType())
 316        ->setAccountDomain($adapter->getAdapterDomain());
 317    }
 318  
 319    protected function willSaveAccount(PhabricatorExternalAccount $account) {
 320      parent::willSaveAccount($account);
 321      $account->setUserPHID($account->getAccountID());
 322    }
 323  
 324    public function willRegisterAccount(PhabricatorExternalAccount $account) {
 325      parent::willRegisterAccount($account);
 326      $account->setAccountID($account->getUserPHID());
 327    }
 328  
 329    public static function getPasswordProvider() {
 330      $providers = self::getAllEnabledProviders();
 331  
 332      foreach ($providers as $provider) {
 333        if ($provider instanceof PhabricatorPasswordAuthProvider) {
 334          return $provider;
 335        }
 336      }
 337  
 338      return null;
 339    }
 340  
 341    public function willRenderLinkedAccount(
 342      PhabricatorUser $viewer,
 343      PHUIObjectItemView $item,
 344      PhabricatorExternalAccount $account) {
 345      return;
 346    }
 347  
 348    public function shouldAllowAccountRefresh() {
 349      return false;
 350    }
 351  
 352    public function shouldAllowEmailTrustConfiguration() {
 353      return false;
 354    }
 355  }


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