Browse Source

try bitbucket downloads first time without auth

also add tests for #5584
berlinger-rarents 8 years ago
parent
commit
8845ea467a

+ 8 - 25
src/Composer/Util/RemoteFilesystem.php

@@ -44,6 +44,7 @@ class RemoteFilesystem
     private $degradedMode = false;
     private $redirects;
     private $maxRedirects = 20;
+    private $bitBucketUrlsTriedWithoutAuth = array();
 
     /**
      * Constructor.
@@ -175,27 +176,6 @@ class RemoteFilesystem
         return $value;
     }
 
-    /**
-     * @link https://github.com/composer/composer/issues/5584
-     *
-     * @param string $urlToBitBucketFile URL to a file at bitbucket.org.
-     *
-     * @return bool Whether the given URL is a public BitBucket download which requires no authentication.
-     */
-    public static function urlIsPublicBitBucketDownload($urlToBitBucketFile)
-    {
-        $path = parse_url($urlToBitBucketFile, PHP_URL_PATH);
-
-        // Path for a public download follows this pattern /{user}/{repo}/downloads/{whatever}
-        // {@link https://blog.bitbucket.org/2009/04/12/new-feature-downloads/}
-        $pathParts = explode('/', $path);
-        if (count($pathParts) >= 4 && $pathParts[2] != 'downloads') {
-            return true;
-        }
-
-        return false;
-    }
-
     /**
      * Get file content or copy action.
      *
@@ -267,7 +247,12 @@ class RemoteFilesystem
         }
 
         if (isset($options['bitbucket-token'])) {
-            $fileUrl .= (false === strpos($fileUrl, '?') ? '?' : '&') . 'access_token='.$options['bitbucket-token'];
+            // First time be optimistic and do not use the token for a BitBucket download.
+            if (isset($this->bitBucketUrlsTriedWithoutAuth[$origFileUrl]) && $this->bitBucketUrlsTriedWithoutAuth[$origFileUrl]) {
+                $fileUrl .= (false === strpos($fileUrl,'?') ? '?' : '&') . 'access_token=' . $options['bitbucket-token'];
+            } else {
+                $this->bitBucketUrlsTriedWithoutAuth[$origFileUrl] = true;
+            }
             unset($options['bitbucket-token']);
         }
 
@@ -363,9 +348,7 @@ class RemoteFilesystem
 
         // check for bitbucket login page asking to authenticate
         if ($originUrl === 'bitbucket.org'
-            && !self::urlIsPublicBitBucketDownload($fileUrl)
-            && substr($fileUrl, -4) === '.zip'
-            && preg_match('{^text/html\b}i', $contentType)
+            && substr($fileUrl, 0, 37) === 'https://bitbucket.org/account/signin/'
         ) {
             $result = false;
             if ($this->retryAuthFailure) {

+ 78 - 0
tests/Composer/Test/Util/RemoteFilesystemTest.php

@@ -191,6 +191,84 @@ class RemoteFilesystemTest extends \PHPUnit_Framework_TestCase
         }
     }
 
+    /**
+     * Provides URLs to public downloads at BitBucket.
+     *
+     * @return string[][]
+     */
+    public function provideBitbucketPublicDownloadUrls()
+    {
+        return array(
+            array('https://bitbucket.org/berlinger-rarents/my-public-repo-with-downloads/downloads/composer-unit-test-download-me.txt', '1234'),
+        );
+    }
+
+    /**
+     * Tests that a BitBucket public download is correctly retrieved.
+     *
+     * @param string $url
+     * @param string $contents
+     * @dataProvider provideBitbucketPublicDownloadUrls
+     */
+    public function testBitBucketPublicDownload($url, $contents)
+    {
+        $io = $this
+            ->getMockBuilder('Composer\IO\ConsoleIO')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $rfs = new RemoteFilesystem($io);
+        $hostname = parse_url($url, PHP_URL_HOST);
+
+        $result = $rfs->getContents($hostname, $url, false);
+
+        $this->assertEquals($contents, $result);
+    }
+
+    /**
+     * Tests that a BitBucket public download is correctly retrieved when `bitbucket-oauth` is configured.
+     *
+     * @param string $url
+     * @param string $contents
+     * @dataProvider provideBitbucketPublicDownloadUrls
+     */
+    public function testBitBucketPublicDownloadWithAuthConfigured($url, $contents)
+    {
+        $io = $this
+            ->getMockBuilder('Composer\IO\ConsoleIO')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $config = $this
+            ->getMockBuilder('Composer\Config')
+            ->getMock();
+        $config
+            ->method('get')
+            ->withAnyParameters()
+            ->willReturn(array());
+
+        $io
+            ->method('hasAuthentication')
+            ->with('bitbucket.org')
+            ->willReturn(true);
+        $io
+            ->method('getAuthentication')
+            ->with('bitbucket.org')
+            ->willReturn(array(
+                'username' => 'x-token-auth',
+                // This token is fake, but it matches a valid token's pattern.
+                'password' => '1A0yeK5Po3ZEeiiRiMWLivS0jirLdoGuaSGq9NvESFx1Fsdn493wUDXC8rz_1iKVRTl1GINHEUCsDxGh5lZ='
+            ));
+
+
+        $rfs = new RemoteFilesystem($io, $config);
+        $hostname = parse_url($url, PHP_URL_HOST);
+
+        $result = $rfs->getContents($hostname, $url, false);
+
+        $this->assertEquals($contents, $result);
+    }
+
     protected function callGetOptionsForUrl($io, array $args = array(), array $options = array(), $fileUrl = '')
     {
         $fs = new RemoteFilesystem($io, null, $options);