@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Replace "bin/people profileimage" with "bin/user enable|empower"

Summary:
Ref T13382.

- Remove "bin/people profileimage" which previously generated profile image caches but now feels obsolete.
- Replace it with "bin/user", with "enable" and "empower" flows. This command is now focused on regaining access to an install after you lock your keys inside.
- Document the various ways to unlock objects and accounts from the CLI.

Test Plan:
- Ran `bin/user enable` and `bin/user empower` with various flags.
- Grepped for `people profileimage` and found no references.
- Grepped for `bin/people` and found no references.
- Read documentation.

Maniphest Tasks: T13382

Differential Revision: https://secure.phabricator.com/D20724

+269 -138
-1
bin/people
··· 1 - ../scripts/people/manage_people.php
+1
bin/user
··· 1 + ../scripts/setup/manage_user.php
-20
scripts/people/manage_people.php
··· 1 - #!/usr/bin/env php 2 - <?php 3 - 4 - $root = dirname(dirname(dirname(__FILE__))); 5 - require_once $root.'/scripts/__init_script__.php'; 6 - 7 - $args = new PhutilArgumentParser($argv); 8 - $args->setSynopsis(<<<EOSYNOPSIS 9 - **people** __command__ [__options__] 10 - Manage user profiles and accounts. 11 - 12 - EOSYNOPSIS 13 - ); 14 - $args->parseStandardArguments(); 15 - 16 - $workflows = id(new PhutilClassMapQuery()) 17 - ->setAncestorClass('PhabricatorPeopleManagementWorkflow') 18 - ->execute(); 19 - $workflows[] = new PhutilHelpArgumentWorkflow(); 20 - $args->parseWorkflows($workflows);
+20
scripts/setup/manage_user.php
··· 1 + #!/usr/bin/env php 2 + <?php 3 + 4 + $root = dirname(dirname(dirname(__FILE__))); 5 + require_once $root.'/scripts/__init_script__.php'; 6 + 7 + $args = new PhutilArgumentParser($argv); 8 + $args->setSynopsis(<<<EOSYNOPSIS 9 + **user** __command__ [__options__] 10 + Modify user accounts to regain access to an install. 11 + 12 + EOSYNOPSIS 13 + ); 14 + $args->parseStandardArguments(); 15 + 16 + $workflows = id(new PhutilClassMapQuery()) 17 + ->setAncestorClass('PhabricatorPeopleManagementWorkflow') 18 + ->execute(); 19 + $workflows[] = new PhutilHelpArgumentWorkflow(); 20 + $args->parseWorkflows($workflows);
+4 -2
src/__phutil_library_map__.php
··· 4051 4051 'PhabricatorPeopleMailEngine' => 'applications/people/mail/PhabricatorPeopleMailEngine.php', 4052 4052 'PhabricatorPeopleMailEngineException' => 'applications/people/mail/PhabricatorPeopleMailEngineException.php', 4053 4053 'PhabricatorPeopleManageProfileMenuItem' => 'applications/people/menuitem/PhabricatorPeopleManageProfileMenuItem.php', 4054 + 'PhabricatorPeopleManagementEmpowerWorkflow' => 'applications/people/management/PhabricatorPeopleManagementEmpowerWorkflow.php', 4055 + 'PhabricatorPeopleManagementEnableWorkflow' => 'applications/people/management/PhabricatorPeopleManagementEnableWorkflow.php', 4054 4056 'PhabricatorPeopleManagementWorkflow' => 'applications/people/management/PhabricatorPeopleManagementWorkflow.php', 4055 4057 'PhabricatorPeopleNewController' => 'applications/people/controller/PhabricatorPeopleNewController.php', 4056 4058 'PhabricatorPeopleNoOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleNoOwnerDatasource.php', ··· 4060 4062 'PhabricatorPeopleProfileCommitsController' => 'applications/people/controller/PhabricatorPeopleProfileCommitsController.php', 4061 4063 'PhabricatorPeopleProfileController' => 'applications/people/controller/PhabricatorPeopleProfileController.php', 4062 4064 'PhabricatorPeopleProfileEditController' => 'applications/people/controller/PhabricatorPeopleProfileEditController.php', 4063 - 'PhabricatorPeopleProfileImageWorkflow' => 'applications/people/management/PhabricatorPeopleProfileImageWorkflow.php', 4064 4065 'PhabricatorPeopleProfileManageController' => 'applications/people/controller/PhabricatorPeopleProfileManageController.php', 4065 4066 'PhabricatorPeopleProfileMenuEngine' => 'applications/people/engine/PhabricatorPeopleProfileMenuEngine.php', 4066 4067 'PhabricatorPeopleProfilePictureController' => 'applications/people/controller/PhabricatorPeopleProfilePictureController.php', ··· 10332 10333 'PhabricatorPeopleMailEngine' => 'Phobject', 10333 10334 'PhabricatorPeopleMailEngineException' => 'Exception', 10334 10335 'PhabricatorPeopleManageProfileMenuItem' => 'PhabricatorProfileMenuItem', 10336 + 'PhabricatorPeopleManagementEmpowerWorkflow' => 'PhabricatorPeopleManagementWorkflow', 10337 + 'PhabricatorPeopleManagementEnableWorkflow' => 'PhabricatorPeopleManagementWorkflow', 10335 10338 'PhabricatorPeopleManagementWorkflow' => 'PhabricatorManagementWorkflow', 10336 10339 'PhabricatorPeopleNewController' => 'PhabricatorPeopleController', 10337 10340 'PhabricatorPeopleNoOwnerDatasource' => 'PhabricatorTypeaheadDatasource', ··· 10341 10344 'PhabricatorPeopleProfileCommitsController' => 'PhabricatorPeopleProfileController', 10342 10345 'PhabricatorPeopleProfileController' => 'PhabricatorPeopleController', 10343 10346 'PhabricatorPeopleProfileEditController' => 'PhabricatorPeopleProfileController', 10344 - 'PhabricatorPeopleProfileImageWorkflow' => 'PhabricatorPeopleManagementWorkflow', 10345 10347 'PhabricatorPeopleProfileManageController' => 'PhabricatorPeopleProfileController', 10346 10348 'PhabricatorPeopleProfileMenuEngine' => 'PhabricatorProfileMenuEngine', 10347 10349 'PhabricatorPeopleProfilePictureController' => 'PhabricatorPeopleProfileController',
+44
src/applications/people/management/PhabricatorPeopleManagementEmpowerWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorPeopleManagementEmpowerWorkflow 4 + extends PhabricatorPeopleManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $arguments = array_merge( 8 + $this->getUserSelectionArguments(), 9 + array()); 10 + 11 + $this 12 + ->setName('empower') 13 + ->setExamples('**empower** --user __username__') 14 + ->setSynopsis(pht('Turn a user account into an administrator account.')) 15 + ->setArguments($arguments); 16 + } 17 + 18 + public function execute(PhutilArgumentParser $args) { 19 + $user = $this->selectUser($args); 20 + $display_name = $user->getUsername(); 21 + 22 + if ($user->getIsAdmin()) { 23 + throw new PhutilArgumentUsageException( 24 + pht( 25 + 'User account "%s" is already an administrator. You can only '. 26 + 'empower accounts that are not yet administrators.', 27 + $display_name)); 28 + } 29 + 30 + $xactions = array(); 31 + $xactions[] = $user->getApplicationTransactionTemplate() 32 + ->setTransactionType(PhabricatorUserEmpowerTransaction::TRANSACTIONTYPE) 33 + ->setNewValue(true); 34 + 35 + $this->applyTransactions($user, $xactions); 36 + 37 + $this->logOkay( 38 + pht('DONE'), 39 + pht('Empowered user account "%s".', $display_name)); 40 + 41 + return 0; 42 + } 43 + 44 + }
+44
src/applications/people/management/PhabricatorPeopleManagementEnableWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorPeopleManagementEnableWorkflow 4 + extends PhabricatorPeopleManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $arguments = array_merge( 8 + $this->getUserSelectionArguments(), 9 + array()); 10 + 11 + $this 12 + ->setName('enable') 13 + ->setExamples('**enable** --user __username__') 14 + ->setSynopsis(pht('Enable a disabled user account.')) 15 + ->setArguments($arguments); 16 + } 17 + 18 + public function execute(PhutilArgumentParser $args) { 19 + $user = $this->selectUser($args); 20 + $display_name = $user->getUsername(); 21 + 22 + if (!$user->getIsDisabled()) { 23 + throw new PhutilArgumentUsageException( 24 + pht( 25 + 'User account "%s" is not disabled. You can only enable accounts '. 26 + 'that are disabled.', 27 + $display_name)); 28 + } 29 + 30 + $xactions = array(); 31 + $xactions[] = $user->getApplicationTransactionTemplate() 32 + ->setTransactionType(PhabricatorUserDisableTransaction::TRANSACTIONTYPE) 33 + ->setNewValue(false); 34 + 35 + $this->applyTransactions($user, $xactions); 36 + 37 + $this->logOkay( 38 + pht('DONE'), 39 + pht('Enabled user account "%s".', $display_name)); 40 + 41 + return 0; 42 + } 43 + 44 + }
+40 -30
src/applications/people/management/PhabricatorPeopleManagementWorkflow.php
··· 3 3 abstract class PhabricatorPeopleManagementWorkflow 4 4 extends PhabricatorManagementWorkflow { 5 5 6 - protected function buildIterator(PhutilArgumentParser $args) { 7 - $usernames = $args->getArg('users'); 6 + final protected function getUserSelectionArguments() { 7 + return array( 8 + array( 9 + 'name' => 'user', 10 + 'param' => 'username', 11 + 'help' => pht('User account to act on.'), 12 + ), 13 + ); 14 + } 8 15 9 - if ($args->getArg('all')) { 10 - if ($usernames) { 11 - throw new PhutilArgumentUsageException( 12 - pht( 13 - 'Specify either a list of users or `%s`, but not both.', 14 - '--all')); 15 - } 16 - return new LiskMigrationIterator(new PhabricatorUser()); 16 + final protected function selectUser(PhutilArgumentParser $argv) { 17 + $username = $argv->getArg('user'); 18 + 19 + if (!strlen($username)) { 20 + throw new PhutilArgumentUsageException( 21 + pht( 22 + 'Select a user account to act on with "--user <username>".')); 17 23 } 18 24 19 - if ($usernames) { 20 - return $this->loadUsersWithUsernames($usernames); 25 + $user = id(new PhabricatorPeopleQuery()) 26 + ->setViewer($this->getViewer()) 27 + ->withUsernames(array($username)) 28 + ->executeOne(); 29 + if (!$user) { 30 + throw new PhutilArgumentUsageException( 31 + pht( 32 + 'No user with username "%s" exists.', 33 + $username)); 21 34 } 22 35 23 - return null; 36 + return $user; 24 37 } 25 38 26 - protected function loadUsersWithUsernames(array $usernames) { 27 - $users = array(); 28 - foreach($usernames as $username) { 29 - $query = id(new PhabricatorPeopleQuery()) 30 - ->setViewer($this->getViewer()) 31 - ->withUsernames(array($username)) 32 - ->executeOne(); 39 + final protected function applyTransactions( 40 + PhabricatorUser $user, 41 + array $xactions) { 42 + assert_instances_of($xactions, 'PhabricatorUserTransaction'); 43 + 44 + $viewer = $this->getViewer(); 45 + $application = id(new PhabricatorPeopleApplication())->getPHID(); 46 + $content_source = $this->newContentSource(); 33 47 34 - if (!$query) { 35 - throw new PhutilArgumentUsageException( 36 - pht( 37 - '"%s" is not a valid username.', 38 - $username)); 39 - } 40 - $users[] = $query; 41 - } 48 + $editor = $user->getApplicationTransactionEditor() 49 + ->setActor($viewer) 50 + ->setActingAsPHID($application) 51 + ->setContentSource($content_source) 52 + ->setContinueOnMissingFields(true); 42 53 43 - return $users; 54 + return $editor->applyTransactions($user, $xactions); 44 55 } 45 - 46 56 47 57 }
-85
src/applications/people/management/PhabricatorPeopleProfileImageWorkflow.php
··· 1 - <?php 2 - 3 - final class PhabricatorPeopleProfileImageWorkflow 4 - extends PhabricatorPeopleManagementWorkflow { 5 - 6 - protected function didConstruct() { 7 - $this 8 - ->setName('profileimage') 9 - ->setExamples('**profileimage** --users __username__') 10 - ->setSynopsis(pht('Generate default profile images.')) 11 - ->setArguments( 12 - array( 13 - array( 14 - 'name' => 'all', 15 - 'help' => pht( 16 - 'Generate default profile images for all users.'), 17 - ), 18 - array( 19 - 'name' => 'force', 20 - 'short' => 'f', 21 - 'help' => pht( 22 - 'Force a default profile image to be replaced.'), 23 - ), 24 - array( 25 - 'name' => 'users', 26 - 'wildcard' => true, 27 - ), 28 - )); 29 - } 30 - 31 - public function execute(PhutilArgumentParser $args) { 32 - $console = PhutilConsole::getConsole(); 33 - 34 - $is_force = $args->getArg('force'); 35 - $is_all = $args->getArg('all'); 36 - 37 - $gd = function_exists('imagecreatefromstring'); 38 - if (!$gd) { 39 - throw new PhutilArgumentUsageException( 40 - pht( 41 - 'GD is not installed for php-cli. Aborting.')); 42 - } 43 - 44 - $iterator = $this->buildIterator($args); 45 - if (!$iterator) { 46 - throw new PhutilArgumentUsageException( 47 - pht( 48 - 'Either specify a list of users to update, or use `%s` '. 49 - 'to update all users.', 50 - '--all')); 51 - } 52 - 53 - $version = PhabricatorFilesComposeAvatarBuiltinFile::VERSION; 54 - $generator = new PhabricatorFilesComposeAvatarBuiltinFile(); 55 - 56 - foreach ($iterator as $user) { 57 - $username = $user->getUsername(); 58 - $default_phid = $user->getDefaultProfileImagePHID(); 59 - $gen_version = $user->getDefaultProfileImageVersion(); 60 - 61 - $generate = false; 62 - if ($gen_version != $version) { 63 - $generate = true; 64 - } 65 - 66 - if ($default_phid == null || $is_force || $generate) { 67 - $console->writeOut( 68 - "%s\n", 69 - pht( 70 - 'Generating profile image for "%s".', 71 - $username)); 72 - 73 - $generator->updateUser($user); 74 - } else { 75 - $console->writeOut( 76 - "%s\n", 77 - pht( 78 - 'Default profile image "%s" already set for "%s".', 79 - $version, 80 - $username)); 81 - } 82 - } 83 - } 84 - 85 - }
+116
src/docs/user/userguide/unlocking.diviner
··· 1 + @title User Guide: Unlocking Objects 2 + @group userguide 3 + 4 + Explains how to access locked or invisible objects and accounts. 5 + 6 + Overview 7 + ======== 8 + 9 + Phabricator tries to make it difficult for users to lock themselves out of 10 + things, but you can occasionally end up in situations where no one has access 11 + to an object that you need access to. 12 + 13 + For example, sometimes the only user who had edit permission for something has 14 + left the organization, or you configured a "Phase of the Moon" policy rule and 15 + the stars aren't currently aligned. 16 + 17 + You can use various CLI tools to unlock objects and accounts if you need to 18 + regain access. 19 + 20 + 21 + Unlocking Accounts 22 + ================== 23 + 24 + If you need to regain access to an object, the easiest approach is usually to 25 + recover access to the account which owns it, then change the object policies 26 + to be more open using the web UI. 27 + 28 + For example, if an important task was accidentally locked so that only a user 29 + who is currently on vacation can edit it, you can log in as that user and 30 + change the edit policy to something more permissive. 31 + 32 + To regain access to an account: 33 + 34 + ``` 35 + $ ./bin/auth recover <username> 36 + ``` 37 + 38 + If the account you're recovering access to has MFA or other session prompts, 39 + use the `--force-full-session` to bypass them: 40 + 41 + ``` 42 + $ ./bin/auth recover <username> --force-full-session 43 + ``` 44 + 45 + In either case, the command will give you a link you a one-time link you can 46 + use to access the account from the web UI. From there, you can open up objects 47 + or change settings. 48 + 49 + 50 + Unlocking MFA 51 + ============= 52 + 53 + You can completely strip MFA from a user account with: 54 + 55 + ``` 56 + $ ./bin/auth strip --user <username> ... 57 + ``` 58 + 59 + For detailed help on managing and stripping MFA, see the instructions in 60 + @{article:User Guide: Multi-Factor Authentication} 61 + 62 + 63 + Unlocking Objects 64 + ================= 65 + 66 + If you aren't sure who owns an object, or no user account has access to an 67 + object, you can directly change object policies from the CLI: 68 + 69 + ``` 70 + $ ./bin/policy unlock <object> [--view ...] [--edit ...] [--owner ...] 71 + ``` 72 + 73 + To identify the object you want to unlock, you can specify an object name (like 74 + `T123`) or a PHID as the `<object>` parameter. 75 + 76 + Use the `--view` and `--edit` flags (and, for some objects, the `--owner` 77 + flag) to specify new policies for the object. 78 + 79 + For example, to make task `T123` editable by user `@alice`, run: 80 + 81 + ``` 82 + $ ./bin/policy unlock T123 --edit alice 83 + ``` 84 + 85 + Not every object has mutable view and edit policies, and not every object has 86 + an owner, so each flag only works on some types of objects. 87 + 88 + From here, you can log in to the web UI and change the relevant policies to 89 + whatever you want to set them to. 90 + 91 + 92 + No Enabled Users 93 + ================ 94 + 95 + If you accidentally disabled all administrator accounts, you can enable a 96 + disabled account from the CLI like this: 97 + 98 + ``` 99 + $ ./bin/user enable --user <username> 100 + ``` 101 + 102 + From here, recover the account or log in normally. 103 + 104 + 105 + No Administrators 106 + ================= 107 + 108 + If you accidentally deleted all the administrator accounts, you can empower 109 + a user as an administrator from the CLI like this: 110 + 111 + ``` 112 + $ ./bin/user empower --user <username> 113 + ``` 114 + 115 + This will upgrade the user account from a regular account to an administrator 116 + account.