/** * normalize url and authentication info * @param string $origin domain text * @param string $url * @param IO\IOInterface $io */ public function __construct($origin, $url, IO\IOInterface $io) { $this->origin = $origin; $this->importURL($url); if ($this->username && $this->password) { $io->setAuthentication($origin, $this->username, $this->password); } elseif ($io->hasAuthentication($origin)) { $auth = $io->getAuthentication($origin); $this->username = $auth['username']; $this->password = $auth['password']; } }
/** * normalize url and authentication info * @param string $origin domain text * @param string $url * @param IO/IOInterface $io */ public function __construct($origin, $url, IO\IOInterface $io) { // normalize github origin if (substr($origin, -10) === 'github.com') { $origin = 'github.com'; $this->special = 'github'; } $this->origin = $origin; $this->importURL($url); if ($this->username && $this->password) { $io->setAuthentication($origin, $this->username, $this->password); } elseif ($io->hasAuthentication($origin)) { $auth = $io->getAuthentication($origin); $this->username = $auth['username']; $this->password = $auth['password']; } }
public function runCommand($commandCallable, $url, $cwd, $initialClone = false) { if (preg_match('{^(http|git):}i', $url) && $this->config->get('secure-http')) { throw new TransportException("Your configuration does not allow connection to {$url}. See https://getcomposer.org/doc/06-config.md#secure-http for details."); } if ($initialClone) { $origCwd = $cwd; $cwd = null; } if (preg_match('{^ssh://[^@]+@[^:]+:[^0-9]+}', $url)) { throw new \InvalidArgumentException('The source URL ' . $url . ' is invalid, ssh URLs should have a port number after ":".' . "\n" . 'Use ssh://git@example.com:22/path or just git@example.com:path if you do not want to provide a password or custom port.'); } if (!$initialClone) { // capture username/password from URL if there is one $this->process->execute('git remote -v', $output, $cwd); if (preg_match('{^(?:composer|origin)\\s+https?://(.+):(.+)@([^/]+)}im', $output, $match)) { $this->io->setAuthentication($match[3], urldecode($match[1]), urldecode($match[2])); } } $protocols = $this->config->get('github-protocols'); if (!is_array($protocols)) { throw new \RuntimeException('Config value "github-protocols" must be an array, got ' . gettype($protocols)); } // public github, autoswitch protocols if (preg_match('{^(?:https?|git)://' . self::getGitHubDomainsRegex($this->config) . '/(.*)}', $url, $match)) { $messages = array(); foreach ($protocols as $protocol) { if ('ssh' === $protocol) { $protoUrl = "git@" . $match[1] . ":" . $match[2]; } else { $protoUrl = $protocol . "://" . $match[1] . "/" . $match[2]; } if (0 === $this->process->execute(call_user_func($commandCallable, $protoUrl), $ignoredOutput, $cwd)) { return; } $messages[] = '- ' . $protoUrl . "\n" . preg_replace('#^#m', ' ', $this->process->getErrorOutput()); if ($initialClone) { $this->filesystem->removeDirectory($origCwd); } } // failed to checkout, first check git accessibility $this->throwException('Failed to clone ' . self::sanitizeUrl($url) . ' via ' . implode(', ', $protocols) . ' protocols, aborting.' . "\n\n" . implode("\n", $messages), $url); } // if we have a private github url and the ssh protocol is disabled then we skip it and directly fallback to https $bypassSshForGitHub = preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\\.git$}i', $url) && !in_array('ssh', $protocols, true); $command = call_user_func($commandCallable, $url); $auth = null; if ($bypassSshForGitHub || 0 !== $this->process->execute($command, $ignoredOutput, $cwd)) { // private github repository without git access, try https with auth if (preg_match('{^git@' . self::getGitHubDomainsRegex($this->config) . ':(.+?)\\.git$}i', $url, $match)) { if (!$this->io->hasAuthentication($match[1])) { $gitHubUtil = new GitHub($this->io, $this->config, $this->process); $message = 'Cloning failed using an ssh key for authentication, enter your GitHub credentials to access private repos'; if (!$gitHubUtil->authorizeOAuth($match[1]) && $this->io->isInteractive()) { $gitHubUtil->authorizeOAuthInteractively($match[1], $message); } } if ($this->io->hasAuthentication($match[1])) { $auth = $this->io->getAuthentication($match[1]); $authUrl = 'https://' . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[1] . '/' . $match[2] . '.git'; $command = call_user_func($commandCallable, $authUrl); if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { return; } } } elseif ($this->isAuthenticationFailure($url, $match)) { // private non-github repo that failed to authenticate if (strpos($match[2], '@')) { list($authParts, $match[2]) = explode('@', $match[2], 2); } $storeAuth = false; if ($this->io->hasAuthentication($match[2])) { $auth = $this->io->getAuthentication($match[2]); } elseif ($this->io->isInteractive()) { $defaultUsername = null; if (isset($authParts) && $authParts) { if (false !== strpos($authParts, ':')) { list($defaultUsername, ) = explode(':', $authParts, 2); } else { $defaultUsername = $authParts; } } $this->io->writeError(' Authentication required (<info>' . parse_url($url, PHP_URL_HOST) . '</info>):'); $auth = array('username' => $this->io->ask(' Username: '******'password' => $this->io->askAndHideAnswer(' Password: '******'store-auths'); } if ($auth) { $authUrl = $match[1] . rawurlencode($auth['username']) . ':' . rawurlencode($auth['password']) . '@' . $match[2] . $match[3]; $command = call_user_func($commandCallable, $authUrl); if (0 === $this->process->execute($command, $ignoredOutput, $cwd)) { $this->io->setAuthentication($match[2], $auth['username'], $auth['password']); $authHelper = new AuthHelper($this->io, $this->config); $authHelper->storeAuth($match[2], $storeAuth); return; } } } if ($initialClone) { $this->filesystem->removeDirectory($origCwd); } $this->throwException('Failed to execute ' . self::sanitizeUrl($command) . "\n\n" . $this->process->getErrorOutput(), $url); } }
/** * @param $io * @param bool $useRedirector * @param $githubDomains * @param $gitlabDomains */ protected function setupAuthentication(IO\IOInterface $io, $useRedirector, array $githubDomains, array $gitlabDomains) { if (preg_match('/\\.github\\.com$/', $this->host)) { $authKey = 'github.com'; if ($useRedirector) { if ($this->host === 'api.github.com' && preg_match('%^/repos(/[^/]+/[^/]+/)zipball(.+)$%', $this->path, $_)) { $this->host = 'codeload.github.com'; $this->path = $_[1] . 'legacy.zip' . $_[2]; } } } else { $authKey = $this->host; } if (!$io->hasAuthentication($authKey)) { if ($this->user || $this->pass) { $io->setAuthentication($authKey, $this->user, $this->pass); } else { return; } } $auth = $io->getAuthentication($authKey); // is github if (in_array($authKey, $githubDomains) && 'x-oauth-basic' === $auth['password']) { $this->addParam('access_token', $auth['username']); $this->user = $this->pass = null; return; } // is gitlab if (in_array($authKey, $gitlabDomains) && 'oauth2' === $auth['password']) { $this->addHeader('authorization', 'Bearer ' . $auth['username']); $this->user = $this->pass = null; return; } // others, includes bitbucket $this->user = $auth['username']; $this->pass = $auth['password']; }
public function notifyInstalls(IOInterface $io) { foreach ($this->notifiablePackages as $repoUrl => $packages) { $repositoryName = parse_url($repoUrl, PHP_URL_HOST); if ($io->hasAuthentication($repositoryName)) { $auth = $io->getAuthentication($repositoryName); $authStr = base64_encode($auth['username'] . ':' . $auth['password']); $authHeader = 'Authorization: Basic ' . $authStr; } // non-batch API, deprecated if (strpos($repoUrl, '%package%')) { foreach ($packages as $package) { $url = str_replace('%package%', $package->getPrettyName(), $repoUrl); $params = array('version' => $package->getPrettyVersion(), 'version_normalized' => $package->getVersion()); $opts = array('http' => array('method' => 'POST', 'header' => array('Content-type: application/x-www-form-urlencoded'), 'content' => http_build_query($params, '', '&'), 'timeout' => 3)); if (isset($authHeader)) { $opts['http']['header'][] = $authHeader; } $context = StreamContextFactory::getContext($url, $opts); @file_get_contents($url, false, $context); } continue; } $postData = array('downloads' => array()); foreach ($packages as $package) { $postData['downloads'][] = array('name' => $package->getPrettyName(), 'version' => $package->getVersion()); } $opts = array('http' => array('method' => 'POST', 'header' => array('Content-Type: application/json'), 'content' => json_encode($postData), 'timeout' => 6)); if (isset($authHeader)) { $opts['http']['header'][] = $authHeader; } $context = StreamContextFactory::getContext($repoUrl, $opts); @file_get_contents($repoUrl, false, $context); } $this->reset(); }