1
0
Fork 0

Support github auth directly in the RemoteFilesystem class

pull/2918/head
Jordi Boggiano 2014-04-11 14:27:14 +02:00
parent 68e910d92a
commit b437c1cc05
11 changed files with 87 additions and 87 deletions

View File

@ -232,7 +232,7 @@ EOT
if (null === $repositoryUrl) { if (null === $repositoryUrl) {
$sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config)); $sourceRepo = new CompositeRepository(Factory::createDefaultRepositories($io, $config));
} elseif ("json" === pathinfo($repositoryUrl, PATHINFO_EXTENSION)) { } 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')) { } elseif (0 === strpos($repositoryUrl, 'http')) {
$sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config); $sourceRepo = new ComposerRepository(array('url' => $repositoryUrl), $io, $config);
} else { } else {

View File

@ -48,7 +48,22 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output) 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()); $this->process = new ProcessExecutor($this->getIO());
$output->write('Checking platform settings: '); $output->write('Checking platform settings: ');
@ -70,21 +85,6 @@ EOT
$this->outputResult($output, $this->checkHttpsProxyFullUriRequestParam()); $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')) { if ($oauth = $config->get('github-oauth')) {
foreach ($oauth as $domain => $token) { foreach ($oauth as $domain => $token) {
$output->write('Checking '.$domain.' oauth access: '); $output->write('Checking '.$domain.' oauth access: ');

View File

@ -57,8 +57,8 @@ EOT
protected function execute(InputInterface $input, OutputInterface $output) protected function execute(InputInterface $input, OutputInterface $output)
{ {
$baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE; $baseUrl = (extension_loaded('openssl') ? 'https' : 'http') . '://' . self::HOMEPAGE;
$remoteFilesystem = new RemoteFilesystem($this->getIO());
$config = Factory::createConfig(); $config = Factory::createConfig();
$remoteFilesystem = new RemoteFilesystem($this->getIO(), $config);
$cacheDir = $config->get('cache-dir'); $cacheDir = $config->get('cache-dir');
$rollbackDir = $config->get('home'); $rollbackDir = $config->get('home');
$localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0]; $localFilename = realpath($_SERVER['argv'][0]) ?: $_SERVER['argv'][0];

View File

@ -56,7 +56,7 @@ class FileDownloader implements DownloaderInterface
$this->io = $io; $this->io = $io;
$this->config = $config; $this->config = $config;
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
$this->rfs = $rfs ?: new RemoteFilesystem($io); $this->rfs = $rfs ?: new RemoteFilesystem($io, $config);
$this->filesystem = $filesystem ?: new Filesystem(); $this->filesystem = $filesystem ?: new Filesystem();
$this->cache = $cache; $this->cache = $cache;
@ -108,7 +108,6 @@ class FileDownloader implements DownloaderInterface
$checksum = $package->getDistSha1Checksum(); $checksum = $package->getDistSha1Checksum();
$cacheKey = $this->getCacheKey($package); $cacheKey = $this->getCacheKey($package);
try {
// download if we don't have it in cache or the cache is invalidated // 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->cache || ($checksum && $checksum !== $this->cache->sha1($cacheKey)) || !$this->cache->copyTo($cacheKey, $fileName)) {
if (!$this->outputProgress) { if (!$this->outputProgress) {
@ -139,23 +138,6 @@ class FileDownloader implements DownloaderInterface
} else { } else {
$this->io->write(' Loading from cache'); $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 (!file_exists($fileName)) { if (!file_exists($fileName)) {
throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the' throw new \UnexpectedValueException($url.' could not be saved to '.$fileName.', make sure the'

View File

@ -275,7 +275,7 @@ class Factory
$lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION) $lockFile = "json" === pathinfo($composerFile, PATHINFO_EXTENSION)
? substr($composerFile, 0, -4).'lock' ? substr($composerFile, 0, -4).'lock'
: $composerFile . '.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); $composer->setLocker($locker);
} }

View File

@ -85,7 +85,7 @@ class ComposerRepository extends ArrayRepository implements StreamableRepository
$this->io = $io; $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->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->loader = new ArrayLoader();
$this->rfs = new RemoteFilesystem($this->io, $this->options); $this->rfs = new RemoteFilesystem($this->io, $this->config, $this->options);
$this->eventDispatcher = $eventDispatcher; $this->eventDispatcher = $eventDispatcher;
} }

View File

@ -57,7 +57,7 @@ class PearRepository extends ArrayRepository
$this->url = rtrim($repoConfig['url'], '/'); $this->url = rtrim($repoConfig['url'], '/');
$this->io = $io; $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->vendorAlias = isset($repoConfig['vendor-alias']) ? $repoConfig['vendor-alias'] : null;
$this->versionParser = new VersionParser(); $this->versionParser = new VersionParser();
} }

View File

@ -57,7 +57,7 @@ abstract class VcsDriver implements VcsDriverInterface
$this->io = $io; $this->io = $io;
$this->config = $config; $this->config = $config;
$this->process = $process ?: new ProcessExecutor($io); $this->process = $process ?: new ProcessExecutor($io);
$this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io); $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io, $config);
} }
/** /**

View File

@ -40,7 +40,7 @@ class GitHub
$this->io = $io; $this->io = $io;
$this->config = $config; $this->config = $config;
$this->process = $process ?: new ProcessExecutor; $this->process = $process ?: new ProcessExecutor;
$this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io); $this->remoteFilesystem = $remoteFilesystem ?: new RemoteFilesystem($io, $config);
} }
/** /**

View File

@ -13,6 +13,7 @@
namespace Composer\Util; namespace Composer\Util;
use Composer\Composer; use Composer\Composer;
use Composer\Config;
use Composer\IO\IOInterface; use Composer\IO\IOInterface;
use Composer\Downloader\TransportException; use Composer\Downloader\TransportException;
@ -24,6 +25,7 @@ use Composer\Downloader\TransportException;
class RemoteFilesystem class RemoteFilesystem
{ {
private $io; private $io;
private $config;
private $firstCall; private $firstCall;
private $bytesMax; private $bytesMax;
private $originUrl; private $originUrl;
@ -41,9 +43,10 @@ class RemoteFilesystem
* @param IOInterface $io The IO instance * @param IOInterface $io The IO instance
* @param array $options The options * @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->io = $io;
$this->config = $config;
$this->options = $options; $this->options = $options;
} }
@ -267,26 +270,14 @@ class RemoteFilesystem
break; break;
} }
if (!$this->io->isInteractive()) { $this->promptAuthAndRetry($messageCode);
$message = "The '" . $this->fileUrl . "' URL required authentication.\nYou must be using the interactive console";
throw new TransportException($message, 401);
}
$this->promptAuthAndRetry();
break; break;
} }
break; break;
case STREAM_NOTIFY_AUTH_RESULT: case STREAM_NOTIFY_AUTH_RESULT:
if (403 === $messageCode) { if (403 === $messageCode) {
if (!$this->io->isInteractive() || $this->io->hasAuthentication($this->originUrl)) { $this->promptAuthAndRetry($messageCode, $message);
$message = "The '" . $this->fileUrl . "' URL could not be accessed: " . $message;
throw new TransportException($message, 403);
}
$this->promptAuthAndRetry();
break; break;
} }
break; break;
@ -317,12 +308,39 @@ class RemoteFilesystem
} }
} }
protected function promptAuthAndRetry() protected function promptAuthAndRetry($httpStatus, $reason)
{ {
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>):'); $this->io->overwrite(' Authentication required (<info>'.parse_url($this->fileUrl, PHP_URL_HOST).'</info>):');
$username = $this->io->ask(' Username: '); $username = $this->io->ask(' Username: ');
$password = $this->io->askAndHideAnswer(' Password: '); $password = $this->io->askAndHideAnswer(' Password: ');
$this->io->setAuthentication($this->originUrl, $username, $password); $this->io->setAuthentication($this->originUrl, $username, $password);
}
$this->retry = true; $this->retry = true;
throw new TransportException('RETRY'); throw new TransportException('RETRY');

View File

@ -173,7 +173,7 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase
protected function callGetOptionsForUrl($io, array $args = array(), array $options = array()) 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 = new \ReflectionMethod($fs, 'getOptionsForUrl');
$ref->setAccessible(true); $ref->setAccessible(true);