diff --git a/src/applications/people/editor/PhabricatorUserEditor.php b/src/applications/people/editor/PhabricatorUserEditor.php index c8068858da..9ab2c9d60b 100644 --- a/src/applications/people/editor/PhabricatorUserEditor.php +++ b/src/applications/people/editor/PhabricatorUserEditor.php @@ -1,561 +1,507 @@ getID()) { throw new Exception(pht('User has already been created!')); } $is_reassign = false; if ($email->getID()) { if ($allow_reassign) { if ($email->getIsPrimary()) { throw new Exception( pht('Primary email addresses can not be reassigned.')); } $is_reassign = true; } else { throw new Exception(pht('Email has already been created!')); } } if (!PhabricatorUser::validateUsername($user->getUsername())) { $valid = PhabricatorUser::describeValidUsername(); throw new Exception(pht('Username is invalid! %s', $valid)); } // Always set a new user's email address to primary. $email->setIsPrimary(1); // If the primary address is already verified, also set the verified flag // on the user themselves. if ($email->getIsVerified()) { $user->setIsEmailVerified(1); } $this->willAddEmail($email); $user->openTransaction(); try { $user->save(); $email->setUserPHID($user->getPHID()); $email->save(); } catch (AphrontDuplicateKeyQueryException $ex) { // We might have written the user but failed to write the email; if // so, erase the IDs we attached. $user->setID(null); $user->setPHID(null); $user->killTransaction(); throw $ex; } - $log = PhabricatorUserLog::initializeNewLog( - $this->requireActor(), - $user->getPHID(), - PhabricatorUserLog::ACTION_CREATE); - $log->setNewValue($email->getAddress()); - $log->save(); - if ($is_reassign) { $log = PhabricatorUserLog::initializeNewLog( $this->requireActor(), $user->getPHID(), PhabricatorUserLog::ACTION_EMAIL_REASSIGN); $log->setNewValue($email->getAddress()); $log->save(); } $user->saveTransaction(); if ($email->getIsVerified()) { $this->didVerifyEmail($user, $email); } return $this; } - /** - * @task edit - */ - public function updateUser( - PhabricatorUser $user, - PhabricatorUserEmail $email = null) { - - if (!$user->getID()) { - throw new Exception(pht('User has not been created yet!')); - } - - $user->openTransaction(); - $user->save(); - if ($email) { - $email->save(); - } - - $log = PhabricatorUserLog::initializeNewLog( - $this->requireActor(), - $user->getPHID(), - PhabricatorUserLog::ACTION_EDIT); - $log->save(); - - $user->saveTransaction(); - - return $this; - } - - /* -( Editing Roles )------------------------------------------------------ */ /** * @task role */ public function makeSystemAgentUser(PhabricatorUser $user, $system_agent) { $actor = $this->requireActor(); if (!$user->getID()) { throw new Exception(pht('User has not been created yet!')); } $user->openTransaction(); $user->beginWriteLocking(); $user->reload(); if ($user->getIsSystemAgent() == $system_agent) { $user->endWriteLocking(); $user->killTransaction(); return $this; } - $log = PhabricatorUserLog::initializeNewLog( - $actor, - $user->getPHID(), - PhabricatorUserLog::ACTION_SYSTEM_AGENT); - $log->setOldValue($user->getIsSystemAgent()); - $log->setNewValue($system_agent); - $user->setIsSystemAgent((int)$system_agent); $user->save(); - $log->save(); - $user->endWriteLocking(); $user->saveTransaction(); return $this; } /** * @task role */ public function makeMailingListUser(PhabricatorUser $user, $mailing_list) { $actor = $this->requireActor(); if (!$user->getID()) { throw new Exception(pht('User has not been created yet!')); } $user->openTransaction(); $user->beginWriteLocking(); $user->reload(); if ($user->getIsMailingList() == $mailing_list) { $user->endWriteLocking(); $user->killTransaction(); return $this; } - $log = PhabricatorUserLog::initializeNewLog( - $actor, - $user->getPHID(), - PhabricatorUserLog::ACTION_MAILING_LIST); - $log->setOldValue($user->getIsMailingList()); - $log->setNewValue($mailing_list); - $user->setIsMailingList((int)$mailing_list); $user->save(); - $log->save(); - $user->endWriteLocking(); $user->saveTransaction(); return $this; } /* -( Adding, Removing and Changing Email )-------------------------------- */ /** * @task email */ public function addEmail( PhabricatorUser $user, PhabricatorUserEmail $email) { $actor = $this->requireActor(); if (!$user->getID()) { throw new Exception(pht('User has not been created yet!')); } if ($email->getID()) { throw new Exception(pht('Email has already been created!')); } // Use changePrimaryEmail() to change primary email. $email->setIsPrimary(0); $email->setUserPHID($user->getPHID()); $this->willAddEmail($email); $user->openTransaction(); $user->beginWriteLocking(); $user->reload(); try { $email->save(); } catch (AphrontDuplicateKeyQueryException $ex) { $user->endWriteLocking(); $user->killTransaction(); throw $ex; } $log = PhabricatorUserLog::initializeNewLog( $actor, $user->getPHID(), PhabricatorUserLog::ACTION_EMAIL_ADD); $log->setNewValue($email->getAddress()); $log->save(); $user->endWriteLocking(); $user->saveTransaction(); // Try and match this new address against unclaimed `RepositoryIdentity`s PhabricatorWorker::scheduleTask( 'PhabricatorRepositoryIdentityChangeWorker', array('userPHID' => $user->getPHID()), array('objectPHID' => $user->getPHID())); return $this; } /** * @task email */ public function removeEmail( PhabricatorUser $user, PhabricatorUserEmail $email) { $actor = $this->requireActor(); if (!$user->getID()) { throw new Exception(pht('User has not been created yet!')); } if (!$email->getID()) { throw new Exception(pht('Email has not been created yet!')); } $user->openTransaction(); $user->beginWriteLocking(); $user->reload(); $email->reload(); if ($email->getIsPrimary()) { throw new Exception(pht("Can't remove primary email!")); } if ($email->getUserPHID() != $user->getPHID()) { throw new Exception(pht('Email not owned by user!')); } $email->delete(); $log = PhabricatorUserLog::initializeNewLog( $actor, $user->getPHID(), PhabricatorUserLog::ACTION_EMAIL_REMOVE); $log->setOldValue($email->getAddress()); $log->save(); $user->endWriteLocking(); $user->saveTransaction(); $this->revokePasswordResetLinks($user); return $this; } /** * @task email */ public function changePrimaryEmail( PhabricatorUser $user, PhabricatorUserEmail $email) { $actor = $this->requireActor(); if (!$user->getID()) { throw new Exception(pht('User has not been created yet!')); } if (!$email->getID()) { throw new Exception(pht('Email has not been created yet!')); } $user->openTransaction(); $user->beginWriteLocking(); $user->reload(); $email->reload(); if ($email->getUserPHID() != $user->getPHID()) { throw new Exception(pht('User does not own email!')); } if ($email->getIsPrimary()) { throw new Exception(pht('Email is already primary!')); } if (!$email->getIsVerified()) { throw new Exception(pht('Email is not verified!')); } $old_primary = $user->loadPrimaryEmail(); if ($old_primary) { $old_primary->setIsPrimary(0); $old_primary->save(); } $email->setIsPrimary(1); $email->save(); // If the user doesn't have the verified flag set on their account // yet, set it. We've made sure the email is verified above. See // T12635 for discussion. if (!$user->getIsEmailVerified()) { $user->setIsEmailVerified(1); $user->save(); } $log = PhabricatorUserLog::initializeNewLog( $actor, $user->getPHID(), PhabricatorUserLog::ACTION_EMAIL_PRIMARY); $log->setOldValue($old_primary ? $old_primary->getAddress() : null); $log->setNewValue($email->getAddress()); $log->save(); $user->endWriteLocking(); $user->saveTransaction(); if ($old_primary) { $old_primary->sendOldPrimaryEmail($user, $email); } $email->sendNewPrimaryEmail($user); $this->revokePasswordResetLinks($user); return $this; } /** * Verify a user's email address. * * This verifies an individual email address. If the address is the user's * primary address and their account was not previously verified, their * account is marked as email verified. * * @task email */ public function verifyEmail( PhabricatorUser $user, PhabricatorUserEmail $email) { $actor = $this->requireActor(); if (!$user->getID()) { throw new Exception(pht('User has not been created yet!')); } if (!$email->getID()) { throw new Exception(pht('Email has not been created yet!')); } $user->openTransaction(); $user->beginWriteLocking(); $user->reload(); $email->reload(); if ($email->getUserPHID() != $user->getPHID()) { throw new Exception(pht('User does not own email!')); } if (!$email->getIsVerified()) { $email->setIsVerified(1); $email->save(); $log = PhabricatorUserLog::initializeNewLog( $actor, $user->getPHID(), PhabricatorUserLog::ACTION_EMAIL_VERIFY); $log->setNewValue($email->getAddress()); $log->save(); } if (!$user->getIsEmailVerified()) { // If the user just verified their primary email address, mark their // account as email verified. $user_primary = $user->loadPrimaryEmail(); if ($user_primary->getID() == $email->getID()) { $user->setIsEmailVerified(1); $user->save(); } } $user->endWriteLocking(); $user->saveTransaction(); $this->didVerifyEmail($user, $email); } /** * Reassign an unverified email address. */ public function reassignEmail( PhabricatorUser $user, PhabricatorUserEmail $email) { $actor = $this->requireActor(); if (!$user->getID()) { throw new Exception(pht('User has not been created yet!')); } if (!$email->getID()) { throw new Exception(pht('Email has not been created yet!')); } $user->openTransaction(); $user->beginWriteLocking(); $user->reload(); $email->reload(); $old_user = $email->getUserPHID(); if ($old_user != $user->getPHID()) { if ($email->getIsVerified()) { throw new Exception( pht('Verified email addresses can not be reassigned.')); } if ($email->getIsPrimary()) { throw new Exception( pht('Primary email addresses can not be reassigned.')); } $email->setUserPHID($user->getPHID()); $email->save(); $log = PhabricatorUserLog::initializeNewLog( $actor, $user->getPHID(), PhabricatorUserLog::ACTION_EMAIL_REASSIGN); $log->setNewValue($email->getAddress()); $log->save(); } $user->endWriteLocking(); $user->saveTransaction(); } /* -( Internals )---------------------------------------------------------- */ /** * @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()); } } public function revokePasswordResetLinks(PhabricatorUser $user) { // Revoke any outstanding password reset links. If an attacker compromises // an account, changes the email address, and sends themselves a password // reset link, it could otherwise remain live for a short period of time // and allow them to compromise the account again later. PhabricatorAuthTemporaryToken::revokeTokens( $user, array($user->getPHID()), array( PhabricatorAuthOneTimeLoginTemporaryTokenType::TOKENTYPE, PhabricatorAuthPasswordResetTemporaryTokenType::TOKENTYPE, )); } private function didVerifyEmail( PhabricatorUser $user, PhabricatorUserEmail $email) { $event_type = PhabricatorEventType::TYPE_AUTH_DIDVERIFYEMAIL; $event_data = array( 'user' => $user, 'email' => $email, ); $event = id(new PhabricatorEvent($event_type, $event_data)) ->setUser($user); PhutilEventEngine::dispatchEvent($event); } } diff --git a/src/applications/people/storage/PhabricatorUserLog.php b/src/applications/people/storage/PhabricatorUserLog.php index 12cb4cb626..c7550ba8e5 100644 --- a/src/applications/people/storage/PhabricatorUserLog.php +++ b/src/applications/people/storage/PhabricatorUserLog.php @@ -1,223 +1,203 @@ pht('Login'), self::ACTION_LOGIN_PARTIAL => pht('Login: Partial Login'), self::ACTION_LOGIN_FULL => pht('Login: Upgrade to Full'), self::ACTION_LOGIN_FAILURE => pht('Login: Failure'), self::ACTION_LOGIN_LEGALPAD => pht('Login: Signed Required Legalpad Documents'), self::ACTION_LOGOUT => pht('Logout'), self::ACTION_RESET_PASSWORD => pht('Reset Password'), - self::ACTION_CREATE => pht('Create Account'), - self::ACTION_EDIT => pht('Edit Account'), - self::ACTION_ADMIN => pht('Add/Remove Administrator'), - self::ACTION_SYSTEM_AGENT => pht('Add/Remove System Agent'), - self::ACTION_MAILING_LIST => pht('Add/Remove Mailing List'), - self::ACTION_DISABLE => pht('Enable/Disable'), - self::ACTION_APPROVE => pht('Approve Registration'), - self::ACTION_DELETE => pht('Delete User'), self::ACTION_CONDUIT_CERTIFICATE => pht('Conduit: Read Certificate'), self::ACTION_CONDUIT_CERTIFICATE_FAILURE => pht('Conduit: Read Certificate Failure'), self::ACTION_EMAIL_PRIMARY => pht('Email: Change Primary'), self::ACTION_EMAIL_ADD => pht('Email: Add Address'), self::ACTION_EMAIL_REMOVE => pht('Email: Remove Address'), self::ACTION_EMAIL_VERIFY => pht('Email: Verify'), self::ACTION_EMAIL_REASSIGN => pht('Email: Reassign'), self::ACTION_CHANGE_PASSWORD => pht('Change Password'), - self::ACTION_CHANGE_USERNAME => pht('Change Username'), self::ACTION_ENTER_HISEC => pht('Hisec: Enter'), self::ACTION_EXIT_HISEC => pht('Hisec: Exit'), self::ACTION_FAIL_HISEC => pht('Hisec: Failed Attempt'), self::ACTION_MULTI_ADD => pht('Multi-Factor: Add Factor'), self::ACTION_MULTI_REMOVE => pht('Multi-Factor: Remove Factor'), ); } public static function initializeNewLog( PhabricatorUser $actor = null, $object_phid = null, $action = null) { $log = new PhabricatorUserLog(); if ($actor) { $log->setActorPHID($actor->getPHID()); if ($actor->hasSession()) { $session = $actor->getSession(); // NOTE: This is a hash of the real session value, so it's safe to // store it directly in the logs. $log->setSession($session->getSessionKey()); } } $log->setUserPHID((string)$object_phid); $log->setAction($action); $address = PhabricatorEnv::getRemoteAddress(); if ($address) { $log->remoteAddr = $address->getAddress(); } else { $log->remoteAddr = ''; } return $log; } public static function loadRecentEventsFromThisIP($action, $timespan) { $address = PhabricatorEnv::getRemoteAddress(); if (!$address) { return array(); } return id(new PhabricatorUserLog())->loadAllWhere( 'action = %s AND remoteAddr = %s AND dateCreated > %d ORDER BY dateCreated DESC', $action, $address->getAddress(), PhabricatorTime::getNow() - $timespan); } public function save() { $this->details['host'] = php_uname('n'); $this->details['user_agent'] = AphrontRequest::getHTTPHeader('User-Agent'); return parent::save(); } protected function getConfiguration() { return array( self::CONFIG_SERIALIZATION => array( 'oldValue' => self::SERIALIZATION_JSON, 'newValue' => self::SERIALIZATION_JSON, 'details' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'actorPHID' => 'phid?', 'action' => 'text64', 'remoteAddr' => 'text64', 'session' => 'text64?', ), self::CONFIG_KEY_SCHEMA => array( 'actorPHID' => array( 'columns' => array('actorPHID', 'dateCreated'), ), 'userPHID' => array( 'columns' => array('userPHID', 'dateCreated'), ), 'action' => array( 'columns' => array('action', 'dateCreated'), ), 'dateCreated' => array( 'columns' => array('dateCreated'), ), 'remoteAddr' => array( 'columns' => array('remoteAddr', 'dateCreated'), ), 'session' => array( 'columns' => array('session', 'dateCreated'), ), ), ) + parent::getConfiguration(); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return PhabricatorPolicies::POLICY_NOONE; } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { if ($viewer->getIsAdmin()) { return true; } $viewer_phid = $viewer->getPHID(); if ($viewer_phid) { $user_phid = $this->getUserPHID(); if ($viewer_phid == $user_phid) { return true; } $actor_phid = $this->getActorPHID(); if ($viewer_phid == $actor_phid) { return true; } } return false; } public function describeAutomaticCapability($capability) { return array( pht('Users can view their activity and activity that affects them.'), pht('Administrators can always view all activity.'), ); } } diff --git a/src/applications/people/xaction/PhabricatorUserApproveTransaction.php b/src/applications/people/xaction/PhabricatorUserApproveTransaction.php index e458c5822c..77d58bebdf 100644 --- a/src/applications/people/xaction/PhabricatorUserApproveTransaction.php +++ b/src/applications/people/xaction/PhabricatorUserApproveTransaction.php @@ -1,96 +1,92 @@ getIsApproved(); } public function generateNewValue($object, $value) { return (bool)$value; } public function applyInternalEffects($object, $value) { $object->setIsApproved((int)$value); } public function applyExternalEffects($object, $value) { $user = $object; - $this->newUserLog(PhabricatorUserLog::ACTION_APPROVE) - ->setOldValue((bool)$user->getIsApproved()) - ->setNewValue((bool)$value) - ->save(); $actor = $this->getActor(); $title = pht( 'Phabricator Account "%s" Approved', $user->getUsername()); $body = sprintf( "%s\n\n %s\n\n", pht( 'Your Phabricator account (%s) has been approved by %s. You can '. 'login here:', $user->getUsername(), $actor->getUsername()), PhabricatorEnv::getProductionURI('/')); $mail = id(new PhabricatorMetaMTAMail()) ->addTos(array($user->getPHID())) ->addCCs(array($actor->getPHID())) ->setSubject('[Phabricator] '.$title) ->setForceDelivery(true) ->setBody($body) ->saveAndSend(); } public function getTitle() { $new = $this->getNewValue(); if ($new) { return pht( '%s approved this user.', $this->renderAuthor()); } else { return pht( '%s rejected this user.', $this->renderAuthor()); } } public function shouldHideForFeed() { return true; } public function validateTransactions($object, array $xactions) { $actor = $this->getActor(); $errors = array(); foreach ($xactions as $xaction) { $is_approved = (bool)$object->getIsApproved(); if ((bool)$xaction->getNewValue() === $is_approved) { continue; } if (!$actor->getIsAdmin()) { $errors[] = $this->newInvalidError( pht('You must be an administrator to approve users.')); } } return $errors; } public function getRequiredCapabilities( $object, PhabricatorApplicationTransaction $xaction) { // Unlike normal user edits, approvals require admin permissions, which // is enforced by validateTransactions(). return null; } } diff --git a/src/applications/people/xaction/PhabricatorUserDisableTransaction.php b/src/applications/people/xaction/PhabricatorUserDisableTransaction.php index 7a8a1c7966..f259e78ee4 100644 --- a/src/applications/people/xaction/PhabricatorUserDisableTransaction.php +++ b/src/applications/people/xaction/PhabricatorUserDisableTransaction.php @@ -1,79 +1,72 @@ getIsDisabled(); } public function generateNewValue($object, $value) { return (bool)$value; } public function applyInternalEffects($object, $value) { $object->setIsDisabled((int)$value); } - public function applyExternalEffects($object, $value) { - $this->newUserLog(PhabricatorUserLog::ACTION_DISABLE) - ->setOldValue((bool)$object->getIsDisabled()) - ->setNewValue((bool)$value) - ->save(); - } - public function getTitle() { $new = $this->getNewValue(); if ($new) { return pht( '%s disabled this user.', $this->renderAuthor()); } else { return pht( '%s enabled this user.', $this->renderAuthor()); } } public function shouldHideForFeed() { // Don't publish feed stories about disabling users, since this can be // a sensitive action. return true; } public function validateTransactions($object, array $xactions) { $errors = array(); foreach ($xactions as $xaction) { $is_disabled = (bool)$object->getIsDisabled(); if ((bool)$xaction->getNewValue() === $is_disabled) { continue; } // You must have the "Can Disable Users" permission to disable a user. $this->requireApplicationCapability( PeopleDisableUsersCapability::CAPABILITY); if ($this->getActingAsPHID() === $object->getPHID()) { $errors[] = $this->newInvalidError( pht('You can not enable or disable your own account.')); } } return $errors; } public function getRequiredCapabilities( $object, PhabricatorApplicationTransaction $xaction) { // You do not need to be able to edit users to disable them. Instead, this // requirement is replaced with a requirement that you have the "Can // Disable Users" permission. return null; } } diff --git a/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php b/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php index 1b561d3236..5499f5d8cb 100644 --- a/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php +++ b/src/applications/people/xaction/PhabricatorUserEmpowerTransaction.php @@ -1,98 +1,89 @@ getIsAdmin(); } public function generateNewValue($object, $value) { return (bool)$value; } public function applyInternalEffects($object, $value) { $object->setIsAdmin((int)$value); } - public function applyExternalEffects($object, $value) { - $user = $object; - - $this->newUserLog(PhabricatorUserLog::ACTION_ADMIN) - ->setOldValue($this->getOldValue()) - ->setNewValue($value) - ->save(); - } - public function validateTransactions($object, array $xactions) { $user = $object; $actor = $this->getActor(); $errors = array(); foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); if ($old === $new) { continue; } if ($user->getPHID() === $actor->getPHID()) { $errors[] = $this->newInvalidError( pht('After a time, your efforts fail. You can not adjust your own '. 'status as an administrator.'), $xaction); } $is_admin = $actor->getIsAdmin(); $is_omnipotent = $actor->isOmnipotent(); if (!$is_admin && !$is_omnipotent) { $errors[] = $this->newInvalidError( pht('You must be an administrator to create administrators.'), $xaction); } } return $errors; } public function getTitle() { $new = $this->getNewValue(); if ($new) { return pht( '%s empowered this user as an administrator.', $this->renderAuthor()); } else { return pht( '%s defrocked this user.', $this->renderAuthor()); } } public function getTitleForFeed() { $new = $this->getNewValue(); if ($new) { return pht( '%s empowered %s as an administrator.', $this->renderAuthor(), $this->renderObject()); } else { return pht( '%s defrocked %s.', $this->renderAuthor(), $this->renderObject()); } } public function getRequiredCapabilities( $object, PhabricatorApplicationTransaction $xaction) { // Unlike normal user edits, admin promotions require admin // permissions, which is enforced by validateTransactions(). return null; } } diff --git a/src/applications/people/xaction/PhabricatorUserTransactionType.php b/src/applications/people/xaction/PhabricatorUserTransactionType.php index dcd45d480e..89392fd039 100644 --- a/src/applications/people/xaction/PhabricatorUserTransactionType.php +++ b/src/applications/people/xaction/PhabricatorUserTransactionType.php @@ -1,13 +1,4 @@ getActor(), - $this->getObject()->getPHID(), - $action); - } - -} + extends PhabricatorModularTransactionType {} diff --git a/src/applications/people/xaction/PhabricatorUserUsernameTransaction.php b/src/applications/people/xaction/PhabricatorUserUsernameTransaction.php index b436b76716..338b296335 100644 --- a/src/applications/people/xaction/PhabricatorUserUsernameTransaction.php +++ b/src/applications/people/xaction/PhabricatorUserUsernameTransaction.php @@ -1,117 +1,112 @@ getUsername(); } public function generateNewValue($object, $value) { return $value; } public function applyInternalEffects($object, $value) { $object->setUsername($value); } public function applyExternalEffects($object, $value) { $actor = $this->getActor(); $user = $object; $old_username = $this->getOldValue(); $new_username = $this->getNewValue(); - $this->newUserLog(PhabricatorUserLog::ACTION_CHANGE_USERNAME) - ->setOldValue($old_username) - ->setNewValue($new_username) - ->save(); - // The SSH key cache currently includes usernames, so dirty it. See T12554 // for discussion. PhabricatorAuthSSHKeyQuery::deleteSSHKeyCache(); id(new PhabricatorPeopleUsernameMailEngine()) ->setSender($actor) ->setRecipient($object) ->setOldUsername($old_username) ->setNewUsername($new_username) ->sendMail(); } public function getTitle() { return pht( '%s renamed this user from %s to %s.', $this->renderAuthor(), $this->renderOldValue(), $this->renderNewValue()); } public function getTitleForFeed() { return pht( '%s renamed %s from %s to %s.', $this->renderAuthor(), $this->renderObject(), $this->renderOldValue(), $this->renderNewValue()); } public function validateTransactions($object, array $xactions) { $actor = $this->getActor(); $errors = array(); foreach ($xactions as $xaction) { $new = $xaction->getNewValue(); $old = $xaction->getOldValue(); if ($old === $new) { continue; } if (!$actor->getIsAdmin()) { $errors[] = $this->newInvalidError( pht('You must be an administrator to rename users.')); } if (!strlen($new)) { $errors[] = $this->newRequiredError( pht('New username is required.'), $xaction); } else if (!PhabricatorUser::validateUsername($new)) { $errors[] = $this->newInvalidError( PhabricatorUser::describeValidUsername(), $xaction); } $user = id(new PhabricatorPeopleQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withUsernames(array($new)) ->executeOne(); if ($user) { $errors[] = $this->newInvalidError( pht('Another user already has that username.'), $xaction); } } return $errors; } public function getRequiredCapabilities( $object, PhabricatorApplicationTransaction $xaction) { // Unlike normal user edits, renames require admin permissions, which // is enforced by validateTransactions(). return null; } public function shouldTryMFA( $object, PhabricatorApplicationTransaction $xaction) { return true; } }