From 8845ea467a6008b6de6fbbb318f201c6a68d0d34 Mon Sep 17 00:00:00 2001 From: berlinger-rarents Date: Wed, 28 Sep 2016 19:08:24 +0200 Subject: [PATCH] try bitbucket downloads first time without auth also add tests for #5584 --- src/Composer/Util/RemoteFilesystem.php | 33 ++------ .../Test/Util/RemoteFilesystemTest.php | 78 +++++++++++++++++++ 2 files changed, 86 insertions(+), 25 deletions(-) diff --git a/src/Composer/Util/RemoteFilesystem.php b/src/Composer/Util/RemoteFilesystem.php index bfc063ecd..48c7b3698 100644 --- a/src/Composer/Util/RemoteFilesystem.php +++ b/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) { diff --git a/tests/Composer/Test/Util/RemoteFilesystemTest.php b/tests/Composer/Test/Util/RemoteFilesystemTest.php index a7b91ed32..f2bb7a611 100644 --- a/tests/Composer/Test/Util/RemoteFilesystemTest.php +++ b/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);