Skip to content

Commit 8ce2583

Browse files
author
epriestley
committedMar 3, 2017
Provide "bin/auth revoke" with a revoker for Conduit tokens
Summary: Ref T12313. This puts a UI on revoking credentials after a widespread compromise like Cloudbleed or a local one like copy/pasting a token into public chat. For now, I'm only providing a revoker for conduit tokens since that's the immediate use case. Test Plan: - Revoked in user + type, everything + user, everywhere + type, and everything + everywhere modes. - Verified that conduit tokens were destroyed in all cases. Reviewers: chad Reviewed By: chad Maniphest Tasks: T12313 Differential Revision: https://secure.phabricator.com/D17458
1 parent 1460f2b commit 8ce2583

4 files changed

+210
-0
lines changed
 

‎src/__phutil_library_map__.php

+6
Original file line numberDiff line numberDiff line change
@@ -1906,6 +1906,7 @@
19061906
'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php',
19071907
'PhabricatorAuthAuthProviderPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php',
19081908
'PhabricatorAuthConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthConduitAPIMethod.php',
1909+
'PhabricatorAuthConduitTokenRevoker' => 'applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php',
19091910
'PhabricatorAuthConfirmLinkController' => 'applications/auth/controller/PhabricatorAuthConfirmLinkController.php',
19101911
'PhabricatorAuthController' => 'applications/auth/controller/PhabricatorAuthController.php',
19111912
'PhabricatorAuthDAO' => 'applications/auth/storage/PhabricatorAuthDAO.php',
@@ -1946,6 +1947,7 @@
19461947
'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php',
19471948
'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php',
19481949
'PhabricatorAuthManagementRefreshWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php',
1950+
'PhabricatorAuthManagementRevokeWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php',
19491951
'PhabricatorAuthManagementStripWorkflow' => 'applications/auth/management/PhabricatorAuthManagementStripWorkflow.php',
19501952
'PhabricatorAuthManagementTrustOAuthClientWorkflow' => 'applications/auth/management/PhabricatorAuthManagementTrustOAuthClientWorkflow.php',
19511953
'PhabricatorAuthManagementUnlimitWorkflow' => 'applications/auth/management/PhabricatorAuthManagementUnlimitWorkflow.php',
@@ -1971,6 +1973,7 @@
19711973
'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php',
19721974
'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php',
19731975
'PhabricatorAuthRevokeTokenController' => 'applications/auth/controller/PhabricatorAuthRevokeTokenController.php',
1976+
'PhabricatorAuthRevoker' => 'applications/auth/revoker/PhabricatorAuthRevoker.php',
19741977
'PhabricatorAuthSSHKey' => 'applications/auth/storage/PhabricatorAuthSSHKey.php',
19751978
'PhabricatorAuthSSHKeyController' => 'applications/auth/controller/PhabricatorAuthSSHKeyController.php',
19761979
'PhabricatorAuthSSHKeyDeactivateController' => 'applications/auth/controller/PhabricatorAuthSSHKeyDeactivateController.php',
@@ -6853,6 +6856,7 @@
68536856
'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType',
68546857
'PhabricatorAuthAuthProviderPHIDType' => 'PhabricatorPHIDType',
68556858
'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod',
6859+
'PhabricatorAuthConduitTokenRevoker' => 'PhabricatorAuthRevoker',
68566860
'PhabricatorAuthConfirmLinkController' => 'PhabricatorAuthController',
68576861
'PhabricatorAuthController' => 'PhabricatorController',
68586862
'PhabricatorAuthDAO' => 'PhabricatorLiskDAO',
@@ -6896,6 +6900,7 @@
68966900
'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow',
68976901
'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow',
68986902
'PhabricatorAuthManagementRefreshWorkflow' => 'PhabricatorAuthManagementWorkflow',
6903+
'PhabricatorAuthManagementRevokeWorkflow' => 'PhabricatorAuthManagementWorkflow',
68996904
'PhabricatorAuthManagementStripWorkflow' => 'PhabricatorAuthManagementWorkflow',
69006905
'PhabricatorAuthManagementTrustOAuthClientWorkflow' => 'PhabricatorAuthManagementWorkflow',
69016906
'PhabricatorAuthManagementUnlimitWorkflow' => 'PhabricatorAuthManagementWorkflow',
@@ -6925,6 +6930,7 @@
69256930
'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod',
69266931
'PhabricatorAuthRegisterController' => 'PhabricatorAuthController',
69276932
'PhabricatorAuthRevokeTokenController' => 'PhabricatorAuthController',
6933+
'PhabricatorAuthRevoker' => 'Phobject',
69286934
'PhabricatorAuthSSHKey' => array(
69296935
'PhabricatorAuthDAO',
69306936
'PhabricatorPolicyInterface',
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
3+
final class PhabricatorAuthManagementRevokeWorkflow
4+
extends PhabricatorAuthManagementWorkflow {
5+
6+
protected function didConstruct() {
7+
$this
8+
->setName('revoke')
9+
->setExamples(
10+
"**revoke** --type __type__ --from __user__\n".
11+
"**revoke** --everything --everywhere")
12+
->setSynopsis(
13+
pht(
14+
'Revoke credentials which may have been leaked or disclosed.'))
15+
->setArguments(
16+
array(
17+
array(
18+
'name' => 'from',
19+
'param' => 'user',
20+
'help' => pht(
21+
'Revoke credentials for the specified user.'),
22+
),
23+
array(
24+
'name' => 'type',
25+
'param' => 'type',
26+
'help' => pht(
27+
'Revoke credentials of the given type.'),
28+
),
29+
array(
30+
'name' => 'everything',
31+
'help' => pht('Revoke all credentials types.'),
32+
),
33+
array(
34+
'name' => 'everywhere',
35+
'help' => pht('Revoke from all credential owners.'),
36+
),
37+
));
38+
}
39+
40+
public function execute(PhutilArgumentParser $args) {
41+
$viewer = PhabricatorUser::getOmnipotentUser();
42+
43+
$all_types = PhabricatorAuthRevoker::getAllRevokers();
44+
45+
$type = $args->getArg('type');
46+
$is_everything = $args->getArg('everything');
47+
if (!strlen($type) && !$is_everything) {
48+
throw new PhutilArgumentUsageException(
49+
pht(
50+
'Specify the credential type to revoke with "--type" or specify '.
51+
'"--everything".'));
52+
} else if (strlen($type) && $is_everything) {
53+
throw new PhutilArgumentUsageException(
54+
pht(
55+
'Specify the credential type to revoke with "--type" or '.
56+
'"--everything", but not both.'));
57+
} else if ($is_everything) {
58+
$types = $all_types;
59+
} else {
60+
if (empty($all_types[$type])) {
61+
throw new PhutilArgumentUsageException(
62+
pht(
63+
'Credential type "%s" is not valid. Valid credential types '.
64+
'are: %s.',
65+
$type,
66+
implode(', ', array_keys($all_types))));
67+
}
68+
$types = array($all_types[$type]);
69+
}
70+
71+
$is_everywhere = $args->getArg('everywhere');
72+
$from = $args->getArg('from');
73+
$target = null;
74+
if (!strlen($from) && !$is_everywhere) {
75+
throw new PhutilArgumentUsageException(
76+
pht(
77+
'Specify the target to revoke credentals from with "--from" or '.
78+
'specify "--everywhere".'));
79+
} else if (strlen($from) && $is_everywhere) {
80+
throw new PhutilArgumentUsageException(
81+
pht(
82+
'Specify the target to revoke credentials from with "--from" or '.
83+
'specify "--everywhere", but not both.'));
84+
} else if ($is_everywhere) {
85+
// Just carry the flag through.
86+
} else {
87+
$target = id(new PhabricatorObjectQuery())
88+
->setViewer($viewer)
89+
->withNames(array($from))
90+
->executeOne();
91+
if (!$target) {
92+
throw new PhutilArgumentUsageException(
93+
pht(
94+
'Target "%s" is not a valid target to revoke credentials from. '.
95+
'Usually, revoke from "@username".',
96+
$from));
97+
}
98+
}
99+
100+
if ($is_everywhere) {
101+
echo id(new PhutilConsoleBlock())
102+
->addParagraph(
103+
pht(
104+
'You are destroying an entire class of credentials. This may be '.
105+
'very disruptive to users. You should normally do this only if '.
106+
'you suspect there has been a widespread compromise which may '.
107+
'have impacted everyone.'))
108+
->drawConsoleString();
109+
110+
$prompt = pht('Really destroy credentials everywhere?');
111+
if (!phutil_console_confirm($prompt)) {
112+
throw new PhutilArgumentUsageException(
113+
pht('Aborted workflow.'));
114+
}
115+
}
116+
117+
foreach ($types as $type) {
118+
$type->setViewer($viewer);
119+
if ($is_everywhere) {
120+
$count = $type->revokeAllCredentials();
121+
} else {
122+
$count = $type->revokeCredentialsFrom($target);
123+
}
124+
125+
echo tsprintf(
126+
"%s\n",
127+
pht(
128+
'Destroyed %s credential(s) of type "%s".',
129+
new PhutilNumber($count),
130+
$type->getRevokerKey()));
131+
}
132+
133+
echo tsprintf(
134+
"%s\n",
135+
pht('Done.'));
136+
137+
return 0;
138+
}
139+
140+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
<?php
2+
3+
final class PhabricatorAuthConduitTokenRevoker
4+
extends PhabricatorAuthRevoker {
5+
6+
const REVOKERKEY = 'conduit';
7+
8+
public function revokeAllCredentials() {
9+
$table = id(new PhabricatorConduitToken());
10+
$conn = $table->establishConnection('w');
11+
12+
queryfx(
13+
$conn,
14+
'DELETE FROM %T',
15+
$table->getTableName());
16+
17+
return $conn->getAffectedRows();
18+
}
19+
20+
public function revokeCredentialsFrom($object) {
21+
$table = id(new PhabricatorConduitToken());
22+
$conn = $table->establishConnection('w');
23+
24+
queryfx(
25+
$conn,
26+
'DELETE FROM %T WHERE objectPHID = %s',
27+
$table->getTableName(),
28+
$object->getPHID());
29+
30+
return $conn->getAffectedRows();
31+
}
32+
33+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<?php
2+
3+
abstract class PhabricatorAuthRevoker
4+
extends Phobject {
5+
6+
private $viewer;
7+
8+
abstract public function revokeAlLCredentials();
9+
abstract public function revokeCredentialsFrom($object);
10+
11+
public function setViewer(PhabricatorUser $viewer) {
12+
$this->viewer = $viewer;
13+
return $this;
14+
}
15+
16+
public function getViewer() {
17+
return $this->viewer;
18+
}
19+
20+
final public function getRevokerKey() {
21+
return $this->getPhobjectClassConstant('REVOKERKEY');
22+
}
23+
24+
final public static function getAllRevokers() {
25+
return id(new PhutilClassMapQuery())
26+
->setAncestorClass(__CLASS__)
27+
->setUniqueMethod('getRevokerKey')
28+
->execute();
29+
}
30+
31+
}

0 commit comments

Comments
 (0)
Failed to load comments.