Browse Source

Add Util\AuthHelper unit test coverage (#8863)

* Add AuthHelper::addAuthenticationHeader() test on missing authentication credentials.

* Add AuthHelper::addAuthenticationHeader() test on bearer password.

* Add AuthHelper::addAuthenticationHeader() test on Github token.

* Add AuthHelper::addAuthenticationHeader() test on Gitlab Oauth token.

* Add $authenticationDisplayMessage write expectation to AuthHelper::addAuthenticationHeader() tests.

* Add AuthHelper::addAuthenticationHeader() test on Gitlab private token.

* Add AuthHelper::addAuthenticationHeader() test on Bitbucket Oauth token.

* Add AuthHelper::addAuthenticationHeader() test on Bitbucket public urls.

* Add AuthHelper::addAuthenticationHeader() test on Basic Http Authentication.

* Add AuthHelper::isPublicBitBucketDownload() tests.

* Rename AuthHelperTest $credentials variable to $auth.

* Add AuthHelper::storeAuth() test for auto-store option.

* Add AuthHelper::storeAuth() test for user prompt and y(es) answer.

* Add AuthHelper::storeAuth() test for user prompt and n(o) answer.

* Add AuthHelper::storeAuth() test for user prompt with invalid answer.

* Add AuthHelper::promptAuthIfNeeded() test for Github authentication failure.

- add GitHub hard dependency mock (new GitHub(...) mock)

* Run AuthHelper::promptAuthIfNeeded() tests only with PHP > 5.3

* Run AuthHelper::promptAuthIfNeeded() tests only with PHP >= 5.4

* Run AuthHelper::promptAuthIfNeeded() tests only with PHP 5.4

* Exclude PHPStan analyses of '../tests/Composer/Test/Util/Mocks/*'

* Exclude AuthHelper::promptAuthIfNeeded() tests from current pull request.

* Extract repetitive AuthHelperTest authentication expectation into a method.
Michael Chekin 4 years ago
parent
commit
c23670c3ec
1 changed files with 525 additions and 0 deletions
  1. 525 0
      tests/Composer/Test/Util/AuthHelperTest.php

+ 525 - 0
tests/Composer/Test/Util/AuthHelperTest.php

@@ -0,0 +1,525 @@
+<?php
+
+/*
+ * This file is part of Composer.
+ *
+ * (c) Nils Adermann <naderman@naderman.de>
+ *     Jordi Boggiano <j.boggiano@seld.be>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+namespace Composer\Test\Util;
+
+use Composer\IO\IOInterface;
+use Composer\Test\TestCase;
+use Composer\Util\AuthHelper;
+use Composer\Util\Bitbucket;
+use RuntimeException;
+
+/**
+ * @author Michael Chekin <mchekin@gmail.com>
+ */
+class AuthHelperTest extends TestCase
+{
+    /** @type \Composer\IO\IOInterface|\PHPUnit_Framework_MockObject_MockObject */
+    private $io;
+
+    /** @type \Composer\Config|\PHPUnit_Framework_MockObject_MockObject */
+    private $config;
+
+    /** @type AuthHelper */
+    private $authHelper;
+
+    protected function setUp()
+    {
+        $this->io = $this
+            ->getMockBuilder('Composer\IO\IOInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->config = $this->getMockBuilder('Composer\Config')->getMock();
+
+        $this->authHelper = new AuthHelper($this->io, $this->config);
+    }
+
+    public function testAddAuthenticationHeaderWithoutAuthCredentials()
+    {
+        $headers = array(
+            'Accept-Encoding: gzip',
+            'Connection: close'
+        );
+        $origin = 'http://example.org';
+        $url = 'file://' . __FILE__;
+
+        $this->io->expects($this->once())
+            ->method('hasAuthentication')
+            ->with($origin)
+            ->willReturn(false);
+
+        $this->assertSame(
+            $headers,
+            $this->authHelper->addAuthenticationHeader($headers, $origin, $url)
+        );
+    }
+
+    public function testAddAuthenticationHeaderWithBearerPassword()
+    {
+        $headers = array(
+            'Accept-Encoding: gzip',
+            'Connection: close'
+        );
+        $origin = 'http://example.org';
+        $url = 'file://' . __FILE__;
+        $auth = array(
+            'username' => 'my_username',
+            'password' => 'bearer'
+        );
+
+        $this->expectsAuthentication($origin, $auth);
+
+        $expectedHeaders = array_merge($headers, array('Authorization: Bearer ' . $auth['username']));
+
+        $this->assertSame(
+            $expectedHeaders,
+            $this->authHelper->addAuthenticationHeader($headers, $origin, $url)
+        );
+    }
+
+    public function testAddAuthenticationHeaderWithGithubToken()
+    {
+        $headers = array(
+            'Accept-Encoding: gzip',
+            'Connection: close'
+        );
+        $origin = 'github.com';
+        $url = 'https://api.github.com/';
+        $auth = array(
+            'username' => 'my_username',
+            'password' => 'x-oauth-basic'
+        );
+
+        $this->expectsAuthentication($origin, $auth);
+
+        $this->io->expects($this->once())
+            ->method('writeError')
+            ->with('Using GitHub token authentication', true, IOInterface::DEBUG);
+
+        $expectedHeaders = array_merge($headers, array('Authorization: token ' . $auth['username']));
+
+        $this->assertSame(
+            $expectedHeaders,
+            $this->authHelper->addAuthenticationHeader($headers, $origin, $url)
+        );
+    }
+
+    public function testAddAuthenticationHeaderWithGitlabOathToken()
+    {
+        $headers = array(
+            'Accept-Encoding: gzip',
+            'Connection: close'
+        );
+        $origin = 'gitlab.com';
+        $url = 'https://api.gitlab.com/';
+        $auth = array(
+            'username' => 'my_username',
+            'password' => 'oauth2'
+        );
+
+        $this->expectsAuthentication($origin, $auth);
+
+        $this->config->expects($this->once())
+            ->method('get')
+            ->with('gitlab-domains')
+            ->willReturn(array($origin));
+
+        $this->io->expects($this->once())
+            ->method('writeError')
+            ->with('Using GitLab OAuth token authentication', true, IOInterface::DEBUG);
+
+        $expectedHeaders = array_merge($headers, array('Authorization: Bearer ' . $auth['username']));
+
+        $this->assertSame(
+            $expectedHeaders,
+            $this->authHelper->addAuthenticationHeader($headers, $origin, $url)
+        );
+    }
+
+    public function gitlabPrivateTokenProvider()
+    {
+        return array(
+          array('private-token'),
+          array('gitlab-ci-token'),
+        );
+    }
+
+    /**
+     * @dataProvider gitlabPrivateTokenProvider
+     *
+     * @param string $password
+     */
+    public function testAddAuthenticationHeaderWithGitlabPrivateToken($password)
+    {
+        $headers = array(
+            'Accept-Encoding: gzip',
+            'Connection: close'
+        );
+        $origin = 'gitlab.com';
+        $url = 'https://api.gitlab.com/';
+        $auth = array(
+            'username' => 'my_username',
+            'password' => $password
+        );
+
+        $this->expectsAuthentication($origin, $auth);
+
+        $this->config->expects($this->once())
+            ->method('get')
+            ->with('gitlab-domains')
+            ->willReturn(array($origin));
+
+        $this->io->expects($this->once())
+            ->method('writeError')
+            ->with('Using GitLab private token authentication', true, IOInterface::DEBUG);
+
+        $expectedHeaders = array_merge($headers, array('PRIVATE-TOKEN: ' . $auth['username']));
+
+        $this->assertSame(
+            $expectedHeaders,
+            $this->authHelper->addAuthenticationHeader($headers, $origin, $url)
+        );
+    }
+
+    public function testAddAuthenticationHeaderWithBitbucketOathToken()
+    {
+        $headers = array(
+            'Accept-Encoding: gzip',
+            'Connection: close'
+        );
+        $origin = 'bitbucket.org';
+        $url = 'https://bitbucket.org/site/oauth2/authorize';
+        $auth = array(
+            'username' => 'x-token-auth',
+            'password' => 'my_password'
+        );
+
+        $this->expectsAuthentication($origin, $auth);
+
+        $this->config->expects($this->once())
+            ->method('get')
+            ->with('gitlab-domains')
+            ->willReturn(array());
+
+        $this->io->expects($this->once())
+            ->method('writeError')
+            ->with('Using Bitbucket OAuth token authentication', true, IOInterface::DEBUG);
+
+        $expectedHeaders = array_merge($headers, array('Authorization: Bearer ' . $auth['password']));
+
+        $this->assertSame(
+            $expectedHeaders,
+            $this->authHelper->addAuthenticationHeader($headers, $origin, $url)
+        );
+    }
+
+    public function bitbucketPublicUrlProvider()
+    {
+        return array(
+            array('https://bitbucket.org/user/repo/downloads/whatever'),
+            array('https://bbuseruploads.s3.amazonaws.com/9421ee72-638e-43a9-82ea-39cfaae2bfaa/downloads/b87c59d9-54f3-4922-b711-d89059ec3bcf'),
+        );
+    }
+
+    /**
+     * @dataProvider bitbucketPublicUrlProvider
+     *
+     * @param string $url
+     */
+    public function testAddAuthenticationHeaderWithBitbucketPublicUrl($url)
+    {
+        $headers = array(
+            'Accept-Encoding: gzip',
+            'Connection: close'
+        );
+        $origin = 'bitbucket.org';
+        $auth = array(
+            'username' => 'x-token-auth',
+            'password' => 'my_password'
+        );
+
+        $this->expectsAuthentication($origin, $auth);
+
+        $this->config->expects($this->once())
+            ->method('get')
+            ->with('gitlab-domains')
+            ->willReturn(array());
+
+        $this->assertSame(
+            $headers,
+            $this->authHelper->addAuthenticationHeader($headers, $origin, $url)
+        );
+    }
+
+    public function basicHttpAuthenticationProvider()
+    {
+        return array(
+            array(
+                Bitbucket::OAUTH2_ACCESS_TOKEN_URL,
+                'bitbucket.org',
+                array(
+                    'username' => 'x-token-auth',
+                    'password' => 'my_password'
+                )
+            ),
+            array(
+                'https://some-api.url.com',
+                'some-api.url.com',
+                array(
+                    'username' => 'my_username',
+                    'password' => 'my_password'
+                )
+            ),
+        );
+    }
+
+    /**
+     * @dataProvider basicHttpAuthenticationProvider
+     *
+     * @param string $url
+     * @param string $origin
+     * @param array $auth
+     */
+    public function testAddAuthenticationHeaderWithBasicHttpAuthentication($url, $origin, $auth)
+    {
+        $headers = array(
+            'Accept-Encoding: gzip',
+            'Connection: close'
+        );
+
+        $this->expectsAuthentication($origin, $auth);
+
+        $this->config->expects($this->once())
+            ->method('get')
+            ->with('gitlab-domains')
+            ->willReturn(array());
+
+        $this->io->expects($this->once())
+            ->method('writeError')
+            ->with(
+                'Using HTTP basic authentication with username "' . $auth['username'] . '"',
+                true,
+                IOInterface::DEBUG
+            );
+
+        $expectedHeaders = array_merge(
+            $headers,
+            array('Authorization: Basic ' . base64_encode($auth['username'] . ':' . $auth['password']))
+        );
+
+        $this->assertSame(
+            $expectedHeaders,
+            $this->authHelper->addAuthenticationHeader($headers, $origin, $url)
+        );
+    }
+
+    /**
+     * @dataProvider bitbucketPublicUrlProvider
+     *
+     * @param string $url
+     */
+    public function testIsPublicBitBucketDownloadWithBitbucketPublicUrl($url)
+    {
+        $this->assertTrue($this->authHelper->isPublicBitBucketDownload($url));
+    }
+
+    public function testIsPublicBitBucketDownloadWithNonBitbucketPublicUrl()
+    {
+        $this->assertFalse($this->authHelper->isPublicBitBucketDownload(
+            'https://bitbucket.org/site/oauth2/authorize')
+        );
+    }
+
+    public function testStoreAuthAutomatically()
+    {
+        $origin = 'github.com';
+        $storeAuth = true;
+        $auth = array(
+            'username' => 'my_username',
+            'password' => 'my_password'
+        );
+
+        /** @var \Composer\Config\ConfigSourceInterface|\PHPUnit_Framework_MockObject_MockObject $configSource */
+        $configSource = $this
+            ->getMockBuilder('Composer\Config\ConfigSourceInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->config->expects($this->once())
+            ->method('getAuthConfigSource')
+            ->willReturn($configSource);
+
+        $this->io->expects($this->once())
+            ->method('getAuthentication')
+            ->with($origin)
+            ->willReturn($auth);
+
+        $configSource->expects($this->once())
+            ->method('addConfigSetting')
+            ->with('http-basic.'.$origin, $auth)
+            ->willReturn($configSource);
+
+        $this->authHelper->storeAuth($origin, $storeAuth);
+    }
+
+    public function testStoreAuthWithPromptYesAnswer()
+    {
+        $origin = 'github.com';
+        $storeAuth = 'prompt';
+        $auth = array(
+            'username' => 'my_username',
+            'password' => 'my_password'
+        );
+        $answer = 'y';
+        $configSourceName = 'https://api.gitlab.com/source';
+
+        /** @var \Composer\Config\ConfigSourceInterface|\PHPUnit_Framework_MockObject_MockObject $configSource */
+        $configSource = $this
+            ->getMockBuilder('Composer\Config\ConfigSourceInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->config->expects($this->once())
+            ->method('getAuthConfigSource')
+            ->willReturn($configSource);
+
+        $configSource->expects($this->once())
+            ->method('getName')
+            ->willReturn($configSourceName);
+
+        $this->io->expects($this->once())
+            ->method('askAndValidate')
+            ->with(
+                'Do you want to store credentials for '.$origin.' in '.$configSourceName.' ? [Yn] ',
+                $this->anything(),
+                null,
+                'y'
+            )
+            ->willReturnCallback(function ($question, $validator, $attempts, $default) use ($answer) {
+
+                $validator($answer);
+
+                return $answer;
+            });
+
+        $this->io->expects($this->once())
+            ->method('getAuthentication')
+            ->with($origin)
+            ->willReturn($auth);
+
+        $configSource->expects($this->once())
+            ->method('addConfigSetting')
+            ->with('http-basic.'.$origin, $auth)
+            ->willReturn($configSource);
+
+        $this->authHelper->storeAuth($origin, $storeAuth);
+    }
+
+    public function testStoreAuthWithPromptNoAnswer()
+    {
+        $origin = 'github.com';
+        $storeAuth = 'prompt';
+        $answer = 'n';
+        $configSourceName = 'https://api.gitlab.com/source';
+
+        /** @var \Composer\Config\ConfigSourceInterface|\PHPUnit_Framework_MockObject_MockObject $configSource */
+        $configSource = $this
+            ->getMockBuilder('Composer\Config\ConfigSourceInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->config->expects($this->once())
+            ->method('getAuthConfigSource')
+            ->willReturn($configSource);
+
+        $configSource->expects($this->once())
+            ->method('getName')
+            ->willReturn($configSourceName);
+
+        $this->io->expects($this->once())
+            ->method('askAndValidate')
+            ->with(
+                'Do you want to store credentials for '.$origin.' in '.$configSourceName.' ? [Yn] ',
+                $this->anything(),
+                null,
+                'y'
+            )
+            ->willReturnCallback(function ($question, $validator, $attempts, $default) use ($answer) {
+
+                $validator($answer);
+
+                return $answer;
+            });
+
+        $this->authHelper->storeAuth($origin, $storeAuth);
+    }
+
+    /**
+     * @expectedException  RuntimeException
+     */
+    public function testStoreAuthWithPromptInvalidAnswer()
+    {
+        $origin = 'github.com';
+        $storeAuth = 'prompt';
+        $answer = 'invalid';
+        $configSourceName = 'https://api.gitlab.com/source';
+
+        /** @var \Composer\Config\ConfigSourceInterface|\PHPUnit_Framework_MockObject_MockObject $configSource */
+        $configSource = $this
+            ->getMockBuilder('Composer\Config\ConfigSourceInterface')
+            ->disableOriginalConstructor()
+            ->getMock();
+
+        $this->config->expects($this->once())
+            ->method('getAuthConfigSource')
+            ->willReturn($configSource);
+
+        $configSource->expects($this->once())
+            ->method('getName')
+            ->willReturn($configSourceName);
+
+        $this->io->expects($this->once())
+            ->method('askAndValidate')
+            ->with(
+                'Do you want to store credentials for '.$origin.' in '.$configSourceName.' ? [Yn] ',
+                $this->anything(),
+                null,
+                'y'
+            )
+            ->willReturnCallback(function ($question, $validator, $attempts, $default) use ($answer) {
+
+                $validator($answer);
+
+                return $answer;
+            });
+
+        $this->authHelper->storeAuth($origin, $storeAuth);
+    }
+
+    /**
+     * @param $origin
+     * @param $auth
+     */
+    private function expectsAuthentication($origin, $auth)
+    {
+        $this->io->expects($this->once())
+            ->method('hasAuthentication')
+            ->with($origin)
+            ->willReturn(true);
+
+        $this->io->expects($this->once())
+            ->method('getAuthentication')
+            ->with($origin)
+            ->willReturn($auth);
+    }
+}