mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-14 10:52:41 +01:00
ef85f49adc
Summary: This commit doesn't change license of any file. It just makes the license implicit (inherited from LICENSE file in the root directory). We are removing the headers for these reasons: - It wastes space in editors, less code is visible in editor upon opening a file. - It brings noise to diff of the first change of any file every year. - It confuses Git file copy detection when creating small files. - We don't have an explicit license header in other files (JS, CSS, images, documentation). - Using license header in every file is not obligatory: http://www.apache.org/dev/apply-license.html#new. This change is approved by Alma Chao (Lead Open Source and IP Counsel at Facebook). Test Plan: Verified that the license survived only in LICENSE file and that it didn't modify externals. Reviewers: epriestley, davidrecordon Reviewed By: epriestley CC: aran, Korvin Maniphest Tasks: T2035 Differential Revision: https://secure.phabricator.com/D3886
541 lines
12 KiB
PHP
541 lines
12 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Editor class for creating and adjusting users. This class guarantees data
|
|
* integrity and writes logs when user information changes.
|
|
*
|
|
* @task config Configuration
|
|
* @task edit Creating and Editing Users
|
|
* @task role Editing Roles
|
|
* @task email Adding, Removing and Changing Email
|
|
* @task internal Internals
|
|
*/
|
|
final class PhabricatorUserEditor extends PhabricatorEditor {
|
|
|
|
private $logs = array();
|
|
|
|
|
|
/* -( Creating and Editing Users )----------------------------------------- */
|
|
|
|
|
|
/**
|
|
* @task edit
|
|
*/
|
|
public function createNewUser(
|
|
PhabricatorUser $user,
|
|
PhabricatorUserEmail $email) {
|
|
|
|
if ($user->getID()) {
|
|
throw new Exception("User has already been created!");
|
|
}
|
|
|
|
if ($email->getID()) {
|
|
throw new Exception("Email has already been created!");
|
|
}
|
|
|
|
if (!PhabricatorUser::validateUsername($user->getUsername())) {
|
|
$valid = PhabricatorUser::describeValidUsername();
|
|
throw new Exception("Username is invalid! {$valid}");
|
|
}
|
|
|
|
// Always set a new user's email address to primary.
|
|
$email->setIsPrimary(1);
|
|
|
|
$this->willAddEmail($email);
|
|
|
|
$user->openTransaction();
|
|
try {
|
|
$user->save();
|
|
$email->setUserPHID($user->getPHID());
|
|
$email->save();
|
|
} catch (AphrontQueryDuplicateKeyException $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::newLog(
|
|
$this->requireActor(),
|
|
$user,
|
|
PhabricatorUserLog::ACTION_CREATE);
|
|
$log->setNewValue($email->getAddress());
|
|
$log->save();
|
|
|
|
$user->saveTransaction();
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @task edit
|
|
*/
|
|
public function updateUser(
|
|
PhabricatorUser $user,
|
|
PhabricatorUserEmail $email = null) {
|
|
if (!$user->getID()) {
|
|
throw new Exception("User has not been created yet!");
|
|
}
|
|
|
|
$user->openTransaction();
|
|
$user->save();
|
|
if ($email) {
|
|
$email->save();
|
|
}
|
|
|
|
$log = PhabricatorUserLog::newLog(
|
|
$this->requireActor(),
|
|
$user,
|
|
PhabricatorUserLog::ACTION_EDIT);
|
|
$log->save();
|
|
|
|
$user->saveTransaction();
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @task edit
|
|
*/
|
|
public function changePassword(
|
|
PhabricatorUser $user,
|
|
PhutilOpaqueEnvelope $envelope) {
|
|
|
|
if (!$user->getID()) {
|
|
throw new Exception("User has not been created yet!");
|
|
}
|
|
|
|
$user->openTransaction();
|
|
$user->reload();
|
|
|
|
$user->setPassword($envelope);
|
|
$user->save();
|
|
|
|
$log = PhabricatorUserLog::newLog(
|
|
$this->requireActor(),
|
|
$user,
|
|
PhabricatorUserLog::ACTION_CHANGE_PASSWORD);
|
|
$log->save();
|
|
|
|
$user->saveTransaction();
|
|
}
|
|
|
|
|
|
/**
|
|
* @task edit
|
|
*/
|
|
public function changeUsername(PhabricatorUser $user, $username) {
|
|
$actor = $this->requireActor();
|
|
|
|
if (!$user->getID()) {
|
|
throw new Exception("User has not been created yet!");
|
|
}
|
|
|
|
if (!PhabricatorUser::validateUsername($username)) {
|
|
$valid = PhabricatorUser::describeValidUsername();
|
|
throw new Exception("Username is invalid! {$valid}");
|
|
}
|
|
|
|
$old_username = $user->getUsername();
|
|
|
|
$user->openTransaction();
|
|
$user->reload();
|
|
$user->setUsername($username);
|
|
|
|
try {
|
|
$user->save();
|
|
} catch (AphrontQueryDuplicateKeyException $ex) {
|
|
$user->setUsername($old_username);
|
|
$user->killTransaction();
|
|
throw $ex;
|
|
}
|
|
|
|
$log = PhabricatorUserLog::newLog(
|
|
$actor,
|
|
$user,
|
|
PhabricatorUserLog::ACTION_CHANGE_USERNAME);
|
|
$log->setOldValue($old_username);
|
|
$log->setNewValue($username);
|
|
$log->save();
|
|
|
|
$user->saveTransaction();
|
|
|
|
$user->sendUsernameChangeEmail($actor, $old_username);
|
|
}
|
|
|
|
|
|
/* -( Editing Roles )------------------------------------------------------ */
|
|
|
|
|
|
/**
|
|
* @task role
|
|
*/
|
|
public function makeAdminUser(PhabricatorUser $user, $admin) {
|
|
$actor = $this->requireActor();
|
|
|
|
if (!$user->getID()) {
|
|
throw new Exception("User has not been created yet!");
|
|
}
|
|
|
|
$user->openTransaction();
|
|
$user->beginWriteLocking();
|
|
|
|
$user->reload();
|
|
if ($user->getIsAdmin() == $admin) {
|
|
$user->endWriteLocking();
|
|
$user->killTransaction();
|
|
return $this;
|
|
}
|
|
|
|
$log = PhabricatorUserLog::newLog(
|
|
$actor,
|
|
$user,
|
|
PhabricatorUserLog::ACTION_ADMIN);
|
|
$log->setOldValue($user->getIsAdmin());
|
|
$log->setNewValue($admin);
|
|
|
|
$user->setIsAdmin($admin);
|
|
$user->save();
|
|
|
|
$log->save();
|
|
|
|
$user->endWriteLocking();
|
|
$user->saveTransaction();
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* @task role
|
|
*/
|
|
public function makeSystemAgentUser(PhabricatorUser $user, $system_agent) {
|
|
$actor = $this->requireActor();
|
|
|
|
if (!$user->getID()) {
|
|
throw new Exception("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::newLog(
|
|
$actor,
|
|
$user,
|
|
PhabricatorUserLog::ACTION_SYSTEM_AGENT);
|
|
$log->setOldValue($user->getIsSystemAgent());
|
|
$log->setNewValue($system_agent);
|
|
|
|
$user->setIsSystemAgent($system_agent);
|
|
$user->save();
|
|
|
|
$log->save();
|
|
|
|
$user->endWriteLocking();
|
|
$user->saveTransaction();
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @task role
|
|
*/
|
|
public function disableUser(PhabricatorUser $user, $disable) {
|
|
$actor = $this->requireActor();
|
|
|
|
if (!$user->getID()) {
|
|
throw new Exception("User has not been created yet!");
|
|
}
|
|
|
|
$user->openTransaction();
|
|
$user->beginWriteLocking();
|
|
|
|
$user->reload();
|
|
if ($user->getIsDisabled() == $disable) {
|
|
$user->endWriteLocking();
|
|
$user->killTransaction();
|
|
return $this;
|
|
}
|
|
|
|
$log = PhabricatorUserLog::newLog(
|
|
$actor,
|
|
$user,
|
|
PhabricatorUserLog::ACTION_DISABLE);
|
|
$log->setOldValue($user->getIsDisabled());
|
|
$log->setNewValue($disable);
|
|
|
|
$user->setIsDisabled($disable);
|
|
$user->save();
|
|
|
|
$log->save();
|
|
|
|
$user->endWriteLocking();
|
|
$user->saveTransaction();
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @task role
|
|
*/
|
|
public function deleteUser(PhabricatorUser $user, $disable) {
|
|
$actor = $this->requireActor();
|
|
|
|
if (!$user->getID()) {
|
|
throw new Exception("User has not been created yet!");
|
|
}
|
|
|
|
if ($actor->getPHID() == $user->getPHID()) {
|
|
throw new Exception("You can not delete yourself!");
|
|
}
|
|
|
|
$user->openTransaction();
|
|
$ldaps = id(new PhabricatorUserLDAPInfo())->loadAllWhere(
|
|
'userID = %d',
|
|
$user->getID());
|
|
foreach ($ldaps as $ldap) {
|
|
$ldap->delete();
|
|
}
|
|
|
|
$oauths = id(new PhabricatorUserOAuthInfo())->loadAllWhere(
|
|
'userID = %d',
|
|
$user->getID());
|
|
foreach ($oauths as $oauth) {
|
|
$oauth->delete();
|
|
}
|
|
|
|
$prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
|
|
'userPHID = %s',
|
|
$user->getPHID());
|
|
foreach ($prefs as $pref) {
|
|
$pref->delete();
|
|
}
|
|
|
|
$profiles = id(new PhabricatorUserProfile())->loadAllWhere(
|
|
'userPHID = %s',
|
|
$user->getPHID());
|
|
foreach ($profiles as $profile) {
|
|
$profile->delete();
|
|
}
|
|
|
|
$keys = id(new PhabricatorUserSSHKey())->loadAllWhere(
|
|
'userPHID = %s',
|
|
$user->getPHID());
|
|
foreach ($keys as $key) {
|
|
$key->delete();
|
|
}
|
|
|
|
$emails = id(new PhabricatorUserEmail())->loadAllWhere(
|
|
'userPHID = %s',
|
|
$user->getPHID());
|
|
foreach ($emails as $email) {
|
|
$email->delete();
|
|
}
|
|
|
|
$log = PhabricatorUserLog::newLog(
|
|
$actor,
|
|
$user,
|
|
PhabricatorUserLog::ACTION_DELETE);
|
|
$log->save();
|
|
|
|
$user->delete();
|
|
|
|
$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("User has not been created yet!");
|
|
}
|
|
if ($email->getID()) {
|
|
throw new Exception("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 (AphrontQueryDuplicateKeyException $ex) {
|
|
$user->endWriteLocking();
|
|
$user->killTransaction();
|
|
|
|
throw $ex;
|
|
}
|
|
|
|
$log = PhabricatorUserLog::newLog(
|
|
$actor,
|
|
$user,
|
|
PhabricatorUserLog::ACTION_EMAIL_ADD);
|
|
$log->setNewValue($email->getAddress());
|
|
$log->save();
|
|
|
|
$user->endWriteLocking();
|
|
$user->saveTransaction();
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @task email
|
|
*/
|
|
public function removeEmail(
|
|
PhabricatorUser $user,
|
|
PhabricatorUserEmail $email) {
|
|
|
|
$actor = $this->requireActor();
|
|
|
|
if (!$user->getID()) {
|
|
throw new Exception("User has not been created yet!");
|
|
}
|
|
if (!$email->getID()) {
|
|
throw new Exception("Email has not been created yet!");
|
|
}
|
|
|
|
$user->openTransaction();
|
|
$user->beginWriteLocking();
|
|
|
|
$user->reload();
|
|
$email->reload();
|
|
|
|
if ($email->getIsPrimary()) {
|
|
throw new Exception("Can't remove primary email!");
|
|
}
|
|
if ($email->getUserPHID() != $user->getPHID()) {
|
|
throw new Exception("Email not owned by user!");
|
|
}
|
|
|
|
$email->delete();
|
|
|
|
$log = PhabricatorUserLog::newLog(
|
|
$actor,
|
|
$user,
|
|
PhabricatorUserLog::ACTION_EMAIL_REMOVE);
|
|
$log->setOldValue($email->getAddress());
|
|
$log->save();
|
|
|
|
$user->endWriteLocking();
|
|
$user->saveTransaction();
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* @task email
|
|
*/
|
|
public function changePrimaryEmail(
|
|
PhabricatorUser $user,
|
|
PhabricatorUserEmail $email) {
|
|
$actor = $this->requireActor();
|
|
|
|
if (!$user->getID()) {
|
|
throw new Exception("User has not been created yet!");
|
|
}
|
|
if (!$email->getID()) {
|
|
throw new Exception("Email has not been created yet!");
|
|
}
|
|
|
|
$user->openTransaction();
|
|
$user->beginWriteLocking();
|
|
|
|
$user->reload();
|
|
$email->reload();
|
|
|
|
if ($email->getUserPHID() != $user->getPHID()) {
|
|
throw new Exception("User does not own email!");
|
|
}
|
|
|
|
if ($email->getIsPrimary()) {
|
|
throw new Exception("Email is already primary!");
|
|
}
|
|
|
|
if (!$email->getIsVerified()) {
|
|
throw new Exception("Email is not verified!");
|
|
}
|
|
|
|
$old_primary = $user->loadPrimaryEmail();
|
|
if ($old_primary) {
|
|
$old_primary->setIsPrimary(0);
|
|
$old_primary->save();
|
|
}
|
|
|
|
$email->setIsPrimary(1);
|
|
$email->save();
|
|
|
|
$log = PhabricatorUserLog::newLog(
|
|
$actor,
|
|
$user,
|
|
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);
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/* -( 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::isAllowedAddress($email->getAddress())) {
|
|
throw new Exception(PhabricatorUserEmail::describeAllowedAddresses());
|
|
}
|
|
}
|
|
|
|
}
|