1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

Update bin/auth MFA commands for the new "MFA Provider" indirection layer

Summary:
Ref T13222. This updates the CLI tools and documentation for the changes in D19975.

The flags `--type` and `--all-types` retain their current meaning. In most cases, `bin/auth strip --type totp` is sufficient and you don't need to bother looking up the relevant provider PHID. The existing `bin/auth list-factors` is also unchanged.

The new `--provider` flag allows you to select configs from a particular provider in a more granular way. The new `bin/auth list-mfa-providers` provides an easy way to get PHIDs.

(In the Phacility cluster, the "Strip MFA" action just reaches into the database and deletes rows manually, so this isn't terribly important. I verified that the code should still work properly.)

Test Plan:
  - Ran `bin/auth list-mfa-providers`.
  - Stripped by user / type / provider.
  - Grepped for `list-factors` and `auth strip`.
  - Hit all (?) of the various possible error cases.

Reviewers: amckinley

Reviewed By: amckinley

Maniphest Tasks: T13222

Differential Revision: https://secure.phabricator.com/D19976
This commit is contained in:
epriestley 2019-01-15 13:34:31 -08:00
parent 0fcff78253
commit aa48373889
7 changed files with 182 additions and 46 deletions

View file

@ -2254,6 +2254,7 @@ phutil_register_library_map(array(
'PhabricatorAuthManagementCachePKCS8Workflow' => 'applications/auth/management/PhabricatorAuthManagementCachePKCS8Workflow.php', 'PhabricatorAuthManagementCachePKCS8Workflow' => 'applications/auth/management/PhabricatorAuthManagementCachePKCS8Workflow.php',
'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php', 'PhabricatorAuthManagementLDAPWorkflow' => 'applications/auth/management/PhabricatorAuthManagementLDAPWorkflow.php',
'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php', 'PhabricatorAuthManagementListFactorsWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListFactorsWorkflow.php',
'PhabricatorAuthManagementListMFAProvidersWorkflow' => 'applications/auth/management/PhabricatorAuthManagementListMFAProvidersWorkflow.php',
'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php', 'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php',
'PhabricatorAuthManagementRefreshWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php', 'PhabricatorAuthManagementRefreshWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRefreshWorkflow.php',
'PhabricatorAuthManagementRevokeWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php', 'PhabricatorAuthManagementRevokeWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRevokeWorkflow.php',
@ -7892,6 +7893,7 @@ phutil_register_library_map(array(
'PhabricatorAuthFactorConfig' => array( 'PhabricatorAuthFactorConfig' => array(
'PhabricatorAuthDAO', 'PhabricatorAuthDAO',
'PhabricatorPolicyInterface', 'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
), ),
'PhabricatorAuthFactorConfigQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorAuthFactorConfigQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorAuthFactorProvider' => array( 'PhabricatorAuthFactorProvider' => array(
@ -7948,6 +7950,7 @@ phutil_register_library_map(array(
'PhabricatorAuthManagementCachePKCS8Workflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementCachePKCS8Workflow' => 'PhabricatorAuthManagementWorkflow',
'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementLDAPWorkflow' => 'PhabricatorAuthManagementWorkflow',
'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementListFactorsWorkflow' => 'PhabricatorAuthManagementWorkflow',
'PhabricatorAuthManagementListMFAProvidersWorkflow' => 'PhabricatorAuthManagementWorkflow',
'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow',
'PhabricatorAuthManagementRefreshWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementRefreshWorkflow' => 'PhabricatorAuthManagementWorkflow',
'PhabricatorAuthManagementRevokeWorkflow' => 'PhabricatorAuthManagementWorkflow', 'PhabricatorAuthManagementRevokeWorkflow' => 'PhabricatorAuthManagementWorkflow',

View file

@ -14,9 +14,8 @@ final class PhabricatorAuthManagementListFactorsWorkflow
public function execute(PhutilArgumentParser $args) { public function execute(PhutilArgumentParser $args) {
$factors = PhabricatorAuthFactor::getAllFactors(); $factors = PhabricatorAuthFactor::getAllFactors();
$console = PhutilConsole::getConsole();
foreach ($factors as $factor) { foreach ($factors as $factor) {
$console->writeOut( echo tsprintf(
"%s\t%s\n", "%s\t%s\n",
$factor->getFactorKey(), $factor->getFactorKey(),
$factor->getFactorName()); $factor->getFactorName());

View file

@ -0,0 +1,33 @@
<?php
final class PhabricatorAuthManagementListMFAProvidersWorkflow
extends PhabricatorAuthManagementWorkflow {
protected function didConstruct() {
$this
->setName('list-mfa-providers')
->setExamples('**list-mfa-providerrs**')
->setSynopsis(
pht(
'List available multi-factor authentication providers.'))
->setArguments(array());
}
public function execute(PhutilArgumentParser $args) {
$viewer = $this->getViewer();
$providers = id(new PhabricatorAuthFactorProviderQuery())
->setViewer($viewer)
->execute();
foreach ($providers as $provider) {
echo tsprintf(
"%s\t%s\n",
$provider->getPHID(),
$provider->getDisplayName());
}
return 0;
}
}

View file

@ -24,12 +24,22 @@ final class PhabricatorAuthManagementStripWorkflow
'name' => 'type', 'name' => 'type',
'param' => 'factortype', 'param' => 'factortype',
'repeat' => true, 'repeat' => true,
'help' => pht('Strip a specific factor type.'), 'help' => pht(
'Strip a specific factor type. Use `bin/auth list-factors` for '.
'a list of factor types.'),
), ),
array( array(
'name' => 'all-types', 'name' => 'all-types',
'help' => pht('Strip all factors, regardless of type.'), 'help' => pht('Strip all factors, regardless of type.'),
), ),
array(
'name' => 'provider',
'param' => 'phid',
'repeat' => true,
'help' => pht(
'Strip factors for a specific provider. Use '.
'`bin/auth list-mfa-providers` for a list of providers.'),
),
array( array(
'name' => 'force', 'name' => 'force',
'help' => pht('Strip factors without prompting.'), 'help' => pht('Strip factors without prompting.'),
@ -42,6 +52,8 @@ final class PhabricatorAuthManagementStripWorkflow
} }
public function execute(PhutilArgumentParser $args) { public function execute(PhutilArgumentParser $args) {
$viewer = $this->getViewer();
$usernames = $args->getArg('user'); $usernames = $args->getArg('user');
$all_users = $args->getArg('all-users'); $all_users = $args->getArg('all-users');
@ -55,10 +67,8 @@ final class PhabricatorAuthManagementStripWorkflow
} else if (!$usernames && !$all_users) { } else if (!$usernames && !$all_users) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht( pht(
'Use %s to specify which user to strip factors from, or '. 'Use "--user <username>" to specify which user to strip factors '.
'%s to strip factors from all users.', 'from, or "--all-users" to strip factors from all users.'));
'--user',
'--all-users'));
} else if ($usernames) { } else if ($usernames) {
$users = id(new PhabricatorPeopleQuery()) $users = id(new PhabricatorPeopleQuery())
->setViewer($this->getViewer()) ->setViewer($this->getViewer())
@ -79,37 +89,83 @@ final class PhabricatorAuthManagementStripWorkflow
} }
$types = $args->getArg('type'); $types = $args->getArg('type');
$provider_phids = $args->getArg('provider');
$all_types = $args->getArg('all-types'); $all_types = $args->getArg('all-types');
if ($types && $all_types) { if ($types && $all_types) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht( pht(
'Specify either specific factors with --type, or all factors with '. 'Specify either specific factors with "--type", or all factors with '.
'--all-types, but not both.')); '"--all-types", but not both.'));
} else if (!$types && !$all_types) { } else if ($provider_phids && $all_types) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht( pht(
'Use --type to specify which factor to strip, or --all-types to '. 'Specify either specific factors with "--provider", or all factors '.
'strip all factors. Use `auth list-factors` to show the available '. 'with "--all-types", but not both.'));
'factor types.')); } else if (!$types && !$all_types && !$provider_phids) {
throw new PhutilArgumentUsageException(
pht(
'Use "--type <type>" or "--provider <phid>" to specify which '.
'factors to strip, or "--all-types" to strip all factors. '.
'Use `bin/auth list-factors` to show the available factor types '.
'or `bin/auth list-mfa-providers` to show available providers.'));
} }
if ($users && $types) { $type_map = PhabricatorAuthFactor::getAllFactors();
$factors = id(new PhabricatorAuthFactorConfig())->loadAllWhere(
'userPHID IN (%Ls) AND factorKey IN (%Ls)', if ($types) {
mpull($users, 'getPHID'), foreach ($types as $type) {
$types); if (!isset($type_map[$type])) {
} else if ($users) { throw new PhutilArgumentUsageException(
$factors = id(new PhabricatorAuthFactorConfig())->loadAllWhere( pht(
'userPHID IN (%Ls)', 'Factor type "%s" is unknown. Use `bin/auth list-factors` to '.
mpull($users, 'getPHID')); 'get a list of known factor types.',
} else if ($types) { $type));
$factors = id(new PhabricatorAuthFactorConfig())->loadAllWhere( }
'factorKey IN (%Ls)', }
$types);
} else {
$factors = id(new PhabricatorAuthFactorConfig())->loadAll();
} }
$provider_query = id(new PhabricatorAuthFactorProviderQuery())
->setViewer($viewer);
if ($provider_phids) {
$provider_query->withPHIDs($provider_phids);
}
if ($types) {
$provider_query->withProviderFactorKeys($types);
}
$providers = $provider_query->execute();
$providers = mpull($providers, null, 'getPHID');
if ($provider_phids) {
foreach ($provider_phids as $provider_phid) {
if (!isset($providers[$provider_phid])) {
throw new PhutilArgumentUsageException(
pht(
'No provider with PHID "%s" exists. '.
'Use `bin/auth list-mfa-providers` to list providers.',
$provider_phid));
}
}
} else {
if (!$providers) {
throw new PhutilArgumentUsageException(
pht(
'There are no configured multi-factor providers.'));
}
}
$factor_query = id(new PhabricatorAuthFactorConfigQuery())
->setViewer($viewer)
->withFactorProviderPHIDs(array_keys($providers));
if ($users) {
$factor_query->withUserPHIDs(mpull($users, 'getPHID'));
}
$factors = $factor_query->execute();
if (!$factors) { if (!$factors) {
throw new PhutilArgumentUsageException( throw new PhutilArgumentUsageException(
pht('There are no matching factors to strip.')); pht('There are no matching factors to strip.'));
@ -125,14 +181,13 @@ final class PhabricatorAuthManagementStripWorkflow
$console->writeOut("%s\n\n", pht('These auth factors will be stripped:')); $console->writeOut("%s\n\n", pht('These auth factors will be stripped:'));
foreach ($factors as $factor) { foreach ($factors as $factor) {
$impl = $factor->getImplementation(); $provider = $factor->getFactorProvider();
$console->writeOut(
echo tsprintf(
" %s\t%s\t%s\n", " %s\t%s\t%s\n",
$handles[$factor->getUserPHID()]->getName(), $handles[$factor->getUserPHID()]->getName(),
$factor->getFactorKey(), $provider->getProviderFactorKey(),
($impl $provider->getDisplayName());
? $impl->getFactorName()
: '?'));
} }
$is_dry_run = $args->getArg('dry-run'); $is_dry_run = $args->getArg('dry-run');
@ -154,17 +209,9 @@ final class PhabricatorAuthManagementStripWorkflow
$console->writeOut("%s\n", pht('Stripping authentication factors...')); $console->writeOut("%s\n", pht('Stripping authentication factors...'));
$engine = new PhabricatorDestructionEngine();
foreach ($factors as $factor) { foreach ($factors as $factor) {
$user = id(new PhabricatorPeopleQuery()) $engine->destroyObject($factor);
->setViewer($this->getViewer())
->withPHIDs(array($factor->getUserPHID()))
->executeOne();
$factor->delete();
if ($user) {
$user->updateMultiFactorEnrollment();
}
} }
$console->writeOut("%s\n", pht('Done.')); $console->writeOut("%s\n", pht('Done.'));

View file

@ -6,6 +6,7 @@ final class PhabricatorAuthFactorConfigQuery
private $ids; private $ids;
private $phids; private $phids;
private $userPHIDs; private $userPHIDs;
private $factorProviderPHIDs;
public function withIDs(array $ids) { public function withIDs(array $ids) {
$this->ids = $ids; $this->ids = $ids;
@ -22,6 +23,11 @@ final class PhabricatorAuthFactorConfigQuery
return $this; return $this;
} }
public function withFactorProviderPHIDs(array $provider_phids) {
$this->factorProviderPHIDs = $provider_phids;
return $this;
}
public function newResultObject() { public function newResultObject() {
return new PhabricatorAuthFactorConfig(); return new PhabricatorAuthFactorConfig();
} }
@ -54,6 +60,13 @@ final class PhabricatorAuthFactorConfigQuery
$this->userPHIDs); $this->userPHIDs);
} }
if ($this->factorProviderPHIDs !== null) {
$where[] = qsprintf(
$conn,
'factorProviderPHID IN (%Ls)',
$this->factorProviderPHIDs);
}
return $where; return $where;
} }

View file

@ -1,8 +1,11 @@
<?php <?php
final class PhabricatorAuthFactorConfig final class PhabricatorAuthFactorConfig
extends PhabricatorAuthDAO extends PhabricatorAuthDAO
implements PhabricatorPolicyInterface { implements
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface {
protected $userPHID; protected $userPHID;
protected $factorProviderPHID; protected $factorProviderPHID;
@ -77,4 +80,23 @@ final class PhabricatorAuthFactorConfig
return false; return false;
} }
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$user = id(new PhabricatorPeopleQuery())
->setViewer($engine->getViewer())
->withPHIDs(array($this->getUserPHID()))
->executeOne();
$this->delete();
if ($user) {
$user->updateMultiFactorEnrollment();
}
}
} }

View file

@ -126,9 +126,28 @@ You can run `bin/auth help strip` for more detail and all available flags and
arguments. arguments.
This command can selectively strip types of factors. You can use This command can selectively strip types of factors. You can use
`bin/auth list-factors` for a list of available factor types. `bin/auth list-factors` to get a list of available factor types.
```lang=console ```lang=console
# Show supported factor types. # Show supported factor types.
phabricator/ $ ./bin/auth list-factors phabricator/ $ ./bin/auth list-factors
``` ```
Once you've identified the factor types you want to strip, you can strip them
using the `--type` flag to specify one or more factor types:
```lang=console
# Strip all SMS and TOTP factors for a user.
phabricator/ $ ./bin/auth strip --user <username> --type sms --type totp
```
The `bin/auth strip` command can also selectively strip factors for certain
providers. This is more granular than stripping all factors of a given type.
You can use `bin/auth list-mfa-providers` to get a list of providers.
Once you have a provider PHID, use `--provider` to select factors to strip:
```lang=console
# Strip all factors for a particular provider.
phabricator/ $ ./bin/auth strip --user <username> --provider <providerPHID>
```