Support github auth directly in the RemoteFilesystem class
parent
68e910d92a
commit
b437c1cc05
|
@ -232,7 +232,7 @@ EOT
|
|||
if (null === $repositoryUrl) {
|
||||
$sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config));
|
||||
} elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) {
|
||||
$sourceRepo = new FilesystemRepository(new JsonFile($repositoryUrl, new RemoteFilesystem($io)));
|
||||
$sourceRepo = new FilesystemRepository(new JsonFile($repositoryUrl, new RemoteFilesystem($io, $config)));
|
||||
} elseif (0 === strpos($repositoryUrl, 'http')) {
|
||||
$sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config);
|
||||
} else {
|
||||
|
|
|
@ -48,7 +48,22 @@ EOT
|
|||
|
||||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$this->rfs = new RemoteFilesystem($this->getIO());
|
||||
$composer = $this->getComposer(false);
|
||||
if ($composer) {
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$output->write('Checking composer.json: ');
|
||||
$this->outputResult($output, $this->checkComposerSchema());
|
||||
}
|
||||
|
||||
if ($composer) {
|
||||
$config = $composer->getConfig();
|
||||
} else {
|
||||
$config = Factory::createConfig();
|
||||
}
|
||||
|
||||
$this->rfs = new RemoteFilesystem($this->getIO(), $config);
|
||||
$this->process = new ProcessExecutor($this->getIO());
|
||||
|
||||
$output->write('Checking platform settings: ');
|
||||
|
@ -70,21 +85,6 @@ EOT
|
|||
$this->outputResult($output, $this->checkHttpsProxyFullUriRequestParam());
|
||||
}
|
||||
|
||||
$composer = $this->getComposer(false);
|
||||
if ($composer) {
|
||||
$commandEvent = new CommandEvent(PluginEvents::COMMAND, 'diagnose', $input, $output);
|
||||
$composer->getEventDispatcher()->dispatch($commandEvent->getName(), $commandEvent);
|
||||
|
||||
$output->write('Checking composer.json: ');
|
||||
$this->outputResult($output, $this->checkComposerSchema());
|
||||
}
|
||||
|
||||
if ($composer) {
|
||||
$config = $composer->getConfig();
|
||||
} else {
|
||||
$config = Factory::createConfig();
|
||||
}
|
||||
|
||||
if ($oauth = $config->get('github-oauth')) {
|
||||
foreach ($oauth as $domain => $token) {
|
||||
$output->write('Checking '.$domain.' oauth access: ');
|
||||
|
|
|
@ -57,8 +57,8 @@ EOT
|
|||
protected function execute(InputInterface $input, OutputInterface $output)
|
||||
{
|
||||
$baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE;
|
||||
$remoteFilesystem = new RemoteFilesystem($this->getIO());
|
||||
$config = Factory::createConfig();
|
||||
$remoteFilesystem = new RemoteFilesystem($this->getIO(), $config);
|
||||
$cacheDir = $config->get('cache-dir');
|
||||
$rollbackDir = $config->get('home');
|
||||
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];
|
||||
|
|
|
@ -56,7 +56,7 @@ class FileDownloader implements DownloaderInterface
|
|||
$this->io = $io;
|
||||
$this->config = $config;
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
$this->rfs = $rfs ?: new RemoteFilesystem($io);
|
||||
$this->rfs = $rfs ?: new RemoteFilesystem($io, $config);
|
||||
$this->filesystem = $filesystem ?: new Filesystem();
|
||||
$this->cache = $cache;
|
||||
|
||||
|
@ -108,53 +108,35 @@ class FileDownloader implements DownloaderInterface
|
|||
$checksum = $package->getDistSha1Checksum();
|
||||
$cacheKey = $this->getCacheKey($package);
|
||||
|
||||
try {
|
||||
// download if we don't have it in cache or the cache is invalidated
|
||||
if (!$this->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) {
|
||||
if (!$this->outputProgress) {
|
||||
$this->io->write(' Downloading');
|
||||
}
|
||||
// download if we don't have it in cache or the cache is invalidated
|
||||
if (!$this->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) {
|
||||
if (!$this->outputProgress) {
|
||||
$this->io->write(' Downloading');
|
||||
}
|
||||
|
||||
// try to download 3 times then fail hard
|
||||
$retries = 3;
|
||||
while ($retries--) {
|
||||
try {
|
||||
$rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress);
|
||||
break;
|
||||
} catch (TransportException $e) {
|
||||
// if we got an http response with a proper code, then requesting again will probably not help, abort
|
||||
if ((0 !== $e->getCode() && !in_array($e->getCode(),array(500, 502, 503, 504))) || !$retries) {
|
||||
throw $e;
|
||||
}
|
||||
if ($this->io->isVerbose()) {
|
||||
$this->io->write(' Download failed, retrying...');
|
||||
}
|
||||
usleep(500000);
|
||||
// try to download 3 times then fail hard
|
||||
$retries = 3;
|
||||
while ($retries--) {
|
||||
try {
|
||||
$rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress);
|
||||
break;
|
||||
} catch (TransportException $e) {
|
||||
// if we got an http response with a proper code, then requesting again will probably not help, abort
|
||||
if ((0 !== $e->getCode() && !in_array($e->getCode(),array(500, 502, 503, 504))) || !$retries) {
|
||||
throw $e;
|
||||
}
|
||||
if ($this->io->isVerbose()) {
|
||||
$this->io->write(' Download failed, retrying...');
|
||||
}
|
||||
usleep(500000);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->cache) {
|
||||
$this->cache->copyFrom($cacheKey, $fileName);
|
||||
}
|
||||
} else {
|
||||
$this->io->write(' Loading from cache');
|
||||
}
|
||||
} catch (TransportException $e) {
|
||||
if (!in_array($e->getCode(), array(404, 403, 412))) {
|
||||
throw $e;
|
||||
}
|
||||
if ('github.com' === $hostname && !$this->io->hasAuthentication($hostname)) {
|
||||
$message = "\n".'Could not fetch '.$processedUrl.', enter your GitHub credentials '.($e->getCode() === 404 ? 'to access private repos' : 'to go over the API rate limit');
|
||||
$gitHubUtil = new GitHub($this->io, $this->config, null, $rfs);
|
||||
if (!$gitHubUtil->authorizeOAuth($hostname)
|
||||
&& (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($hostname, $message))
|
||||
) {
|
||||
throw $e;
|
||||
}
|
||||
$rfs->copy($hostname, $processedUrl, $fileName, $this->outputProgress);
|
||||
} else {
|
||||
throw $e;
|
||||
if ($this->cache) {
|
||||
$this->cache->copyFrom($cacheKey, $fileName);
|
||||
}
|
||||
} else {
|
||||
$this->io->write(' Loading from cache');
|
||||
}
|
||||
|
||||
if (!file_exists($fileName)) {
|
||||
|
|
|
@ -275,7 +275,7 @@ class Factory
|
|||
$lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION)
|
||||
? substr($composerFile, 0, -4).'lock'
|
||||
: $composerFile . '.lock';
|
||||
$locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io)), $rm, $im, md5_file($composerFile));
|
||||
$locker = new Package\Locker($io, new JsonFile($lockFile, new RemoteFilesystem($io, $config)), $rm, $im, md5_file($composerFile));
|
||||
$composer->setLocker($locker);
|
||||
}
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository
|
|||
$this->io = $io;
|
||||
$this->cache = new Cache($io, $config->get('cache-repo-dir').'/'.preg_replace('{[^a-z0-9.]}i', '-', $this->url), 'a-z0-9.$');
|
||||
$this->loader = new ArrayLoader();
|
||||
$this->rfs = new RemoteFilesystem($this->io, $this->options);
|
||||
$this->rfs = new RemoteFilesystem($this->io, $this->config, $this->options);
|
||||
$this->eventDispatcher = $eventDispatcher;
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ class PearRepository extends ArrayRepository
|
|||
|
||||
$this->url = rtrim($repoConfig['url'], '/');
|
||||
$this->io = $io;
|
||||
$this->rfs = $rfs ?: new RemoteFilesystem($this->io);
|
||||
$this->rfs = $rfs ?: new RemoteFilesystem($this->io, $config);
|
||||
$this->vendorAlias = isset($repoConfig['vendor-alias']) ? $repoConfig['vendor-alias'] : null;
|
||||
$this->versionParser = new VersionParser();
|
||||
}
|
||||
|
|
|
@ -57,7 +57,7 @@ abstract class VcsDriver implements VcsDriverInterface
|
|||
$this->io = $io;
|
||||
$this->config = $config;
|
||||
$this->process = $process ?: new ProcessExecutor($io);
|
||||
$this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io);
|
||||
$this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -40,7 +40,7 @@ class GitHub
|
|||
$this->io = $io;
|
||||
$this->config = $config;
|
||||
$this->process = $process ?: new ProcessExecutor;
|
||||
$this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io);
|
||||
$this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io, $config);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
namespace Composer\Util;
|
||||
|
||||
use Composer\Composer;
|
||||
use Composer\Config;
|
||||
use Composer\IO\IOInterface;
|
||||
use Composer\Downloader\TransportException;
|
||||
|
||||
|
@ -24,6 +25,7 @@ use Composer\Downloader\TransportException;
|
|||
class RemoteFilesystem
|
||||
{
|
||||
private $io;
|
||||
private $config;
|
||||
private $firstCall;
|
||||
private $bytesMax;
|
||||
private $originUrl;
|
||||
|
@ -41,9 +43,10 @@ class RemoteFilesystem
|
|||
* @param IOInterface $io The IO instance
|
||||
* @param array $options The options
|
||||
*/
|
||||
public function __construct(IOInterface $io, $options = array())
|
||||
public function __construct(IOInterface $io, Config $config = null, array $options = array())
|
||||
{
|
||||
$this->io = $io;
|
||||
$this->config = $config;
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
|
@ -267,26 +270,14 @@ class RemoteFilesystem
|
|||
break;
|
||||
}
|
||||
|
||||
if (!$this->io->isInteractive()) {
|
||||
$message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console";
|
||||
|
||||
throw new TransportException($message, 401);
|
||||
}
|
||||
|
||||
$this->promptAuthAndRetry();
|
||||
$this->promptAuthAndRetry($messageCode);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case STREAM_NOTIFY_AUTH_RESULT:
|
||||
if (403 === $messageCode) {
|
||||
if (!$this->io->isInteractive() || $this->io->hasAuthentication($this->originUrl)) {
|
||||
$message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $message;
|
||||
|
||||
throw new TransportException($message, 403);
|
||||
}
|
||||
|
||||
$this->promptAuthAndRetry();
|
||||
$this->promptAuthAndRetry($messageCode, $message);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
@ -317,12 +308,39 @@ class RemoteFilesystem
|
|||
}
|
||||
}
|
||||
|
||||
protected function promptAuthAndRetry()
|
||||
protected function promptAuthAndRetry($httpStatus, $reason)
|
||||
{
|
||||
$this->io->overwrite(' Authentication required (<info>'.parse_url($this->fileUrl, PHP_URL_HOST).'</info>):');
|
||||
$username = $this->io->ask(' Username: ');
|
||||
$password = $this->io->askAndHideAnswer(' Password: ');
|
||||
$this->io->setAuthentication($this->originUrl, $username, $password);
|
||||
if ($this->config && in_array($this->originUrl, $this->config->get('github-domains'), true)) {
|
||||
$message = "\n".'Could not fetch '.$this->fileUrl.', enter your GitHub credentials '.($httpStatus === 404 ? 'to access private repos' : 'to go over the API rate limit');
|
||||
$gitHubUtil = new GitHub($this->io, $this->config, null, $this);
|
||||
if (!$gitHubUtil->authorizeOAuth($this->originUrl)
|
||||
&& (!$this->io->isInteractive() || !$gitHubUtil->authorizeOAuthInteractively($this->originUrl, $message))
|
||||
) {
|
||||
throw new TransportException('Could not authenticate against '.$this->originUrl);
|
||||
}
|
||||
} else {
|
||||
// 404s are only handled for github
|
||||
if ($httpStatus === 404) {
|
||||
return;
|
||||
}
|
||||
|
||||
// fail if we already have auth or the console is not interactive
|
||||
if (!$this->io->isInteractive() || $this->io->hasAuthentication($this->originUrl)) {
|
||||
if ($httpStatus === 401) {
|
||||
$message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console to authenticate";
|
||||
}
|
||||
if ($httpStatus === 403) {
|
||||
$message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $reason;
|
||||
}
|
||||
|
||||
throw new TransportException($message, $httpStatus);
|
||||
}
|
||||
|
||||
$this->io->overwrite(' Authentication required (<info>'.parse_url($this->fileUrl, PHP_URL_HOST).'</info>):');
|
||||
$username = $this->io->ask(' Username: ');
|
||||
$password = $this->io->askAndHideAnswer(' Password: ');
|
||||
$this->io->setAuthentication($this->originUrl, $username, $password);
|
||||
}
|
||||
|
||||
$this->retry = true;
|
||||
throw new TransportException('RETRY');
|
||||
|
|
|
@ -173,7 +173,7 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase
|
|||
|
||||
protected function callGetOptionsForUrl($io, array $args = array(), array $options = array())
|
||||
{
|
||||
$fs = new RemoteFilesystem($io, $options);
|
||||
$fs = new RemoteFilesystem($io, null, $options);
|
||||
$ref = new \ReflectionMethod($fs, 'getOptionsForUrl');
|
||||
$ref->setAccessible(true);
|
||||
|
||||
|
|
Loading…
Reference in New Issue