@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<?php
2
3final class PhabricatorAuthManagementStripWorkflow
4 extends PhabricatorAuthManagementWorkflow {
5
6 protected function didConstruct() {
7 $this
8 ->setName('strip')
9 ->setExamples('**strip** [--user username] [--type type]')
10 ->setSynopsis(pht('Remove multi-factor authentication from an account.'))
11 ->setArguments(
12 array(
13 array(
14 'name' => 'user',
15 'param' => 'username',
16 'repeat' => true,
17 'help' => pht('Strip factors from specified users.'),
18 ),
19 array(
20 'name' => 'all-users',
21 'help' => pht('Strip factors from all users.'),
22 ),
23 array(
24 'name' => 'type',
25 'param' => 'factortype',
26 'repeat' => true,
27 'help' => pht(
28 'Strip a specific factor type. Use `bin/auth list-factors` for '.
29 'a list of factor types.'),
30 ),
31 array(
32 'name' => 'all-types',
33 'help' => pht('Strip all factors, regardless of type.'),
34 ),
35 array(
36 'name' => 'provider',
37 'param' => 'phid',
38 'repeat' => true,
39 'help' => pht(
40 'Strip factors for a specific provider. Use '.
41 '`bin/auth list-mfa-providers` for a list of providers.'),
42 ),
43 array(
44 'name' => 'force',
45 'help' => pht('Strip factors without prompting.'),
46 ),
47 array(
48 'name' => 'dry-run',
49 'help' => pht('Show factors, but do not strip them.'),
50 ),
51 ));
52 }
53
54 public function execute(PhutilArgumentParser $args) {
55 $viewer = $this->getViewer();
56
57 $usernames = $args->getArg('user');
58 $all_users = $args->getArg('all-users');
59
60 if ($usernames && $all_users) {
61 throw new PhutilArgumentUsageException(
62 pht(
63 'Specify either specific users with %s, or all users with '.
64 '%s, but not both.',
65 '--user',
66 '--all-users'));
67 } else if (!$usernames && !$all_users) {
68 throw new PhutilArgumentUsageException(
69 pht(
70 'Use "--user <username>" to specify which user to strip factors '.
71 'from, or "--all-users" to strip factors from all users.'));
72 } else if ($usernames) {
73 $users = id(new PhabricatorPeopleQuery())
74 ->setViewer($this->getViewer())
75 ->withUsernames($usernames)
76 ->execute();
77
78 $users_by_username = mpull($users, null, 'getUsername');
79 foreach ($usernames as $username) {
80 if (empty($users_by_username[$username])) {
81 throw new PhutilArgumentUsageException(
82 pht(
83 'No user exists with username "%s".',
84 $username));
85 }
86 }
87 } else {
88 $users = null;
89 }
90
91 $types = $args->getArg('type');
92 $provider_phids = $args->getArg('provider');
93 $all_types = $args->getArg('all-types');
94 if ($types && $all_types) {
95 throw new PhutilArgumentUsageException(
96 pht(
97 'Specify either specific factors with "--type", or all factors with '.
98 '"--all-types", but not both.'));
99 } else if ($provider_phids && $all_types) {
100 throw new PhutilArgumentUsageException(
101 pht(
102 'Specify either specific factors with "--provider", or all factors '.
103 'with "--all-types", but not both.'));
104 } else if (!$types && !$all_types && !$provider_phids) {
105 throw new PhutilArgumentUsageException(
106 pht(
107 'Use "--type <type>" or "--provider <phid>" to specify which '.
108 'factors to strip, or "--all-types" to strip all factors. '.
109 'Use `bin/auth list-factors` to show the available factor types '.
110 'or `bin/auth list-mfa-providers` to show available providers.'));
111 }
112
113 $type_map = PhabricatorAuthFactor::getAllFactors();
114
115 if ($types) {
116 foreach ($types as $type) {
117 if (!isset($type_map[$type])) {
118 throw new PhutilArgumentUsageException(
119 pht(
120 'Factor type "%s" is unknown. Use `bin/auth list-factors` to '.
121 'get a list of known factor types.',
122 $type));
123 }
124 }
125 }
126
127 $provider_query = id(new PhabricatorAuthFactorProviderQuery())
128 ->setViewer($viewer);
129
130 if ($provider_phids) {
131 $provider_query->withPHIDs($provider_phids);
132 }
133
134 if ($types) {
135 $provider_query->withProviderFactorKeys($types);
136 }
137
138 $providers = $provider_query->execute();
139 $providers = mpull($providers, null, 'getPHID');
140
141 if ($provider_phids) {
142 foreach ($provider_phids as $provider_phid) {
143 if (!isset($providers[$provider_phid])) {
144 throw new PhutilArgumentUsageException(
145 pht(
146 'No provider with PHID "%s" exists. '.
147 'Use `bin/auth list-mfa-providers` to list providers.',
148 $provider_phid));
149 }
150 }
151 } else {
152 if (!$providers) {
153 throw new PhutilArgumentUsageException(
154 pht(
155 'There are no configured multi-factor providers.'));
156 }
157 }
158
159 $factor_query = id(new PhabricatorAuthFactorConfigQuery())
160 ->setViewer($viewer)
161 ->withFactorProviderPHIDs(array_keys($providers));
162
163 if ($users) {
164 $factor_query->withUserPHIDs(mpull($users, 'getPHID'));
165 }
166
167 $factors = $factor_query->execute();
168
169 if (!$factors) {
170 throw new PhutilArgumentUsageException(
171 pht('There are no matching factors to strip.'));
172 }
173
174 $handles = id(new PhabricatorHandleQuery())
175 ->setViewer($this->getViewer())
176 ->withPHIDs(mpull($factors, 'getUserPHID'))
177 ->execute();
178
179 $console = PhutilConsole::getConsole();
180
181 $console->writeOut("%s\n\n", pht('These auth factors will be stripped:'));
182
183 foreach ($factors as $factor) {
184 $provider = $factor->getFactorProvider();
185
186 echo tsprintf(
187 " %s\t%s\t%s\n",
188 $handles[$factor->getUserPHID()]->getName(),
189 $provider->getProviderFactorKey(),
190 $provider->getDisplayName());
191 }
192
193 $is_dry_run = $args->getArg('dry-run');
194 if ($is_dry_run) {
195 $console->writeOut(
196 "\n%s\n",
197 pht('End of dry run.'));
198
199 return 0;
200 }
201
202 $force = $args->getArg('force');
203 if (!$force) {
204 if (!$console->confirm(pht('Strip these authentication factors?'))) {
205 throw new PhutilArgumentUsageException(
206 pht('User aborted the workflow.'));
207 }
208 }
209
210 $console->writeOut("%s\n", pht('Stripping authentication factors...'));
211
212 $engine = new PhabricatorDestructionEngine();
213 foreach ($factors as $factor) {
214 $engine->destroyObject($factor);
215 }
216
217 $console->writeOut("%s\n", pht('Done.'));
218
219 return 0;
220 }
221
222}