Browse Source

Support github auth directly in the RemoteFilesystem class

Jordi Boggiano 11 years ago
parent
commit
b437c1cc05

+ 1 - 1
src/Composer/Command/CreateProjectCommand.php

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

+ 16 - 16
src/Composer/Command/DiagnoseCommand.php

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

+ 1 - 1
src/Composer/Command/SelfUpdateCommand.php

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

+ 25 - 43
src/Composer/Downloader/FileDownloader.php

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

+ 1 - 1
src/Composer/Factory.php

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

+ 1 - 1
src/Composer/Repository/ComposerRepository.php

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

+ 1 - 1
src/Composer/Repository/PearRepository.php

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

+ 1 - 1
src/Composer/Repository/Vcs/VcsDriver.php

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

+ 1 - 1
src/Composer/Util/GitHub.php

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

+ 38 - 20
src/Composer/Util/RemoteFilesystem.php

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

+ 1 - 1
tests/Composer/Test/Util/RemoteFilesystemTest.php

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