From 57fbc1d43311430d69fd9cf187f64105caacc73e Mon Sep 17 00:00:00 2001 From: Tom Anderson Date: Thu, 24 Apr 2014 11:36:42 -0700 Subject: [PATCH] 100% unit test of Github, Google, Facebook and LinkedIn --- src/Provider/Facebook.php | 16 +++-- src/Provider/IdentityProvider.php | 74 ++++++++++++-------- src/Provider/LinkedIn.php | 4 +- src/Token/AccessToken.php | 2 +- tests/src/Provider/FacebookTest.php | 100 ++++++++++++++++++++++++++++ tests/src/Provider/GithubTest.php | 50 ++++++++++++-- tests/src/Provider/GoogleTest.php | 97 +++++++++++++++++++++++++++ tests/src/Provider/LinkedInTest.php | 97 +++++++++++++++++++++++++++ 8 files changed, 399 insertions(+), 41 deletions(-) create mode 100644 tests/src/Provider/FacebookTest.php create mode 100644 tests/src/Provider/GoogleTest.php create mode 100644 tests/src/Provider/LinkedInTest.php diff --git a/src/Provider/Facebook.php b/src/Provider/Facebook.php index 3311fff..a545422 100644 --- a/src/Provider/Facebook.php +++ b/src/Provider/Facebook.php @@ -24,18 +24,22 @@ class Facebook extends IdentityProvider public function userDetails($response, \League\OAuth2\Client\Token\AccessToken $token) { - $imageHeaders = get_headers('https://graph.facebook.com/me/picture?type=normal&access_token='.$token->accessToken, 1); + $client = $this->getHttpClient(); + $client->setBaseUrl('https://graph.facebook.com/me/picture?type=normal&access_token=' . $token->accessToken); + $request = $client->get()->send(); + $info = $request->getInfo(); + $imageUrl = $info['url']; $user = new User; $user->uid = $response->id; - $user->nickname = isset($response->username) ? $response->username : null; + $user->nickname = (isset($response->username)) ? $response->username : null; $user->name = $response->name; $user->firstName = $response->first_name; $user->lastName = $response->last_name; - $user->email = isset($response->email) ? $response->email : null; - $user->location = isset($response->hometown->name) ? $response->hometown->name : null; - $user->description = isset($response->bio) ? $response->bio : null; - $user->imageUrl = $imageHeaders['Location']; + $user->email = (isset($response->email)) ? $response->email : null; + $user->location = (isset($response->hometown->name)) ? $response->hometown->name : null; + $user->description = (isset($response->bio)) ? $response->bio : null; + $user->imageUrl = ($imageUrl) ? $imageUrl: null; $user->urls = array( 'Facebook' => $response->link, ); diff --git a/src/Provider/IdentityProvider.php b/src/Provider/IdentityProvider.php index c0ae7a4..8741383 100644 --- a/src/Provider/IdentityProvider.php +++ b/src/Provider/IdentityProvider.php @@ -3,6 +3,7 @@ namespace League\OAuth2\Client\Provider; use Guzzle\Service\Client as GuzzleClient; +use Guzzle\Http\Exception\BadResponseException; use League\OAuth2\Client\Token\AccessToken as AccessToken; use League\OAuth2\Client\Exception\IDPException as IDPException; use League\OAuth2\Client\Grant\GrantInterface; @@ -27,10 +28,10 @@ abstract class IdentityProvider public $responseType = 'json'; - protected $cachedUserDetailsResponse; - public $headers = null; + protected $httpClient; + /** @var int This represents: PHP_QUERY_RFC1738. The default encryption type for the http_build_query setup */ protected $httpBuildEncType = 1; @@ -41,6 +42,22 @@ abstract class IdentityProvider $this->{$option} = $value; } } + + $this->setHttpClient(new GuzzleClient); + } + + public function setHttpClient(GuzzleClient $client) + { + $this->httpClient = $client; + + return $this; + } + + public function getHttpClient() + { + $client = clone $this->httpClient; + + return $client; } abstract public function urlAuthorize(); @@ -77,11 +94,13 @@ abstract class IdentityProvider return $this->urlAuthorize() . '?' . $this->httpBuildQuery($params, '', '&', PHP_QUERY_RFC1738); } + // @codeCoverageIgnoreStart public function authorize($options = array()) { header('Location: ' . $this->getAuthorizationUrl($options)); exit; } + // @codeCoverageIgnoreEnd public function getAccessToken($grant = 'authorization_code', $params = array()) { @@ -105,19 +124,21 @@ abstract class IdentityProvider $requestParams = $grant->prepRequestParams($defaultParams, $params); try { - switch ($this->method) { - case 'get': - $client = new GuzzleClient($this->urlAccessToken() . '?' . $this->httpBuildQuery($requestParams, '', '&', PHP_QUERY_RFC1738)); + switch (strtoupper($this->method)) { + case 'GET': + $client = $this->getHttpClient(); + $client->setBaseUrl($this->urlAccessToken() . '?' . $this->httpBuildQuery($requestParams, '', '&', PHP_QUERY_RFC1738)); $request = $client->send(); $response = $request->getBody(); break; - case 'post': - $client = new GuzzleClient($this->urlAccessToken()); + case 'POST': + $client = $this->getHttpClient(); + $client->setBaseUrl($this->urlAccessToken()); $request = $client->post(null, null, $requestParams)->send(); $response = $request->getBody(); break; } - } catch (\Guzzle\Http\Exception\BadResponseException $e) { + } catch (BadResponseException $e) { $raw_response = explode("\n", $e->getResponse()); $response = end($raw_response); } @@ -188,33 +209,30 @@ abstract class IdentityProvider return $url; } + protected function fetchUserDetails(AccessToken $token, $force = false) { - if (! $this->cachedUserDetailsResponse || $force == true) { + $url = $this->urlUserDetails($token); - $url = $this->urlUserDetails($token); + try { - try { - - $client = new GuzzleClient($url); - - if ($this->headers) { - $client->setDefaultOption('headers', $this->headers); - } - - $request = $client->get()->send(); - $response = $request->getBody(); - $this->cachedUserDetailsResponse = $response; - - } catch (\Guzzle\Http\Exception\BadResponseException $e) { - - $raw_response = explode("\n", $e->getResponse()); - throw new IDPException(end($raw_response)); + $client = $this->getHttpClient(); + $client->setBaseUrl($url); + if ($this->headers) { + $client->setDefaultOption('headers', $this->headers); } + + $request = $client->get()->send(); + $response = $request->getBody(); + + } catch (BadResponseException $e) { + // @codeCoverageIgnoreStart + $raw_response = explode("\n", $e->getResponse()); + throw new IDPException(end($raw_response)); + // @codeCoverageIgnoreEnd } - return $this->cachedUserDetailsResponse; + return $response; } - } diff --git a/src/Provider/LinkedIn.php b/src/Provider/LinkedIn.php index 6922806..d9677f4 100644 --- a/src/Provider/LinkedIn.php +++ b/src/Provider/LinkedIn.php @@ -20,7 +20,7 @@ class LinkedIn extends IdentityProvider public function urlUserDetails(\League\OAuth2\Client\Token\AccessToken $token) { - return 'https://api.linkedin.com/v1/people/~:('.implode(",", $this->fields).')?format=json&oauth2_access_token='.$token; + return 'https://api.linkedin.com/v1/people/~:(' . implode(",", $this->fields) . ')?format=json&oauth2_access_token=' . $token; } public function userDetails($response, \League\OAuth2\Client\Token\AccessToken $token) @@ -28,7 +28,7 @@ class LinkedIn extends IdentityProvider $user = new User; $user->uid = $response->id; - $user->name = $response->firstName.' '.$response->lastName; + $user->name = $response->firstName . ' ' . $response->lastName; $user->firstName = $response->firstName; $user->lastName = $response->lastName; $user->email = isset($response->emailAddress) ? $response->emailAddress : null; diff --git a/src/Token/AccessToken.php b/src/Token/AccessToken.php index 2710099..0784e85 100755 --- a/src/Token/AccessToken.php +++ b/src/Token/AccessToken.php @@ -34,7 +34,7 @@ class AccessToken */ public function __construct(array $options = null) { - if ( ! isset($options['access_token'])) { + if (! isset($options['access_token'])) { throw new \InvalidArgumentException('Required option not passed: access_token' . PHP_EOL.print_r($options, true)); } diff --git a/tests/src/Provider/FacebookTest.php b/tests/src/Provider/FacebookTest.php new file mode 100644 index 0000000..1250c09 --- /dev/null +++ b/tests/src/Provider/FacebookTest.php @@ -0,0 +1,100 @@ +provider = new \League\OAuth2\Client\Provider\Facebook(array( + 'clientId' => 'mock_client_id', + 'clientSecret' => 'mock_secret', + 'redirectUri' => 'none', + )); + } + + protected function tearDown() + { +# m::close(); + } + + public function testAuthorizationUrl() + { + $url = $this->provider->getAuthorizationUrl(); + $uri = parse_url($url); + parse_str($uri['query'], $query); + + $this->assertArrayHasKey('client_id', $query); + $this->assertArrayHasKey('redirect_uri', $query); + $this->assertArrayHasKey('state', $query); + $this->assertArrayHasKey('scope', $query); + $this->assertArrayHasKey('response_type', $query); + $this->assertArrayHasKey('approval_prompt', $query); + } + + public function testUrlAccessToken() + { + $url = $this->provider->urlAccessToken(); + $uri = parse_url($url); + + $this->assertEquals('/oauth/access_token', $uri['path']); + } + + + public function testGetAccessToken() + { + $response = m::mock('Guzzle\Http\Message\Response'); + $response->shouldReceive('getBody')->times(1)->andReturn('access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&uid=1'); + + $client = m::mock('Guzzle\Service\Client'); + $client->shouldReceive('setBaseUrl')->times(1); + $client->shouldReceive('post->send')->times(1)->andReturn($response); + $this->provider->setHttpClient($client); + + $token = $this->provider->getAccessToken('authorization_code', array('code' => 'mock_authorization_code')); + +# print_r($token);die(); + + $this->assertEquals('mock_access_token', $token->accessToken); + $this->assertLessThanOrEqual(time() + 3600, $token->expires); + $this->assertGreaterThanOrEqual(time(), $token->expires); + $this->assertEquals('mock_refresh_token', $token->refreshToken); + $this->assertEquals('1', $token->uid); + } + + public function testScopes() + { + $this->assertEquals(array('offline_access', 'email', 'read_stream'), $this->provider->getScopes()); + } + + public function testUserData() + { + $postResponse = m::mock('Guzzle\Http\Message\Response'); + $postResponse->shouldReceive('getBody')->times(1)->andReturn('access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&uid=1'); + + $getResponse = m::mock('Guzzle\Http\Message\Response'); + $getResponse->shouldReceive('getBody')->andReturn('{"id": 12345, "name": "mock_name", "username": "mock_username", "first_name": "mock_first_name", "last_name": "mock_last_name", "email": "mock_email", "Location": "mock_home", "bio": "mock_description", "link": "mock_facebook_url"}'); + $getResponse->shouldReceive('getInfo')->andReturn(array('url' => 'mock_image_url')); + + $client = m::mock('Guzzle\Service\Client'); + $client->shouldReceive('setBaseUrl')->times(1); + $client->shouldReceive('post->send')->times(1)->andReturn($postResponse); + $client->shouldReceive('get->send')->andReturn($getResponse); + $this->provider->setHttpClient($client); + + $token = $this->provider->getAccessToken('authorization_code', array('code' => 'mock_authorization_code')); + $user = $this->provider->getUserDetails($token); + + $this->assertEquals(12345, $this->provider->getUserUid($token)); + $this->assertEquals(array('mock_first_name', 'mock_last_name'), $this->provider->getUserScreenName($token)); + $this->assertEquals('mock_email', $this->provider->getUserEmail($token)); + $this->assertEquals('mock_email', $user->email); + } +} diff --git a/tests/src/Provider/GithubTest.php b/tests/src/Provider/GithubTest.php index 350bcc3..6250728 100644 --- a/tests/src/Provider/GithubTest.php +++ b/tests/src/Provider/GithubTest.php @@ -27,7 +27,7 @@ class GithubTest extends \PHPUnit_Framework_TestCase { $url = $this->provider->getAuthorizationUrl(); $uri = parse_url($url); - parse_str($uri['query'], $query); + parse_str($uri['query'], $query); $this->assertArrayHasKey('client_id', $query); $this->assertArrayHasKey('redirect_uri', $query); @@ -45,10 +45,52 @@ class GithubTest extends \PHPUnit_Framework_TestCase $this->assertEquals('/login/oauth/access_token', $uri['path']); } -/* + public function testGetAccessToken() { - $t = $this->provider->getAccessToken('authorization_code', array('code' => 'mock_authorization_code')); + $response = m::mock('Guzzle\Http\Message\Response'); + $response->shouldReceive('getBody')->times(1)->andReturn('access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&uid=1'); + + $client = m::mock('Guzzle\Service\Client'); + $client->shouldReceive('setBaseUrl')->times(1); + $client->shouldReceive('post->send')->times(1)->andReturn($response); + $this->provider->setHttpClient($client); + + $token = $this->provider->getAccessToken('authorization_code', array('code' => 'mock_authorization_code')); + + $this->assertEquals('mock_access_token', $token->accessToken); + $this->assertLessThanOrEqual(time() + 3600, $token->expires); + $this->assertGreaterThanOrEqual(time(), $token->expires); + $this->assertEquals('mock_refresh_token', $token->refreshToken); + $this->assertEquals('1', $token->uid); + } + + public function testScopes() + { + $this->provider->setScopes(array('user', 'repo')); + $this->assertEquals(array('user', 'repo'), $this->provider->getScopes()); + } + + public function testUserData() + { + $postResponse = m::mock('Guzzle\Http\Message\Response'); + $postResponse->shouldReceive('getBody')->times(1)->andReturn('access_token=mock_access_token&expires=3600&refresh_token=mock_refresh_token&uid=1'); + + $getResponse = m::mock('Guzzle\Http\Message\Response'); + $getResponse->shouldReceive('getBody')->times(1)->andReturn('{"id": 12345, "login": "mock_login", "name": "mock_name", "email": "mock_email"}'); + + $client = m::mock('Guzzle\Service\Client'); + $client->shouldReceive('setBaseUrl')->times(1); + $client->shouldReceive('post->send')->times(1)->andReturn($postResponse); + $client->shouldReceive('get->send')->times(1)->andReturn($getResponse); + $this->provider->setHttpClient($client); + + $token = $this->provider->getAccessToken('authorization_code', array('code' => 'mock_authorization_code')); + $user = $this->provider->getUserDetails($token); + + $this->assertEquals(12345, $this->provider->getUserUid($token)); + $this->assertEquals('mock_name', $this->provider->getUserScreenName($token)); + $this->assertEquals('mock_name', $user->name); + $this->assertEquals('mock_email', $this->provider->getUserEmail($token)); } -*/ } diff --git a/tests/src/Provider/GoogleTest.php b/tests/src/Provider/GoogleTest.php new file mode 100644 index 0000000..22954a6 --- /dev/null +++ b/tests/src/Provider/GoogleTest.php @@ -0,0 +1,97 @@ +provider = new \League\OAuth2\Client\Provider\Google(array( + 'clientId' => 'mock_client_id', + 'clientSecret' => 'mock_secret', + 'redirectUri' => 'none', + )); + } + + protected function tearDown() + { +# m::close(); + } + + public function testAuthorizationUrl() + { + $url = $this->provider->getAuthorizationUrl(); + $uri = parse_url($url); + parse_str($uri['query'], $query); + + $this->assertArrayHasKey('client_id', $query); + $this->assertArrayHasKey('redirect_uri', $query); + $this->assertArrayHasKey('state', $query); + $this->assertArrayHasKey('scope', $query); + $this->assertArrayHasKey('response_type', $query); + $this->assertArrayHasKey('approval_prompt', $query); + } + + public function testUrlAccessToken() + { + $url = $this->provider->urlAccessToken(); + $uri = parse_url($url); + + $this->assertEquals('/o/oauth2/token', $uri['path']); + } + + + public function testGetAccessToken() + { + $response = m::mock('Guzzle\Http\Message\Response'); + $response->shouldReceive('getBody')->times(1)->andReturn('{"access_token": "mock_access_token", "expires": 3600, "refresh_token": "mock_refresh_token", "uid": 1}'); + + $client = m::mock('Guzzle\Service\Client'); + $client->shouldReceive('setBaseUrl')->times(1); + $client->shouldReceive('post->send')->times(1)->andReturn($response); + $this->provider->setHttpClient($client); + + $token = $this->provider->getAccessToken('authorization_code', array('code' => 'mock_authorization_code')); + +# print_r($token);die(); + + $this->assertEquals('mock_access_token', $token->accessToken); + $this->assertLessThanOrEqual(time() + 3600, $token->expires); + $this->assertGreaterThanOrEqual(time(), $token->expires); + $this->assertEquals('mock_refresh_token', $token->refreshToken); + $this->assertEquals('1', $token->uid); + } + + public function testScopes() + { + $this->assertEquals(array('https://www.googleapis.com/auth/userinfo.profile', 'https://www.googleapis.com/auth/userinfo.email'), $this->provider->getScopes()); + } + + public function testUserData() + { + $postResponse = m::mock('Guzzle\Http\Message\Response'); + $postResponse->shouldReceive('getBody')->times(1)->andReturn('{"access_token": "mock_access_token", "expires": 3600, "refresh_token": "mock_refresh_token", "uid": 1}'); + + $getResponse = m::mock('Guzzle\Http\Message\Response'); + $getResponse->shouldReceive('getBody')->times(1)->andReturn('{"id": 12345, "name": "mock_name", "given_name": "mock_first_name", "family_name": "mock_last_name", "email": "mock_email"}'); + + $client = m::mock('Guzzle\Service\Client'); + $client->shouldReceive('setBaseUrl')->times(1); + $client->shouldReceive('post->send')->times(1)->andReturn($postResponse); + $client->shouldReceive('get->send')->times(1)->andReturn($getResponse); + $this->provider->setHttpClient($client); + + $token = $this->provider->getAccessToken('authorization_code', array('code' => 'mock_authorization_code')); + $user = $this->provider->getUserDetails($token); + + $this->assertEquals(12345, $this->provider->getUserUid($token)); + $this->assertEquals(array('mock_first_name', 'mock_last_name'), $this->provider->getUserScreenName($token)); + $this->assertEquals('mock_email', $this->provider->getUserEmail($token)); + $this->assertEquals('mock_email', $user->email); + } +} diff --git a/tests/src/Provider/LinkedInTest.php b/tests/src/Provider/LinkedInTest.php new file mode 100644 index 0000000..c77c605 --- /dev/null +++ b/tests/src/Provider/LinkedInTest.php @@ -0,0 +1,97 @@ +provider = new \League\OAuth2\Client\Provider\LinkedIn(array( + 'clientId' => 'mock_client_id', + 'clientSecret' => 'mock_secret', + 'redirectUri' => 'none', + )); + } + + protected function tearDown() + { +# m::close(); + } + + public function testAuthorizationUrl() + { + $url = $this->provider->getAuthorizationUrl(); + $uri = parse_url($url); + parse_str($uri['query'], $query); + + $this->assertArrayHasKey('client_id', $query); + $this->assertArrayHasKey('redirect_uri', $query); + $this->assertArrayHasKey('state', $query); + $this->assertArrayHasKey('scope', $query); + $this->assertArrayHasKey('response_type', $query); + $this->assertArrayHasKey('approval_prompt', $query); + } + + public function testUrlAccessToken() + { + $url = $this->provider->urlAccessToken(); + $uri = parse_url($url); + + $this->assertEquals('/uas/oauth2/accessToken', $uri['path']); + } + + + public function testGetAccessToken() + { + $response = m::mock('Guzzle\Http\Message\Response'); + $response->shouldReceive('getBody')->times(1)->andReturn('{"access_token": "mock_access_token", "expires": 3600, "refresh_token": "mock_refresh_token", "uid": 1}'); + + $client = m::mock('Guzzle\Service\Client'); + $client->shouldReceive('setBaseUrl')->times(1); + $client->shouldReceive('post->send')->times(1)->andReturn($response); + $this->provider->setHttpClient($client); + + $token = $this->provider->getAccessToken('authorization_code', array('code' => 'mock_authorization_code')); + +# print_r($token);die(); + + $this->assertEquals('mock_access_token', $token->accessToken); + $this->assertLessThanOrEqual(time() + 3600, $token->expires); + $this->assertGreaterThanOrEqual(time(), $token->expires); + $this->assertEquals('mock_refresh_token', $token->refreshToken); + $this->assertEquals('1', $token->uid); + } + + public function testScopes() + { + $this->assertEquals(array('r_basicprofile r_emailaddress r_contactinfo'), $this->provider->getScopes()); + } + + public function testUserData() + { + $postResponse = m::mock('Guzzle\Http\Message\Response'); + $postResponse->shouldReceive('getBody')->times(1)->andReturn('{"access_token": "mock_access_token", "expires": 3600, "refresh_token": "mock_refresh_token", "uid": 1}'); + + $getResponse = m::mock('Guzzle\Http\Message\Response'); + $getResponse->shouldReceive('getBody')->times(1)->andReturn('{"id": 12345, "firstName": "mock_first_name", "lastName": "mock_last_name", "emailAddress": "mock_email", "location": { "name": "mock_location" }, "headline": "mock_headline", "pictureUrl": "mock_picture_url", "publicProfileUrl": "mock_profile_url"}'); + + $client = m::mock('Guzzle\Service\Client'); + $client->shouldReceive('setBaseUrl')->times(1); + $client->shouldReceive('post->send')->times(1)->andReturn($postResponse); + $client->shouldReceive('get->send')->times(1)->andReturn($getResponse); + $this->provider->setHttpClient($client); + + $token = $this->provider->getAccessToken('authorization_code', array('code' => 'mock_authorization_code')); + $user = $this->provider->getUserDetails($token); + + $this->assertEquals(12345, $this->provider->getUserUid($token)); + $this->assertEquals(array('mock_first_name', 'mock_last_name'), $this->provider->getUserScreenName($token)); + $this->assertEquals('mock_email', $this->provider->getUserEmail($token)); + $this->assertEquals('mock_email', $user->email); + } +}