1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 03:20:59 +01:00

Add "Mailing List" users

Summary:
Ref T8387. Adds new mailing list users.

This doesn't migrate anything yet. I also need to update the "Email Addresses" panel to let administrators change the list address.

Test Plan:
  - Created and edited a mailing list user.
  - Viewed profile.
  - Viewed People list.
  - Searched for lists / nonlists.
  - Grepped for all uses of `getIsDisabled()` / `getIsSystemAgent()` and added relevant corresponding behaviors.
  - Hit the web/api/ssh session blocks.

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: eadler, tycho.tatitscheff, epriestley

Maniphest Tasks: T8387

Differential Revision: https://secure.phabricator.com/D13123
This commit is contained in:
epriestley 2015-06-02 08:52:00 -07:00
parent 13f0dac0ed
commit 992c199577
22 changed files with 244 additions and 43 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_user.user
ADD isMailingList BOOL NOT NULL;

View file

@ -182,11 +182,11 @@ try {
'P' => $user->getPHID(),
));
if (!$user->isUserActivated()) {
if (!$user->canEstablishSSHSessions()) {
throw new Exception(
pht(
'Your account ("%s") is not activated. Visit the web interface '.
'for more information.',
'Your account ("%s") does not have permission to establish SSH '.
'sessions. Visit the web interface for more information.',
$user->getUsername()));
}

View file

@ -125,7 +125,7 @@ if (strlen($password)) {
$is_system_agent = $user->getIsSystemAgent();
$set_system_agent = phutil_console_confirm(
pht('Is this user a bot/script?'),
pht('Is this user a bot?'),
$default_no = !$is_system_agent);
$verify_email = null;
@ -165,7 +165,7 @@ printf($tpl, pht('Password'), null,
printf(
$tpl,
pht('Bot/Script'),
pht('Bot'),
$original->getIsSystemAgent() ? 'Y' : 'N',
$set_system_agent ? 'Y' : 'N');

View file

@ -158,6 +158,21 @@ final class PhabricatorAuthSessionEngine extends Phobject {
$session_dict[substr($key, 2)] = $value;
}
}
$user = $user_table->loadFromArray($info);
switch ($session_type) {
case PhabricatorAuthSession::TYPE_WEB:
// Explicitly prevent bots and mailing lists from establishing web
// sessions. It's normally impossible to attach authentication to these
// accounts, and likewise impossible to generate sessions, but it's
// technically possible that a session could exist in the database. If
// one does somehow, refuse to load it.
if (!$user->canEstablishWebSessions()) {
return null;
}
break;
}
$session = id(new PhabricatorAuthSession())->loadFromArray($session_dict);
$ttl = PhabricatorAuthSession::getSessionTypeTTL($session_type);
@ -181,7 +196,6 @@ final class PhabricatorAuthSessionEngine extends Phobject {
unset($unguarded);
}
$user = $user_table->loadFromArray($info);
$user->attachSession($session);
return $user;
}

View file

@ -475,10 +475,10 @@ final class PhabricatorConduitAPIController
ConduitAPIRequest $request,
PhabricatorUser $user) {
if (!$user->isUserActivated()) {
if (!$user->canEstablishAPISessions()) {
return array(
'ERR-USER-DISABLED',
pht('User account is not activated.'),
'ERR-INVALID-AUTH',
pht('User account is not permitted to use the API.'),
);
}

View file

@ -20,6 +20,10 @@ final class PhabricatorConduitTokensSettingsPanel
}
public function isEnabled() {
if ($this->getUser()->getIsMailingList()) {
return false;
}
return true;
}

View file

@ -19,6 +19,10 @@ final class DiffusionSetPasswordSettingsPanel extends PhabricatorSettingsPanel {
}
public function isEnabled() {
if ($this->getUser()->getIsMailingList()) {
return false;
}
return PhabricatorEnv::getEnvConfig('diffusion.allow-http-auth');
}

View file

@ -18,6 +18,9 @@ abstract class UserConduitAPIMethod extends ConduitAPIMethod {
if ($user->getIsSystemAgent()) {
$roles[] = 'agent';
}
if ($user->getIsMailingList()) {
$roles[] = 'list';
}
if ($user->getIsAdmin()) {
$roles[] = 'admin';
}

View file

@ -4,8 +4,6 @@ final class PhabricatorPeopleCreateController
extends PhabricatorPeopleController {
public function handleRequest(AphrontRequest $request) {
$this->requireApplicationCapability(
PeopleCreateUsersCapability::CAPABILITY);
$admin = $request->getUser();
id(new PhabricatorAuthSessionEngine())->requireHighSecuritySession(
@ -17,7 +15,7 @@ final class PhabricatorPeopleCreateController
if ($request->isFormPost()) {
$v_type = $request->getStr('type');
if ($v_type == 'standard' || $v_type == 'bot') {
if ($v_type == 'standard' || $v_type == 'bot' || $v_type == 'list') {
return id(new AphrontRedirectResponse())->setURI(
$this->getApplicationURI('new/'.$v_type.'/'));
}
@ -41,6 +39,41 @@ final class PhabricatorPeopleCreateController
$bot_admin = pht(
'Administrators have greater access to edit these accounts.');
$types = array();
$can_create = $this->hasApplicationCapability(
PeopleCreateUsersCapability::CAPABILITY);
if ($can_create) {
$types[] = array(
'type' => 'standard',
'name' => pht('Create Standard User'),
'help' => pht('Create a standard user account.'),
);
}
$types[] = array(
'type' => 'bot',
'name' => pht('Create Bot User'),
'help' => pht('Create a new user for use with automated scripts.'),
);
$types[] = array(
'type' => 'list',
'name' => pht('Create Mailing List User'),
'help' => pht(
'Create a mailing list user to represent an existing, external '.
'mailing list like a Google Group or a Mailman list.'),
);
$buttons = id(new AphrontFormRadioButtonControl())
->setLabel(pht('Account Type'))
->setName('type')
->setValue($v_type);
foreach ($types as $type) {
$buttons->addButton($type['type'], $type['name'], $type['help']);
}
$form = id(new AphrontFormView())
->setUser($admin)
->appendRemarkupInstructions(
@ -49,19 +82,7 @@ final class PhabricatorPeopleCreateController
'explanation of user account types, see [[ %s | User Guide: '.
'Account Roles ]].',
PhabricatorEnv::getDoclink('User Guide: Account Roles')))
->appendChild(
id(new AphrontFormRadioButtonControl())
->setLabel(pht('Account Type'))
->setName('type')
->setValue($v_type)
->addButton(
'standard',
pht('Create Standard User'),
hsprintf('%s<br /><br />%s', $standard_caption, $standard_admin))
->addButton(
'bot',
pht('Create Bot/Script User'),
hsprintf('%s<br /><br />%s', $bot_caption, $bot_admin)))
->appendChild($buttons)
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($this->getApplicationURI())

View file

@ -33,20 +33,12 @@ final class PhabricatorPeopleListController
$crumbs = parent::buildApplicationCrumbs();
$viewer = $this->getRequest()->getUser();
$can_create = $this->hasApplicationCapability(
PeopleCreateUsersCapability::CAPABILITY);
if ($can_create) {
if ($viewer->getIsAdmin()) {
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create New User'))
->setHref($this->getApplicationURI('create/'))
->setIcon('fa-plus-square'));
} else if ($viewer->getIsAdmin()) {
$crumbs->addAction(
id(new PHUIListItemView())
->setName(pht('Create New Bot'))
->setHref($this->getApplicationURI('new/bot/'))
->setIcon('fa-plus-square'));
}
return $crumbs;

View file

@ -7,15 +7,19 @@ final class PhabricatorPeopleNewController
$type = $request->getURIData('type');
$admin = $request->getUser();
$is_bot = false;
$is_list = false;
switch ($type) {
case 'standard':
$this->requireApplicationCapability(
PeopleCreateUsersCapability::CAPABILITY);
$is_bot = false;
break;
case 'bot':
$is_bot = true;
break;
case 'list':
$is_list = true;
break;
default:
return new Aphront404Response();
}
@ -77,8 +81,8 @@ final class PhabricatorPeopleNewController
// 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) {
// If the user is a bot or list, approve their email too.
if ($is_bot || $is_list) {
$email->setIsVerified(1);
}
@ -92,7 +96,13 @@ final class PhabricatorPeopleNewController
->makeSystemAgentUser($user, true);
}
if ($welcome_checked && !$is_bot) {
if ($is_list) {
id(new PhabricatorUserEditor())
->setActor($admin)
->makeMailingListUser($user, true);
}
if ($welcome_checked && !$is_bot && !$is_list) {
$user->sendWelcomeEmail($admin);
}
@ -123,7 +133,10 @@ final class PhabricatorPeopleNewController
if ($is_bot) {
$form->appendRemarkupInstructions(
pht('You are creating a new **bot/script** user account.'));
pht('You are creating a new **bot** user account.'));
} else if ($is_list) {
$form->appendRemarkupInstructions(
pht('You are creating a new **mailing list** user account.'));
} else {
$form->appendRemarkupInstructions(
pht('You are creating a new **standard** user account.'));
@ -150,7 +163,7 @@ final class PhabricatorPeopleNewController
->setCaption(PhabricatorUserEmail::describeAllowedAddresses())
->setError($e_email));
if (!$is_bot) {
if (!$is_bot && !$is_list) {
$form->appendChild(
id(new AphrontFormCheckboxControl())
->addCheckbox(
@ -171,7 +184,7 @@ final class PhabricatorPeopleNewController
->appendChild(id(new AphrontFormDividerControl()))
->appendRemarkupInstructions(
pht(
'**Why do bot/script accounts need an email address?**'.
'**Why do bot 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 '.

View file

@ -37,6 +37,9 @@ final class PhabricatorUserRolesField
if ($user->getIsSystemAgent()) {
$roles[] = pht('Bot');
}
if ($user->getIsMailingList()) {
$roles[] = pht('Mailing List');
}
if ($roles) {
return implode(', ', $roles);

View file

@ -278,6 +278,43 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
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;
}
/**
* @task role

View file

@ -44,6 +44,10 @@ final class PhabricatorPeopleUserPHIDType extends PhabricatorPHIDType {
$handle->setFullName($user->getFullName());
$handle->setImageURI($user->getProfileImageURI());
if ($user->getIsMailingList()) {
$handle->setIcon('fa-envelope-o');
}
$availability = null;
if (!$user->isUserActivated()) {
$availability = PhabricatorObjectHandle::AVAILABILITY_DISABLED;

View file

@ -12,6 +12,7 @@ final class PhabricatorPeopleQuery
private $dateCreatedBefore;
private $isAdmin;
private $isSystemAgent;
private $isMailingList;
private $isDisabled;
private $isApproved;
private $nameLike;
@ -67,6 +68,11 @@ final class PhabricatorPeopleQuery
return $this;
}
public function withIsMailingList($mailing_list) {
$this->isMailingList = $mailing_list;
return $this;
}
public function withIsDisabled($disabled) {
$this->isDisabled = $disabled;
return $this;
@ -340,6 +346,13 @@ final class PhabricatorPeopleQuery
(int)$this->isSystemAgent);
}
if ($this->isMailingList !== null) {
$where[] = qsprintf(
$conn_r,
'user.isMailingList = %d',
(int)$this->isMailingList);
}
if (strlen($this->nameLike)) {
$where[] = qsprintf(
$conn_r,

View file

@ -33,6 +33,10 @@ final class PhabricatorPeopleSearchEngine
'isSystemAgent',
$this->readBoolFromRequest($request, 'isSystemAgent'));
$saved->setParameter(
'isMailingList',
$this->readBoolFromRequest($request, 'isMailingList'));
$saved->setParameter(
'needsApproval',
$this->readBoolFromRequest($request, 'needsApproval'));
@ -77,6 +81,7 @@ final class PhabricatorPeopleSearchEngine
$is_admin = $saved->getParameter('isAdmin');
$is_disabled = $saved->getParameter('isDisabled');
$is_system_agent = $saved->getParameter('isSystemAgent');
$is_mailing_list = $saved->getParameter('isMailingList');
$needs_approval = $saved->getParameter('needsApproval');
if ($is_admin !== null) {
@ -91,6 +96,10 @@ final class PhabricatorPeopleSearchEngine
$query->withIsSystemAgent($is_system_agent);
}
if ($is_mailing_list !== null) {
$query->withIsMailingList($is_mailing_list);
}
if ($needs_approval !== null) {
$query->withIsApproved(!$needs_approval);
}
@ -121,6 +130,7 @@ final class PhabricatorPeopleSearchEngine
$is_admin = $this->getBoolFromQuery($saved, 'isAdmin');
$is_disabled = $this->getBoolFromQuery($saved, 'isDisabled');
$is_system_agent = $this->getBoolFromQuery($saved, 'isSystemAgent');
$is_mailing_list = $this->getBoolFromQuery($saved, 'isMailingList');
$needs_approval = $this->getBoolFromQuery($saved, 'needsApproval');
$form
@ -167,6 +177,17 @@ final class PhabricatorPeopleSearchEngine
'true' => pht('Show Only Bots'),
'false' => pht('Hide Bots'),
)))
->appendChild(
id(new AphrontFormSelectControl())
->setName('isMailingList')
->setLabel(pht('Mailing Lists'))
->setValue($is_mailing_list)
->setOptions(
array(
'' => pht('(Show All)'),
'true' => pht('Show Only Mailing Lists'),
'false' => pht('Hide Mailing Lists'),
)))
->appendChild(
id(new AphrontFormSelectControl())
->setName('needsApproval')
@ -278,6 +299,10 @@ final class PhabricatorPeopleSearchEngine
$item->addIcon('fa-desktop', pht('Bot'));
}
if ($user->getIsMailingList()) {
$item->addIcon('fa-envelope-o', pht('Mailing List'));
}
if ($viewer->getIsAdmin()) {
$user_id = $user->getID();
if ($is_approval) {

View file

@ -38,6 +38,7 @@ final class PhabricatorUser
protected $conduitCertificate;
protected $isSystemAgent = 0;
protected $isMailingList = 0;
protected $isAdmin = 0;
protected $isDisabled = 0;
protected $isEmailVerified = 0;
@ -73,6 +74,8 @@ final class PhabricatorUser
return (bool)$this->isDisabled;
case 'isSystemAgent':
return (bool)$this->isSystemAgent;
case 'isMailingList':
return (bool)$this->isMailingList;
case 'isEmailVerified':
return (bool)$this->isEmailVerified;
case 'isApproved':
@ -112,6 +115,46 @@ final class PhabricatorUser
return true;
}
public function canEstablishWebSessions() {
if (!$this->isUserActivated()) {
return false;
}
if ($this->getIsMailingList()) {
return false;
}
if ($this->getIsSystemAgent()) {
return false;
}
return true;
}
public function canEstablishAPISessions() {
if (!$this->isUserActivated()) {
return false;
}
if ($this->getIsMailingList()) {
return false;
}
return true;
}
public function canEstablishSSHSessions() {
if (!$this->isUserActivated()) {
return false;
}
if ($this->getIsMailingList()) {
return false;
}
return true;
}
/**
* Returns `true` if this is a standard user who is logged in. Returns `false`
* for logged out, anonymous, or external users.
@ -140,6 +183,7 @@ final class PhabricatorUser
'consoleTab' => 'text64',
'conduitCertificate' => 'text255',
'isSystemAgent' => 'bool',
'isMailingList' => 'bool',
'isDisabled' => 'bool',
'isAdmin' => 'bool',
'timezoneIdentifier' => 'text255',
@ -1032,7 +1076,7 @@ final class PhabricatorUser
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::POLICY_PUBLIC;
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->getIsSystemAgent()) {
if ($this->getIsSystemAgent() || $this->getIsMailingList()) {
return PhabricatorPolicies::POLICY_ADMIN;
} else {
return PhabricatorPolicies::POLICY_NOONE;

View file

@ -16,6 +16,7 @@ final class PhabricatorUserLog extends PhabricatorUserDAO
const ACTION_ADMIN = 'admin';
const ACTION_SYSTEM_AGENT = 'system-agent';
const ACTION_MAILING_LIST = 'mailing-list';
const ACTION_DISABLE = 'disable';
const ACTION_APPROVE = 'approve';
const ACTION_DELETE = 'delete';
@ -62,6 +63,7 @@ final class PhabricatorUserLog extends PhabricatorUserDAO
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'),

View file

@ -54,7 +54,9 @@ final class PhabricatorPeopleDatasource
if ($user->getIsDisabled()) {
$closed = pht('Disabled');
} else if ($user->getIsSystemAgent()) {
$closed = pht('Bot/Script');
$closed = pht('Bot');
} else if ($user->getIsMailingList()) {
$closed = pht('Mailing List');
}
$result = id(new PhabricatorTypeaheadResult())
@ -65,6 +67,10 @@ final class PhabricatorPeopleDatasource
->setPriorityType('user')
->setClosed($closed);
if ($user->getIsMailingList()) {
$result->setIcon('fa-envelope-o');
}
if ($this->enrichResults) {
$display_type = 'User';
if ($user->getIsAdmin()) {

View file

@ -97,6 +97,8 @@ final class PhabricatorSettingsMainController
$result = array();
foreach ($panels as $key => $panel) {
$panel->setUser($this->user);
if (!$panel->isEnabled()) {
continue;
}

View file

@ -19,6 +19,14 @@ final class PhabricatorConduitCertificateSettingsPanel
return pht('Authentication');
}
public function isEnabled() {
if ($this->getUser()->getIsMailingList()) {
return false;
}
return true;
}
public function processRequest(AphrontRequest $request) {
$user = $this->getUser();
$viewer = $request->getUser();

View file

@ -19,6 +19,10 @@ final class PhabricatorSSHKeysSettingsPanel extends PhabricatorSettingsPanel {
}
public function isEnabled() {
if ($this->getUser()->getIsMailingList()) {
return false;
}
return true;
}