mirror of
https://we.phorge.it/source/phorge.git
synced 2025-03-28 04:00:17 +01:00
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
This commit is contained in:
parent
1460f2b85c
commit
8ce25838f5
4 changed files with 210 additions and 0 deletions
|
@ -1906,6 +1906,7 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'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 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthAuthFactorPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthAuthProviderPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorAuthConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'PhabricatorAuthConduitTokenRevoker' => 'PhabricatorAuthRevoker',
|
||||
'PhabricatorAuthConfirmLinkController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthController' => 'PhabricatorController',
|
||||
'PhabricatorAuthDAO' => 'PhabricatorLiskDAO',
|
||||
|
@ -6896,6 +6900,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
||||
'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
||||
'PhabricatorAuthManagementRefreshWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
||||
'PhabricatorAuthManagementRevokeWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
||||
'PhabricatorAuthManagementStripWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
||||
'PhabricatorAuthManagementTrustOAuthClientWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
||||
'PhabricatorAuthManagementUnlimitWorkflow' => 'PhabricatorAuthManagementWorkflow',
|
||||
|
@ -6925,6 +6930,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAuthQueryPublicKeysConduitAPIMethod' => 'PhabricatorAuthConduitAPIMethod',
|
||||
'PhabricatorAuthRegisterController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthRevokeTokenController' => 'PhabricatorAuthController',
|
||||
'PhabricatorAuthRevoker' => 'Phobject',
|
||||
'PhabricatorAuthSSHKey' => array(
|
||||
'PhabricatorAuthDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
|
|
|
@ -0,0 +1,140 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthManagementRevokeWorkflow
|
||||
extends PhabricatorAuthManagementWorkflow {
|
||||
|
||||
protected function didConstruct() {
|
||||
$this
|
||||
->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;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorAuthConduitTokenRevoker
|
||||
extends PhabricatorAuthRevoker {
|
||||
|
||||
const REVOKERKEY = 'conduit';
|
||||
|
||||
public function revokeAllCredentials() {
|
||||
$table = id(new PhabricatorConduitToken());
|
||||
$conn = $table->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();
|
||||
}
|
||||
|
||||
}
|
31
src/applications/auth/revoker/PhabricatorAuthRevoker.php
Normal file
31
src/applications/auth/revoker/PhabricatorAuthRevoker.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorAuthRevoker
|
||||
extends Phobject {
|
||||
|
||||
private $viewer;
|
||||
|
||||
abstract public function revokeAlLCredentials();
|
||||
abstract public function revokeCredentialsFrom($object);
|
||||
|
||||
public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->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();
|
||||
}
|
||||
|
||||
}
|
Loading…
Add table
Reference in a new issue