diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..af1f417 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +composer.lock +/vendor diff --git a/composer.json b/composer.json index 1849ba0..0abba01 100644 --- a/composer.json +++ b/composer.json @@ -4,12 +4,13 @@ "homepage": "http://fortifi.co", "license": "BSD-3-Clause", "require": { - "fortifi/fortifi-api": "^7", - "league/oauth2-client": "~0.11", - "packaged/helpers": "~1.7" + "fortifi/fortifi-api": "~7.20", + "league/oauth2-client": "~2.2", + "packaged/helpers": "~1.7", + "packaged/api": "1.0.0" }, "require-dev": { - "phpunit/phpunit": "~4.8" + "phpunit/phpunit": "~6.3" }, "autoload": { "psr-4": { @@ -20,6 +21,10 @@ { "type": "vcs", "url": "git@github.com:fortifi/fortifi-api.git" + }, + { + "type": "vcs", + "url": "git@github.com:packaged/api.git" } ] } diff --git a/src/Fortifi.php b/src/Fortifi.php index 24b9679..1048ec0 100644 --- a/src/Fortifi.php +++ b/src/Fortifi.php @@ -68,7 +68,7 @@ public static function setEnvironment($env = self::ENV_STAGE) $prefix = 's'; } - static::$apiHost = $prefix . 'api.fortifi.co'; + static::$apiHost = $prefix . 'api.fortifi.io'; } public static function getInstance( @@ -102,7 +102,7 @@ public static function getInstance( public function getApi() { - $this->_api->setAccessToken($this->getToken()->accessToken); + $this->_api->setAccessToken($this->getToken()->getToken()); return $this->_api; } @@ -147,7 +147,7 @@ public function getRequestClientIp() 'HTTP_X_CLUSTER_CLIENT_IP', 'HTTP_FORWARDED_FOR', 'HTTP_FORWARDED', - 'REMOTE_ADDR' + 'REMOTE_ADDR', ]; if($ip === null) { diff --git a/src/Models/FortifiModel.php b/src/Models/FortifiModel.php index b57c41b..3d51475 100644 --- a/src/Models/FortifiModel.php +++ b/src/Models/FortifiModel.php @@ -27,7 +27,7 @@ public static function processRequest( catch(AccessDeniedException $e) { $token = $fortifi->getToken(true); - $fortifi->getApi()->setAccessToken($token->accessToken); + $fortifi->getApi()->setAccessToken($token->getToken()); return $request->get(); } } diff --git a/src/OAuth/FortifiAccessToken.php b/src/OAuth/FortifiAccessToken.php index eba6a74..7555a7d 100644 --- a/src/OAuth/FortifiAccessToken.php +++ b/src/OAuth/FortifiAccessToken.php @@ -14,6 +14,10 @@ class FortifiAccessToken extends AccessToken */ public function __construct(array $options = null) { + if(isset($options['uid']) && !isset($options['resource_owner_id'])) + { + $options['resource_owner_id'] = $options['uid']; + } parent::__construct($options); if(isset($options['session_secret'])) diff --git a/src/OAuth/FortifiGrant.php b/src/OAuth/FortifiGrant.php new file mode 100644 index 0000000..320e43e --- /dev/null +++ b/src/OAuth/FortifiGrant.php @@ -0,0 +1,7 @@ +_url; } - /** - * @return string - */ - public function urlAuthorize() - { - return Path::buildUnix($this->_url, 'oauth', 'authorize'); - } - - /** - * @return string - */ - public function urlAccessToken() - { - return Path::buildUnix($this->_url, 'oauth', 'access-token'); - } - - /** - * @param AccessToken $token - * - * @return string - */ - public function urlUserDetails(AccessToken $token) - { - return Path::buildUnix($this->_url, 'auth', 'details') - . '?access_token=' . $token->accessToken; - } - /** * @param AccessToken $token * @@ -106,19 +74,22 @@ public function urlUserDetails(AccessToken $token) public function urlLogout(AccessToken $token) { return Path::buildUnix($this->_url, 'auth', 'logout') - . '?access_token=' . $token->accessToken; + . '?access_token=' . $token->getToken(); } /** - * @param $response - * @param AccessToken $token + * Generates a resource owner object from a successful resource owner + * details request. + * + * @param array $response + * @param AccessToken $token * - * @return OAuthUser + * @return ResourceOwnerInterface */ - public function userDetails($response, AccessToken $token) + protected function createResourceOwner(array $response, AccessToken $token) { - $user = new OAuthUser(); - $result = Objects::property($response, 'result', $response); + $result = isset($response['result']) ? $response['result'] : []; + /** * @var $result AuthUserDetailsResponse */ @@ -126,8 +97,7 @@ public function userDetails($response, AccessToken $token) { $result = AuthUserDetailsResponse::make($result); } - $user->setAuthUserDetails($result); - $user->exchangeArray( + $user = new OAuthUser( [ 'uid' => $result->userFid, 'nickname' => ValueAs::nonempty( @@ -144,109 +114,66 @@ public function userDetails($response, AccessToken $token) 'firstName' => $result->firstName, 'lastName' => $result->lastName, 'description' => $result->description, - 'email' => $result->username - ] + 'email' => $result->username, + ], $result->userFid ); + $user->setAuthUserDetails($result); return $user; } - public function userUid($response, AccessToken $token) + public function userUid(ResourceOwnerInterface $owner) { - return $this->_getProperty($response, ['userFid', 'authedFid', 'id']); - } - - public function userEmail($response, AccessToken $token) - { - return $this->_getProperty($response, ['email', 'username']); + return Arrays::inonempty( + $owner->toArray(), + ['userFid', 'authedFid', 'uid'] + ); } - public function userScreenName($response, AccessToken $token) + public function userEmail(ResourceOwnerInterface $owner) { - return $this->_getProperty($response, ['displayName', 'name', 'firstName']); + return Arrays::inonempty($owner->toArray(), ['email', 'username']); } - protected function _getProperty($object, $propertyList) + public function userScreenName(ResourceOwnerInterface $owner) { - $properties = (array)$propertyList; - $result = Objects::pnonempty($object, $properties); - if($result === null && isset($object->result)) - { - $result = Objects::pnonempty($object->result, $properties); - } - return $result; + return Arrays::inonempty( + $owner->toArray(), + ['displayName', 'name', 'firstName'] + ); } public function logout(AccessToken $token) { - $this->fetchProviderData( - $this->urlLogout($token), - $this->getHeaders($token) + $this->getParsedResponse( + $this->getAuthenticatedRequest( + "POST", + $this->urlLogout($token), + $token, + ['headers' => $this->getHeaders($token)] + ) ); return true; } - protected function fetchUserDetails(AccessToken $token) + protected function fetchResourceOwnerDetails(AccessToken $token) { if($this->_userDetails === null) { - $this->setUserDetailsCache(parent::fetchUserDetails($token)); + $this->setUserDetailsCache(parent::fetchResourceOwnerDetails($token)); } return $this->_userDetails; } - protected function fetchProviderData($url, array $headers = []) + public function setUserDetailsCache($response) { - $time = microtime(true); - try + if(is_string($response)) { - $client = $this->getHttpClient(); - $client->setBaseUrl($url); - - if($headers) - { - $client->setDefaultOption('headers', $headers); - } - - $request = $client->get()->send(); - $response = $request->getBody(); + $this->_userDetails = $this->parseJson($response); } - catch(BadResponseException $e) + else { - // @codeCoverageIgnoreStart - $decode = new JsonFormat(); - try - { - $totalTime = microtime(true) - $time; - - $response = $decode->decode( - new Response( - $e->getResponse()->getStatusCode(), - $e->getResponse()->getHeaders()->getAll(), - new Stream($e->getResponse()->getBody()->getStream()) - ), - $totalTime - ); - - throw new \Exception( - $response->getApiCallData()->getStatusMessage(), - $response->getApiCallData()->getStatusCode(), - $e - ); - } - catch(InvalidApiResponseException $ex) - { - $raw_response = explode("\n", $e->getResponse()); - throw new IDPException(end($raw_response)); - } - // @codeCoverageIgnoreEnd + $this->_userDetails = $response; } - - return $response; - } - - public function setUserDetailsCache($response) - { - $this->_userDetails = $response; return $this; } @@ -268,4 +195,92 @@ public function getHeaders($token = null) $headers = parent::getHeaders($token); return array_merge($headers, ['X-Fortifi-Org' => $this->_orgFid]); } + + /** + * Returns the base URL for authorizing a client. + * + * Eg. https://oauth.service.com/authorize + * + * @return string + */ + public function getBaseAuthorizationUrl() + { + return Path::buildUnix($this->_url, 'oauth', 'authorize'); + } + + /** + * Returns the base URL for requesting an access token. + * + * Eg. https://oauth.service.com/token + * + * @param array $params + * + * @return string + */ + public function getBaseAccessTokenUrl(array $params) + { + return Path::buildUnix($this->_url, 'oauth', 'access-token'); + } + + /** + * Returns the URL for requesting the resource owner's details. + * + * @param AccessToken $token + * + * @return string + */ + public function getResourceOwnerDetailsUrl(AccessToken $token) + { + return Path::buildUnix($this->_url, 'auth', 'details') + . '?access_token=' . $token->getToken(); + } + + /** + * Returns the default scopes used by this provider. + * + * This should only be the scopes that are required to request the details + * of the resource owner, rather than all the available scopes. + * + * @return array + */ + protected function getDefaultScopes() + { + return []; + } + + /** + * Checks a provider response for errors. + * + * @throws IdentityProviderException + * + * @param ResponseInterface $response + * @param array|string $data Parsed response data + * + * @return void + */ + protected function checkResponse(ResponseInterface $response, $data) + { + //error_log(print_r(['data' => $data, 'resp' => $response], true)); + } + + /** + * Creates an access token from a response. + * + * The grant that was used to fetch the response can be used to provide + * additional context. + * + * @param array $response + * @param AbstractGrant $grant + * + * @return AccessToken + */ + protected function createAccessToken(array $response, AbstractGrant $grant) + { + if($grant instanceof FortifiGrant) + { + return $grant->handleResponse($response); + } + return new AccessToken($response); + } + } diff --git a/src/OAuth/OAuthUser.php b/src/OAuth/OAuthUser.php index 13e1685..2a35d20 100644 --- a/src/OAuth/OAuthUser.php +++ b/src/OAuth/OAuthUser.php @@ -2,12 +2,21 @@ namespace Fortifi\Sdk\OAuth; use Fortifi\FortifiApi\Auth\Responses\AuthUserDetailsResponse; -use League\OAuth2\Client\Entity\User; +use League\OAuth2\Client\Provider\GenericResourceOwner; -class OAuthUser extends User +class OAuthUser extends GenericResourceOwner { protected $_authUserDetails; + public function getId() + { + if($this->_authUserDetails instanceof AuthUserDetailsResponse) + { + return $this->_authUserDetails->userFid ?: $this->resourceOwnerId; + } + return $this->resourceOwnerId; + } + /** * @return AuthUserDetailsResponse */ diff --git a/src/OAuth/PasswordGrant.php b/src/OAuth/PasswordGrant.php index 216e2f5..77abc19 100644 --- a/src/OAuth/PasswordGrant.php +++ b/src/OAuth/PasswordGrant.php @@ -3,7 +3,7 @@ use League\OAuth2\Client\Grant\Password; -class PasswordGrant extends Password +class PasswordGrant extends Password implements FortifiGrant { public function handleResponse($response = []) { diff --git a/src/OAuth/RefreshTokenGrant.php b/src/OAuth/RefreshTokenGrant.php index 5fd2a68..259dc3a 100644 --- a/src/OAuth/RefreshTokenGrant.php +++ b/src/OAuth/RefreshTokenGrant.php @@ -3,7 +3,7 @@ use League\OAuth2\Client\Grant\RefreshToken; -class RefreshTokenGrant extends RefreshToken +class RefreshTokenGrant extends RefreshToken implements FortifiGrant { public function handleResponse($response = []) { diff --git a/src/OAuth/ServiceAccountGrant.php b/src/OAuth/ServiceAccountGrant.php index cb259ab..d02dc2c 100644 --- a/src/OAuth/ServiceAccountGrant.php +++ b/src/OAuth/ServiceAccountGrant.php @@ -1,31 +1,35 @@ _apiUser = $user; - $this->_apiKey = $key; + return 'service_account'; } - public function __toString() + protected function getRequiredRequestParameters() { - return 'service_account'; + return ['api_user', 'api_key']; + } + + public function __construct($user, $key) + { + $this->_apiUser = $user; + $this->_apiKey = $key; } - public function prepRequestParams($defaultParams, $params) + public function prepareRequestParameters(array $defaults, array $options) { - $params['grant_type'] = 'service_account'; - $params['api_user'] = $this->_apiUser; - $params['api_key'] = $this->_apiKey; + $options['api_user'] = $this->_apiUser; + $options['api_key'] = $this->_apiKey; - return array_merge($defaultParams, $params); + return parent::prepareRequestParameters($defaults, $options); } public function handleResponse($response = []) diff --git a/tests/OAuth/FortifiAccessTokenTest.php b/tests/OAuth/FortifiAccessTokenTest.php index 2b0bc5e..d5be503 100644 --- a/tests/OAuth/FortifiAccessTokenTest.php +++ b/tests/OAuth/FortifiAccessTokenTest.php @@ -2,8 +2,9 @@ namespace Fortifi\Tests\Sdk\OAuth; use Fortifi\Sdk\OAuth\FortifiAccessToken; +use PHPUnit\Framework\TestCase; -class FortifiAccessTokenTest extends \PHPUnit_Framework_TestCase +class FortifiAccessTokenTest extends TestCase { public function testSessionSecret() { diff --git a/tests/OAuth/FortifiProviderTest.php b/tests/OAuth/FortifiProviderTest.php index b7848cc..b733356 100644 --- a/tests/OAuth/FortifiProviderTest.php +++ b/tests/OAuth/FortifiProviderTest.php @@ -4,12 +4,14 @@ use Fortifi\Sdk\OAuth\Client; use Fortifi\Sdk\OAuth\FortifiProvider; use League\OAuth2\Client\Token\AccessToken; +use PHPUnit\Framework\TestCase; +use Psr\Http\Message\RequestInterface; -class FortifiProviderTest extends \PHPUnit_Framework_TestCase +class FortifiProviderTest extends TestCase { public function testUrls() { - $fortifiUrl = 'http://org-dc-1448-6f535.fortifi.co'; + $fortifiUrl = 'http://org-dc-1448-6f535.fortifi.io'; $provider = new FortifiProvider(); $provider->setFortifiUrl($fortifiUrl); $provider->setOrgFid('org-dc-1448-6f535'); @@ -18,17 +20,19 @@ public function testUrls() $this->assertEquals( $fortifiUrl . '/oauth/access-token', - $provider->urlAccessToken() + $provider->getBaseAccessTokenUrl([]) ); $this->assertEquals( $fortifiUrl . '/oauth/authorize', - $provider->urlAuthorize() + $provider->getBaseAuthorizationUrl() ); $this->assertEquals( $fortifiUrl . '/auth/details?access_token=abc', - $provider->urlUserDetails(new AccessToken(['access_token' => 'abc'])) + $provider->getResourceOwnerDetailsUrl( + new AccessToken(['access_token' => 'abc']) + ) ); $this->assertEquals( @@ -71,15 +75,15 @@ public function testUserDetails() $provider = new FortifiProvider(); $provider->setUserDetailsCache($response); - $user = $provider->getUserDetails($token); - $this->assertEquals($details->userFid, $user->uid); + $user = $provider->getResourceOwner($token); + $this->assertEquals($details->userFid, $user->getId()); - $this->assertEquals($details->username, $provider->getUserEmail($token)); + $this->assertEquals($details->username, $provider->userEmail($user)); $this->assertEquals( $details->displayName, - $provider->getUserScreenName($token) + $provider->userScreenName($user) ); - $this->assertEquals($details->userFid, $provider->getUserUid($token)); + $this->assertEquals($details->userFid, $provider->userUid($user)); } public function testClient() @@ -105,11 +109,11 @@ public function testCalls() $token = new AccessToken(['access_token' => 'abc']); $provider = new MockProvider(); - $fortifiUrl = 'http://api.fortifi.co'; + $fortifiUrl = 'http://api.fortifi.io'; $provider->setFortifiUrl($fortifiUrl); $provider->setOrgFid('org-xyz'); - $provider->getUserDetails($token); + $provider->getResourceOwner($token); $this->assertEquals( $fortifiUrl . '/auth/details?access_token=abc', $provider->getLastUrl() @@ -130,11 +134,11 @@ class MockProvider extends FortifiProvider protected $_lastUrl; protected $_lastHeaders; - protected function fetchProviderData($url, array $headers = []) + public function getResponse(RequestInterface $request) { - $this->_lastUrl = $url; - $this->_lastHeaders = $headers; - return true; + $this->_lastUrl = (string)$request->getUri(); + $this->_lastHeaders = $request->getHeaders(); + return parent::getResponse($request); } public function getLastUrl() diff --git a/tests/OAuth/OAuthUserTest.php b/tests/OAuth/OAuthUserTest.php index 1e97bc0..a53f52f 100644 --- a/tests/OAuth/OAuthUserTest.php +++ b/tests/OAuth/OAuthUserTest.php @@ -3,12 +3,13 @@ use Fortifi\FortifiApi\Auth\Responses\AuthUserDetailsResponse; use Fortifi\Sdk\OAuth\OAuthUser; +use PHPUnit\Framework\TestCase; -class OAuthUserTest extends \PHPUnit_Framework_TestCase +class OAuthUserTest extends TestCase { public function testAuthUserDetails() { - $user = new OAuthUser(); + $user = new OAuthUser([], ""); $details = new AuthUserDetailsResponse(); $details->userFid = 'testing'; $user->setAuthUserDetails($details); diff --git a/tests/OAuth/PasswordGrantTest.php b/tests/OAuth/PasswordGrantTest.php index 93ee8b4..dd39ecf 100644 --- a/tests/OAuth/PasswordGrantTest.php +++ b/tests/OAuth/PasswordGrantTest.php @@ -2,8 +2,9 @@ namespace Fortifi\Tests\Sdk\OAuth; use Fortifi\Sdk\OAuth\PasswordGrant; +use PHPUnit\Framework\TestCase; -class PasswordGrantTest extends \PHPUnit_Framework_TestCase +class PasswordGrantTest extends TestCase { public function testHandleResponse() { diff --git a/tests/OAuth/RefreshTokenGrantTest.php b/tests/OAuth/RefreshTokenGrantTest.php index 453caba..0c62197 100644 --- a/tests/OAuth/RefreshTokenGrantTest.php +++ b/tests/OAuth/RefreshTokenGrantTest.php @@ -2,8 +2,9 @@ namespace Fortifi\Tests\Sdk\OAuth; use Fortifi\Sdk\OAuth\RefreshTokenGrant; +use PHPUnit\Framework\TestCase; -class RefreshTokenGrantTest extends \PHPUnit_Framework_TestCase +class RefreshTokenGrantTest extends TestCase { public function testHandleResponse() {