@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 PhabricatorAuthManagementRevokeWorkflow
4 extends PhabricatorAuthManagementWorkflow {
5
6 protected function didConstruct() {
7 $this
8 ->setName('revoke')
9 ->setExamples(
10 "**revoke** --list\n".
11 "**revoke** --type __type__ --from __@user__\n".
12 "**revoke** --everything --everywhere")
13 ->setSynopsis(
14 pht(
15 'Revoke credentials which may have been leaked or disclosed.'))
16 ->setArguments(
17 array(
18 array(
19 'name' => 'from',
20 'param' => 'object',
21 'help' => pht(
22 'Revoke credentials for the specified object. To revoke '.
23 'credentials for a user, use "@username".'),
24 ),
25 array(
26 'name' => 'type',
27 'param' => 'type',
28 'help' => pht('Revoke credentials of the given type.'),
29 ),
30 array(
31 'name' => 'list',
32 'help' => pht(
33 'List information about available credential revokers.'),
34 ),
35 array(
36 'name' => 'everything',
37 'help' => pht('Revoke all credentials types.'),
38 ),
39 array(
40 'name' => 'everywhere',
41 'help' => pht('Revoke from all credential owners.'),
42 ),
43 array(
44 'name' => 'force',
45 'help' => pht('Revoke credentials without prompting.'),
46 ),
47 ));
48 }
49
50 public function execute(PhutilArgumentParser $args) {
51 $viewer = $this->getViewer();
52
53 $all_types = PhabricatorAuthRevoker::getAllRevokers();
54 $is_force = $args->getArg('force');
55
56 // The "--list" flag is compatible with revoker selection flags like
57 // "--type" to filter the list, but not compatible with target selection
58 // flags like "--from".
59 $is_list = $args->getArg('list');
60
61 $type = $args->getArg('type');
62 $is_everything = $args->getArg('everything');
63 if ($type === null && !$is_everything) {
64 if ($is_list) {
65 // By default, "bin/auth revoke --list" implies "--everything".
66 $types = $all_types;
67 } else {
68 throw new PhutilArgumentUsageException(
69 pht(
70 'Specify the credential type to revoke with "--type" or specify '.
71 '"--everything". Use "--list" to list available credential '.
72 'types.'));
73 }
74 } else if (phutil_nonempty_string($type) && $is_everything) {
75 throw new PhutilArgumentUsageException(
76 pht(
77 'Specify the credential type to revoke with "--type" or '.
78 '"--everything", but not both.'));
79 } else if ($is_everything) {
80 $types = $all_types;
81 } else {
82 if (empty($all_types[$type])) {
83 throw new PhutilArgumentUsageException(
84 pht(
85 'Credential type "%s" is not valid. Valid credential types '.
86 'are: %s.',
87 $type,
88 implode(', ', array_keys($all_types))));
89 }
90 $types = array($all_types[$type]);
91 }
92
93 $is_everywhere = $args->getArg('everywhere');
94 $from = $args->getArg('from');
95
96 if ($is_list) {
97 if ($from !== null || $is_everywhere) {
98 throw new PhutilArgumentUsageException(
99 pht(
100 'You can not "--list" and revoke credentials (with "--from" or '.
101 '"--everywhere") in the same operation.'));
102 }
103 }
104
105 if ($is_list) {
106 $last_key = last_key($types);
107 foreach ($types as $key => $type) {
108 echo tsprintf(
109 "**%s** (%s)\n\n",
110 $type->getRevokerKey(),
111 $type->getRevokerName());
112
113 id(new PhutilConsoleBlock())
114 ->addParagraph(tsprintf('%B', $type->getRevokerDescription()))
115 ->draw();
116 }
117
118 return 0;
119 }
120
121 $target = null;
122 if (!phutil_nonempty_string($from) && !$is_everywhere) {
123 throw new PhutilArgumentUsageException(
124 pht(
125 'Specify the target to revoke credentials from with "--from" or '.
126 'specify "--everywhere".'));
127 } else if (phutil_nonempty_string($from) && $is_everywhere) {
128 throw new PhutilArgumentUsageException(
129 pht(
130 'Specify the target to revoke credentials from with "--from" or '.
131 'specify "--everywhere", but not both.'));
132 } else if ($is_everywhere) {
133 // Just carry the flag through.
134 } else {
135 $target = id(new PhabricatorObjectQuery())
136 ->setViewer($viewer)
137 ->withNames(array($from))
138 ->executeOne();
139 if (!$target) {
140 throw new PhutilArgumentUsageException(
141 pht(
142 'Target "%s" is not a valid target to revoke credentials from. '.
143 'Usually, revoke from "@username".',
144 $from));
145 }
146 }
147
148 if ($is_everywhere && !$is_force) {
149 echo id(new PhutilConsoleBlock())
150 ->addParagraph(
151 pht(
152 'You are destroying an entire class of credentials. This may be '.
153 'very disruptive to users. You should normally do this only if '.
154 'you suspect there has been a widespread compromise which may '.
155 'have impacted everyone.'))
156 ->drawConsoleString();
157
158 $prompt = pht('Really destroy credentials everywhere?');
159 if (!phutil_console_confirm($prompt)) {
160 throw new PhutilArgumentUsageException(
161 pht('Aborted workflow.'));
162 }
163 }
164
165 foreach ($types as $type) {
166 $type->setViewer($viewer);
167 if ($is_everywhere) {
168 $count = $type->revokeAllCredentials();
169 } else {
170 $count = $type->revokeCredentialsFrom($target);
171 }
172
173 echo tsprintf(
174 "%s\n",
175 pht(
176 'Destroyed %s credential(s) of type "%s".',
177 new PhutilNumber($count),
178 $type->getRevokerKey()));
179
180 $guidance = $type->getRevokerNextSteps();
181 if ($guidance !== null) {
182 echo tsprintf(
183 "%s\n",
184 $guidance);
185 }
186 }
187
188 echo tsprintf(
189 "%s\n",
190 pht('Done.'));
191
192 return 0;
193 }
194
195}