@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
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Add `bin/auth list-factors` and `bin/auth strip` to remove multi-factor auth

Summary:
Ref T4398. The major goals here is to let administrators strip auth factors in two cases:

- A user lost their phone and needs access restored to their account; or
- an install previously used an API-based factor like SMS, but want to stop supporting it (this isn't possible today).

Test Plan:
- Used `bin/auth list-factors` to show installed factors.
- Used `bin/auth strip` with various mixtures of flags to selectively choose and strip factors from accounts.
- Also ran `bin/auth refresh` to verify refreshing OAuth tokens works (small `OAuth` vs `OAuth2` tweak).

Reviewers: btrahan

Reviewed By: btrahan

Subscribers: epriestley

Maniphest Tasks: T4398

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

+198 -2
+4
src/__phutil_library_map__.php
··· 1219 1219 'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php', 1220 1220 'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php', 1221 1221 'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php', 1222 + 'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php', 1222 1223 'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php', 1223 1224 'PhabricatorAuthManagementRefreshWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php', 1225 + 'PhabricatorAuthManagementStripWorkflow' => 'applications/auth/management/PhabricatorAuthManagementStripWorkflow.php', 1224 1226 'PhabricatorAuthManagementWorkflow' => 'applications/auth/management/PhabricatorAuthManagementWorkflow.php', 1225 1227 'PhabricatorAuthNeedsApprovalController' => 'applications/auth/controller/PhabricatorAuthNeedsApprovalController.php', 1226 1228 'PhabricatorAuthNewController' => 'applications/auth/controller/config/PhabricatorAuthNewController.php', ··· 3982 3984 'PhabricatorAuthListController' => 'PhabricatorAuthProviderConfigController', 3983 3985 'PhabricatorAuthLoginController' => 'PhabricatorAuthController', 3984 3986 'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow', 3987 + 'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow', 3985 3988 'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow', 3986 3989 'PhabricatorAuthManagementRefreshWorkflow' => 'PhabricatorAuthManagementWorkflow', 3990 + 'PhabricatorAuthManagementStripWorkflow' => 'PhabricatorAuthManagementWorkflow', 3987 3991 'PhabricatorAuthManagementWorkflow' => 'PhabricatorManagementWorkflow', 3988 3992 'PhabricatorAuthNeedsApprovalController' => 'PhabricatorAuthController', 3989 3993 'PhabricatorAuthNewController' => 'PhabricatorAuthProviderConfigController',
+28
src/applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthManagementListFactorsWorkflow 4 + extends PhabricatorAuthManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $this 8 + ->setName('list-factors') 9 + ->setExamples('**list-factors**') 10 + ->setSynopsis(pht('List available multi-factor authentication factors.')) 11 + ->setArguments(array()); 12 + } 13 + 14 + public function execute(PhutilArgumentParser $args) { 15 + $factors = PhabricatorAuthFactor::getAllFactors(); 16 + 17 + $console = PhutilConsole::getConsole(); 18 + foreach ($factors as $factor) { 19 + $console->writeOut( 20 + "%s\t%s\n", 21 + $factor->getFactorKey(), 22 + $factor->getFactorName()); 23 + } 24 + 25 + return 0; 26 + } 27 + 28 + }
+2 -2
src/applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php
··· 96 96 } 97 97 98 98 $provider = $providers[$key]; 99 - if (!($provider instanceof PhabricatorAuthProviderOAuth)) { 99 + if (!($provider instanceof PhabricatorAuthProviderOAuth2)) { 100 100 $console->writeOut( 101 101 "> %s\n", 102 - pht("Skipping, provider is not an OAuth provider.")); 102 + pht("Skipping, provider is not an OAuth2 provider.")); 103 103 continue; 104 104 } 105 105
+164
src/applications/auth/management/PhabricatorAuthManagementStripWorkflow.php
··· 1 + <?php 2 + 3 + final class PhabricatorAuthManagementStripWorkflow 4 + extends PhabricatorAuthManagementWorkflow { 5 + 6 + protected function didConstruct() { 7 + $this 8 + ->setName('strip') 9 + ->setExamples('**strip** [--user username] [--type type]') 10 + ->setSynopsis( 11 + pht( 12 + 'Remove multi-factor authentication from an account.')) 13 + ->setArguments( 14 + array( 15 + array( 16 + 'name' => 'user', 17 + 'param' => 'username', 18 + 'repeat' => true, 19 + 'help' => pht('Strip factors from specified users.'), 20 + ), 21 + array( 22 + 'name' => 'all-users', 23 + 'help' => pht('Strip factors from all users.'), 24 + ), 25 + array( 26 + 'name' => 'type', 27 + 'param' => 'factortype', 28 + 'repeat' => true, 29 + 'help' => pht('Strip a specific factor type.'), 30 + ), 31 + array( 32 + 'name' => 'all-types', 33 + 'help' => pht('Strip all factors, regardless of type.'), 34 + ), 35 + array( 36 + 'name' => 'force', 37 + 'help' => pht('Strip factors without prompting.'), 38 + ), 39 + array( 40 + 'name' => 'dry-run', 41 + 'help' => pht('Show factors, but do not strip them.'), 42 + ), 43 + )); 44 + } 45 + 46 + public function execute(PhutilArgumentParser $args) { 47 + $usernames = $args->getArg('user'); 48 + $all_users = $args->getArg('all-users'); 49 + 50 + if ($usernames && $all_users) { 51 + throw new PhutilArgumentUsageException( 52 + pht( 53 + 'Specify either specific users with --user, or all users with '. 54 + '--all-users, but not both.')); 55 + } else if (!$usernames && !$all_users) { 56 + throw new PhutilArgumentUsageException( 57 + pht( 58 + 'Use --user to specify which user to strip factors from, or '. 59 + '--all-users to strip factors from all users.')); 60 + } else if ($usernames) { 61 + $users = id(new PhabricatorPeopleQuery()) 62 + ->setViewer($this->getViewer()) 63 + ->withUsernames($usernames) 64 + ->execute(); 65 + 66 + $users_by_username = mpull($users, null, 'getUsername'); 67 + foreach ($usernames as $username) { 68 + if (empty($users_by_username[$username])) { 69 + throw new PhutilArgumentUsageException( 70 + pht( 71 + 'No user exists with username "%s".', 72 + $username)); 73 + } 74 + } 75 + } else { 76 + $users = null; 77 + } 78 + 79 + $types = $args->getArg('type'); 80 + $all_types = $args->getArg('all-types'); 81 + if ($types && $all_types) { 82 + throw new PhutilArgumentUsageException( 83 + pht( 84 + 'Specify either specific factors with --type, or all factors with '. 85 + '--all-types, but not both.')); 86 + } else if (!$types && !$all_types) { 87 + throw new PhutilArgumentUsageException( 88 + pht( 89 + 'Use --type to specify which factor to strip, or --all-types to '. 90 + 'strip all factors. Use `auth list-factors` to show the available '. 91 + 'factor types.')); 92 + } 93 + 94 + if ($users && $types) { 95 + $factors = id(new PhabricatorAuthFactorConfig())->loadAllWhere( 96 + 'userPHID IN (%Ls) AND factorKey IN (%Ls)', 97 + mpull($users, 'getPHID'), 98 + $types); 99 + } else if ($users) { 100 + $factors = id(new PhabricatorAuthFactorConfig())->loadAllWhere( 101 + 'userPHID IN (%Ls)', 102 + mpull($users, 'getPHID')); 103 + } else if ($types) { 104 + $factors = id(new PhabricatorAuthFactorConfig())->loadAllWhere( 105 + 'factorKey IN (%Ls)', 106 + $types); 107 + } else { 108 + $factors = id(new PhabricatorAuthFactorConfig())->loadAll(); 109 + } 110 + 111 + if (!$factors) { 112 + throw new PhutilArgumentUsageException( 113 + pht('There are no matching factors to strip.')); 114 + } 115 + 116 + $handles = id(new PhabricatorHandleQuery()) 117 + ->setViewer($this->getViewer()) 118 + ->withPHIDs(mpull($factors, 'getUserPHID')) 119 + ->execute(); 120 + 121 + $console = PhutilConsole::getConsole(); 122 + 123 + $console->writeOut("%s\n\n", pht("These auth factors will be stripped:")); 124 + 125 + foreach ($factors as $factor) { 126 + $impl = $factor->getImplementation(); 127 + $console->writeOut( 128 + " %s\t%s\t%s\n", 129 + $handles[$factor->getUserPHID()]->getName(), 130 + $factor->getFactorKey(), 131 + ($impl 132 + ? $impl->getFactorName() 133 + : '?')); 134 + } 135 + 136 + $is_dry_run = $args->getArg('dry-run'); 137 + if ($is_dry_run) { 138 + $console->writeOut( 139 + "\n%s\n", 140 + pht('End of dry run.')); 141 + 142 + return 0; 143 + } 144 + 145 + $force = $args->getArg('force'); 146 + if (!$force) { 147 + if (!$console->confirm(pht('Strip these authentication factors?'))) { 148 + throw new PhutilArgumentUsageException( 149 + pht('User aborted the workflow.')); 150 + } 151 + } 152 + 153 + $console->writeOut("%s\n", pht('Stripping authentication factors...')); 154 + 155 + foreach ($factors as $factor) { 156 + $factor->delete(); 157 + } 158 + 159 + $console->writeOut("%s\n", pht('Done.')); 160 + 161 + return 0; 162 + } 163 + 164 + }