@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.

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

+210
+6
src/__phutil_library_map__.php
··· 1906 1906 'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php', 1907 1907 'PhabricatorAuthAuthProviderPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php', 1908 1908 'PhabricatorAuthConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthConduitAPIMethod.php', 1909 + 'PhabricatorAuthConduitTokenRevoker' => 'applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php', 1909 1910 'PhabricatorAuthConfirmLinkController' => 'applications/auth/controller/PhabricatorAuthConfirmLinkController.php', 1910 1911 'PhabricatorAuthController' => 'applications/auth/controller/PhabricatorAuthController.php', 1911 1912 'PhabricatorAuthDAO' => 'applications/auth/storage/PhabricatorAuthDAO.php', ··· 1946 1947 'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php', 1947 1948 'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php', 1948 1949 'PhabricatorAuthManagementRefreshWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php', 1950 + 'PhabricatorAuthManagementRevokeWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php', 1949 1951 'PhabricatorAuthManagementStripWorkflow' => 'applications/auth/management/PhabricatorAuthManagementStripWorkflow.php', 1950 1952 'PhabricatorAuthManagementTrustOAuthClientWorkflow' => 'applications/auth/management/PhabricatorAuthManagementTrustOAuthClientWorkflow.php', 1951 1953 'PhabricatorAuthManagementUnlimitWorkflow' => 'applications/auth/management/PhabricatorAuthManagementUnlimitWorkflow.php', ··· 1971 1973 'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php', 1972 1974 'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php', 1973 1975 'PhabricatorAuthRevokeTokenController' => 'applications/auth/controller/PhabricatorAuthRevokeTokenController.php', 1976 + 'PhabricatorAuthRevoker' => 'applications/auth/revoker/PhabricatorAuthRevoker.php', 1974 1977 'PhabricatorAuthSSHKey' => 'applications/auth/storage/PhabricatorAuthSSHKey.php', 1975 1978 'PhabricatorAuthSSHKeyController' => 'applications/auth/controller/PhabricatorAuthSSHKeyController.php', 1976 1979 'PhabricatorAuthSSHKeyDeactivateController' => 'applications/auth/controller/PhabricatorAuthSSHKeyDeactivateController.php', ··· 6853 6856 'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType', 6854 6857 'PhabricatorAuthAuthProviderPHIDType' => 'PhabricatorPHIDType', 6855 6858 'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod', 6859 + 'PhabricatorAuthConduitTokenRevoker' => 'PhabricatorAuthRevoker', 6856 6860 'PhabricatorAuthConfirmLinkController' => 'PhabricatorAuthController', 6857 6861 'PhabricatorAuthController' => 'PhabricatorController', 6858 6862 'PhabricatorAuthDAO' => 'PhabricatorLiskDAO', ··· 6896 6900 'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow', 6897 6901 'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow', 6898 6902 'PhabricatorAuthManagementRefreshWorkflow' => 'PhabricatorAuthManagementWorkflow', 6903 + 'PhabricatorAuthManagementRevokeWorkflow' => 'PhabricatorAuthManagementWorkflow', 6899 6904 'PhabricatorAuthManagementStripWorkflow' => 'PhabricatorAuthManagementWorkflow', 6900 6905 'PhabricatorAuthManagementTrustOAuthClientWorkflow' => 'PhabricatorAuthManagementWorkflow', 6901 6906 'PhabricatorAuthManagementUnlimitWorkflow' => 'PhabricatorAuthManagementWorkflow', ··· 6925 6930 'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod', 6926 6931 'PhabricatorAuthRegisterController' => 'PhabricatorAuthController', 6927 6932 'PhabricatorAuthRevokeTokenController' => 'PhabricatorAuthController', 6933 + 'PhabricatorAuthRevoker' => 'Phobject', 6928 6934 'PhabricatorAuthSSHKey' => array( 6929 6935 'PhabricatorAuthDAO', 6930 6936 'PhabricatorPolicyInterface',
+140
src/applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php
··· 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 + }
+33
src/applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php
··· 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 + }
+31
src/applications/auth/revoker/PhabricatorAuthRevoker.php
··· 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 + }