From b1f3f8b8fac3e6444b992762971c4287f97d2b46 Mon Sep 17 00:00:00 2001 From: PrinsFrank <25006490+PrinsFrank@users.noreply.github.com> Date: Fri, 16 Dec 2022 14:31:28 +0100 Subject: [PATCH] Prompt users in interactive mode for where to store the credentials if a local auth config file exists (#11188) --- src/Composer/Config.php | 12 ++++++++++++ src/Composer/Factory.php | 2 +- src/Composer/Util/Bitbucket.php | 15 +++++++++++---- src/Composer/Util/GitHub.php | 13 ++++++++++--- src/Composer/Util/GitLab.php | 13 ++++++++++--- 5 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/Composer/Config.php b/src/Composer/Config.php index 38e87e0a5..4555203f1 100644 --- a/src/Composer/Config.php +++ b/src/Composer/Config.php @@ -102,6 +102,8 @@ class Config private $configSource; /** @var ConfigSourceInterface */ private $authConfigSource; + /** @var ConfigSourceInterface|null */ + private $localAuthConfigSource = null; /** @var bool */ private $useEnvironment; /** @var array */ @@ -153,6 +155,16 @@ class Config return $this->authConfigSource; } + public function setLocalAuthConfigSource(ConfigSourceInterface $source): void + { + $this->localAuthConfigSource = $source; + } + + public function getLocalAuthConfigSource(): ?ConfigSourceInterface + { + return $this->localAuthConfigSource; + } + /** * Merges new config values with the existing ones (overriding) * diff --git a/src/Composer/Factory.php b/src/Composer/Factory.php index 40a08da1c..f82a9c390 100644 --- a/src/Composer/Factory.php +++ b/src/Composer/Factory.php @@ -336,7 +336,7 @@ class Factory $io->writeError('Loading config file ' . $localAuthFile->getPath(), true, IOInterface::DEBUG); self::validateJsonSchema($io, $localAuthFile, JsonFile::AUTH_SCHEMA); $config->merge(['config' => $localAuthFile->read()], $localAuthFile->getPath()); - $config->setAuthConfigSource(new JsonConfigSource($localAuthFile, true)); + $config->setLocalAuthConfigSource(new JsonConfigSource($localAuthFile, true)); } } diff --git a/src/Composer/Util/Bitbucket.php b/src/Composer/Util/Bitbucket.php index f5f2cc9c9..1c9eeb65d 100644 --- a/src/Composer/Util/Bitbucket.php +++ b/src/Composer/Util/Bitbucket.php @@ -141,11 +141,17 @@ class Bitbucket $this->io->writeError($message); } + $localAuthConfig = $this->config->getLocalAuthConfigSource(); $url = 'https://support.atlassian.com/bitbucket-cloud/docs/use-oauth-on-bitbucket-cloud/'; $this->io->writeError(sprintf('Follow the instructions on %s', $url)); - $this->io->writeError(sprintf('to create a consumer. It will be stored in "%s" for future use by Composer.', $this->config->getAuthConfigSource()->getName())); + $this->io->writeError(sprintf('to create a consumer. It will be stored in "%s" for future use by Composer.', ($localAuthConfig !== null ? $localAuthConfig->getName() . ' OR ' : '') . $this->config->getAuthConfigSource()->getName())); $this->io->writeError('Ensure you enter a "Callback URL" (http://example.com is fine) or it will not be possible to create an Access Token (this callback url will not be used by composer)'); + $storeInLocalAuthConfig = false; + if ($localAuthConfig !== null) { + $storeInLocalAuthConfig = $this->io->askConfirmation('A local auth config source was found, do you want to store the token there?', true); + } + $consumerKey = trim((string) $this->io->askAndHideAnswer('Consumer Key (hidden): ')); if (!$consumerKey) { @@ -171,7 +177,8 @@ class Bitbucket } // store value in user config - $this->storeInAuthConfig($originUrl, $consumerKey, $consumerSecret); + $authConfigSource = $storeInLocalAuthConfig && $localAuthConfig !== null ? $localAuthConfig : $this->config->getAuthConfigSource(); + $this->storeInAuthConfig($authConfigSource, $originUrl, $consumerKey, $consumerSecret); // Remove conflicting basic auth credentials (if available) $this->config->getAuthConfigSource()->removeConfigSetting('http-basic.' . $originUrl); @@ -195,7 +202,7 @@ class Bitbucket return ''; } - $this->storeInAuthConfig($originUrl, $consumerKey, $consumerSecret); + $this->storeInAuthConfig($this->config->getLocalAuthConfigSource() ?? $this->config->getAuthConfigSource(), $originUrl, $consumerKey, $consumerSecret); if (!isset($this->token['access_token'])) { throw new \LogicException('Failed to initialize token above'); @@ -207,7 +214,7 @@ class Bitbucket /** * Store the new/updated credentials to the configuration */ - private function storeInAuthConfig(string $originUrl, string $consumerKey, string $consumerSecret): void + private function storeInAuthConfig(Config\ConfigSourceInterface $authConfigSource, string $originUrl, string $consumerKey, string $consumerSecret): void { $this->config->getConfigSource()->removeConfigSetting('bitbucket-oauth.'.$originUrl); diff --git a/src/Composer/Util/GitHub.php b/src/Composer/Util/GitHub.php index c1f693d55..2ac30e3b8 100644 --- a/src/Composer/Util/GitHub.php +++ b/src/Composer/Util/GitHub.php @@ -95,12 +95,18 @@ class GitHub $this->io->writeError(sprintf('When working with _public_ GitHub repositories only, head to %s to retrieve a token.', $url)); $this->io->writeError('This token will have read-only permission for public information only.'); + $localAuthConfig = $this->config->getLocalAuthConfigSource(); $url = 'https://'.$originUrl.'/settings/tokens/new?scopes=repo&description=' . str_replace('%20', '+', rawurlencode($note)); $this->io->writeError(sprintf('When you need to access _private_ GitHub repositories as well, go to %s', $url)); $this->io->writeError('Note that such tokens have broad read/write permissions on your behalf, even if not needed by Composer.'); - $this->io->writeError(sprintf('Tokens will be stored in plain text in "%s" for future use by Composer.', $this->config->getAuthConfigSource()->getName())); + $this->io->writeError(sprintf('Tokens will be stored in plain text in "%s" for future use by Composer.', ($localAuthConfig !== null ? $localAuthConfig->getName() . ' OR ' : '') . $this->config->getAuthConfigSource()->getName())); $this->io->writeError('For additional information, check https://getcomposer.org/doc/articles/authentication-for-private-packages.md#github-oauth'); + $storeInLocalAuthConfig = false; + if ($localAuthConfig !== null) { + $storeInLocalAuthConfig = $this->io->askConfirmation('A local auth config source was found, do you want to store the token there?', true); + } + $token = trim((string) $this->io->askAndHideAnswer('Token (hidden): ')); if ($token === '') { @@ -129,9 +135,10 @@ class GitHub throw $e; } - // store value in user config + // store value in local/user config + $authConfigSource = $storeInLocalAuthConfig && $localAuthConfig !== null ? $localAuthConfig : $this->config->getAuthConfigSource(); $this->config->getConfigSource()->removeConfigSetting('github-oauth.'.$originUrl); - $this->config->getAuthConfigSource()->addConfigSetting('github-oauth.'.$originUrl, $token); + $authConfigSource->addConfigSetting('github-oauth.'.$originUrl, $token); $this->io->writeError('Token stored successfully.'); diff --git a/src/Composer/Util/GitLab.php b/src/Composer/Util/GitLab.php index d1878058f..40b0fec00 100644 --- a/src/Composer/Util/GitLab.php +++ b/src/Composer/Util/GitLab.php @@ -125,10 +125,16 @@ class GitLab $this->io->writeError($message); } - $this->io->writeError(sprintf('A token will be created and stored in "%s", your password will never be stored', $this->config->getAuthConfigSource()->getName())); + $localAuthConfig = $this->config->getLocalAuthConfigSource(); + $this->io->writeError(sprintf('A token will be created and stored in "%s", your password will never be stored', ($localAuthConfig !== null ? $localAuthConfig->getName() . ' OR ' : '') . $this->config->getAuthConfigSource()->getName())); $this->io->writeError('To revoke access to this token you can visit '.$scheme.'://'.$originUrl.'/-/profile/applications'); $this->io->writeError('Alternatively you can setup an personal access token on '.$scheme.'://'.$originUrl.'/-/profile/personal_access_tokens and store it under "gitlab-token" see https://getcomposer.org/doc/articles/authentication-for-private-packages.md#gitlab-token for more details.'); + $storeInLocalAuthConfig = false; + if ($localAuthConfig !== null) { + $storeInLocalAuthConfig = $this->io->askConfirmation('A local auth config source was found, do you want to store the token there?', true); + } + $attemptCounter = 0; while ($attemptCounter++ < 5) { @@ -160,9 +166,10 @@ class GitLab $this->io->setAuthentication($originUrl, $response['access_token'], 'oauth2'); + $authConfigSource = $storeInLocalAuthConfig && $localAuthConfig !== null ? $localAuthConfig : $this->config->getAuthConfigSource(); // store value in user config in auth file if (isset($response['expires_in'])) { - $this->config->getAuthConfigSource()->addConfigSetting( + $authConfigSource->addConfigSetting( 'gitlab-oauth.'.$originUrl, [ 'expires-at' => intval($response['created_at']) + intval($response['expires_in']), @@ -171,7 +178,7 @@ class GitLab ] ); } else { - $this->config->getAuthConfigSource()->addConfigSetting('gitlab-oauth.'.$originUrl, $response['access_token']); + $authConfigSource->addConfigSetting('gitlab-oauth.'.$originUrl, $response['access_token']); } return true;