[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

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

   1  <?php
   2  
   3  final class PhabricatorAuthRegisterController
   4    extends PhabricatorAuthController {
   5  
   6    private $accountKey;
   7  
   8    public function shouldRequireLogin() {
   9      return false;
  10    }
  11  
  12    public function willProcessRequest(array $data) {
  13      $this->accountKey = idx($data, 'akey');
  14    }
  15  
  16    public function processRequest() {
  17      $request = $this->getRequest();
  18  
  19      if ($request->getUser()->isLoggedIn()) {
  20        return $this->renderError(pht('You are already logged in.'));
  21      }
  22  
  23      $is_setup = false;
  24      if (strlen($this->accountKey)) {
  25        $result = $this->loadAccountForRegistrationOrLinking($this->accountKey);
  26        list($account, $provider, $response) = $result;
  27        $is_default = false;
  28      } else if ($this->isFirstTimeSetup()) {
  29        list($account, $provider, $response) = $this->loadSetupAccount();
  30        $is_default = true;
  31        $is_setup = true;
  32      } else {
  33        list($account, $provider, $response) = $this->loadDefaultAccount();
  34        $is_default = true;
  35      }
  36  
  37      if ($response) {
  38        return $response;
  39      }
  40  
  41      if (!$provider->shouldAllowRegistration()) {
  42  
  43        // TODO: This is a routine error if you click "Login" on an external
  44        // auth source which doesn't allow registration. The error should be
  45        // more tailored.
  46  
  47        return $this->renderError(
  48          pht(
  49            'The account you are attempting to register with uses an '.
  50            'authentication provider ("%s") which does not allow registration. '.
  51            'An administrator may have recently disabled registration with this '.
  52            'provider.',
  53            $provider->getProviderName()));
  54      }
  55  
  56      $user = new PhabricatorUser();
  57  
  58      $default_username = $account->getUsername();
  59      $default_realname = $account->getRealName();
  60  
  61      $default_email = $account->getEmail();
  62      if (!PhabricatorUserEmail::isValidAddress($default_email)) {
  63        $default_email = null;
  64      }
  65  
  66      if ($default_email !== null) {
  67        // If the account source provided an email, but it's not allowed by
  68        // the configuration, roadblock the user. Previously, we let the user
  69        // pick a valid email address instead, but this does not align well with
  70        // user expectation and it's not clear the cases it enables are valuable.
  71        // See discussion in T3472.
  72        if (!PhabricatorUserEmail::isAllowedAddress($default_email)) {
  73          return $this->renderError(
  74            array(
  75              pht(
  76                'The account you are attempting to register with has an invalid '.
  77                'email address (%s). This Phabricator install only allows '.
  78                'registration with specific email addresses:',
  79                $default_email),
  80              phutil_tag('br'),
  81              phutil_tag('br'),
  82              PhabricatorUserEmail::describeAllowedAddresses(),
  83            ));
  84        }
  85  
  86        // If the account source provided an email, but another account already
  87        // has that email, just pretend we didn't get an email.
  88  
  89        // TODO: See T3340.
  90        // TODO: See T3472.
  91  
  92        if ($default_email !== null) {
  93          $same_email = id(new PhabricatorUserEmail())->loadOneWhere(
  94            'address = %s',
  95            $default_email);
  96          if ($same_email) {
  97            $default_email = null;
  98          }
  99        }
 100      }
 101  
 102      $profile = id(new PhabricatorRegistrationProfile())
 103        ->setDefaultUsername($default_username)
 104        ->setDefaultEmail($default_email)
 105        ->setDefaultRealName($default_realname)
 106        ->setCanEditUsername(true)
 107        ->setCanEditEmail(($default_email === null))
 108        ->setCanEditRealName(true)
 109        ->setShouldVerifyEmail(false);
 110  
 111      $event_type = PhabricatorEventType::TYPE_AUTH_WILLREGISTERUSER;
 112      $event_data = array(
 113        'account' => $account,
 114        'profile' => $profile,
 115      );
 116  
 117      $event = id(new PhabricatorEvent($event_type, $event_data))
 118        ->setUser($user);
 119      PhutilEventEngine::dispatchEvent($event);
 120  
 121      $default_username = $profile->getDefaultUsername();
 122      $default_email = $profile->getDefaultEmail();
 123      $default_realname = $profile->getDefaultRealName();
 124  
 125      $can_edit_username = $profile->getCanEditUsername();
 126      $can_edit_email = $profile->getCanEditEmail();
 127      $can_edit_realname = $profile->getCanEditRealName();
 128  
 129      $must_set_password = $provider->shouldRequireRegistrationPassword();
 130  
 131      $can_edit_anything = $profile->getCanEditAnything() || $must_set_password;
 132      $force_verify = $profile->getShouldVerifyEmail();
 133  
 134      // Automatically verify the administrator's email address during first-time
 135      // setup.
 136      if ($is_setup) {
 137        $force_verify = true;
 138      }
 139  
 140      $value_username = $default_username;
 141      $value_realname = $default_realname;
 142      $value_email = $default_email;
 143      $value_password = null;
 144  
 145      $errors = array();
 146  
 147      $require_real_name = PhabricatorEnv::getEnvConfig('user.require-real-name');
 148  
 149      $e_username = strlen($value_username) ? null : true;
 150      $e_realname = $require_real_name ? true : null;
 151      $e_email = strlen($value_email) ? null : true;
 152      $e_password = true;
 153      $e_captcha = true;
 154  
 155      $min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length');
 156      $min_len = (int)$min_len;
 157  
 158      if ($request->isFormPost() || !$can_edit_anything) {
 159        $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
 160  
 161        if ($must_set_password) {
 162          $e_captcha = pht('Again');
 163  
 164          $captcha_ok = AphrontFormRecaptchaControl::processCaptcha($request);
 165          if (!$captcha_ok) {
 166            $errors[] = pht('Captcha response is incorrect, try again.');
 167            $e_captcha = pht('Invalid');
 168          }
 169        }
 170  
 171        if ($can_edit_username) {
 172          $value_username = $request->getStr('username');
 173          if (!strlen($value_username)) {
 174            $e_username = pht('Required');
 175            $errors[] = pht('Username is required.');
 176          } else if (!PhabricatorUser::validateUsername($value_username)) {
 177            $e_username = pht('Invalid');
 178            $errors[] = PhabricatorUser::describeValidUsername();
 179          } else {
 180            $e_username = null;
 181          }
 182        }
 183  
 184        if ($must_set_password) {
 185          $value_password = $request->getStr('password');
 186          $value_confirm = $request->getStr('confirm');
 187          if (!strlen($value_password)) {
 188            $e_password = pht('Required');
 189            $errors[] = pht('You must choose a password.');
 190          } else if ($value_password !== $value_confirm) {
 191            $e_password = pht('No Match');
 192            $errors[] = pht('Password and confirmation must match.');
 193          } else if (strlen($value_password) < $min_len) {
 194            $e_password = pht('Too Short');
 195            $errors[] = pht(
 196              'Password is too short (must be at least %d characters long).',
 197              $min_len);
 198          } else if (
 199            PhabricatorCommonPasswords::isCommonPassword($value_password)) {
 200  
 201            $e_password = pht('Very Weak');
 202            $errors[] = pht(
 203              'Password is pathologically weak. This password is one of the '.
 204              'most common passwords in use, and is extremely easy for '.
 205              'attackers to guess. You must choose a stronger password.');
 206          } else {
 207            $e_password = null;
 208          }
 209        }
 210  
 211        if ($can_edit_email) {
 212          $value_email = $request->getStr('email');
 213          if (!strlen($value_email)) {
 214            $e_email = pht('Required');
 215            $errors[] = pht('Email is required.');
 216          } else if (!PhabricatorUserEmail::isValidAddress($value_email)) {
 217            $e_email = pht('Invalid');
 218            $errors[] = PhabricatorUserEmail::describeValidAddresses();
 219          } else if (!PhabricatorUserEmail::isAllowedAddress($value_email)) {
 220            $e_email = pht('Disallowed');
 221            $errors[] = PhabricatorUserEmail::describeAllowedAddresses();
 222          } else {
 223            $e_email = null;
 224          }
 225        }
 226  
 227        if ($can_edit_realname) {
 228          $value_realname = $request->getStr('realName');
 229          if (!strlen($value_realname) && $require_real_name) {
 230            $e_realname = pht('Required');
 231            $errors[] = pht('Real name is required.');
 232          } else {
 233            $e_realname = null;
 234          }
 235        }
 236  
 237        if (!$errors) {
 238          $image = $this->loadProfilePicture($account);
 239          if ($image) {
 240            $user->setProfileImagePHID($image->getPHID());
 241          }
 242  
 243          try {
 244            if ($force_verify) {
 245              $verify_email = true;
 246            } else {
 247              $verify_email =
 248                ($account->getEmailVerified()) &&
 249                ($value_email === $default_email);
 250            }
 251  
 252            if ($provider->shouldTrustEmails() &&
 253                $value_email === $default_email) {
 254              $verify_email = true;
 255            }
 256  
 257            $email_obj = id(new PhabricatorUserEmail())
 258              ->setAddress($value_email)
 259              ->setIsVerified((int)$verify_email);
 260  
 261            $user->setUsername($value_username);
 262            $user->setRealname($value_realname);
 263  
 264            if ($is_setup) {
 265              $must_approve = false;
 266            } else {
 267              $must_approve = PhabricatorEnv::getEnvConfig(
 268                'auth.require-approval');
 269            }
 270  
 271            if ($must_approve) {
 272              $user->setIsApproved(0);
 273            } else {
 274              $user->setIsApproved(1);
 275            }
 276  
 277            $user->openTransaction();
 278  
 279              $editor = id(new PhabricatorUserEditor())
 280                ->setActor($user);
 281  
 282              $editor->createNewUser($user, $email_obj);
 283              if ($must_set_password) {
 284                $envelope = new PhutilOpaqueEnvelope($value_password);
 285                $editor->changePassword($user, $envelope);
 286              }
 287  
 288              if ($is_setup) {
 289                $editor->makeAdminUser($user, true);
 290              }
 291  
 292              $account->setUserPHID($user->getPHID());
 293              $provider->willRegisterAccount($account);
 294              $account->save();
 295  
 296            $user->saveTransaction();
 297  
 298            if (!$email_obj->getIsVerified()) {
 299              $email_obj->sendVerificationEmail($user);
 300            }
 301  
 302            if ($must_approve) {
 303              $this->sendWaitingForApprovalEmail($user);
 304            }
 305  
 306            return $this->loginUser($user);
 307          } catch (AphrontDuplicateKeyQueryException $exception) {
 308            $same_username = id(new PhabricatorUser())->loadOneWhere(
 309              'userName = %s',
 310              $user->getUserName());
 311  
 312            $same_email = id(new PhabricatorUserEmail())->loadOneWhere(
 313              'address = %s',
 314              $value_email);
 315  
 316            if ($same_username) {
 317              $e_username = pht('Duplicate');
 318              $errors[] = pht('Another user already has that username.');
 319            }
 320  
 321            if ($same_email) {
 322              // TODO: See T3340.
 323              $e_email = pht('Duplicate');
 324              $errors[] = pht('Another user already has that email.');
 325            }
 326  
 327            if (!$same_username && !$same_email) {
 328              throw $exception;
 329            }
 330          }
 331        }
 332  
 333        unset($unguarded);
 334      }
 335  
 336      $form = id(new AphrontFormView())
 337        ->setUser($request->getUser());
 338  
 339      if (!$is_default) {
 340        $form->appendChild(
 341          id(new AphrontFormMarkupControl())
 342            ->setLabel(pht('External Account'))
 343            ->setValue(
 344              id(new PhabricatorAuthAccountView())
 345                ->setUser($request->getUser())
 346                ->setExternalAccount($account)
 347                ->setAuthProvider($provider)));
 348      }
 349  
 350  
 351      if ($can_edit_username) {
 352        $form->appendChild(
 353          id(new AphrontFormTextControl())
 354            ->setLabel(pht('Phabricator Username'))
 355            ->setName('username')
 356            ->setValue($value_username)
 357            ->setError($e_username));
 358      } else {
 359        $form->appendChild(
 360          id(new AphrontFormMarkupControl())
 361            ->setLabel(pht('Phabricator Username'))
 362            ->setValue($value_username)
 363            ->setError($e_username));
 364      }
 365  
 366      if ($must_set_password) {
 367        $form->appendChild(
 368          id(new AphrontFormPasswordControl())
 369            ->setLabel(pht('Password'))
 370            ->setName('password')
 371            ->setError($e_password)
 372            ->setCaption(
 373              $min_len
 374                ? pht('Minimum length of %d characters.', $min_len)
 375                : null));
 376        $form->appendChild(
 377          id(new AphrontFormPasswordControl())
 378            ->setLabel(pht('Confirm Password'))
 379            ->setName('confirm')
 380            ->setError($e_password));
 381      }
 382  
 383      if ($can_edit_email) {
 384        $form->appendChild(
 385          id(new AphrontFormTextControl())
 386            ->setLabel(pht('Email'))
 387            ->setName('email')
 388            ->setValue($value_email)
 389            ->setCaption(PhabricatorUserEmail::describeAllowedAddresses())
 390            ->setError($e_email));
 391      }
 392  
 393      if ($can_edit_realname) {
 394        $form->appendChild(
 395          id(new AphrontFormTextControl())
 396            ->setLabel(pht('Real Name'))
 397            ->setName('realName')
 398            ->setValue($value_realname)
 399            ->setError($e_realname));
 400      }
 401  
 402      if ($must_set_password) {
 403        $form->appendChild(
 404          id(new AphrontFormRecaptchaControl())
 405            ->setLabel(pht('Captcha'))
 406            ->setError($e_captcha));
 407      }
 408  
 409      $submit = id(new AphrontFormSubmitControl());
 410  
 411      if ($is_setup) {
 412        $submit
 413          ->setValue(pht('Create Admin Account'));
 414      } else {
 415        $submit
 416          ->addCancelButton($this->getApplicationURI('start/'))
 417          ->setValue(pht('Register Phabricator Account'));
 418      }
 419  
 420  
 421      $form->appendChild($submit);
 422  
 423      $crumbs = $this->buildApplicationCrumbs();
 424  
 425      if ($is_setup) {
 426        $crumbs->addTextCrumb(pht('Setup Admin Account'));
 427          $title = pht('Welcome to Phabricator');
 428      } else {
 429        $crumbs->addTextCrumb(pht('Register'));
 430        $crumbs->addTextCrumb($provider->getProviderName());
 431          $title = pht('Phabricator Registration');
 432      }
 433  
 434      $welcome_view = null;
 435      if ($is_setup) {
 436        $welcome_view = id(new AphrontErrorView())
 437          ->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
 438          ->setTitle(pht('Welcome to Phabricator'))
 439          ->appendChild(
 440            pht(
 441              'Installation is complete. Register your administrator account '.
 442              'below to log in. You will be able to configure options and add '.
 443              'other authentication mechanisms (like LDAP or OAuth) later on.'));
 444      }
 445  
 446      $object_box = id(new PHUIObjectBoxView())
 447        ->setHeaderText($title)
 448        ->setForm($form)
 449        ->setFormErrors($errors);
 450  
 451      return $this->buildApplicationPage(
 452        array(
 453          $crumbs,
 454          $welcome_view,
 455          $object_box,
 456        ),
 457        array(
 458          'title' => $title,
 459        ));
 460    }
 461  
 462    private function loadDefaultAccount() {
 463      $providers = PhabricatorAuthProvider::getAllEnabledProviders();
 464      $account = null;
 465      $provider = null;
 466      $response = null;
 467  
 468      foreach ($providers as $key => $candidate_provider) {
 469        if (!$candidate_provider->shouldAllowRegistration()) {
 470          unset($providers[$key]);
 471          continue;
 472        }
 473        if (!$candidate_provider->isDefaultRegistrationProvider()) {
 474          unset($providers[$key]);
 475        }
 476      }
 477  
 478      if (!$providers) {
 479        $response = $this->renderError(
 480          pht(
 481            'There are no configured default registration providers.'));
 482        return array($account, $provider, $response);
 483      } else if (count($providers) > 1) {
 484        $response = $this->renderError(
 485          pht(
 486            'There are too many configured default registration providers.'));
 487        return array($account, $provider, $response);
 488      }
 489  
 490      $provider = head($providers);
 491      $account = $provider->getDefaultExternalAccount();
 492  
 493      return array($account, $provider, $response);
 494    }
 495  
 496    private function loadSetupAccount() {
 497      $provider = new PhabricatorPasswordAuthProvider();
 498      $provider->attachProviderConfig(
 499        id(new PhabricatorAuthProviderConfig())
 500          ->setShouldAllowRegistration(1)
 501          ->setShouldAllowLogin(1)
 502          ->setIsEnabled(true));
 503  
 504      $account = $provider->getDefaultExternalAccount();
 505      $response = null;
 506      return array($account, $provider, $response);
 507    }
 508  
 509    private function loadProfilePicture(PhabricatorExternalAccount $account) {
 510      $phid = $account->getProfileImagePHID();
 511      if (!$phid) {
 512        return null;
 513      }
 514  
 515      // NOTE: Use of omnipotent user is okay here because the registering user
 516      // can not control the field value, and we can't use their user object to
 517      // do meaningful policy checks anyway since they have not registered yet.
 518      // Reaching this means the user holds the account secret key and the
 519      // registration secret key, and thus has permission to view the image.
 520  
 521      $file = id(new PhabricatorFileQuery())
 522        ->setViewer(PhabricatorUser::getOmnipotentUser())
 523        ->withPHIDs(array($phid))
 524        ->executeOne();
 525      if (!$file) {
 526        return null;
 527      }
 528  
 529      try {
 530        $xformer = new PhabricatorImageTransformer();
 531        return $xformer->executeProfileTransform(
 532          $file,
 533          $width = 50,
 534          $min_height = 50,
 535          $max_height = 50);
 536      } catch (Exception $ex) {
 537        phlog($ex);
 538        return null;
 539      }
 540    }
 541  
 542    protected function renderError($message) {
 543      return $this->renderErrorPage(
 544        pht('Registration Failed'),
 545        array($message));
 546    }
 547  
 548    private function sendWaitingForApprovalEmail(PhabricatorUser $user) {
 549      $title = '[Phabricator] '.pht(
 550        'New User "%s" Awaiting Approval',
 551        $user->getUsername());
 552  
 553      $body = new PhabricatorMetaMTAMailBody();
 554  
 555      $body->addRawSection(
 556        pht(
 557          'Newly registered user "%s" is awaiting account approval by an '.
 558          'administrator.',
 559          $user->getUsername()));
 560  
 561      $body->addLinkSection(
 562        pht('APPROVAL QUEUE'),
 563        PhabricatorEnv::getProductionURI(
 564          '/people/query/approval/'));
 565  
 566      $body->addLinkSection(
 567        pht('DISABLE APPROVAL QUEUE'),
 568        PhabricatorEnv::getProductionURI(
 569          '/config/edit/auth.require-approval/'));
 570  
 571      $admins = id(new PhabricatorPeopleQuery())
 572        ->setViewer(PhabricatorUser::getOmnipotentUser())
 573        ->withIsAdmin(true)
 574        ->execute();
 575  
 576      if (!$admins) {
 577        return;
 578      }
 579  
 580      $mail = id(new PhabricatorMetaMTAMail())
 581        ->addTos(mpull($admins, 'getPHID'))
 582        ->setSubject($title)
 583        ->setBody($body->render())
 584        ->saveAndSend();
 585    }
 586  
 587  }


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