private function buildJoinsClause($conn_r) { $joins = array(); if ($this->emails) { $email_table = new PhabricatorUserEmail(); $joins[] = qsprintf($conn_r, 'JOIN %T email ON email.userPHID = user.PHID', $email_table->getTableName()); } $joins = implode(' ', $joins); return $joins; }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorMetaMTAApplicationEmailTransaction::TYPE_ADDRESS: foreach ($xactions as $xaction) { $email = $xaction->getNewValue(); if (!strlen($email)) { // We'll deal with this below. continue; } if (!PhabricatorUserEmail::isValidAddress($email)) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Email address is not formatted properly.')); } } $missing = $this->validateIsEmptyTextField($object->getAddress(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('You must provide an email address.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } break; } return $errors; }
public function testEmailValidation() { $tests = array('*****@*****.**' => true, '_-.@.-_' => true, '.@.com' => true, '*****@*****.**' => true, '*****@*****.**' => true, '1@22.33.44.55' => true, '999@999.999' => true, 'user@2001:0db8:85a3:0042:1000:8a2e:0370:7334' => true, '!..!@o.O' => true, '' => false, str_repeat('a', 256) . '@example.com' => false, 'quack' => false, '@gmail.com' => false, 'usergmail.com' => false, '"user" user@gmail.com' => false, 'a,b@evil.com' => false, 'a;b@evil.com' => false, 'ab@evil.com;cd@evil.com' => false, 'x@y@z.com' => false, '@@' => false, '@' => false, 'user@' => false, "user@domain.com\n" => false, "user@\ndomain.com" => false, "\nuser@domain.com" => false, "user@domain.com\r" => false, "user@\rdomain.com" => false, "\ruser@domain.com" => false); foreach ($tests as $input => $expect) { $actual = PhabricatorUserEmail::isValidAddress($input); $this->assertEqual($expect, $actual, $input); } }
/** * Is this a live account which has passed required approvals? Returns true * if this is an enabled, verified (if required), approved (if required) * account, and false otherwise. * * @return bool True if this is a standard, usable account. */ public function isUserActivated() { if ($this->getIsDisabled()) { return false; } if (!$this->getIsApproved()) { return false; } if (PhabricatorUserEmail::isEmailVerificationRequired()) { if (!$this->getIsEmailVerified()) { return false; } } return true; }
public function validateSender(PhabricatorMetaMTAReceivedMail $mail, PhabricatorUser $sender) { $failure_reason = null; if ($sender->getIsDisabled()) { $failure_reason = pht('Your account (%s) is disabled, so you can not interact with ' . 'Phabricator over email.', $sender->getUsername()); } else { if ($sender->getIsStandardUser()) { if (!$sender->getIsApproved()) { $failure_reason = pht('Your account (%s) has not been approved yet. You can not interact ' . 'with Phabricator over email until your account is approved.', $sender->getUsername()); } else { if (PhabricatorUserEmail::isEmailVerificationRequired() && !$sender->getIsEmailVerified()) { $failure_reason = pht('You have not verified the email address for your account (%s). ' . 'You must verify your email address before you can interact ' . 'with Phabricator over email.', $sender->getUsername()); } } } } if ($failure_reason) { throw new PhabricatorMetaMTAReceivedMailProcessingException(MetaMTAReceivedMailStatus::STATUS_DISABLED_SENDER, $failure_reason); } }
/** * @task internal */ private function willAddEmail(PhabricatorUserEmail $email) { // Hard check before write to prevent creation of disallowed email // addresses. Normally, the application does checks and raises more // user friendly errors for us, but we omit the courtesy checks on some // pathways like administrative scripts for simplicity. if (!PhabricatorUserEmail::isAllowedAddress($email->getAddress())) { throw new Exception(PhabricatorUserEmail::describeAllowedAddresses()); } }
/** * Hash a one-time login key for storage as a temporary token. * * @param PhabricatorUser User this key is for. * @param PhabricatorUserEmail Optionally, email to verify when * link is used. * @param string The one time login key. * @return string Hash of the key. * task onetime */ private function getOneTimeLoginKeyHash(PhabricatorUser $user, PhabricatorUserEmail $email = null, $key = null) { $parts = array($key, $user->getAccountSecret()); if ($email) { $parts[] = $email->getVerificationCode(); } return PhabricatorHash::digest(implode(':', $parts)); }
public function shouldRequireEmailVerification() { return PhabricatorUserEmail::isEmailVerificationRequired(); }
public function processRequest() { $request = $this->getRequest(); if ($request->getUser()->isLoggedIn()) { return $this->renderError(pht('You are already logged in.')); } $is_setup = false; if (strlen($this->accountKey)) { $result = $this->loadAccountForRegistrationOrLinking($this->accountKey); list($account, $provider, $response) = $result; $is_default = false; } else { if ($this->isFirstTimeSetup()) { list($account, $provider, $response) = $this->loadSetupAccount(); $is_default = true; $is_setup = true; } else { list($account, $provider, $response) = $this->loadDefaultAccount(); $is_default = true; } } if ($response) { return $response; } $invite = $this->loadInvite(); if (!$provider->shouldAllowRegistration()) { if ($invite) { // If the user has an invite, we allow them to register with any // provider, even a login-only provider. } else { // TODO: This is a routine error if you click "Login" on an external // auth source which doesn't allow registration. The error should be // more tailored. return $this->renderError(pht('The account you are attempting to register with uses an ' . 'authentication provider ("%s") which does not allow ' . 'registration. An administrator may have recently disabled ' . 'registration with this provider.', $provider->getProviderName())); } } $user = new PhabricatorUser(); $default_username = $account->getUsername(); $default_realname = $account->getRealName(); $default_email = $account->getEmail(); if ($invite) { $default_email = $invite->getEmailAddress(); } if (!PhabricatorUserEmail::isValidAddress($default_email)) { $default_email = null; } if ($default_email !== null) { // We should bypass policy here becase e.g. limiting an application use // to a subset of users should not allow the others to overwrite // configured application emails $application_email = id(new PhabricatorMetaMTAApplicationEmailQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withAddresses(array($default_email))->executeOne(); if ($application_email) { $default_email = null; } } if ($default_email !== null) { // If the account source provided an email, but it's not allowed by // the configuration, roadblock the user. Previously, we let the user // pick a valid email address instead, but this does not align well with // user expectation and it's not clear the cases it enables are valuable. // See discussion in T3472. if (!PhabricatorUserEmail::isAllowedAddress($default_email)) { return $this->renderError(array(pht('The account you are attempting to register with has an invalid ' . 'email address (%s). This Phabricator install only allows ' . 'registration with specific email addresses:', $default_email), phutil_tag('br'), phutil_tag('br'), PhabricatorUserEmail::describeAllowedAddresses())); } // If the account source provided an email, but another account already // has that email, just pretend we didn't get an email. // TODO: See T3472. if ($default_email !== null) { $same_email = id(new PhabricatorUserEmail())->loadOneWhere('address = %s', $default_email); if ($same_email) { if ($invite) { // We're allowing this to continue. The fact that we loaded the // invite means that the address is nonprimary and unverified and // we're OK to steal it. } else { $default_email = null; } } } } $profile = id(new PhabricatorRegistrationProfile())->setDefaultUsername($default_username)->setDefaultEmail($default_email)->setDefaultRealName($default_realname)->setCanEditUsername(true)->setCanEditEmail($default_email === null)->setCanEditRealName(true)->setShouldVerifyEmail(false); $event_type = PhabricatorEventType::TYPE_AUTH_WILLREGISTERUSER; $event_data = array('account' => $account, 'profile' => $profile); $event = id(new PhabricatorEvent($event_type, $event_data))->setUser($user); PhutilEventEngine::dispatchEvent($event); $default_username = $profile->getDefaultUsername(); $default_email = $profile->getDefaultEmail(); $default_realname = $profile->getDefaultRealName(); $can_edit_username = $profile->getCanEditUsername(); $can_edit_email = $profile->getCanEditEmail(); $can_edit_realname = $profile->getCanEditRealName(); $must_set_password = $provider->shouldRequireRegistrationPassword(); $can_edit_anything = $profile->getCanEditAnything() || $must_set_password; $force_verify = $profile->getShouldVerifyEmail(); // Automatically verify the administrator's email address during first-time // setup. if ($is_setup) { $force_verify = true; } $value_username = $default_username; $value_realname = $default_realname; $value_email = $default_email; $value_password = null; $errors = array(); $require_real_name = PhabricatorEnv::getEnvConfig('user.require-real-name'); $e_username = strlen($value_username) ? null : true; $e_realname = $require_real_name ? true : null; $e_email = strlen($value_email) ? null : true; $e_password = true; $e_captcha = true; $skip_captcha = false; if ($invite) { // If the user is accepting an invite, assume they're trustworthy enough // that we don't need to CAPTCHA them. $skip_captcha = true; } $min_len = PhabricatorEnv::getEnvConfig('account.minimum-password-length'); $min_len = (int) $min_len; $from_invite = $request->getStr('invite'); if ($from_invite && $can_edit_username) { $value_username = $request->getStr('username'); $e_username = null; } if (($request->isFormPost() || !$can_edit_anything) && !$from_invite) { $unguarded = AphrontWriteGuard::beginScopedUnguardedWrites(); if ($must_set_password && !$skip_captcha) { $e_captcha = pht('Again'); $captcha_ok = AphrontFormRecaptchaControl::processCaptcha($request); if (!$captcha_ok) { $errors[] = pht('Captcha response is incorrect, try again.'); $e_captcha = pht('Invalid'); } } if ($can_edit_username) { $value_username = $request->getStr('username'); if (!strlen($value_username)) { $e_username = pht('Required'); $errors[] = pht('Username is required.'); } else { if (!PhabricatorUser::validateUsername($value_username)) { $e_username = pht('Invalid'); $errors[] = PhabricatorUser::describeValidUsername(); } else { $e_username = null; } } } if ($must_set_password) { $value_password = $request->getStr('password'); $value_confirm = $request->getStr('confirm'); if (!strlen($value_password)) { $e_password = pht('Required'); $errors[] = pht('You must choose a password.'); } else { if ($value_password !== $value_confirm) { $e_password = pht('No Match'); $errors[] = pht('Password and confirmation must match.'); } else { if (strlen($value_password) < $min_len) { $e_password = pht('Too Short'); $errors[] = pht('Password is too short (must be at least %d characters long).', $min_len); } else { if (PhabricatorCommonPasswords::isCommonPassword($value_password)) { $e_password = pht('Very Weak'); $errors[] = pht('Password is pathologically weak. This password is one of the ' . 'most common passwords in use, and is extremely easy for ' . 'attackers to guess. You must choose a stronger password.'); } else { $e_password = null; } } } } } if ($can_edit_email) { $value_email = $request->getStr('email'); if (!strlen($value_email)) { $e_email = pht('Required'); $errors[] = pht('Email is required.'); } else { if (!PhabricatorUserEmail::isValidAddress($value_email)) { $e_email = pht('Invalid'); $errors[] = PhabricatorUserEmail::describeValidAddresses(); } else { if (!PhabricatorUserEmail::isAllowedAddress($value_email)) { $e_email = pht('Disallowed'); $errors[] = PhabricatorUserEmail::describeAllowedAddresses(); } else { $e_email = null; } } } } if ($can_edit_realname) { $value_realname = $request->getStr('realName'); if (!strlen($value_realname) && $require_real_name) { $e_realname = pht('Required'); $errors[] = pht('Real name is required.'); } else { $e_realname = null; } } if (!$errors) { $image = $this->loadProfilePicture($account); if ($image) { $user->setProfileImagePHID($image->getPHID()); } try { $verify_email = false; if ($force_verify) { $verify_email = true; } if ($value_email === $default_email) { if ($account->getEmailVerified()) { $verify_email = true; } if ($provider->shouldTrustEmails()) { $verify_email = true; } if ($invite) { $verify_email = true; } } $email_obj = null; if ($invite) { // If we have a valid invite, this email may exist but be // nonprimary and unverified, so we'll reassign it. $email_obj = id(new PhabricatorUserEmail())->loadOneWhere('address = %s', $value_email); } if (!$email_obj) { $email_obj = id(new PhabricatorUserEmail())->setAddress($value_email); } $email_obj->setIsVerified((int) $verify_email); $user->setUsername($value_username); $user->setRealname($value_realname); if ($is_setup) { $must_approve = false; } else { if ($invite) { $must_approve = false; } else { $must_approve = PhabricatorEnv::getEnvConfig('auth.require-approval'); } } if ($must_approve) { $user->setIsApproved(0); } else { $user->setIsApproved(1); } if ($invite) { $allow_reassign_email = true; } else { $allow_reassign_email = false; } $user->openTransaction(); $editor = id(new PhabricatorUserEditor())->setActor($user); $editor->createNewUser($user, $email_obj, $allow_reassign_email); if ($must_set_password) { $envelope = new PhutilOpaqueEnvelope($value_password); $editor->changePassword($user, $envelope); } if ($is_setup) { $editor->makeAdminUser($user, true); } $account->setUserPHID($user->getPHID()); $provider->willRegisterAccount($account); $account->save(); $user->saveTransaction(); if (!$email_obj->getIsVerified()) { $email_obj->sendVerificationEmail($user); } if ($must_approve) { $this->sendWaitingForApprovalEmail($user); } if ($invite) { $invite->setAcceptedByPHID($user->getPHID())->save(); } return $this->loginUser($user); } catch (AphrontDuplicateKeyQueryException $exception) { $same_username = id(new PhabricatorUser())->loadOneWhere('userName = %s', $user->getUserName()); $same_email = id(new PhabricatorUserEmail())->loadOneWhere('address = %s', $value_email); if ($same_username) { $e_username = pht('Duplicate'); $errors[] = pht('Another user already has that username.'); } if ($same_email) { // TODO: See T3340. $e_email = pht('Duplicate'); $errors[] = pht('Another user already has that email.'); } if (!$same_username && !$same_email) { throw $exception; } } } unset($unguarded); } $form = id(new AphrontFormView())->setUser($request->getUser()); if (!$is_default) { $form->appendChild(id(new AphrontFormMarkupControl())->setLabel(pht('External Account'))->setValue(id(new PhabricatorAuthAccountView())->setUser($request->getUser())->setExternalAccount($account)->setAuthProvider($provider))); } if ($can_edit_username) { $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Phabricator Username'))->setName('username')->setValue($value_username)->setError($e_username)); } else { $form->appendChild(id(new AphrontFormMarkupControl())->setLabel(pht('Phabricator Username'))->setValue($value_username)->setError($e_username)); } if ($can_edit_realname) { $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Real Name'))->setName('realName')->setValue($value_realname)->setError($e_realname)); } if ($must_set_password) { $form->appendChild(id(new AphrontFormPasswordControl())->setLabel(pht('Password'))->setName('password')->setError($e_password)); $form->appendChild(id(new AphrontFormPasswordControl())->setLabel(pht('Confirm Password'))->setName('confirm')->setError($e_password)->setCaption($min_len ? pht('Minimum length of %d characters.', $min_len) : null)); } if ($can_edit_email) { $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Email'))->setName('email')->setValue($value_email)->setCaption(PhabricatorUserEmail::describeAllowedAddresses())->setError($e_email)); } if ($must_set_password && !$skip_captcha) { $form->appendChild(id(new AphrontFormRecaptchaControl())->setLabel(pht('Captcha'))->setError($e_captcha)); } $submit = id(new AphrontFormSubmitControl()); if ($is_setup) { $submit->setValue(pht('Create Admin Account')); } else { $submit->addCancelButton($this->getApplicationURI('start/'))->setValue(pht('Register Phabricator Account')); } $form->appendChild($submit); $crumbs = $this->buildApplicationCrumbs(); if ($is_setup) { $crumbs->addTextCrumb(pht('Setup Admin Account')); $title = pht('Welcome to Phabricator'); } else { $crumbs->addTextCrumb(pht('Register')); $crumbs->addTextCrumb($provider->getProviderName()); $title = pht('Phabricator Registration'); } $welcome_view = null; if ($is_setup) { $welcome_view = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_NOTICE)->setTitle(pht('Welcome to Phabricator'))->appendChild(pht('Installation is complete. Register your administrator account ' . 'below to log in. You will be able to configure options and add ' . 'other authentication mechanisms (like LDAP or OAuth) later on.')); } $object_box = id(new PHUIObjectBoxView())->setHeaderText($title)->setForm($form)->setFormErrors($errors); $invite_header = null; if ($invite) { $invite_header = $this->renderInviteHeader($invite); } return $this->buildApplicationPage(array($crumbs, $welcome_view, $invite_header, $object_box), array('title' => $title)); }
public function processRequest() { $request = $this->getRequest(); $admin = $request->getUser(); switch ($this->type) { case 'standard': $is_bot = false; break; case 'bot': $is_bot = true; break; default: return new Aphront404Response(); } $user = new PhabricatorUser(); $require_real_name = PhabricatorEnv::getEnvConfig('user.require-real-name'); $e_username = true; $e_realname = $require_real_name ? true : null; $e_email = true; $errors = array(); $welcome_checked = true; $new_email = null; $request = $this->getRequest(); if ($request->isFormPost()) { $welcome_checked = $request->getInt('welcome'); $user->setUsername($request->getStr('username')); $new_email = $request->getStr('email'); if (!strlen($new_email)) { $errors[] = pht('Email is required.'); $e_email = pht('Required'); } else { if (!PhabricatorUserEmail::isAllowedAddress($new_email)) { $e_email = pht('Invalid'); $errors[] = PhabricatorUserEmail::describeAllowedAddresses(); } else { $e_email = null; } } $user->setRealName($request->getStr('realname')); if (!strlen($user->getUsername())) { $errors[] = pht('Username is required.'); $e_username = pht('Required'); } else { if (!PhabricatorUser::validateUsername($user->getUsername())) { $errors[] = PhabricatorUser::describeValidUsername(); $e_username = pht('Invalid'); } else { $e_username = null; } } if (!strlen($user->getRealName()) && $require_real_name) { $errors[] = pht('Real name is required.'); $e_realname = pht('Required'); } else { $e_realname = null; } if (!$errors) { try { $email = id(new PhabricatorUserEmail())->setAddress($new_email)->setIsVerified(0); // Automatically approve the user, since an admin is creating them. $user->setIsApproved(1); // If the user is a bot, approve their email too. if ($is_bot) { $email->setIsVerified(1); } id(new PhabricatorUserEditor())->setActor($admin)->createNewUser($user, $email); if ($is_bot) { id(new PhabricatorUserEditor())->setActor($admin)->makeSystemAgentUser($user, true); } if ($welcome_checked && !$is_bot) { $user->sendWelcomeEmail($admin); } $response = id(new AphrontRedirectResponse())->setURI('/p/' . $user->getUsername() . '/'); return $response; } catch (AphrontDuplicateKeyQueryException $ex) { $errors[] = pht('Username and email must be unique.'); $same_username = id(new PhabricatorUser())->loadOneWhere('username = %s', $user->getUsername()); $same_email = id(new PhabricatorUserEmail())->loadOneWhere('address = %s', $new_email); if ($same_username) { $e_username = pht('Duplicate'); } if ($same_email) { $e_email = pht('Duplicate'); } } } } $form = id(new AphrontFormView())->setUser($admin); if ($is_bot) { $form->appendRemarkupInstructions(pht('You are creating a new **bot/script** user account.')); } else { $form->appendRemarkupInstructions(pht('You are creating a new **standard** user account.')); } $form->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Username'))->setName('username')->setValue($user->getUsername())->setError($e_username))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Real Name'))->setName('realname')->setValue($user->getRealName())->setError($e_realname))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Email'))->setName('email')->setValue($new_email)->setCaption(PhabricatorUserEmail::describeAllowedAddresses())->setError($e_email)); if (!$is_bot) { $form->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox('welcome', 1, pht('Send "Welcome to Phabricator" email with login instructions.'), $welcome_checked)); } $form->appendChild(id(new AphrontFormSubmitControl())->addCancelButton($this->getApplicationURI())->setValue(pht('Create User'))); if ($is_bot) { $form->appendChild(id(new AphrontFormDividerControl()))->appendRemarkupInstructions(pht('**Why do bot/script accounts need an email address?**' . "\n\n" . 'Although bots do not normally receive email from Phabricator, ' . 'they can interact with other systems which require an email ' . 'address. Examples include:' . "\n\n" . " - If the account takes actions which //send// email, we need " . " an address to use in the //From// header.\n" . " - If the account creates commits, Git and Mercurial require " . " an email address for authorship.\n" . " - If you send email //to// Phabricator on behalf of the " . " account, the address can identify the sender.\n" . " - Some internal authentication functions depend on accounts " . " having an email address.\n" . "\n\n" . "The address will automatically be verified, so you do not need " . "to be able to receive mail at this address, and can enter some " . "invalid or nonexistent (but correctly formatted) address like " . "`bot@yourcompany.com` if you prefer.")); } $title = pht('Create New User'); $form_box = id(new PHUIObjectBoxView())->setHeaderText($title)->setFormErrors($errors)->setForm($form); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb($title); return $this->buildApplicationPage(array($crumbs, $form_box), array('title' => $title)); }
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) { $joins = parent::buildJoinClauseParts($conn); if ($this->emails) { $email_table = new PhabricatorUserEmail(); $joins[] = qsprintf($conn, 'JOIN %T email ON email.userPHID = user.PHID', $email_table->getTableName()); } if ($this->nameTokens) { foreach ($this->nameTokens as $key => $token) { $token_table = 'token_' . $key; $joins[] = qsprintf($conn, 'JOIN %T %T ON %T.userID = user.id AND %T.token LIKE %>', PhabricatorUser::NAMETOKEN_TABLE, $token_table, $token_table, $token_table, $token); } } return $joins; }
/** * Send a notification email from $user to this address, informing the * recipient that this is no longer their account's primary address. * * @param PhabricatorUser The user sending the notification. * @param PhabricatorUserEmail New primary email address. * @return this * @task email */ public function sendOldPrimaryEmail(PhabricatorUser $user, PhabricatorUserEmail $new) { $username = $user->getUsername(); $old_address = $this->getAddress(); $new_address = $new->getAddress(); $body = <<<EOBODY Hi {$username}, This email address ({$old_address}) is no longer your primary email address. Going forward, Phabricator will send all email to your new primary email address ({$new_address}). EOBODY; id(new PhabricatorMetaMTAMail())->addRawTos(array($old_address))->setForceDelivery(true)->setSubject('[Phabricator] Primary Address Changed')->setBody($body)->setFrom($user->getPHID())->setRelatedPHID($user->getPHID())->saveAndSend(); }
/* * Copyright 2012 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ echo "Migrating user emails...\n"; $table = new PhabricatorUser(); $conn = $table->establishConnection('r'); $emails = queryfx_all($conn, 'SELECT phid, email FROM %T', $table->getTableName()); $emails = ipull($emails, 'email', 'phid'); $etable = new PhabricatorUserEmail(); $econn = $etable->establishConnection('w'); foreach ($emails as $phid => $email) { // NOTE: Grandfather all existing email in as primary / verified. We generate // verification codes because they are used for password resets, etc. echo "Migrating '{$phid}'...\n"; queryfx($econn, 'INSERT INTO %T (userPHID, address, verificationCode, isVerified, isPrimary) VALUES (%s, %s, %s, 1, 1)', $etable->getTableName(), $phid, $email, Filesystem::readRandomCharacters(24)); } echo "Done.\n";
private function returnNewAddressResponse(AphrontRequest $request, PhutilURI $uri, $new) { $user = $request->getUser(); $e_email = true; $email = trim($request->getStr('email')); $errors = array(); if ($request->isDialogFormPost()) { if ($new == 'verify') { // The user clicked "Done" from the "an email has been sent" dialog. return id(new AphrontReloadResponse())->setURI($uri); } if (!strlen($email)) { $e_email = 'Required'; $errors[] = 'Email is required.'; } else { if (!PhabricatorUserEmail::isAllowedAddress($email)) { $e_email = 'Invalid'; $errors[] = PhabricatorUserEmail::describeAllowedAddresses(); } } if (!$errors) { $object = id(new PhabricatorUserEmail())->setAddress($email)->setIsVerified(0); try { id(new PhabricatorUserEditor())->setActor($user)->addEmail($user, $object); $object->sendVerificationEmail($user); $dialog = id(new AphrontDialogView())->setUser($user)->addHiddenInput('new', 'verify')->setTitle('Verification Email Sent')->appendChild('<p>A verification email has been sent. Click the link in the ' . 'email to verify your address.</p>')->setSubmitURI($uri)->addSubmitButton('Done'); return id(new AphrontDialogResponse())->setDialog($dialog); } catch (AphrontQueryDuplicateKeyException $ex) { $email = 'Duplicate'; $errors[] = 'Another user already has this email.'; } } } if ($errors) { $errors = id(new AphrontErrorView())->setWidth(AphrontErrorView::WIDTH_DIALOG)->setErrors($errors); } $form = id(new AphrontFormLayoutView())->appendChild(id(new AphrontFormTextControl())->setLabel('Email')->setName('email')->setValue($request->getStr('email'))->setCaption(PhabricatorUserEmail::describeAllowedAddresses())->setError($e_email)); $dialog = id(new AphrontDialogView())->setUser($user)->addHiddenInput('new', 'true')->setTitle('New Address')->appendChild($errors)->appendChild($form)->addSubmitButton('Save')->addCancelButton($uri); return id(new AphrontDialogResponse())->setDialog($dialog); }
/** * @task internal */ private function willAddEmail(PhabricatorUserEmail $email) { // Hard check before write to prevent creation of disallowed email // addresses. Normally, the application does checks and raises more // user friendly errors for us, but we omit the courtesy checks on some // pathways like administrative scripts for simplicity. if (!PhabricatorUserEmail::isValidAddress($email->getAddress())) { throw new Exception(PhabricatorUserEmail::describeValidAddresses()); } if (!PhabricatorUserEmail::isAllowedAddress($email->getAddress())) { throw new Exception(PhabricatorUserEmail::describeAllowedAddresses()); } $application_email = id(new PhabricatorMetaMTAApplicationEmailQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withAddresses(array($email->getAddress()))->executeOne(); if ($application_email) { throw new Exception($application_email->getInUseMessage()); } }
private function returnNewAddressResponse(AphrontRequest $request, PhutilURI $uri, $new) { $user = $this->getUser(); $viewer = $this->getViewer(); $e_email = true; $email = null; $errors = array(); if ($request->isDialogFormPost()) { $email = trim($request->getStr('email')); if ($new == 'verify') { // The user clicked "Done" from the "an email has been sent" dialog. return id(new AphrontReloadResponse())->setURI($uri); } PhabricatorSystemActionEngine::willTakeAction(array($viewer->getPHID()), new PhabricatorSettingsAddEmailAction(), 1); if (!strlen($email)) { $e_email = pht('Required'); $errors[] = pht('Email is required.'); } else { if (!PhabricatorUserEmail::isValidAddress($email)) { $e_email = pht('Invalid'); $errors[] = PhabricatorUserEmail::describeValidAddresses(); } else { if (!PhabricatorUserEmail::isAllowedAddress($email)) { $e_email = pht('Disallowed'); $errors[] = PhabricatorUserEmail::describeAllowedAddresses(); } } } if ($e_email === true) { $application_email = id(new PhabricatorMetaMTAApplicationEmailQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withAddresses(array($email))->executeOne(); if ($application_email) { $e_email = pht('In Use'); $errors[] = $application_email->getInUseMessage(); } } if (!$errors) { $object = id(new PhabricatorUserEmail())->setAddress($email)->setIsVerified(0); // If an administrator is editing a mailing list, automatically verify // the address. if ($viewer->getPHID() != $user->getPHID()) { if ($viewer->getIsAdmin()) { $object->setIsVerified(1); } } try { id(new PhabricatorUserEditor())->setActor($viewer)->addEmail($user, $object); if ($object->getIsVerified()) { // If we autoverified the address, just reload the page. return id(new AphrontReloadResponse())->setURI($uri); } $object->sendVerificationEmail($user); $dialog = id(new AphrontDialogView())->setUser($user)->addHiddenInput('new', 'verify')->setTitle(pht('Verification Email Sent'))->appendChild(phutil_tag('p', array(), pht('A verification email has been sent. Click the link in the ' . 'email to verify your address.')))->setSubmitURI($uri)->addSubmitButton(pht('Done')); return id(new AphrontDialogResponse())->setDialog($dialog); } catch (AphrontDuplicateKeyQueryException $ex) { $e_email = pht('Duplicate'); $errors[] = pht('Another user already has this email.'); } } } if ($errors) { $errors = id(new PHUIInfoView())->setErrors($errors); } $form = id(new PHUIFormLayoutView())->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Email'))->setName('email')->setValue($email)->setCaption(PhabricatorUserEmail::describeAllowedAddresses())->setError($e_email)); $dialog = id(new AphrontDialogView())->setUser($viewer)->addHiddenInput('new', 'true')->setTitle(pht('New Address'))->appendChild($errors)->appendChild($form)->addSubmitButton(pht('Save'))->addCancelButton($uri); return id(new AphrontDialogResponse())->setDialog($dialog); }
<?php echo "Migrating user emails...\n"; $table = new PhabricatorUser(); $table->openTransaction(); $conn = $table->establishConnection('w'); $emails = queryfx_all($conn, 'SELECT phid, email FROM %T LOCK IN SHARE MODE', $table->getTableName()); $emails = ipull($emails, 'email', 'phid'); $etable = new PhabricatorUserEmail(); foreach ($emails as $phid => $email) { // NOTE: Grandfather all existing email in as primary / verified. We generate // verification codes because they are used for password resets, etc. echo "Migrating '{$phid}'...\n"; queryfx($conn, 'INSERT INTO %T (userPHID, address, verificationCode, isVerified, isPrimary) VALUES (%s, %s, %s, 1, 1)', $etable->getTableName(), $phid, $email, Filesystem::readRandomCharacters(24)); } $table->saveTransaction(); echo "Done.\n";
/** * Send a notification email from $user to this address, informing the * recipient that this is no longer their account's primary address. * * @param PhabricatorUser The user sending the notification. * @param PhabricatorUserEmail New primary email address. * @return this * @task email */ public function sendOldPrimaryEmail(PhabricatorUser $user, PhabricatorUserEmail $new) { $username = $user->getUsername(); $old_address = $this->getAddress(); $new_address = $new->getAddress(); $body = sprintf("%s\n\n%s\n", pht('Hi %s', $username), pht('This email address (%s) is no longer your primary email address. ' . 'Going forward, Phabricator will send all email to your new primary ' . 'email address (%s).', $old_address, $new_address)); id(new PhabricatorMetaMTAMail())->addRawTos(array($old_address))->setForceDelivery(true)->setSubject(pht('[Phabricator] Primary Address Changed'))->setBody($body)->setFrom($user->getPHID())->setRelatedPHID($user->getPHID())->saveAndSend(); }
public function processRequest() { $provider = $this->getLDAProvider(); $ldap_info = $this->getLDAPInfo(); $request = $this->getRequest(); $errors = array(); $e_username = true; $e_email = true; $e_realname = true; $user = new PhabricatorUser(); $user->setUsername(); $user->setRealname($provider->retrieveUserRealName()); $new_email = $provider->retrieveUserEmail(); if ($new_email) { // If the user's LDAP provider account has an email address but the // email address domain is not allowed by the Phabricator configuration, // we just pretend the provider did not supply an address. // // For instance, if the user uses LDAP Auth and their email address // is "*****@*****.**" but Phabricator is configured to require users // use "@company.com" addresses, we show a prompt below and tell the user // to provide their "@company.com" address. They can still use the LDAP // account to login, they just need to associate their account with an // allowed address. // // If the email address is fine, we just use it and don't prompt the user. if (!PhabricatorUserEmail::isAllowedAddress($new_email)) { $new_email = null; } } $show_email_input = $new_email === null; if ($request->isFormPost()) { $user->setUsername($request->getStr('username')); $username = $user->getUsername(); if (!strlen($user->getUsername())) { $e_username = '******'; $errors[] = 'Username is required.'; } else { if (!PhabricatorUser::validateUsername($username)) { $e_username = '******'; $errors[] = PhabricatorUser::describeValidUsername(); } else { $e_username = null; } } if (!$new_email) { $new_email = trim($request->getStr('email')); if (!$new_email) { $e_email = 'Required'; $errors[] = 'Email is required.'; } else { $e_email = null; } } if ($new_email) { if (!PhabricatorUserEmail::isAllowedAddress($new_email)) { $e_email = 'Invalid'; $errors[] = PhabricatorUserEmail::describeAllowedAddresses(); } } if (!strlen($user->getRealName())) { $user->setRealName($request->getStr('realname')); if (!strlen($user->getRealName())) { $e_realname = 'Required'; $errors[] = 'Real name is required.'; } else { $e_realname = null; } } if (!$errors) { try { // NOTE: We don't verify LDAP email addresses by default because // LDAP providers might associate email addresses with accounts that // haven't actually verified they own them. We could selectively // auto-verify some providers that we trust here, but the stakes for // verifying an email address are high because having a corporate // address at a company is sometimes the key to the castle. $email_obj = id(new PhabricatorUserEmail())->setAddress($new_email)->setIsVerified(0); id(new PhabricatorUserEditor())->setActor($user)->createNewUser($user, $email_obj); $ldap_info->setUserID($user->getID()); $ldap_info->save(); $session_key = $user->establishSession('web'); $request->setCookie('phusr', $user->getUsername()); $request->setCookie('phsid', $session_key); $email_obj->sendVerificationEmail($user); return id(new AphrontRedirectResponse())->setURI('/'); } catch (AphrontQueryDuplicateKeyException $exception) { $same_username = id(new PhabricatorUser())->loadOneWhere('userName = %s', $user->getUserName()); $same_email = id(new PhabricatorUserEmail())->loadOneWhere('address = %s', $new_email); if ($same_username) { $e_username = '******'; $errors[] = 'That username or email is not unique.'; } else { if ($same_email) { $e_email = 'Duplicate'; $errors[] = 'That email is not unique.'; } else { throw $exception; } } } } } $error_view = null; if ($errors) { $error_view = new AphrontErrorView(); $error_view->setTitle('Registration Failed'); $error_view->setErrors($errors); } // Strip the URI down to the path, because otherwise we'll trigger // external CSRF protection (by having a protocol in the form "action") // and generate a form with no CSRF token. $action_uri = new PhutilURI('/ldap/login/'); $action_path = $action_uri->getPath(); $form = new AphrontFormView(); $form->setUser($request->getUser())->setAction($action_path)->appendChild(id(new AphrontFormTextControl())->setLabel('Username')->setName('username')->setValue($user->getUsername())->setError($e_username)); $form->appendChild(id(new AphrontFormPasswordControl())->setLabel('Password')->setName('password')); if ($show_email_input) { $form->appendChild(id(new AphrontFormTextControl())->setLabel('Email')->setName('email')->setValue($request->getStr('email'))->setError($e_email)); } if ($provider->retrieveUserRealName() === null) { $form->appendChild(id(new AphrontFormTextControl())->setLabel('Real Name')->setName('realname')->setValue($request->getStr('realname'))->setError($e_realname)); } $form->appendChild(id(new AphrontFormSubmitControl())->setValue('Create Account')); $panel = new AphrontPanelView(); $panel->setHeader('Create New Account'); $panel->setWidth(AphrontPanelView::WIDTH_FORM); $panel->appendChild($form); return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Create New Account')); }
public function shouldRequireEmailVerification() { $need_verify = PhabricatorUserEmail::isEmailVerificationRequired(); $need_login = $this->shouldRequireLogin(); return $need_login && $need_verify; }
private function processBasicRequest(PhabricatorUser $user) { $request = $this->getRequest(); $admin = $request->getUser(); $e_username = true; $e_realname = true; $e_email = true; $errors = array(); $welcome_checked = true; $new_email = null; $request = $this->getRequest(); if ($request->isFormPost()) { $welcome_checked = $request->getInt('welcome'); if (!$user->getID()) { $user->setUsername($request->getStr('username')); $new_email = $request->getStr('email'); if (!strlen($new_email)) { $errors[] = 'Email is required.'; $e_email = 'Required'; } else { if (!PhabricatorUserEmail::isAllowedAddress($new_email)) { $e_email = 'Invalid'; $errors[] = PhabricatorUserEmail::describeAllowedAddresses(); } else { $e_email = null; } } if ($request->getStr('role') == 'agent') { $user->setIsSystemAgent(true); } } $user->setRealName($request->getStr('realname')); if (!strlen($user->getUsername())) { $errors[] = "Username is required."; $e_username = '******'; } else { if (!PhabricatorUser::validateUsername($user->getUsername())) { $errors[] = PhabricatorUser::describeValidUsername(); $e_username = '******'; } else { $e_username = null; } } if (!strlen($user->getRealName())) { $errors[] = 'Real name is required.'; $e_realname = 'Required'; } else { $e_realname = null; } if (!$errors) { try { $is_new = !$user->getID(); if (!$is_new) { id(new PhabricatorUserEditor())->setActor($admin)->updateUser($user); } else { $email = id(new PhabricatorUserEmail())->setAddress($new_email)->setIsVerified(0); id(new PhabricatorUserEditor())->setActor($admin)->createNewUser($user, $email); } if ($welcome_checked) { $user->sendWelcomeEmail($admin); } $response = id(new AphrontRedirectResponse())->setURI('/people/edit/' . $user->getID() . '/?saved=true'); return $response; } catch (AphrontQueryDuplicateKeyException $ex) { $errors[] = 'Username and email must be unique.'; $same_username = id(new PhabricatorUser())->loadOneWhere('username = %s', $user->getUsername()); $same_email = id(new PhabricatorUserEmail())->loadOneWhere('address = %s', $new_email); if ($same_username) { $e_username = '******'; } if ($same_email) { $e_email = 'Duplicate'; } } } } $error_view = null; if ($errors) { $error_view = id(new AphrontErrorView())->setTitle('Form Errors')->setErrors($errors); } $form = new AphrontFormView(); $form->setUser($admin); if ($user->getID()) { $form->setAction('/people/edit/' . $user->getID() . '/'); } else { $form->setAction('/people/edit/'); } if ($user->getID()) { $is_immutable = true; } else { $is_immutable = false; } $form->appendChild(id(new AphrontFormTextControl())->setLabel('Username')->setName('username')->setValue($user->getUsername())->setError($e_username)->setDisabled($is_immutable))->appendChild(id(new AphrontFormTextControl())->setLabel('Real Name')->setName('realname')->setValue($user->getRealName())->setError($e_realname)); if (!$user->getID()) { $form->appendChild(id(new AphrontFormTextControl())->setLabel('Email')->setName('email')->setDisabled($is_immutable)->setValue($new_email)->setCaption(PhabricatorUserEmail::describeAllowedAddresses())->setError($e_email)); } else { $email = $user->loadPrimaryEmail(); if ($email) { $status = $email->getIsVerified() ? 'Verified' : 'Unverified'; } else { $status = 'No Email Address'; } $form->appendChild(id(new AphrontFormStaticControl())->setLabel('Email')->setValue($status)); $form->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox('welcome', 1, 'Re-send "Welcome to Phabricator" email.', false)); } $form->appendChild($this->getRoleInstructions()); if (!$user->getID()) { $form->appendChild(id(new AphrontFormSelectControl())->setLabel('Role')->setName('role')->setValue('user')->setOptions(array('user' => 'Normal User', 'agent' => 'System Agent'))->setCaption('You can create a "system agent" account for bots, scripts, ' . 'etc.'))->appendChild(id(new AphrontFormCheckboxControl())->addCheckbox('welcome', 1, 'Send "Welcome to Phabricator" email.', $welcome_checked)); } else { $roles = array(); if ($user->getIsSystemAgent()) { $roles[] = 'System Agent'; } if ($user->getIsAdmin()) { $roles[] = 'Admin'; } if ($user->getIsDisabled()) { $roles[] = 'Disabled'; } if (!$roles) { $roles[] = 'Normal User'; } $roles = implode(', ', $roles); $form->appendChild(id(new AphrontFormStaticControl())->setLabel('Roles')->setValue($roles)); } $form->appendChild(id(new AphrontFormSubmitControl())->setValue('Save')); $panel = new AphrontPanelView(); if ($user->getID()) { $panel->setHeader('Edit User'); } else { $panel->setHeader('Create New User'); } $panel->appendChild($form); $panel->setWidth(AphrontPanelView::WIDTH_FORM); return array($error_view, $panel); }
private function loadUserForEmail(PhabricatorUserEmail $email) { $user = id(new PhabricatorHandleQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withPHIDs(array($email->getUserPHID()))->executeOne(); if (!$user) { throw new Exception(pht('Email record ("%s") has bad associated user PHID ("%s").', $email->getAddress(), $email->getUserPHID())); } return $user; }
private function validateAuthenticatedUser(ConduitAPIRequest $request, PhabricatorUser $user) { if ($user->getIsDisabled()) { return array('ERR-USER-DISABLED', 'User is disabled.'); } if (PhabricatorUserEmail::isEmailVerificationRequired()) { $email = $user->loadPrimaryEmail(); if (!$email) { return array('ERR-USER-NOEMAIL', 'User has no primary email address.'); } if (!$email->getIsVerified()) { return array('ERR-USER-UNVERIFIED', 'User has unverified email address.'); } } $request->setUser($user); return null; }
private function generateEmailToken(PhabricatorUserEmail $email, $offset = 0) { $key = implode('-', array(PhabricatorEnv::getEnvConfig('phabricator.csrf-key'), $this->getPHID(), $email->getVerificationCode())); return $this->generateToken(time() + $offset * self::EMAIL_CYCLE_FREQUENCY, self::EMAIL_CYCLE_FREQUENCY, $key, self::EMAIL_TOKEN_LENGTH); }