diff --git a/bin/auth b/bin/auth new file mode 120000 index 0000000000..01e2796608 --- /dev/null +++ b/bin/auth @@ -0,0 +1 @@ +../scripts/setup/manage_auth.php \ No newline at end of file diff --git a/scripts/setup/manage_auth.php b/scripts/setup/manage_auth.php new file mode 100755 index 0000000000..acace43163 --- /dev/null +++ b/scripts/setup/manage_auth.php @@ -0,0 +1,22 @@ +#!/usr/bin/env php +setTagline('manage authentication'); +$args->setSynopsis(<<parseStandardArguments(); + +$workflows = array( + new PhabricatorAuthManagementListWorkflow(), + new PhutilHelpArgumentWorkflow(), +); + +$args->parseWorkflows($workflows); diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 6864cdb5c6..3211fffd0d 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -823,6 +823,8 @@ phutil_register_library_map(array( 'PhabricatorAuthLinkController' => 'applications/auth/controller/PhabricatorAuthLinkController.php', 'PhabricatorAuthListController' => 'applications/auth/controller/config/PhabricatorAuthListController.php', 'PhabricatorAuthLoginController' => 'applications/auth/controller/PhabricatorAuthLoginController.php', + 'PhabricatorAuthManagementRecoverWorkflow' => 'applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php', + 'PhabricatorAuthManagementWorkflow' => 'applications/auth/management/PhabricatorAuthManagementWorkflow.php', 'PhabricatorAuthNewController' => 'applications/auth/controller/config/PhabricatorAuthNewController.php', 'PhabricatorAuthProvider' => 'applications/auth/provider/PhabricatorAuthProvider.php', 'PhabricatorAuthProviderConfig' => 'applications/auth/storage/PhabricatorAuthProviderConfig.php', @@ -2704,6 +2706,8 @@ phutil_register_library_map(array( 1 => 'PhabricatorApplicationSearchResultsControllerInterface', ), 'PhabricatorAuthLoginController' => 'PhabricatorAuthController', + 'PhabricatorAuthManagementRecoverWorkflow' => 'PhabricatorAuthManagementWorkflow', + 'PhabricatorAuthManagementWorkflow' => 'PhutilArgumentWorkflow', 'PhabricatorAuthNewController' => 'PhabricatorAuthProviderConfigController', 'PhabricatorAuthProviderConfig' => array( diff --git a/src/applications/auth/controller/PhabricatorAuthStartController.php b/src/applications/auth/controller/PhabricatorAuthStartController.php index 0fa6222b36..e755d42b9d 100644 --- a/src/applications/auth/controller/PhabricatorAuthStartController.php +++ b/src/applications/auth/controller/PhabricatorAuthStartController.php @@ -47,7 +47,10 @@ final class PhabricatorAuthStartController return $this->renderError( pht( "This Phabricator install is not configured with any enabled ". - "authentication providers which can be used to log in.")); + "authentication providers which can be used to log in. If you ". + "have accidentally locked yourself out by disabling all providers, ". + "you can use `phabricator/bin/auth recover ` to ". + "recover access to an administrative account.")); } $next_uri = $request->getStr('next'); diff --git a/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php b/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php new file mode 100644 index 0000000000..99e7fa0178 --- /dev/null +++ b/src/applications/auth/management/PhabricatorAuthManagementRecoverWorkflow.php @@ -0,0 +1,89 @@ +setName('recover') + ->setExamples('**recover** __username__') + ->setSynopsis( + 'Recover access to an administrative account if you have locked '. + 'yourself out of Phabricator.') + ->setArguments( + array( + 'username' => array( + 'name' => 'username', + 'wildcard' => true, + ), + )); + } + + public function execute(PhutilArgumentParser $args) { + + $can_recover = id(new PhabricatorPeopleQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withIsAdmin(true) + ->execute(); + if (!$can_recover) { + throw new PhutilArgumentUsageException( + pht( + 'This Phabricator installation has no recoverable administrator '. + 'accounts. You can use `bin/accountadmin` to create a new '. + 'administrator account or make an existing user an administrator.')); + } + $can_recover = mpull($can_recover, 'getUsername'); + sort($can_recover); + $can_recover = implode(', ', $can_recover); + + $usernames = $args->getArg('username'); + if (!$usernames) { + throw new PhutilArgumentUsageException( + pht('You must specify the username of the account to recover.')); + } else if (count($usernames) > 1) { + throw new PhutilArgumentUsageException( + pht('You can only recover the username for one account.')); + } + + $username = head($usernames); + + $user = id(new PhabricatorPeopleQuery()) + ->setViewer(PhabricatorUser::getOmnipotentUser()) + ->withUsernames(array($username)) + ->executeOne(); + + if (!$user) { + throw new PhutilArgumentUsageException( + pht( + 'No such user "%s". Recoverable administrator accounts are: %s.', + $username, + $can_recover)); + } + + if (!$user->getIsAdmin()) { + throw new PhutilArgumentUsageException( + pht( + 'You can only recover administrator accounts, but %s is not an '. + 'administrator. Recoverable administrator accounts are: %s.', + $username, + $can_recover)); + } + + $console = PhutilConsole::getConsole(); + $console->writeOut( + pht( + 'Use this link to recover access to the "%s" account:', + $username)); + $console->writeOut("\n\n"); + $console->writeOut(" %s", $user->getEmailLoginURI()); + $console->writeOut("\n\n"); + $console->writeOut( + pht( + 'After logging in, you can use the "Auth" application to add or '. + 'restore authentication providers and allow normal logins to '. + 'succeed.')."\n"); + + return 0; + } + +} diff --git a/src/applications/auth/management/PhabricatorAuthManagementWorkflow.php b/src/applications/auth/management/PhabricatorAuthManagementWorkflow.php new file mode 100644 index 0000000000..60643c1e4e --- /dev/null +++ b/src/applications/auth/management/PhabricatorAuthManagementWorkflow.php @@ -0,0 +1,10 @@ +