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) {
$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 {

View File

@ -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: ');

View File

@ -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];

View File

@ -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)) {

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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);
}
/**

View File

@ -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);
}
/**

View File

@ -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');

View File

@ -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);