/var/www/vhosts/ihelp.ro/httpdocs/vendor/composer/composer/src/Composer/Util
NameSizeModeActions
Http/-0755rm
AuthHelper.php146420644editdlrm
Bitbucket.php99370644editdlrm
ComposerMirror.php24000644editdlrm
ConfigValidator.php92640644editdlrm
ErrorHandler.php24940644editdlrm
Filesystem.php287120644editdlrm
Git.php227350644editdlrm
GitHub.php85520644editdlrm
GitLab.php122860644editdlrm
Hg.php32630644editdlrm
HttpDownloader.php183980644editdlrm
IniHelper.php16460644editdlrm
Loop.php36110644editdlrm
MetadataMinifier.php6500644editdlrm
NoProxyPattern.php108710644editdlrm
PackageInfo.php10920644editdlrm
PackageSorter.php41830644editdlrm
Perforce.php188160644editdlrm
Platform.php87840644editdlrm
ProcessExecutor.php142000644editdlrm
RemoteFilesystem.php274710644editdlrm
Silencer.php20990644editdlrm
StreamContextFactory.php95460644editdlrm
Svn.php98900644editdlrm
SyncHelper.php27760644editdlrm
Tar.php17720644editdlrm
TlsHelper.php70470644editdlrm
Url.php53790644editdlrm
Zip.php33930644editdlrm
Edit: /var/www/vhosts/ihelp.ro/httpdocs/vendor/composer/composer/src/Composer/Util/AuthHelper.php (14642B)
* Jordi Boggiano * * For the full copyright and license information, please view the LICENSE * file that was distributed with this source code. */ namespace Composer\Util; use Composer\Config; use Composer\IO\IOInterface; use Composer\Downloader\TransportException; use Composer\Pcre\Preg; /** * @author Jordi Boggiano */ class AuthHelper { /** @var IOInterface */ protected $io; /** @var Config */ protected $config; /** @var array Map of origins to message displayed */ private $displayedOriginAuthentications = []; /** @var array Map of URLs and whether they already retried with authentication from Bitbucket */ private $bitbucketRetry = []; public function __construct(IOInterface $io, Config $config) { $this->io = $io; $this->config = $config; } /** * @param 'prompt'|bool $storeAuth */ public function storeAuth(string $origin, $storeAuth): void { $store = false; $configSource = $this->config->getAuthConfigSource(); if ($storeAuth === true) { $store = $configSource; } elseif ($storeAuth === 'prompt') { $answer = $this->io->askAndValidate( 'Do you want to store credentials for '.$origin.' in '.$configSource->getName().' ? [Yn] ', static function ($value): string { $input = strtolower(substr(trim($value), 0, 1)); if (in_array($input, ['y','n'])) { return $input; } throw new \RuntimeException('Please answer (y)es or (n)o'); }, null, 'y' ); if ($answer === 'y') { $store = $configSource; } } if ($store) { $store->addConfigSetting( 'http-basic.'.$origin, $this->io->getAuthentication($origin) ); } } /** * @param int $statusCode HTTP status code that triggered this call * @param string|null $reason a message/description explaining why this was called * @param string[] $headers * @param int $retryCount the amount of retries already done on this URL * @return array containing retry (bool) and storeAuth (string|bool) keys, if retry is true the request should be * retried, if storeAuth is true then on a successful retry the authentication should be persisted to auth.json * @phpstan-return array{retry: bool, storeAuth: 'prompt'|bool} */ public function promptAuthIfNeeded(string $url, string $origin, int $statusCode, ?string $reason = null, array $headers = [], int $retryCount = 0): array { $storeAuth = false; if (in_array($origin, $this->config->get('github-domains'), true)) { $gitHubUtil = new GitHub($this->io, $this->config, null); $message = "\n"; $rateLimited = $gitHubUtil->isRateLimited($headers); $requiresSso = $gitHubUtil->requiresSso($headers); if ($requiresSso) { $ssoUrl = $gitHubUtil->getSsoUrl($headers); $message = 'GitHub API token requires SSO authorization. Authorize this token at ' . $ssoUrl . "\n"; $this->io->writeError($message); if (!$this->io->isInteractive()) { throw new TransportException('Could not authenticate against ' . $origin, 403); } $this->io->ask('After authorizing your token, confirm that you would like to retry the request'); return ['retry' => true, 'storeAuth' => $storeAuth]; } if ($rateLimited) { $rateLimit = $gitHubUtil->getRateLimit($headers); if ($this->io->hasAuthentication($origin)) { $message = 'Review your configured GitHub OAuth token or enter a new one to go over the API rate limit.'; } else { $message = 'Create a GitHub OAuth token to go over the API rate limit.'; } $message = sprintf( 'GitHub API limit (%d calls/hr) is exhausted, could not fetch '.$url.'. '.$message.' You can also wait until %s for the rate limit to reset.', $rateLimit['limit'], $rateLimit['reset'] )."\n"; } else { $message .= 'Could not fetch '.$url.', please '; if ($this->io->hasAuthentication($origin)) { $message .= 'review your configured GitHub OAuth token or enter a new one to access private repos'; } else { $message .= 'create a GitHub OAuth token to access private repos'; } } if (!$gitHubUtil->authorizeOAuth($origin) && (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($origin, $message)) ) { throw new TransportException('Could not authenticate against '.$origin, 401); } } elseif (in_array($origin, $this->config->get('gitlab-domains'), true)) { $message = "\n".'Could not fetch '.$url.', enter your ' . $origin . ' credentials ' .($statusCode === 401 ? 'to access private repos' : 'to go over the API rate limit'); $gitLabUtil = new GitLab($this->io, $this->config, null); $auth = null; if ($this->io->hasAuthentication($origin)) { $auth = $this->io->getAuthentication($origin); if (in_array($auth['password'], ['gitlab-ci-token', 'private-token', 'oauth2'], true)) { throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode); } } if (!$gitLabUtil->authorizeOAuth($origin) && (!$this->io->isInteractive() || !$gitLabUtil->authorizeOAuthInteractively(parse_url($url, PHP_URL_SCHEME), $origin, $message)) ) { throw new TransportException('Could not authenticate against '.$origin, 401); } if ($auth !== null && $this->io->hasAuthentication($origin)) { if ($auth === $this->io->getAuthentication($origin)) { throw new TransportException("Invalid credentials for '" . $url . "', aborting.", $statusCode); } } } elseif ($origin === 'bitbucket.org' || $origin === 'api.bitbucket.org') { $askForOAuthToken = true; $origin = 'bitbucket.org'; if ($this->io->hasAuthentication($origin)) { $auth = $this->io->getAuthentication($origin); if ($auth['username'] !== 'x-token-auth') { $bitbucketUtil = new Bitbucket($this->io, $this->config); $accessToken = $bitbucketUtil->requestToken($origin, $auth['username'], $auth['password']); if (!empty($accessToken)) { $this->io->setAuthentication($origin, 'x-token-auth', $accessToken); $askForOAuthToken = false; } } elseif (!isset($this->bitbucketRetry[$url])) { // when multiple requests fire at the same time, they will all fail and the first one resets the token to be correct above but then the others // reach the code path and without this fallback they would end up throwing below // see https://github.com/composer/composer/pull/11464 for more details $askForOAuthToken = false; $this->bitbucketRetry[$url] = true; } else { throw new TransportException('Could not authenticate against ' . $origin, 401); } } if ($askForOAuthToken) { $message = "\n".'Could not fetch ' . $url . ', please create a bitbucket OAuth token to ' . (($statusCode === 401 || $statusCode === 403) ? 'access private repos' : 'go over the API rate limit'); $bitBucketUtil = new Bitbucket($this->io, $this->config); if (!$bitBucketUtil->authorizeOAuth($origin) && (!$this->io->isInteractive() || !$bitBucketUtil->authorizeOAuthInteractively($origin, $message)) ) { throw new TransportException('Could not authenticate against ' . $origin, 401); } } } else { // 404s are only handled for github if ($statusCode === 404) { return ['retry' => false, 'storeAuth' => false]; } // fail if the console is not interactive if (!$this->io->isInteractive()) { if ($statusCode === 401) { $message = "The '" . $url . "' URL required authentication (HTTP 401).\nYou must be using the interactive console to authenticate"; } elseif ($statusCode === 403) { $message = "The '" . $url . "' URL could not be accessed (HTTP 403): " . $reason; } else { $message = "Unknown error code '" . $statusCode . "', reason: " . $reason; } throw new TransportException($message, $statusCode); } // fail if we already have auth if ($this->io->hasAuthentication($origin)) { // if two or more requests are started together for the same host, and the first // received authentication already, we let the others retry before failing them if ($retryCount === 0) { return ['retry' => true, 'storeAuth' => false]; } throw new TransportException("Invalid credentials (HTTP $statusCode) for '$url', aborting.", $statusCode); } $this->io->writeError(' Authentication required ('.$origin.'):'); $username = $this->io->ask(' Username: '); $password = $this->io->askAndHideAnswer(' Password: '); $this->io->setAuthentication($origin, $username, $password); $storeAuth = $this->config->get('store-auths'); } return ['retry' => true, 'storeAuth' => $storeAuth]; } /** * @param string[] $headers * * @return string[] updated headers array */ public function addAuthenticationHeader(array $headers, string $origin, string $url): array { if ($this->io->hasAuthentication($origin)) { $authenticationDisplayMessage = null; $auth = $this->io->getAuthentication($origin); if ($auth['password'] === 'bearer') { $headers[] = 'Authorization: Bearer '.$auth['username']; } elseif ('github.com' === $origin && 'x-oauth-basic' === $auth['password']) { // only add the access_token if it is actually a github API URL if (Preg::isMatch('{^https?://api\.github\.com/}', $url)) { $headers[] = 'Authorization: token '.$auth['username']; $authenticationDisplayMessage = 'Using GitHub token authentication'; } } elseif ( in_array($origin, $this->config->get('gitlab-domains'), true) && in_array($auth['password'], ['oauth2', 'private-token', 'gitlab-ci-token'], true) ) { if ($auth['password'] === 'oauth2') { $headers[] = 'Authorization: Bearer '.$auth['username']; $authenticationDisplayMessage = 'Using GitLab OAuth token authentication'; } else { $headers[] = 'PRIVATE-TOKEN: '.$auth['username']; $authenticationDisplayMessage = 'Using GitLab private token authentication'; } } elseif ( 'bitbucket.org' === $origin && $url !== Bitbucket::OAUTH2_ACCESS_TOKEN_URL && 'x-token-auth' === $auth['username'] ) { if (!$this->isPublicBitBucketDownload($url)) { $headers[] = 'Authorization: Bearer ' . $auth['password']; $authenticationDisplayMessage = 'Using Bitbucket OAuth token authentication'; } } else { $authStr = base64_encode($auth['username'] . ':' . $auth['password']); $headers[] = 'Authorization: Basic '.$authStr; $authenticationDisplayMessage = 'Using HTTP basic authentication with username "' . $auth['username'] . '"'; } if ($authenticationDisplayMessage && (!isset($this->displayedOriginAuthentications[$origin]) || $this->displayedOriginAuthentications[$origin] !== $authenticationDisplayMessage)) { $this->io->writeError($authenticationDisplayMessage, true, IOInterface::DEBUG); $this->displayedOriginAuthentications[$origin] = $authenticationDisplayMessage; } } elseif (in_array($origin, ['api.bitbucket.org', 'api.github.com'], true)) { return $this->addAuthenticationHeader($headers, str_replace('api.', '', $origin), $url); } return $headers; } /** * @link https://github.com/composer/composer/issues/5584 * * @param string $urlToBitBucketFile URL to a file at bitbucket.org. * * @return bool Whether the given URL is a public BitBucket download which requires no authentication. */ public function isPublicBitBucketDownload(string $urlToBitBucketFile): bool { $domain = parse_url($urlToBitBucketFile, PHP_URL_HOST); if (strpos($domain, 'bitbucket.org') === false) { // Bitbucket downloads are hosted on amazonaws. // We do not need to authenticate there at all return true; } $path = parse_url($urlToBitBucketFile, PHP_URL_PATH); // Path for a public download follows this pattern /{user}/{repo}/downloads/{whatever} // {@link https://blog.bitbucket.org/2009/04/12/new-feature-downloads/} $pathParts = explode('/', $path); return count($pathParts) >= 4 && $pathParts[3] === 'downloads'; } }