mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 08:42:41 +01:00
Consolidate user editing code
Summary: - We currently have some bugs in account creation due to nontransactional user/email editing. - We save $user, then try to save $email. This may fail for various reasons, commonly because the email isn't unique. - This leaves us with a $user with no email. - Also, logging of edits is somewhat inconsistent across various edit mechanisms. - Move all editing to a `PhabricatorUserEditor` class. - Handle some broken-data cases more gracefully. Test Plan: - Created and edited a user with `accountadmin`. - Created a user with `add_user.php` - Created and edited a user with People editor. - Created a user with OAuth. - Edited user information via Settings. - Tried to create an OAuth user with a duplicate email address, got a proper error. - Tried to create a user via People with a duplicate email address, got a proper error. Reviewers: btrahan, vrana, jungejason Reviewed By: btrahan CC: tberman, aran Maniphest Tasks: T1184 Differential Revision: https://secure.phabricator.com/D2569
This commit is contained in:
parent
eb310888e5
commit
70fd96037b
16 changed files with 502 additions and 87 deletions
|
@ -121,7 +121,6 @@ $is_admin = $user->getIsAdmin();
|
||||||
$set_admin = phutil_console_confirm(
|
$set_admin = phutil_console_confirm(
|
||||||
'Should this user be an administrator?',
|
'Should this user be an administrator?',
|
||||||
$default_no = !$is_admin);
|
$default_no = !$is_admin);
|
||||||
$user->setIsAdmin($set_admin);
|
|
||||||
|
|
||||||
echo "\n\nACCOUNT SUMMARY\n\n";
|
echo "\n\nACCOUNT SUMMARY\n\n";
|
||||||
$tpl = "%12s %-30s %-30s\n";
|
$tpl = "%12s %-30s %-30s\n";
|
||||||
|
@ -149,21 +148,30 @@ if (!phutil_console_confirm("Save these changes?", $default_no = false)) {
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
$user->save();
|
$user->openTransaction();
|
||||||
if ($changed_pass !== false) {
|
|
||||||
// This must happen after saving the user because we use their PHID as a
|
|
||||||
// component of the password hash.
|
|
||||||
$user->setPassword($changed_pass);
|
|
||||||
$user->save();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($new_email) {
|
$editor = new PhabricatorUserEditor();
|
||||||
id(new PhabricatorUserEmail())
|
|
||||||
->setUserPHID($user->getPHID())
|
// TODO: This is wrong, but we have a chicken-and-egg problem when you use
|
||||||
->setAddress($new_email)
|
// this script to create the first user.
|
||||||
->setIsVerified(1)
|
$editor->setActor($user);
|
||||||
->setIsPrimary(1)
|
|
||||||
->save();
|
if ($new_email) {
|
||||||
}
|
$email = id(new PhabricatorUserEmail())
|
||||||
|
->setAddress($new_email)
|
||||||
|
->setIsVerified(1);
|
||||||
|
|
||||||
|
$editor->createNewUser($user, $email);
|
||||||
|
} else {
|
||||||
|
$editor->updateUser($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
$editor->makeAdminUser($user, $set_admin);
|
||||||
|
|
||||||
|
if ($changed_pass !== false) {
|
||||||
|
$editor->changePassword($user, $changed_pass);
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->saveTransaction();
|
||||||
|
|
||||||
echo "Saved changes.\n";
|
echo "Saved changes.\n";
|
||||||
|
|
|
@ -61,14 +61,14 @@ if ($existing_email) {
|
||||||
$user = new PhabricatorUser();
|
$user = new PhabricatorUser();
|
||||||
$user->setUsername($username);
|
$user->setUsername($username);
|
||||||
$user->setRealname($realname);
|
$user->setRealname($realname);
|
||||||
$user->save();
|
|
||||||
|
|
||||||
$email_object = id(new PhabricatorUserEmail())
|
$email_object = id(new PhabricatorUserEmail())
|
||||||
->setUserPHID($user->getPHID())
|
|
||||||
->setAddress($email)
|
->setAddress($email)
|
||||||
->setIsVerified(1)
|
->setIsVerified(1);
|
||||||
->setIsPrimary(1)
|
|
||||||
->save();
|
id(new PhabricatorUserEditor())
|
||||||
|
->setActor($admin)
|
||||||
|
->createNewUser($user, $email_object);
|
||||||
|
|
||||||
$user->sendWelcomeEmail($admin);
|
$user->sendWelcomeEmail($admin);
|
||||||
|
|
||||||
|
|
|
@ -956,6 +956,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorUserAccountSettingsPanelController' => 'applications/people/controller/settings/panels/account',
|
'PhabricatorUserAccountSettingsPanelController' => 'applications/people/controller/settings/panels/account',
|
||||||
'PhabricatorUserConduitSettingsPanelController' => 'applications/people/controller/settings/panels/conduit',
|
'PhabricatorUserConduitSettingsPanelController' => 'applications/people/controller/settings/panels/conduit',
|
||||||
'PhabricatorUserDAO' => 'applications/people/storage/base',
|
'PhabricatorUserDAO' => 'applications/people/storage/base',
|
||||||
|
'PhabricatorUserEditor' => 'applications/people/editor',
|
||||||
'PhabricatorUserEmail' => 'applications/people/storage/email',
|
'PhabricatorUserEmail' => 'applications/people/storage/email',
|
||||||
'PhabricatorUserEmailPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/emailpref',
|
'PhabricatorUserEmailPreferenceSettingsPanelController' => 'applications/people/controller/settings/panels/emailpref',
|
||||||
'PhabricatorUserEmailSettingsPanelController' => 'applications/people/controller/settings/panels/email',
|
'PhabricatorUserEmailSettingsPanelController' => 'applications/people/controller/settings/panels/email',
|
||||||
|
|
|
@ -83,7 +83,6 @@ final class PhabricatorOAuthDefaultRegistrationController
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$user->save();
|
|
||||||
|
|
||||||
// NOTE: We don't verify OAuth email addresses by default because
|
// NOTE: We don't verify OAuth email addresses by default because
|
||||||
// OAuth providers might associate email addresses with accounts that
|
// OAuth providers might associate email addresses with accounts that
|
||||||
|
@ -92,12 +91,14 @@ final class PhabricatorOAuthDefaultRegistrationController
|
||||||
// verifying an email address are high because having a corporate
|
// verifying an email address are high because having a corporate
|
||||||
// address at a company is sometimes the key to the castle.
|
// address at a company is sometimes the key to the castle.
|
||||||
|
|
||||||
$new_email = id(new PhabricatorUserEmail())
|
|
||||||
->setUserPHID($user->getPHID())
|
$email_obj = id(new PhabricatorUserEmail())
|
||||||
->setAddress($new_email)
|
->setAddress($new_email)
|
||||||
->setIsPrimary(1)
|
->setIsVerified(0);
|
||||||
->setIsVerified(0)
|
|
||||||
->save();
|
id(new PhabricatorUserEditor())
|
||||||
|
->setActor($user)
|
||||||
|
->createNewUser($user, $email_obj);
|
||||||
|
|
||||||
$oauth_info->setUserID($user->getID());
|
$oauth_info->setUserID($user->getID());
|
||||||
$oauth_info->save();
|
$oauth_info->save();
|
||||||
|
@ -106,7 +107,7 @@ final class PhabricatorOAuthDefaultRegistrationController
|
||||||
$request->setCookie('phusr', $user->getUsername());
|
$request->setCookie('phusr', $user->getUsername());
|
||||||
$request->setCookie('phsid', $session_key);
|
$request->setCookie('phsid', $session_key);
|
||||||
|
|
||||||
$new_email->sendVerificationEmail($user);
|
$email_obj->sendVerificationEmail($user);
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())->setURI('/');
|
return id(new AphrontRedirectResponse())->setURI('/');
|
||||||
} catch (AphrontQueryDuplicateKeyException $exception) {
|
} catch (AphrontQueryDuplicateKeyException $exception) {
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
phutil_require_module('phabricator', 'applications/auth/controller/oauthregistration/base');
|
phutil_require_module('phabricator', 'applications/auth/controller/oauthregistration/base');
|
||||||
phutil_require_module('phabricator', 'applications/files/storage/file');
|
phutil_require_module('phabricator', 'applications/files/storage/file');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/editor');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/email');
|
phutil_require_module('phabricator', 'applications/people/storage/email');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
phutil_require_module('phabricator', 'view/form/base');
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
|
|
|
@ -44,7 +44,8 @@ final class ConduitAPI_user_disable_Method
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function execute(ConduitAPIRequest $request) {
|
protected function execute(ConduitAPIRequest $request) {
|
||||||
if (!$request->getUser()->getIsAdmin()) {
|
$actor = $request->getUser();
|
||||||
|
if (!$actor->getIsAdmin()) {
|
||||||
throw new ConduitException('ERR-PERMISSIONS');
|
throw new ConduitException('ERR-PERMISSIONS');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,8 +60,9 @@ final class ConduitAPI_user_disable_Method
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($users as $user) {
|
foreach ($users as $user) {
|
||||||
$user->setIsDisabled(true);
|
id(new PhabricatorUserEditor())
|
||||||
$user->save();
|
->setActor($actor)
|
||||||
|
->disableUser($user, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
phutil_require_module('phabricator', 'applications/conduit/method/user/base');
|
phutil_require_module('phabricator', 'applications/conduit/method/user/base');
|
||||||
phutil_require_module('phabricator', 'applications/conduit/protocol/exception');
|
phutil_require_module('phabricator', 'applications/conduit/protocol/exception');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/editor');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
|
@ -165,22 +165,18 @@ final class PhabricatorPeopleEditController
|
||||||
try {
|
try {
|
||||||
$is_new = !$user->getID();
|
$is_new = !$user->getID();
|
||||||
|
|
||||||
$user->save();
|
if (!$is_new) {
|
||||||
|
id(new PhabricatorUserEditor())
|
||||||
if ($is_new) {
|
->setActor($admin)
|
||||||
|
->updateUser($user);
|
||||||
|
} else {
|
||||||
$email = id(new PhabricatorUserEmail())
|
$email = id(new PhabricatorUserEmail())
|
||||||
->setUserPHID($user->getPHID())
|
|
||||||
->setAddress($new_email)
|
->setAddress($new_email)
|
||||||
->setIsPrimary(1)
|
->setIsVerified(0);
|
||||||
->setIsVerified(0)
|
|
||||||
->save();
|
|
||||||
|
|
||||||
$log = PhabricatorUserLog::newLog(
|
id(new PhabricatorUserEditor())
|
||||||
$admin,
|
->setActor($admin)
|
||||||
$user,
|
->createNewUser($user, $email);
|
||||||
PhabricatorUserLog::ACTION_CREATE);
|
|
||||||
$log->save();
|
|
||||||
|
|
||||||
if ($welcome_checked) {
|
if ($welcome_checked) {
|
||||||
$user->sendWelcomeEmail($admin);
|
$user->sendWelcomeEmail($admin);
|
||||||
|
@ -255,13 +251,17 @@ final class PhabricatorPeopleEditController
|
||||||
->setValue($new_email)
|
->setValue($new_email)
|
||||||
->setError($e_email));
|
->setError($e_email));
|
||||||
} else {
|
} else {
|
||||||
|
$email = $user->loadPrimaryEmail();
|
||||||
|
if ($email) {
|
||||||
|
$status = $email->getIsVerified() ? 'Verified' : 'Unverified';
|
||||||
|
} else {
|
||||||
|
$status = 'No Email Address';
|
||||||
|
}
|
||||||
|
|
||||||
$form->appendChild(
|
$form->appendChild(
|
||||||
id(new AphrontFormStaticControl())
|
id(new AphrontFormStaticControl())
|
||||||
->setLabel('Email')
|
->setLabel('Email')
|
||||||
->setValue(
|
->setValue($status));
|
||||||
$user->loadPrimaryEmail()->getIsVerified()
|
|
||||||
? 'Verified'
|
|
||||||
: 'Unverified'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$form->appendChild($this->getRoleInstructions());
|
$form->appendChild($this->getRoleInstructions());
|
||||||
|
@ -354,31 +354,21 @@ final class PhabricatorPeopleEditController
|
||||||
$new_admin = (bool)$request->getBool('is_admin');
|
$new_admin = (bool)$request->getBool('is_admin');
|
||||||
$old_admin = (bool)$user->getIsAdmin();
|
$old_admin = (bool)$user->getIsAdmin();
|
||||||
if ($new_admin != $old_admin) {
|
if ($new_admin != $old_admin) {
|
||||||
$log = clone $log_template;
|
id(new PhabricatorUserEditor())
|
||||||
$log->setAction(PhabricatorUserLog::ACTION_ADMIN);
|
->setActor($admin)
|
||||||
$log->setOldValue($old_admin);
|
->makeAdminUser($user, $new_admin);
|
||||||
$log->setNewValue($new_admin);
|
|
||||||
$user->setIsAdmin($new_admin);
|
|
||||||
$logs[] = $log;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$new_disabled = (bool)$request->getBool('is_disabled');
|
$new_disabled = (bool)$request->getBool('is_disabled');
|
||||||
$old_disabled = (bool)$user->getIsDisabled();
|
$old_disabled = (bool)$user->getIsDisabled();
|
||||||
if ($new_disabled != $old_disabled) {
|
if ($new_disabled != $old_disabled) {
|
||||||
$log = clone $log_template;
|
id(new PhabricatorUserEditor())
|
||||||
$log->setAction(PhabricatorUserLog::ACTION_DISABLE);
|
->setActor($admin)
|
||||||
$log->setOldValue($old_disabled);
|
->disableUser($user, $new_disabled);
|
||||||
$log->setNewValue($new_disabled);
|
|
||||||
$user->setIsDisabled($new_disabled);
|
|
||||||
$logs[] = $log;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$errors) {
|
if (!$errors) {
|
||||||
$user->save();
|
|
||||||
foreach ($logs as $log) {
|
|
||||||
$log->save();
|
|
||||||
}
|
|
||||||
return id(new AphrontRedirectResponse())
|
return id(new AphrontRedirectResponse())
|
||||||
->setURI($request->getRequestURI()->alter('saved', 'true'));
|
->setURI($request->getRequestURI()->alter('saved', 'true'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
phutil_require_module('phabricator', 'aphront/response/404');
|
phutil_require_module('phabricator', 'aphront/response/404');
|
||||||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
phutil_require_module('phabricator', 'applications/people/controller/base');
|
phutil_require_module('phabricator', 'applications/people/controller/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/editor');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/email');
|
phutil_require_module('phabricator', 'applications/people/storage/email');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/log');
|
phutil_require_module('phabricator', 'applications/people/storage/log');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/user');
|
phutil_require_module('phabricator', 'applications/people/storage/user');
|
||||||
|
|
|
@ -175,13 +175,14 @@ final class PhabricatorUserEmailSettingsPanelController
|
||||||
|
|
||||||
if (!$errors) {
|
if (!$errors) {
|
||||||
$object = id(new PhabricatorUserEmail())
|
$object = id(new PhabricatorUserEmail())
|
||||||
->setUserPHID($user->getPHID())
|
|
||||||
->setAddress($email)
|
->setAddress($email)
|
||||||
->setIsVerified(0)
|
->setIsVerified(0);
|
||||||
->setIsPrimary(0);
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$object->save();
|
|
||||||
|
id(new PhabricatorUserEditor())
|
||||||
|
->setActor($user)
|
||||||
|
->addEmail($user, $object);
|
||||||
|
|
||||||
$object->sendVerificationEmail($user);
|
$object->sendVerificationEmail($user);
|
||||||
|
|
||||||
|
@ -245,7 +246,11 @@ final class PhabricatorUserEmailSettingsPanelController
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
$email->delete();
|
|
||||||
|
id(new PhabricatorUserEditor())
|
||||||
|
->setActor($user)
|
||||||
|
->removeEmail($user, $email);
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -314,21 +319,9 @@ final class PhabricatorUserEmailSettingsPanelController
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
|
|
||||||
// TODO: Transactions!
|
id(new PhabricatorUserEditor())
|
||||||
|
->setActor($user)
|
||||||
$email->setIsPrimary(1);
|
->changePrimaryEmail($user, $email);
|
||||||
|
|
||||||
$old_primary = $user->loadPrimaryEmail();
|
|
||||||
if ($old_primary) {
|
|
||||||
$old_primary->setIsPrimary(0);
|
|
||||||
$old_primary->save();
|
|
||||||
}
|
|
||||||
$email->save();
|
|
||||||
|
|
||||||
if ($old_primary) {
|
|
||||||
$old_primary->sendOldPrimaryEmail($user, $email);
|
|
||||||
}
|
|
||||||
$email->sendNewPrimaryEmail($user);
|
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())->setURI($uri);
|
return id(new AphrontRedirectResponse())->setURI($uri);
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,7 @@ phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
phutil_require_module('phabricator', 'aphront/response/reload');
|
phutil_require_module('phabricator', 'aphront/response/reload');
|
||||||
phutil_require_module('phabricator', 'applications/people/controller/settings/panels/base');
|
phutil_require_module('phabricator', 'applications/people/controller/settings/panels/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/editor');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/email');
|
phutil_require_module('phabricator', 'applications/people/storage/email');
|
||||||
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
|
||||||
phutil_require_module('phabricator', 'view/control/table');
|
phutil_require_module('phabricator', 'view/control/table');
|
||||||
|
|
|
@ -79,13 +79,16 @@ final class PhabricatorUserPasswordSettingsPanelController
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$errors) {
|
if (!$errors) {
|
||||||
$user->setPassword($pass);
|
|
||||||
// This write is unguarded because the CSRF token has already
|
// This write is unguarded because the CSRF token has already
|
||||||
// been checked in the call to $request->isFormPost() and
|
// been checked in the call to $request->isFormPost() and
|
||||||
// the CSRF token depends on the password hash, so when it
|
// the CSRF token depends on the password hash, so when it
|
||||||
// is changed here the CSRF token check will fail.
|
// is changed here the CSRF token check will fail.
|
||||||
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||||
$user->save();
|
|
||||||
|
id(new PhabricatorUserEditor())
|
||||||
|
->setActor($user)
|
||||||
|
->changePassword($user, $pass);
|
||||||
|
|
||||||
unset($unguarded);
|
unset($unguarded);
|
||||||
|
|
||||||
if ($valid_token) {
|
if ($valid_token) {
|
||||||
|
|
|
@ -10,6 +10,7 @@ phutil_require_module('phabricator', 'aphront/response/400');
|
||||||
phutil_require_module('phabricator', 'aphront/response/redirect');
|
phutil_require_module('phabricator', 'aphront/response/redirect');
|
||||||
phutil_require_module('phabricator', 'aphront/writeguard');
|
phutil_require_module('phabricator', 'aphront/writeguard');
|
||||||
phutil_require_module('phabricator', 'applications/people/controller/settings/panels/base');
|
phutil_require_module('phabricator', 'applications/people/controller/settings/panels/base');
|
||||||
|
phutil_require_module('phabricator', 'applications/people/editor');
|
||||||
phutil_require_module('phabricator', 'applications/people/storage/email');
|
phutil_require_module('phabricator', 'applications/people/storage/email');
|
||||||
phutil_require_module('phabricator', 'infrastructure/env');
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
phutil_require_module('phabricator', 'view/form/base');
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
|
|
393
src/applications/people/editor/PhabricatorUserEditor.php
Normal file
393
src/applications/people/editor/PhabricatorUserEditor.php
Normal file
|
@ -0,0 +1,393 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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 {
|
||||||
|
|
||||||
|
private $actor;
|
||||||
|
private $logs = array();
|
||||||
|
|
||||||
|
|
||||||
|
/* -( Configuration )------------------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task config
|
||||||
|
*/
|
||||||
|
public function setActor(PhabricatorUser $actor) {
|
||||||
|
$this->actor = $actor;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( 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!");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always set a new user's email address to primary.
|
||||||
|
$email->setIsPrimary(1);
|
||||||
|
|
||||||
|
$user->openTransaction();
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$email->setUserPHID($user->getPHID());
|
||||||
|
|
||||||
|
try {
|
||||||
|
$email->save();
|
||||||
|
} catch (AphrontQueryDuplicateKeyException $ex) {
|
||||||
|
$user->killTransaction();
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$log = PhabricatorUserLog::newLog(
|
||||||
|
$this->actor,
|
||||||
|
$user,
|
||||||
|
PhabricatorUserLog::ACTION_CREATE);
|
||||||
|
$log->setNewValue($email->getAddress());
|
||||||
|
$log->save();
|
||||||
|
|
||||||
|
$user->saveTransaction();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task edit
|
||||||
|
*/
|
||||||
|
public function updateUser(PhabricatorUser $user) {
|
||||||
|
if (!$user->getID()) {
|
||||||
|
throw new Exception("User has not been created yet!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$actor = $this->requireActor();
|
||||||
|
$user->openTransaction();
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$log = PhabricatorUserLog::newLog(
|
||||||
|
$actor,
|
||||||
|
$user,
|
||||||
|
PhabricatorUserLog::ACTION_EDIT);
|
||||||
|
$log->save();
|
||||||
|
|
||||||
|
$user->saveTransaction();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @task edit
|
||||||
|
*/
|
||||||
|
public function changePassword(PhabricatorUser $user, $password) {
|
||||||
|
if (!$user->getID()) {
|
||||||
|
throw new Exception("User has not been created yet!");
|
||||||
|
}
|
||||||
|
|
||||||
|
$user->openTransaction();
|
||||||
|
$user->reload();
|
||||||
|
|
||||||
|
$user->setPassword($password);
|
||||||
|
$user->save();
|
||||||
|
|
||||||
|
$log = PhabricatorUserLog::newLog(
|
||||||
|
$this->actor,
|
||||||
|
$user,
|
||||||
|
PhabricatorUserLog::ACTION_CHANGE_PASSWORD);
|
||||||
|
$log->save();
|
||||||
|
|
||||||
|
$user->saveTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( 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 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( 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());
|
||||||
|
|
||||||
|
$user->openTransaction();
|
||||||
|
$user->beginWriteLocking();
|
||||||
|
|
||||||
|
$user->reload();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$email->save();
|
||||||
|
} catch (AphrontQueryDuplicateKeyException $ex) {
|
||||||
|
$user->endWriteLocking();
|
||||||
|
$user->killTransaction();
|
||||||
|
|
||||||
|
throw $ex;
|
||||||
|
}
|
||||||
|
|
||||||
|
$log = PhabricatorUserLog::newLog(
|
||||||
|
$this->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(
|
||||||
|
$this->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 requireActor() {
|
||||||
|
if (!$this->actor) {
|
||||||
|
throw new Exception("User edit requires actor!");
|
||||||
|
}
|
||||||
|
return $this->actor;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
12
src/applications/people/editor/__init__.php
Normal file
12
src/applications/people/editor/__init__.php
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* This file is automatically generated. Lint this module to rebuild it.
|
||||||
|
* @generated
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_module('phabricator', 'applications/people/storage/log');
|
||||||
|
|
||||||
|
|
||||||
|
phutil_require_source('PhabricatorUserEditor.php');
|
|
@ -24,6 +24,7 @@ final class PhabricatorUserLog extends PhabricatorUserDAO {
|
||||||
const ACTION_RESET_PASSWORD = 'reset-pass';
|
const ACTION_RESET_PASSWORD = 'reset-pass';
|
||||||
|
|
||||||
const ACTION_CREATE = 'create';
|
const ACTION_CREATE = 'create';
|
||||||
|
const ACTION_EDIT = 'edit';
|
||||||
|
|
||||||
const ACTION_ADMIN = 'admin';
|
const ACTION_ADMIN = 'admin';
|
||||||
const ACTION_DISABLE = 'disable';
|
const ACTION_DISABLE = 'disable';
|
||||||
|
@ -31,6 +32,12 @@ final class PhabricatorUserLog extends PhabricatorUserDAO {
|
||||||
const ACTION_CONDUIT_CERTIFICATE = 'conduit-cert';
|
const ACTION_CONDUIT_CERTIFICATE = 'conduit-cert';
|
||||||
const ACTION_CONDUIT_CERTIFICATE_FAILURE = 'conduit-cert-fail';
|
const ACTION_CONDUIT_CERTIFICATE_FAILURE = 'conduit-cert-fail';
|
||||||
|
|
||||||
|
const ACTION_EMAIL_PRIMARY = 'email-primary';
|
||||||
|
const ACTION_EMAIL_REMOVE = 'email-remove';
|
||||||
|
const ACTION_EMAIL_ADD = 'email-add';
|
||||||
|
|
||||||
|
const ACTION_CHANGE_PASSWORD = 'change-password';
|
||||||
|
|
||||||
protected $actorPHID;
|
protected $actorPHID;
|
||||||
protected $userPHID;
|
protected $userPHID;
|
||||||
protected $action;
|
protected $action;
|
||||||
|
@ -75,7 +82,7 @@ final class PhabricatorUserLog extends PhabricatorUserDAO {
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
if (!$this->remoteAddr) {
|
if (!$this->remoteAddr) {
|
||||||
$this->remoteAddr = idx($_SERVER, 'REMOTE_ADDR');
|
$this->remoteAddr = idx($_SERVER, 'REMOTE_ADDR', '');
|
||||||
}
|
}
|
||||||
if (!$this->session) {
|
if (!$this->session) {
|
||||||
$this->setSession(idx($_COOKIE, 'phsid'));
|
$this->setSession(idx($_COOKIE, 'phsid'));
|
||||||
|
|
Loading…
Reference in a new issue