mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-19 05:12:41 +01:00
Revise administrative workflow for user creation
Summary: - When an administrator creates a user, provide an option to send a welcome email. Right now this workflow kind of dead-ends. - Prevent administrators from changing the "System Agent" flag. If they can change it, they can grab another user's certificate and then act as them. This is a vaguely weaker security policy than is exhibited elsewhere in the application. Instead, make user accounts immutably normal users or system agents at creation time. - Prevent administrators from changing email addresses after account creation. Same deal as conduit certs. The 'bin/accountadmin' script can still do this if a user has a real problem. - Prevent administrators from resetting passwords. There's no need for this anymore with welcome emails plus email login and it raises the same issues. Test Plan: - Created a new account, selected "send welcome email", got a welcome email, logged in with the link inside it. - Created a new system agent. - Reset an account's password. Reviewed By: aran Reviewers: tuomaspelkonen, jungejason, aran CC: anjali, aran, epriestley Differential Revision: 379
This commit is contained in:
parent
729d2f9c93
commit
301fed1b43
6 changed files with 118 additions and 100 deletions
|
@ -64,7 +64,20 @@ class PhabricatorEmailLoginController extends PhabricatorAuthController {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$errors) {
|
if (!$errors) {
|
||||||
$etoken = $target_user->generateEmailToken();
|
$uri = $target_user->getEmailLoginURI();
|
||||||
|
$body = <<<EOBODY
|
||||||
|
Condolences on forgetting your password. You can use this link to reset it:
|
||||||
|
|
||||||
|
{$uri}
|
||||||
|
|
||||||
|
After you set a new password, consider writing it down on a sticky note and
|
||||||
|
attaching it to your monitor so you don't forget again! Choosing a very short,
|
||||||
|
easy-to-remember password like "cat" or "1234" might also help.
|
||||||
|
|
||||||
|
Best Wishes,
|
||||||
|
Phabricator
|
||||||
|
|
||||||
|
EOBODY;
|
||||||
|
|
||||||
$mail = new PhabricatorMetaMTAMail();
|
$mail = new PhabricatorMetaMTAMail();
|
||||||
$mail->setSubject('[Phabricator] Password Reset');
|
$mail->setSubject('[Phabricator] Password Reset');
|
||||||
|
@ -73,9 +86,7 @@ class PhabricatorEmailLoginController extends PhabricatorAuthController {
|
||||||
array(
|
array(
|
||||||
$target_user->getPHID(),
|
$target_user->getPHID(),
|
||||||
));
|
));
|
||||||
$mail->setBody(
|
$mail->setBody($body);
|
||||||
"here is your link ".
|
|
||||||
PhabricatorEnv::getURI('/login/etoken/'.$etoken.'/').'?email='.phutil_escape_uri($target_user->getEmail()));
|
|
||||||
$mail->saveAndSend();
|
$mail->saveAndSend();
|
||||||
|
|
||||||
$view = new AphrontRequestFailureView();
|
$view = new AphrontRequestFailureView();
|
||||||
|
|
|
@ -19,7 +19,6 @@ phutil_require_module('phabricator', 'view/form/error');
|
||||||
phutil_require_module('phabricator', 'view/layout/panel');
|
phutil_require_module('phabricator', 'view/layout/panel');
|
||||||
phutil_require_module('phabricator', 'view/page/failure');
|
phutil_require_module('phabricator', 'view/page/failure');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'markup');
|
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,6 @@ class PhabricatorPeopleEditController extends PhabricatorPeopleController {
|
||||||
|
|
||||||
$views = array(
|
$views = array(
|
||||||
'basic' => 'Basic Information',
|
'basic' => 'Basic Information',
|
||||||
'password' => 'Reset Password',
|
|
||||||
'role' => 'Edit Role',
|
'role' => 'Edit Role',
|
||||||
'cert' => 'Conduit Certificate',
|
'cert' => 'Conduit Certificate',
|
||||||
);
|
);
|
||||||
|
@ -73,9 +72,6 @@ class PhabricatorPeopleEditController extends PhabricatorPeopleController {
|
||||||
case 'basic':
|
case 'basic':
|
||||||
$response = $this->processBasicRequest($user);
|
$response = $this->processBasicRequest($user);
|
||||||
break;
|
break;
|
||||||
case 'password':
|
|
||||||
$response = $this->processPasswordRequest($user);
|
|
||||||
break;
|
|
||||||
case 'role':
|
case 'role':
|
||||||
$response = $this->processRoleRequest($user);
|
$response = $this->processRoleRequest($user);
|
||||||
break;
|
break;
|
||||||
|
@ -124,13 +120,21 @@ class PhabricatorPeopleEditController extends PhabricatorPeopleController {
|
||||||
$e_email = true;
|
$e_email = true;
|
||||||
$errors = array();
|
$errors = array();
|
||||||
|
|
||||||
|
$welcome_checked = true;
|
||||||
|
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
|
$welcome_checked = $request->getInt('welcome');
|
||||||
|
|
||||||
if (!$user->getID()) {
|
if (!$user->getID()) {
|
||||||
$user->setUsername($request->getStr('username'));
|
$user->setUsername($request->getStr('username'));
|
||||||
|
$user->setEmail($request->getStr('email'));
|
||||||
|
|
||||||
|
if ($request->getStr('role') == 'agent') {
|
||||||
|
$user->setIsSystemAgent(true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
$user->setRealName($request->getStr('realname'));
|
$user->setRealName($request->getStr('realname'));
|
||||||
$user->setEmail($request->getStr('email'));
|
|
||||||
|
|
||||||
if (!strlen($user->getUsername())) {
|
if (!strlen($user->getUsername())) {
|
||||||
$errors[] = "Username is required.";
|
$errors[] = "Username is required.";
|
||||||
|
@ -158,13 +162,52 @@ class PhabricatorPeopleEditController extends PhabricatorPeopleController {
|
||||||
|
|
||||||
if (!$errors) {
|
if (!$errors) {
|
||||||
try {
|
try {
|
||||||
|
$is_new = !$user->getID();
|
||||||
|
|
||||||
$user->save();
|
$user->save();
|
||||||
|
|
||||||
$log = PhabricatorUserLog::newLog(
|
if ($is_new) {
|
||||||
$admin,
|
$log = PhabricatorUserLog::newLog(
|
||||||
$user,
|
$admin,
|
||||||
PhabricatorUserLog::ACTION_CREATE);
|
$user,
|
||||||
$log->save();
|
PhabricatorUserLog::ACTION_CREATE);
|
||||||
|
$log->save();
|
||||||
|
|
||||||
|
if ($welcome_checked) {
|
||||||
|
$admin_username = $admin->getUserName();
|
||||||
|
$admin_realname = $admin->getRealName();
|
||||||
|
$user_username = $user->getUserName();
|
||||||
|
|
||||||
|
$base_uri = PhabricatorEnv::getProductionURI('/');
|
||||||
|
|
||||||
|
$uri = $user->getEmailLoginURI();
|
||||||
|
$body = <<<EOBODY
|
||||||
|
Welcome to Phabricator!
|
||||||
|
|
||||||
|
{$admin_username} ({$admin_realname}) has created an account for you.
|
||||||
|
|
||||||
|
Username: {$user_username}
|
||||||
|
|
||||||
|
To login to Phabricator, follow this link and set a password:
|
||||||
|
|
||||||
|
{$uri}
|
||||||
|
|
||||||
|
After you have set a password, you can login in the future by going here:
|
||||||
|
|
||||||
|
{$base_uri}
|
||||||
|
|
||||||
|
Love,
|
||||||
|
Phabricator
|
||||||
|
|
||||||
|
EOBODY;
|
||||||
|
$mail = id(new PhabricatorMetaMTAMail())
|
||||||
|
->addTos(array($user->getPHID()))
|
||||||
|
->setSubject('[Phabricator] Welcome to Phabricator')
|
||||||
|
->setBody($body)
|
||||||
|
->setFrom($admin->getPHID())
|
||||||
|
->saveAndSend();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$response = id(new AphrontRedirectResponse())
|
$response = id(new AphrontRedirectResponse())
|
||||||
->setURI('/people/edit/'.$user->getID().'/?saved=true');
|
->setURI('/people/edit/'.$user->getID().'/?saved=true');
|
||||||
|
@ -228,12 +271,46 @@ class PhabricatorPeopleEditController extends PhabricatorPeopleController {
|
||||||
id(new AphrontFormTextControl())
|
id(new AphrontFormTextControl())
|
||||||
->setLabel('Email')
|
->setLabel('Email')
|
||||||
->setName('email')
|
->setName('email')
|
||||||
|
->setDisabled($is_immutable)
|
||||||
->setValue($user->getEmail())
|
->setValue($user->getEmail())
|
||||||
->setError($e_email))
|
->setError($e_email));
|
||||||
|
|
||||||
|
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 {
|
||||||
|
$form->appendChild(
|
||||||
|
id(new AphrontFormStaticControl())
|
||||||
|
->setLabel('Role')
|
||||||
|
->setValue(
|
||||||
|
$user->getIsSystemAgent()
|
||||||
|
? 'System Agent'
|
||||||
|
: 'Normal User'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$form
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormSubmitControl())
|
id(new AphrontFormSubmitControl())
|
||||||
->setValue('Save')
|
->setValue('Save'));
|
||||||
->addCancelButton('/people/'));
|
|
||||||
|
|
||||||
$panel = new AphrontPanelView();
|
$panel = new AphrontPanelView();
|
||||||
if ($user->getID()) {
|
if ($user->getID()) {
|
||||||
|
@ -248,69 +325,6 @@ class PhabricatorPeopleEditController extends PhabricatorPeopleController {
|
||||||
return array($error_view, $panel);
|
return array($error_view, $panel);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function processPasswordRequest(PhabricatorUser $user) {
|
|
||||||
$request = $this->getRequest();
|
|
||||||
$admin = $request->getUser();
|
|
||||||
|
|
||||||
$e_password = true;
|
|
||||||
$errors = array();
|
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
|
||||||
if (strlen($request->getStr('password'))) {
|
|
||||||
$user->setPassword($request->getStr('password'));
|
|
||||||
$e_password = null;
|
|
||||||
} else {
|
|
||||||
$errors[] = 'Password is required.';
|
|
||||||
$e_password = 'Required';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!$errors) {
|
|
||||||
$user->save();
|
|
||||||
|
|
||||||
$log = PhabricatorUserLog::newLog(
|
|
||||||
$admin,
|
|
||||||
$user,
|
|
||||||
PhabricatorUserLog::ACTION_RESET_PASSWORD);
|
|
||||||
$log->save();
|
|
||||||
|
|
||||||
return id(new AphrontRedirectResponse())
|
|
||||||
->setURI($request->getRequestURI()->alter('saved', 'true'));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$error_view = null;
|
|
||||||
if ($errors) {
|
|
||||||
$error_view = id(new AphrontErrorView())
|
|
||||||
->setTitle('Form Errors')
|
|
||||||
->setErrors($errors);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
$form = id(new AphrontFormView())
|
|
||||||
->setUser($admin)
|
|
||||||
->setAction($request->getRequestURI()->alter('saved', null))
|
|
||||||
->appendChild(
|
|
||||||
'<p class="aphront-form-instructions">Submitting this form will '.
|
|
||||||
'change this user\'s password. They will no longer be able to login '.
|
|
||||||
'with their old password.</p>')
|
|
||||||
->appendChild(
|
|
||||||
id(new AphrontFormTextControl())
|
|
||||||
->setLabel('New Password')
|
|
||||||
->setName('password')
|
|
||||||
->setError($e_password))
|
|
||||||
->appendChild(
|
|
||||||
id(new AphrontFormSubmitControl())
|
|
||||||
->setValue('Reset Password'));
|
|
||||||
|
|
||||||
|
|
||||||
$panel = new AphrontPanelView();
|
|
||||||
$panel->setHeader('Reset Password');
|
|
||||||
$panel->setWidth(AphrontPanelView::WIDTH_FORM);
|
|
||||||
$panel->appendChild($form);
|
|
||||||
|
|
||||||
return array($error_view, $panel);
|
|
||||||
}
|
|
||||||
|
|
||||||
private function processRoleRequest(PhabricatorUser $user) {
|
private function processRoleRequest(PhabricatorUser $user) {
|
||||||
$request = $this->getRequest();
|
$request = $this->getRequest();
|
||||||
$admin = $request->getUser();
|
$admin = $request->getUser();
|
||||||
|
@ -352,13 +366,6 @@ class PhabricatorPeopleEditController extends PhabricatorPeopleController {
|
||||||
$user->setIsDisabled($new_disabled);
|
$user->setIsDisabled($new_disabled);
|
||||||
$logs[] = $log;
|
$logs[] = $log;
|
||||||
}
|
}
|
||||||
|
|
||||||
$new_agent = (bool)$request->getBool('is_agent');
|
|
||||||
$old_agent = (bool)$user->getIsSystemAgent();
|
|
||||||
if ($new_agent != $old_agent) {
|
|
||||||
// TODO: Get rid of this, move it to the create flow.
|
|
||||||
$user->setIsSystemAgent($new_agent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!$errors) {
|
if (!$errors) {
|
||||||
|
@ -405,14 +412,6 @@ class PhabricatorPeopleEditController extends PhabricatorPeopleController {
|
||||||
1,
|
1,
|
||||||
'Disabled: can not login.',
|
'Disabled: can not login.',
|
||||||
$user->getIsDisabled())
|
$user->getIsDisabled())
|
||||||
->setDisabled($is_self))
|
|
||||||
->appendChild(
|
|
||||||
id(new AphrontFormCheckboxControl())
|
|
||||||
->addCheckbox(
|
|
||||||
'is_agent',
|
|
||||||
1,
|
|
||||||
'Agent: system agent (robot).',
|
|
||||||
$user->getIsSystemAgent())
|
|
||||||
->setDisabled($is_self));
|
->setDisabled($is_self));
|
||||||
|
|
||||||
if (!$is_self) {
|
if (!$is_self) {
|
||||||
|
@ -459,9 +458,7 @@ class PhabricatorPeopleEditController extends PhabricatorPeopleController {
|
||||||
id(new AphrontFormStaticControl())
|
id(new AphrontFormStaticControl())
|
||||||
->setLabel('Certificate')
|
->setLabel('Certificate')
|
||||||
->setValue(
|
->setValue(
|
||||||
'You may only view the certificates for System Agents. Mark '.
|
'You may only view the certificates of System Agents.'));
|
||||||
'this account as a "system agent" in the "Edit Role" tab to '.
|
|
||||||
'view the corresponding certificate.'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$panel = new AphrontPanelView();
|
$panel = new AphrontPanelView();
|
||||||
|
|
|
@ -8,11 +8,14 @@
|
||||||
|
|
||||||
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/metamta/storage/mail');
|
||||||
phutil_require_module('phabricator', 'applications/people/controller/base');
|
phutil_require_module('phabricator', 'applications/people/controller/base');
|
||||||
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');
|
||||||
|
phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
phutil_require_module('phabricator', 'view/form/base');
|
phutil_require_module('phabricator', 'view/form/base');
|
||||||
phutil_require_module('phabricator', 'view/form/control/checkbox');
|
phutil_require_module('phabricator', 'view/form/control/checkbox');
|
||||||
|
phutil_require_module('phabricator', 'view/form/control/select');
|
||||||
phutil_require_module('phabricator', 'view/form/control/static');
|
phutil_require_module('phabricator', 'view/form/control/static');
|
||||||
phutil_require_module('phabricator', 'view/form/control/submit');
|
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||||
phutil_require_module('phabricator', 'view/form/control/text');
|
phutil_require_module('phabricator', 'view/form/control/text');
|
||||||
|
|
|
@ -248,7 +248,7 @@ class PhabricatorUser extends PhabricatorUserDAO {
|
||||||
return $session_key;
|
return $session_key;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function generateEmailToken($offset = 0) {
|
private function generateEmailToken($offset = 0) {
|
||||||
return $this->generateToken(
|
return $this->generateToken(
|
||||||
time() + ($offset * self::EMAIL_CYCLE_FREQUENCY),
|
time() + ($offset * self::EMAIL_CYCLE_FREQUENCY),
|
||||||
self::EMAIL_CYCLE_FREQUENCY,
|
self::EMAIL_CYCLE_FREQUENCY,
|
||||||
|
@ -266,6 +266,13 @@ class PhabricatorUser extends PhabricatorUserDAO {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getEmailLoginURI() {
|
||||||
|
$token = $this->generateEmailToken();
|
||||||
|
$uri = PhabricatorEnv::getProductionURI('/login/etoken/'.$token.'/');
|
||||||
|
$uri = new PhutilURI($uri);
|
||||||
|
return $uri->alter('email', $this->getEmail());
|
||||||
|
}
|
||||||
|
|
||||||
public function loadPreferences() {
|
public function loadPreferences() {
|
||||||
if ($this->preferences) {
|
if ($this->preferences) {
|
||||||
return $this->preferences;
|
return $this->preferences;
|
||||||
|
|
|
@ -15,6 +15,7 @@ phutil_require_module('phabricator', 'infrastructure/env');
|
||||||
phutil_require_module('phabricator', 'storage/queryfx');
|
phutil_require_module('phabricator', 'storage/queryfx');
|
||||||
|
|
||||||
phutil_require_module('phutil', 'filesystem');
|
phutil_require_module('phutil', 'filesystem');
|
||||||
|
phutil_require_module('phutil', 'parser/uri');
|
||||||
phutil_require_module('phutil', 'utils');
|
phutil_require_module('phutil', 'utils');
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue