Support github auth directly in the RemoteFilesystem class
parent
68e910d92a
commit
b437c1cc05
|
@ -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 {
|
||||||
|
|
|
@ -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: ');
|
||||||
|
|
|
@ -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];
|
||||||
|
|
|
@ -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'
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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');
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue