2022-02-23 15:58:18 +00:00
< ? php declare ( strict_types = 1 );
2016-03-14 01:33:13 +00:00
/*
* 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 ;
2023-06-07 12:35:16 +00:00
use Composer\Test\Mock\IOMock ;
2016-03-14 01:33:13 +00:00
use Composer\Util\Bitbucket ;
2018-10-31 11:44:54 +00:00
use Composer\Util\Http\Response ;
2020-02-07 03:18:45 +00:00
use Composer\Test\TestCase ;
2016-03-14 01:33:13 +00:00
/**
* @ author Paul Wenke < wenke . paul @ gmail . com >
*/
2017-11-04 14:52:13 +00:00
class BitbucketTest extends TestCase
2016-03-14 01:33:13 +00:00
{
2021-10-27 14:18:46 +00:00
/** @var string */
2016-03-14 01:33:13 +00:00
private $username = 'username' ;
2021-10-27 14:18:46 +00:00
/** @var string */
2016-03-14 01:33:13 +00:00
private $password = 'password' ;
2021-10-27 14:18:46 +00:00
/** @var string */
2016-06-11 13:05:36 +00:00
private $consumer_key = 'consumer_key' ;
2021-10-27 14:18:46 +00:00
/** @var string */
2016-06-11 13:05:36 +00:00
private $consumer_secret = 'consumer_secret' ;
2021-10-27 14:18:46 +00:00
/** @var string */
2016-03-14 01:33:13 +00:00
private $message = 'mymessage' ;
2021-10-27 14:18:46 +00:00
/** @var string */
2016-03-14 01:33:13 +00:00
private $origin = 'bitbucket.org' ;
2021-10-27 14:18:46 +00:00
/** @var string */
2016-03-14 01:33:13 +00:00
private $token = 'bitbuckettoken' ;
2023-06-07 12:35:16 +00:00
/** @var IOMock */
2016-06-11 13:05:36 +00:00
private $io ;
2021-10-27 14:18:46 +00:00
/** @var \Composer\Util\HttpDownloader&\PHPUnit\Framework\MockObject\MockObject */
2018-10-31 11:44:54 +00:00
private $httpDownloader ;
2021-10-27 14:18:46 +00:00
/** @var \Composer\Config&\PHPUnit\Framework\MockObject\MockObject */
2016-06-11 13:05:36 +00:00
private $config ;
2021-10-27 14:18:46 +00:00
/** @var Bitbucket */
2016-06-11 13:05:36 +00:00
private $bitbucket ;
2016-12-26 23:39:02 +00:00
/** @var int */
private $time ;
2016-06-11 13:05:36 +00:00
2021-12-08 16:03:05 +00:00
protected function setUp () : void
2016-06-11 13:05:36 +00:00
{
2023-06-07 12:35:16 +00:00
$this -> io = $this -> getIOMock ();
2016-06-11 13:05:36 +00:00
2018-10-31 11:44:54 +00:00
$this -> httpDownloader = $this
-> getMockBuilder ( 'Composer\Util\HttpDownloader' )
2016-06-11 13:05:36 +00:00
-> disableOriginalConstructor ()
-> getMock ()
;
2018-04-12 08:24:56 +00:00
$this -> config = $this -> getMockBuilder ( 'Composer\Config' ) -> getMock ();
2016-06-11 13:05:36 +00:00
2016-12-26 23:39:02 +00:00
$this -> time = time ();
2018-10-31 11:44:54 +00:00
$this -> bitbucket = new Bitbucket ( $this -> io , $this -> config , null , $this -> httpDownloader , $this -> time );
2016-06-11 13:05:36 +00:00
}
2022-02-18 09:38:54 +00:00
public function testRequestAccessTokenWithValidOAuthConsumer () : void
2016-06-11 13:05:36 +00:00
{
2023-06-07 12:35:16 +00:00
$this -> io -> expects ([
[ 'auth' => [ $this -> origin , $this -> consumer_key , $this -> consumer_secret ]],
]);
2016-06-11 13:05:36 +00:00
2018-10-31 11:44:54 +00:00
$this -> httpDownloader -> expects ( $this -> once ())
-> method ( 'get' )
2016-06-11 13:05:36 +00:00
-> with (
Bitbucket :: OAUTH2_ACCESS_TOKEN_URL ,
2022-08-17 12:20:07 +00:00
[
2016-06-11 13:05:36 +00:00
'retry-auth-failure' => false ,
2022-08-17 12:20:07 +00:00
'http' => [
2016-06-11 13:05:36 +00:00
'method' => 'POST' ,
'content' => 'grant_type=client_credentials' ,
2022-08-17 12:20:07 +00:00
],
]
2016-06-11 13:05:36 +00:00
)
-> willReturn (
2018-10-31 11:44:54 +00:00
new Response (
2022-08-17 12:20:07 +00:00
[ 'url' => Bitbucket :: OAUTH2_ACCESS_TOKEN_URL ],
2018-10-31 11:44:54 +00:00
200 ,
2022-08-17 12:20:07 +00:00
[],
2018-10-31 11:44:54 +00:00
sprintf (
'{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}' ,
$this -> token
)
2016-06-11 13:05:36 +00:00
)
);
2016-12-26 23:39:02 +00:00
$this -> config -> expects ( $this -> once ())
-> method ( 'get' )
-> with ( 'bitbucket-oauth' )
-> willReturn ( null );
$this -> setExpectationsForStoringAccessToken ();
2016-06-11 13:05:36 +00:00
$this -> assertEquals (
2016-12-26 23:39:02 +00:00
$this -> token ,
2016-06-11 13:05:36 +00:00
$this -> bitbucket -> requestToken ( $this -> origin , $this -> consumer_key , $this -> consumer_secret )
);
}
2022-02-22 15:47:09 +00:00
public function testRequestAccessTokenWithValidOAuthConsumerAndValidStoredAccessToken () : Bitbucket
2016-12-27 11:48:54 +00:00
{
$this -> config -> expects ( $this -> once ())
-> method ( 'get' )
-> with ( 'bitbucket-oauth' )
-> willReturn (
2022-08-17 12:20:07 +00:00
[
$this -> origin => [
2016-12-27 11:48:54 +00:00
'access-token' => $this -> token ,
'access-token-expiration' => $this -> time + 1800 ,
'consumer-key' => $this -> consumer_key ,
2017-03-08 14:07:29 +00:00
'consumer-secret' => $this -> consumer_secret ,
2022-08-17 12:20:07 +00:00
],
]
2016-12-27 11:48:54 +00:00
);
$this -> assertEquals (
$this -> token ,
$this -> bitbucket -> requestToken ( $this -> origin , $this -> consumer_key , $this -> consumer_secret )
);
2020-04-28 09:45:30 +00:00
return $this -> bitbucket ;
2016-12-27 11:48:54 +00:00
}
2022-02-18 09:38:54 +00:00
public function testRequestAccessTokenWithValidOAuthConsumerAndExpiredAccessToken () : void
2016-12-27 11:48:54 +00:00
{
$this -> config -> expects ( $this -> once ())
-> method ( 'get' )
-> with ( 'bitbucket-oauth' )
-> willReturn (
2022-08-17 12:20:07 +00:00
[
$this -> origin => [
2016-12-27 11:48:54 +00:00
'access-token' => 'randomExpiredToken' ,
'access-token-expiration' => $this -> time - 400 ,
'consumer-key' => $this -> consumer_key ,
2017-03-08 14:07:29 +00:00
'consumer-secret' => $this -> consumer_secret ,
2022-08-17 12:20:07 +00:00
],
]
2016-12-27 11:48:54 +00:00
);
2023-06-07 12:35:16 +00:00
$this -> io -> expects ([
[ 'auth' => [ $this -> origin , $this -> consumer_key , $this -> consumer_secret ]],
]);
2016-12-27 11:48:54 +00:00
2018-10-31 11:44:54 +00:00
$this -> httpDownloader -> expects ( $this -> once ())
-> method ( 'get' )
2016-12-27 11:48:54 +00:00
-> with (
Bitbucket :: OAUTH2_ACCESS_TOKEN_URL ,
2022-08-17 12:20:07 +00:00
[
2016-12-27 11:48:54 +00:00
'retry-auth-failure' => false ,
2022-08-17 12:20:07 +00:00
'http' => [
2016-12-27 11:48:54 +00:00
'method' => 'POST' ,
'content' => 'grant_type=client_credentials' ,
2022-08-17 12:20:07 +00:00
],
]
2016-12-27 11:48:54 +00:00
)
-> willReturn (
2018-10-31 11:44:54 +00:00
new Response (
2022-08-17 12:20:07 +00:00
[ 'url' => Bitbucket :: OAUTH2_ACCESS_TOKEN_URL ],
2018-10-31 11:44:54 +00:00
200 ,
2022-08-17 12:20:07 +00:00
[],
2018-10-31 11:44:54 +00:00
sprintf (
'{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refreshtoken", "token_type": "bearer"}' ,
$this -> token
)
2016-12-27 11:48:54 +00:00
)
);
$this -> setExpectationsForStoringAccessToken ();
$this -> assertEquals (
$this -> token ,
$this -> bitbucket -> requestToken ( $this -> origin , $this -> consumer_key , $this -> consumer_secret )
);
}
2022-02-18 09:38:54 +00:00
public function testRequestAccessTokenWithUsernameAndPassword () : void
2016-06-11 13:05:36 +00:00
{
2023-06-07 12:35:16 +00:00
$this -> io -> expects ([
[ 'auth' => [ $this -> origin , $this -> username , $this -> password ]],
[ 'text' => 'Invalid OAuth consumer provided.' ],
[ 'text' => 'This can have three reasons:' ],
[ 'text' => '1. You are authenticating with a bitbucket username/password combination' ],
[ 'text' => '2. You are using an OAuth consumer, but didn\'t configure a (dummy) callback url' ],
[ 'text' => '3. You are using an OAuth consumer, but didn\'t configure it as private consumer' ],
], true );
2016-06-11 13:05:36 +00:00
2018-10-31 11:44:54 +00:00
$this -> httpDownloader -> expects ( $this -> once ())
-> method ( 'get' )
2016-06-11 13:05:36 +00:00
-> with (
Bitbucket :: OAUTH2_ACCESS_TOKEN_URL ,
2022-08-17 12:20:07 +00:00
[
2016-06-11 13:05:36 +00:00
'retry-auth-failure' => false ,
2022-08-17 12:20:07 +00:00
'http' => [
2016-06-11 13:05:36 +00:00
'method' => 'POST' ,
'content' => 'grant_type=client_credentials' ,
2022-08-17 12:20:07 +00:00
],
]
2016-06-11 13:05:36 +00:00
)
-> willThrowException (
new \Composer\Downloader\TransportException (
sprintf (
'The \'%s\' URL could not be accessed: HTTP/1.1 400 BAD REQUEST' ,
Bitbucket :: OAUTH2_ACCESS_TOKEN_URL
),
400
)
);
2016-12-26 23:39:02 +00:00
$this -> config -> expects ( $this -> once ())
-> method ( 'get' )
-> with ( 'bitbucket-oauth' )
-> willReturn ( null );
$this -> assertEquals ( '' , $this -> bitbucket -> requestToken ( $this -> origin , $this -> username , $this -> password ));
2016-06-11 13:05:36 +00:00
}
2022-02-18 09:38:54 +00:00
public function testRequestAccessTokenWithUsernameAndPasswordWithUnauthorizedResponse () : void
2020-04-28 09:45:30 +00:00
{
$this -> config -> expects ( $this -> once ())
-> method ( 'get' )
-> with ( 'bitbucket-oauth' )
-> willReturn ( null );
2023-06-07 12:35:16 +00:00
$this -> io -> expects ([
[ 'auth' => [ $this -> origin , $this -> username , $this -> password ]],
[ 'text' => 'Invalid OAuth consumer provided.' ],
[ 'text' => 'You can also add it manually later by using "composer config --global --auth bitbucket-oauth.bitbucket.org <consumer-key> <consumer-secret>"' ],
], true );
2020-04-28 09:45:30 +00:00
$this -> httpDownloader -> expects ( $this -> once ())
-> method ( 'get' )
-> with (
Bitbucket :: OAUTH2_ACCESS_TOKEN_URL ,
2022-08-17 12:20:07 +00:00
[
2020-04-28 09:45:30 +00:00
'retry-auth-failure' => false ,
2022-08-17 12:20:07 +00:00
'http' => [
2020-04-28 09:45:30 +00:00
'method' => 'POST' ,
'content' => 'grant_type=client_credentials' ,
2022-08-17 12:20:07 +00:00
],
]
2020-04-28 09:45:30 +00:00
)
2020-11-22 13:48:56 +00:00
-> willThrowException ( new \Composer\Downloader\TransportException ( 'HTTP/1.1 401 UNAUTHORIZED' , 401 ));
2020-04-28 09:45:30 +00:00
$this -> assertEquals ( '' , $this -> bitbucket -> requestToken ( $this -> origin , $this -> username , $this -> password ));
}
2022-02-18 09:38:54 +00:00
public function testRequestAccessTokenWithUsernameAndPasswordWithNotFoundResponse () : void
2020-04-28 09:45:30 +00:00
{
2021-12-09 19:55:26 +00:00
self :: expectException ( 'Composer\Downloader\TransportException' );
2020-04-28 09:45:30 +00:00
$this -> config -> expects ( $this -> once ())
-> method ( 'get' )
-> with ( 'bitbucket-oauth' )
-> willReturn ( null );
2023-06-07 12:35:16 +00:00
$this -> io -> expects ([
[ 'auth' => [ $this -> origin , $this -> username , $this -> password ]],
]);
2020-04-28 09:45:30 +00:00
2020-11-22 13:48:56 +00:00
$exception = new \Composer\Downloader\TransportException ( 'HTTP/1.1 404 NOT FOUND' , 404 );
2020-04-28 09:45:30 +00:00
$this -> httpDownloader -> expects ( $this -> once ())
-> method ( 'get' )
-> with (
Bitbucket :: OAUTH2_ACCESS_TOKEN_URL ,
2022-08-17 12:20:07 +00:00
[
2020-04-28 09:45:30 +00:00
'retry-auth-failure' => false ,
2022-08-17 12:20:07 +00:00
'http' => [
2020-04-28 09:45:30 +00:00
'method' => 'POST' ,
'content' => 'grant_type=client_credentials' ,
2022-08-17 12:20:07 +00:00
],
]
2020-04-28 09:45:30 +00:00
)
-> willThrowException ( $exception );
$this -> bitbucket -> requestToken ( $this -> origin , $this -> username , $this -> password );
}
2022-02-18 09:38:54 +00:00
public function testUsernamePasswordAuthenticationFlow () : void
2016-03-14 01:33:13 +00:00
{
2023-06-07 12:35:16 +00:00
$this -> io -> expects ([
[ 'text' => $this -> message ],
[ 'ask' => 'Consumer Key (hidden): ' , 'reply' => $this -> consumer_key ],
[ 'ask' => 'Consumer Secret (hidden): ' , 'reply' => $this -> consumer_secret ],
]);
2016-03-14 01:33:13 +00:00
2018-10-31 11:44:54 +00:00
$this -> httpDownloader
2016-03-14 01:33:13 +00:00
-> expects ( $this -> once ())
2018-10-31 11:44:54 +00:00
-> method ( 'get' )
2016-03-14 01:33:13 +00:00
-> with (
2018-10-31 11:44:54 +00:00
$this -> equalTo ( $url = sprintf ( 'https://%s/site/oauth2/access_token' , $this -> origin )),
2016-03-14 01:33:13 +00:00
$this -> anything ()
)
2016-12-26 23:39:02 +00:00
-> willReturn (
2018-10-31 11:44:54 +00:00
new Response (
2022-08-17 12:20:07 +00:00
[ 'url' => $url ],
2018-10-31 11:44:54 +00:00
200 ,
2022-08-17 12:20:07 +00:00
[],
2018-10-31 11:44:54 +00:00
sprintf (
'{"access_token": "%s", "scopes": "repository", "expires_in": 3600, "refresh_token": "refresh_token", "token_type": "bearer"}' ,
$this -> token
)
2016-07-02 15:03:01 +00:00
)
2020-09-11 21:13:42 +00:00
)
2016-12-26 23:39:02 +00:00
;
2016-07-02 15:03:01 +00:00
2016-12-26 23:39:02 +00:00
$this -> setExpectationsForStoringAccessToken ( true );
2016-07-02 15:03:01 +00:00
2016-06-11 13:05:36 +00:00
$this -> assertTrue ( $this -> bitbucket -> authorizeOAuthInteractively ( $this -> origin , $this -> message ));
2016-03-14 01:33:13 +00:00
}
2022-02-18 09:38:54 +00:00
public function testAuthorizeOAuthInteractivelyWithEmptyUsername () : void
2020-04-28 09:45:30 +00:00
{
$authConfigSourceMock = $this -> getMockBuilder ( 'Composer\Config\ConfigSourceInterface' ) -> getMock ();
$this -> config -> expects ( $this -> atLeastOnce ())
-> method ( 'getAuthConfigSource' )
-> willReturn ( $authConfigSourceMock );
2023-06-07 12:35:16 +00:00
$this -> io -> expects ([
[ 'ask' => 'Consumer Key (hidden): ' , 'reply' => '' ],
]);
2020-04-28 09:45:30 +00:00
$this -> assertFalse ( $this -> bitbucket -> authorizeOAuthInteractively ( $this -> origin , $this -> message ));
}
2022-02-18 09:38:54 +00:00
public function testAuthorizeOAuthInteractivelyWithEmptyPassword () : void
2020-04-28 09:45:30 +00:00
{
$authConfigSourceMock = $this -> getMockBuilder ( 'Composer\Config\ConfigSourceInterface' ) -> getMock ();
$this -> config -> expects ( $this -> atLeastOnce ())
-> method ( 'getAuthConfigSource' )
-> willReturn ( $authConfigSourceMock );
2023-06-07 12:35:16 +00:00
$this -> io -> expects ([
[ 'text' => $this -> message ],
[ 'ask' => 'Consumer Key (hidden): ' , 'reply' => $this -> consumer_key ],
[ 'ask' => 'Consumer Secret (hidden): ' , 'reply' => '' ],
]);
2020-04-28 09:45:30 +00:00
$this -> assertFalse ( $this -> bitbucket -> authorizeOAuthInteractively ( $this -> origin , $this -> message ));
}
2022-02-18 09:38:54 +00:00
public function testAuthorizeOAuthInteractivelyWithRequestAccessTokenFailure () : void
2020-04-28 09:45:30 +00:00
{
$authConfigSourceMock = $this -> getMockBuilder ( 'Composer\Config\ConfigSourceInterface' ) -> getMock ();
$this -> config -> expects ( $this -> atLeastOnce ())
-> method ( 'getAuthConfigSource' )
-> willReturn ( $authConfigSourceMock );
2023-06-07 12:35:16 +00:00
$this -> io -> expects ([
[ 'text' => $this -> message ],
[ 'ask' => 'Consumer Key (hidden): ' , 'reply' => $this -> consumer_key ],
[ 'ask' => 'Consumer Secret (hidden): ' , 'reply' => $this -> consumer_secret ],
]);
2020-04-28 09:45:30 +00:00
$this -> httpDownloader
-> expects ( $this -> once ())
-> method ( 'get' )
-> with (
$this -> equalTo ( $url = sprintf ( 'https://%s/site/oauth2/access_token' , $this -> origin )),
$this -> anything ()
)
-> willThrowException (
new \Composer\Downloader\TransportException (
sprintf (
'The \'%s\' URL could not be accessed: HTTP/1.1 400 BAD REQUEST' ,
Bitbucket :: OAUTH2_ACCESS_TOKEN_URL
),
400
)
);
$this -> assertFalse ( $this -> bitbucket -> authorizeOAuthInteractively ( $this -> origin , $this -> message ));
}
2022-02-22 15:47:09 +00:00
private function setExpectationsForStoringAccessToken ( bool $removeBasicAuth = false ) : void
2016-03-14 01:33:13 +00:00
{
2018-04-12 08:24:56 +00:00
$configSourceMock = $this -> getMockBuilder ( 'Composer\Config\ConfigSourceInterface' ) -> getMock ();
2016-12-26 23:39:02 +00:00
$this -> config -> expects ( $this -> once ())
-> method ( 'getConfigSource' )
-> willReturn ( $configSourceMock );
2016-03-14 01:33:13 +00:00
2016-12-26 23:39:02 +00:00
$configSourceMock -> expects ( $this -> once ())
2016-03-14 01:33:13 +00:00
-> method ( 'removeConfigSetting' )
2016-12-26 23:39:02 +00:00
-> with ( 'bitbucket-oauth.' . $this -> origin );
2018-04-12 08:24:56 +00:00
$authConfigSourceMock = $this -> getMockBuilder ( 'Composer\Config\ConfigSourceInterface' ) -> getMock ();
2016-12-26 23:39:02 +00:00
$this -> config -> expects ( $this -> atLeastOnce ())
-> method ( 'getAuthConfigSource' )
-> willReturn ( $authConfigSourceMock );
$authConfigSourceMock -> expects ( $this -> once ())
-> method ( 'addConfigSetting' )
-> with (
'bitbucket-oauth.' . $this -> origin ,
2022-08-17 12:20:07 +00:00
[
2016-12-26 23:39:02 +00:00
" consumer-key " => $this -> consumer_key ,
" consumer-secret " => $this -> consumer_secret ,
" access-token " => $this -> token ,
2017-03-08 14:07:29 +00:00
" access-token-expiration " => $this -> time + 3600 ,
2022-08-17 12:20:07 +00:00
]
2016-12-26 23:39:02 +00:00
);
2016-03-14 01:33:13 +00:00
2016-12-26 23:39:02 +00:00
if ( $removeBasicAuth ) {
$authConfigSourceMock -> expects ( $this -> once ())
-> method ( 'removeConfigSetting' )
-> with ( 'http-basic.' . $this -> origin );
}
2016-03-14 01:33:13 +00:00
}
2020-04-28 09:45:30 +00:00
2022-02-18 09:38:54 +00:00
public function testGetTokenWithoutAccessToken () : void
2020-04-28 09:45:30 +00:00
{
$this -> assertSame ( '' , $this -> bitbucket -> getToken ());
}
/**
* @ depends testRequestAccessTokenWithValidOAuthConsumerAndValidStoredAccessToken
*/
2022-02-18 09:38:54 +00:00
public function testGetTokenWithAccessToken ( Bitbucket $bitbucket ) : void
2020-04-28 09:45:30 +00:00
{
$this -> assertSame ( $this -> token , $bitbucket -> getToken ());
}
2022-02-18 09:38:54 +00:00
public function testAuthorizeOAuthWithWrongOriginUrl () : void
2020-04-28 09:45:30 +00:00
{
2020-11-22 13:48:56 +00:00
$this -> assertFalse ( $this -> bitbucket -> authorizeOAuth ( 'non-' . $this -> origin ));
2020-04-28 09:45:30 +00:00
}
2022-02-18 09:38:54 +00:00
public function testAuthorizeOAuthWithoutAvailableGitConfigToken () : void
2020-04-28 09:45:30 +00:00
{
2021-12-09 16:09:07 +00:00
$process = $this -> getProcessExecutorMock ();
2022-08-17 12:20:07 +00:00
$process -> expects ([], false , [ 'return' => - 1 ]);
2020-04-28 09:45:30 +00:00
$bitbucket = new Bitbucket ( $this -> io , $this -> config , $process , $this -> httpDownloader , $this -> time );
$this -> assertFalse ( $bitbucket -> authorizeOAuth ( $this -> origin ));
}
2022-02-18 09:38:54 +00:00
public function testAuthorizeOAuthWithAvailableGitConfigToken () : void
2020-04-28 09:45:30 +00:00
{
2021-12-09 16:09:07 +00:00
$process = $this -> getProcessExecutorMock ();
2020-04-28 09:45:30 +00:00
$bitbucket = new Bitbucket ( $this -> io , $this -> config , $process , $this -> httpDownloader , $this -> time );
$this -> assertTrue ( $bitbucket -> authorizeOAuth ( $this -> origin ));
}
2016-03-14 01:33:13 +00:00
}