diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1906,6 +1906,7 @@ 'PhabricatorAuthAuthFactorPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthFactorPHIDType.php', 'PhabricatorAuthAuthProviderPHIDType' => 'applications/auth/phid/PhabricatorAuthAuthProviderPHIDType.php', 'PhabricatorAuthConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthConduitAPIMethod.php', + 'PhabricatorAuthConduitTokenRevoker' => 'applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php', 'PhabricatorAuthConfirmLinkController' => 'applications/auth/controller/PhabricatorAuthConfirmLinkController.php', 'PhabricatorAuthController' => 'applications/auth/controller/PhabricatorAuthController.php', 'PhabricatorAuthDAO' => 'applications/auth/storage/PhabricatorAuthDAO.php', @@ -1946,6 +1947,7 @@ 'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php', 'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php', 'PhabricatorAuthManagementRefreshWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php', + 'PhabricatorAuthManagementRevokeWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php', 'PhabricatorAuthManagementStripWorkflow' => 'applications/auth/management/PhabricatorAuthManagementStripWorkflow.php', 'PhabricatorAuthManagementTrustOAuthClientWorkflow' => 'applications/auth/management/PhabricatorAuthManagementTrustOAuthClientWorkflow.php', 'PhabricatorAuthManagementUnlimitWorkflow' => 'applications/auth/management/PhabricatorAuthManagementUnlimitWorkflow.php', @@ -1971,6 +1973,7 @@ 'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'applications/auth/conduit/PhabricatorAuthQueryPublicKeysConduitAPIMethod.php', 'PhabricatorAuthRegisterController' => 'applications/auth/controller/PhabricatorAuthRegisterController.php', 'PhabricatorAuthRevokeTokenController' => 'applications/auth/controller/PhabricatorAuthRevokeTokenController.php', + 'PhabricatorAuthRevoker' => 'applications/auth/revoker/PhabricatorAuthRevoker.php', 'PhabricatorAuthSSHKey' => 'applications/auth/storage/PhabricatorAuthSSHKey.php', 'PhabricatorAuthSSHKeyController' => 'applications/auth/controller/PhabricatorAuthSSHKeyController.php', 'PhabricatorAuthSSHKeyDeactivateController' => 'applications/auth/controller/PhabricatorAuthSSHKeyDeactivateController.php', @@ -6853,6 +6856,7 @@ 'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType', 'PhabricatorAuthAuthProviderPHIDType' => 'PhabricatorPHIDType', 'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod', + 'PhabricatorAuthConduitTokenRevoker' => 'PhabricatorAuthRevoker', 'PhabricatorAuthConfirmLinkController' => 'PhabricatorAuthController', 'PhabricatorAuthController' => 'PhabricatorController', 'PhabricatorAuthDAO' => 'PhabricatorLiskDAO', @@ -6896,6 +6900,7 @@ 'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementRefreshWorkflow' => 'PhabricatorAuthManagementWorkflow', + 'PhabricatorAuthManagementRevokeWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementStripWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementTrustOAuthClientWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementUnlimitWorkflow' => 'PhabricatorAuthManagementWorkflow', @@ -6925,6 +6930,7 @@ 'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod', 'PhabricatorAuthRegisterController' => 'PhabricatorAuthController', 'PhabricatorAuthRevokeTokenController' => 'PhabricatorAuthController', + 'PhabricatorAuthRevoker' => 'Phobject', 'PhabricatorAuthSSHKey' => array( 'PhabricatorAuthDAO', 'PhabricatorPolicyInterface', diff --git a/src/applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php b/src/applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php new file mode 100644 --- /dev/null +++ b/src/applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php @@ -0,0 +1,140 @@ +setName('revoke') + ->setExamples( + "**revoke** --type __type__ --from __user__\n". + "**revoke** --everything --everywhere") + ->setSynopsis( + pht( + 'Revoke credentials which may have been leaked or disclosed.')) + ->setArguments( + array( + array( + 'name' => 'from', + 'param' => 'user', + 'help' => pht( + 'Revoke credentials for the specified user.'), + ), + array( + 'name' => 'type', + 'param' => 'type', + 'help' => pht( + 'Revoke credentials of the given type.'), + ), + array( + 'name' => 'everything', + 'help' => pht('Revoke all credentials types.'), + ), + array( + 'name' => 'everywhere', + 'help' => pht('Revoke from all credential owners.'), + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + $viewer = PhabricatorUser::getOmnipotentUser(); + + $all_types = PhabricatorAuthRevoker::getAllRevokers(); + + $type = $args->getArg('type'); + $is_everything = $args->getArg('everything'); + if (!strlen($type) && !$is_everything) { + throw new PhutilArgumentUsageException( + pht( + 'Specify the credential type to revoke with "--type" or specify '. + '"--everything".')); + } else if (strlen($type) && $is_everything) { + throw new PhutilArgumentUsageException( + pht( + 'Specify the credential type to revoke with "--type" or '. + '"--everything", but not both.')); + } else if ($is_everything) { + $types = $all_types; + } else { + if (empty($all_types[$type])) { + throw new PhutilArgumentUsageException( + pht( + 'Credential type "%s" is not valid. Valid credential types '. + 'are: %s.', + $type, + implode(', ', array_keys($all_types)))); + } + $types = array($all_types[$type]); + } + + $is_everywhere = $args->getArg('everywhere'); + $from = $args->getArg('from'); + $target = null; + if (!strlen($from) && !$is_everywhere) { + throw new PhutilArgumentUsageException( + pht( + 'Specify the target to revoke credentals from with "--from" or '. + 'specify "--everywhere".')); + } else if (strlen($from) && $is_everywhere) { + throw new PhutilArgumentUsageException( + pht( + 'Specify the target to revoke credentials from with "--from" or '. + 'specify "--everywhere", but not both.')); + } else if ($is_everywhere) { + // Just carry the flag through. + } else { + $target = id(new PhabricatorObjectQuery()) + ->setViewer($viewer) + ->withNames(array($from)) + ->executeOne(); + if (!$target) { + throw new PhutilArgumentUsageException( + pht( + 'Target "%s" is not a valid target to revoke credentials from. '. + 'Usually, revoke from "@username".', + $from)); + } + } + + if ($is_everywhere) { + echo id(new PhutilConsoleBlock()) + ->addParagraph( + pht( + 'You are destroying an entire class of credentials. This may be '. + 'very disruptive to users. You should normally do this only if '. + 'you suspect there has been a widespread compromise which may '. + 'have impacted everyone.')) + ->drawConsoleString(); + + $prompt = pht('Really destroy credentials everywhere?'); + if (!phutil_console_confirm($prompt)) { + throw new PhutilArgumentUsageException( + pht('Aborted workflow.')); + } + } + + foreach ($types as $type) { + $type->setViewer($viewer); + if ($is_everywhere) { + $count = $type->revokeAllCredentials(); + } else { + $count = $type->revokeCredentialsFrom($target); + } + + echo tsprintf( + "%s\n", + pht( + 'Destroyed %s credential(s) of type "%s".', + new PhutilNumber($count), + $type->getRevokerKey())); + } + + echo tsprintf( + "%s\n", + pht('Done.')); + + return 0; + } + +} diff --git a/src/applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php b/src/applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php new file mode 100644 --- /dev/null +++ b/src/applications/auth/revoker/PhabricatorAuthConduitTokenRevoker.php @@ -0,0 +1,33 @@ +establishConnection('w'); + + queryfx( + $conn, + 'DELETE FROM %T', + $table->getTableName()); + + return $conn->getAffectedRows(); + } + + public function revokeCredentialsFrom($object) { + $table = id(new PhabricatorConduitToken()); + $conn = $table->establishConnection('w'); + + queryfx( + $conn, + 'DELETE FROM %T WHERE objectPHID = %s', + $table->getTableName(), + $object->getPHID()); + + return $conn->getAffectedRows(); + } + +} diff --git a/src/applications/auth/revoker/PhabricatorAuthRevoker.php b/src/applications/auth/revoker/PhabricatorAuthRevoker.php new file mode 100644 --- /dev/null +++ b/src/applications/auth/revoker/PhabricatorAuthRevoker.php @@ -0,0 +1,31 @@ +viewer = $viewer; + return $this; + } + + public function getViewer() { + return $this->viewer; + } + + final public function getRevokerKey() { + return $this->getPhobjectClassConstant('REVOKERKEY'); + } + + final public static function getAllRevokers() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getRevokerKey') + ->execute(); + } + +}