diff --git a/composer.json b/composer.json index e9125efe..f9ef87e4 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,8 @@ "scripts": { "test": "SHELL_INTERACTIVE=1 vendor/bin/phpunit --colors=always --verbose ", "test-ci": "vendor/bin/phpunit --coverage-text --coverage-clover=build/coverage.xml", - "phpcs": "SHELL_INTERACTIVE=1 ./vendor/bin/phpcs --standard=phpcs-ruleset.xml -s", + "phpcs": "./vendor/bin/phpcs --standard=phpcs-ruleset.xml -s .", + "phpcs-path": "SHELL_INTERACTIVE=1 ./vendor/bin/phpcs --standard=phpcs-ruleset.xml -s", "phpcbf": "./vendor/bin/phpcbf --standard=phpcs-ruleset.xml .", "phpcbf-path": "SHELL_INTERACTIVE=1 ./vendor/bin/phpcbf --standard=phpcs-ruleset.xml", "sniffs": "./vendor/bin/phpcs --standard=phpcs-ruleset.xml -e" diff --git a/phpcs-ruleset.xml b/phpcs-ruleset.xml index 63f17085..6ff94bb0 100644 --- a/phpcs-ruleset.xml +++ b/phpcs-ruleset.xml @@ -61,10 +61,12 @@ + + @@ -75,7 +77,10 @@ - + + + + diff --git a/src/API/Management/ClientGrants.php b/src/API/Management/ClientGrants.php index f551cdbe..25b6a319 100644 --- a/src/API/Management/ClientGrants.php +++ b/src/API/Management/ClientGrants.php @@ -2,75 +2,193 @@ namespace Auth0\SDK\API\Management; -use Auth0\SDK\API\Header\ContentType; +use Auth0\SDK\Exception\CoreException; +/** + * Class ClientGrants. + * Handles requests to the Client Grants endpoint of the v2 Management API. + * + * @package Auth0\SDK\API\Management + */ class ClientGrants extends GenericResource { + /** + * Get all Client Grants, by page if desired. + * Required scope: "read:client_grants" + * + * @param array $params Additional URL parameters to send: + * - "audience" to filter be a specific API audience identifier. + * - "client_id" to return an object. + * - "include_totals" to return an object. + * @param null|integer $page The page number, zero based. + * @param null|integer $per_page The amount of entries per page. * - * @param string $id - * @param null|string $audience * @return mixed + * + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + * + * @link https://auth0.com/docs/api/management/v2#!/Client_Grants/get_client_grants */ - public function get($id, $audience = null) + public function getAll(array $params = [], $page = null, $per_page = null) { - $request = $this->apiClient->get() - ->addPath('client-grants'); + if (null !== $page) { + $params['page'] = abs(intval($page)); + } - if ($audience !== null) { - $request = $request->withParam('audience', $audience); + if (null !== $per_page) { + $params['per_page'] = abs(intval($per_page)); } - return $request->call(); + return $this->apiClient->method('get') + ->addPath('client-grants') + ->withDictParams($params) + ->call(); + } + + /** + * Get Client Grants by audience. + * Required scope: "read:client_grants" + * + * @param string $audience API Audience to filter by. + * @param null|integer $page The page number, zero based. + * @param null|integer $per_page The amount of entries per page. + * + * @return mixed + * + * @throws CoreException Thrown when $audience is empty or not a string. + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + * + * @link https://auth0.com/docs/api/management/v2#!/Client_Grants/get_client_grants + */ + public function getByAudience($audience, $page = null, $per_page = null) + { + if (empty($audience) || ! is_string($audience)) { + throw new CoreException('Empty or invalid "audience" parameter.'); + } + + return $this->getAll(['audience' => $audience], $page, $per_page); + } + + /** + * Get Client Grants by Client ID. + * Required scope: "read:client_grants" + * + * @param string $client_id Client ID to filter by. + * @param null|integer $page The page number, zero based. + * @param null|integer $per_page The amount of entries per page. + * + * @return mixed + * + * @throws CoreException Thrown when $client_id is empty or not a string. + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + * + * @link https://auth0.com/docs/api/management/v2#!/Client_Grants/get_client_grants + */ + public function getByClientId($client_id, $page = null, $per_page = null) + { + if (empty($client_id) || ! is_string($client_id)) { + throw new CoreException('Empty or invalid "client_id" parameter.'); + } + + return $this->getAll(['client_id' => $client_id], $page, $per_page); + } + + /** + * Create a new Client Grant. + * Required scope: "create:client_grants" + * + * @param string $client_id Client ID to receive the grant. + * @param string $audience Audience identifier for the API being granted. + * @param array $scope Array of scopes for the grant. + * + * @return mixed + * + * @throws CoreException Thrown when $client_id or $audience are empty or not a string. + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + * + * @link https://auth0.com/docs/api/management/v2#!/Client_Grants/post_client_grants + */ + public function create($client_id, $audience, array $scope = []) + { + if (empty($client_id) || ! is_string($client_id)) { + throw new CoreException('Empty or invalid "client_id" parameter.'); + } + + if (empty($audience) || ! is_string($audience)) { + throw new CoreException('Empty or invalid "audience" parameter.'); + } + + return $this->apiClient->method('post') + ->addPath('client-grants') + ->withBody(json_encode([ + 'client_id' => $client_id, + 'audience' => $audience, + 'scope' => $scope, + ])) + ->call(); } /** + * Delete a Client Grant by ID. + * Required scope: "delete:client_grants" + * + * @param string $id Client Grant ID to delete. * - * @param string $client_id - * @param string $audience - * @param string $scope * @return mixed + * + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + * + * @link https://auth0.com/docs/api/management/v2#!/Client_Grants/delete_client_grants_by_id */ - public function create($client_id, $audience, $scope) + public function delete($id) { - return $this->apiClient->post() - ->addPath('client-grants') - ->withHeader(new ContentType('application/json')) - ->withBody(json_encode([ - 'client_id' => $client_id, - 'audience' => $audience, - 'scope' => $scope, - ])) - ->call(); + return $this->apiClient->method('delete') + ->addPath('client-grants', $id) + ->call(); } /** + * Update an existing Client Grant. + * Required scope: "update:client_grants" + * + * @param string $id Client Grant ID to update. + * @param array $scope Array of scopes to update; will replace existing scopes, not merge. * - * @param string $id - * @param null|string $audience * @return mixed + * + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + * + * @link https://auth0.com/docs/api/management/v2#!/Client_Grants/patch_client_grants_by_id */ - public function delete($id, $audience = null) + public function update($id, array $scope) { - return $this->apiClient->delete() - ->addPath('client-grants', $id) - ->call(); + return $this->apiClient->method('patch') + ->addPath('client-grants', $id) + ->withBody(json_encode(['scope' => $scope,])) + ->call(); } /** + * Get a Client Grant. + * TODO: Deprecate, cannot get a Client Grant by ID. + * + * @param string $id Client Grant ID. + * @param null|string $audience Client Grant audience to filter by. * - * @param string $id - * @param string $scope * @return mixed + * + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. */ - public function update($id, $scope) + public function get($id, $audience = null) { - return $this->apiClient->patch() - ->addPath('client-grants', $id) - ->withHeader(new ContentType('application/json')) - ->withBody(json_encode([ - 'scope' => $scope, - ])) - ->call(); + $request = $this->apiClient->get() + ->addPath('client-grants'); + + if ($audience !== null) { + $request = $request->withParam('audience', $audience); + } + + return $request->call(); } } diff --git a/tests/API/ApiTests.php b/tests/API/ApiTests.php index 70b41683..19d5b2cf 100644 --- a/tests/API/ApiTests.php +++ b/tests/API/ApiTests.php @@ -2,11 +2,18 @@ namespace Auth0\Tests\API; use Auth0\SDK\API\Helpers\TokenGenerator; +use Auth0\SDK\API\Management; use josegonzalez\Dotenv\Loader; class ApiTests extends \PHPUnit_Framework_TestCase { + /** + * + * @var array + */ + protected static $env = []; + protected function getEnv() { return self::getEnvStatic(); @@ -46,4 +53,33 @@ protected static function getTokenStatic($env, $scopes) ); return $generator->generate($scopes); } + + /** + * Return an API client used during self::setUpBeforeClass(). + * + * @param string $endpoint Endpoint name used for token generation. + * @param array $actions Actions required for token generation. + * + * @return mixed + */ + protected static function getApiStatic($endpoint, array $actions) + { + self::$env = self::getEnvStatic(); + $token = self::getTokenStatic(self::$env, [$endpoint => ['actions' => $actions]]); + $api_client = new Management($token, self::$env['DOMAIN']); + return $api_client->$endpoint; + } + + /** + * Does an error message contain a specific string? + * + * @param \Exception $e - Error object. + * @param string $str - String to find in the error message. + * + * @return boolean + */ + protected function errorHasString(\Exception $e, $str) + { + return ! (false === strpos($e->getMessage(), $str)); + } } diff --git a/tests/API/BasicCrudTest.php b/tests/API/BasicCrudTest.php index 25376493..fe77fb93 100644 --- a/tests/API/BasicCrudTest.php +++ b/tests/API/BasicCrudTest.php @@ -12,13 +12,6 @@ abstract class BasicCrudTest extends ApiTests */ protected $domain; - /** - * Environment variables, generated in self::__construct(). - * - * @var array - */ - protected $env; - /** * API client to test. * @@ -93,8 +86,8 @@ abstract protected function afterUpdate($updated_entity); public function __construct() { parent::__construct(); - $this->env = $this->getEnv(); - $this->domain = $this->env['DOMAIN']; + self::$env = $this->getEnv(); + $this->domain = self::$env['DOMAIN']; $this->api = $this->getApiClient(); $this->rand = rand(); } @@ -122,19 +115,6 @@ protected function getId($entity) return $entity[$this->id_name]; } - /** - * Does an error message contain a specific string? - * - * @param \Exception $e - Error object. - * @param string $str - String to find in the error message. - * - * @return boolean - */ - protected function errorHasString(\Exception $e, $str) - { - return ! (false === strpos($e->getMessage(), $str)); - } - /** * Check that HTTP options have been set correctly. */ diff --git a/tests/API/Management/AuthApiTest.php b/tests/API/Management/AuthApiTest.php index 7dbed183..f4131153 100644 --- a/tests/API/Management/AuthApiTest.php +++ b/tests/API/Management/AuthApiTest.php @@ -59,7 +59,7 @@ public function testOauthToken() $token = $api->client_credentials( [ - 'audience' => 'tests' + 'audience' => 'https://'.$env['DOMAIN'].'/api/v2/' ] ); diff --git a/tests/API/Management/ClientGrantsTest.php b/tests/API/Management/ClientGrantsTest.php new file mode 100644 index 00000000..e33f2c48 --- /dev/null +++ b/tests/API/Management/ClientGrantsTest.php @@ -0,0 +1,173 @@ +getAll(); + $this->assertNotEmpty($all_results); + + $expected_client_id = $all_results[0]['client_id'] ?: null; + $this->assertNotNull($expected_client_id); + + $expected_audience = $all_results[0]['audience'] ?: null; + $this->assertNotNull($expected_audience); + + $audience_results = self::$api->getByAudience($expected_audience); + $this->assertNotEmpty($audience_results); + $this->assertEquals($expected_audience, $audience_results[0]['audience']); + + $client_id_results = self::$api->getByClientId($expected_client_id); + $this->assertNotEmpty($client_id_results); + $this->assertEquals($expected_client_id, $client_id_results[0]['client_id']); + } + + /** + * Test that pagination parameters are passed to the endpoint. + * + * @return void + * + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + */ + public function testGetWithPagination() + { + $expected_count = 2; + + $results_1 = self::$api->getAll([], 0, $expected_count); + $this->assertCount($expected_count, $results_1); + + $expected_page = 1; + $results_2 = self::$api->getAll([], $expected_page, 1); + $this->assertCount(1, $results_2); + $this->assertEquals($results_1[$expected_page]['client_id'], $results_2[0]['client_id']); + $this->assertEquals($results_1[$expected_page]['audience'], $results_2[0]['audience']); + } + + /** + * Test that the "include_totals" parameter works. + * + * @return void + * + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + */ + public function testGetAllIncludeTotals() + { + $expected_page = 1; + $expected_count = 2; + + $results = self::$api->getAll(['include_totals' => true], $expected_page, $expected_count); + $this->assertArrayHasKey('total', $results); + $this->assertEquals($expected_page * $expected_count, $results['start']); + $this->assertEquals($expected_count, $results['limit']); + $this->assertNotEmpty($results['client_grants']); + } + + /** + * Test that we can create, update, and delete a Client Grant. + * + * @return void + * + * @throws CoreException Thrown when there is a problem with parameters passed to the method. + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + */ + public function testCreateUpdateDeleteGrant() + { + $client_id = self::$env['APP_CLIENT_ID']; + $audience = self::TESTS_API_AUDIENCE; + + // Create a Client Grant with just one of the testing scopes. + $create_result = self::$api->create($client_id, $audience, [self::$scopes[0]]); + $this->assertArrayHasKey('id', $create_result); + $this->assertEquals($client_id, $create_result['client_id']); + $this->assertEquals($audience, $create_result['audience']); + $this->assertEquals([self::$scopes[0]], $create_result['scope']); + + $grant_id = $create_result['id']; + + // Test patching the created Client Grant. + $update_result = self::$api->update($grant_id, self::$scopes); + $this->assertEquals(self::$scopes, $update_result['scope']); + + // Test deleting the created Client Grant. + $delete_result = self::$api->delete($grant_id); + $this->assertNull($delete_result); + } + + /** + * Test that create method throws errors correctly. + * + * @return void + * + * @throws \Exception Thrown by the Guzzle HTTP client when there is a problem with the API call. + */ + public function testCreateGrantExceptions() + { + $throws_missing_client_id_exception = false; + try { + self::$api->create('', self::TESTS_API_AUDIENCE, []); + } catch (CoreException $e) { + $throws_missing_client_id_exception = $this->errorHasString($e, 'Empty or invalid "client_id" parameter'); + } + + $this->assertTrue($throws_missing_client_id_exception); + + $throws_missing_audience_exception = false; + try { + self::$api->create(self::$env['APP_CLIENT_ID'], '', []); + } catch (CoreException $e) { + $throws_missing_audience_exception = $this->errorHasString($e, 'Empty or invalid "audience" parameter'); + } + + $this->assertTrue($throws_missing_audience_exception); + } +} diff --git a/tests/API/Management/ClientsTest.php b/tests/API/Management/ClientsTest.php index 6981742c..04720986 100644 --- a/tests/API/Management/ClientsTest.php +++ b/tests/API/Management/ClientsTest.php @@ -26,7 +26,7 @@ class ClientsTest extends BasicCrudTest */ protected function getApiClient() { - $token = $this->getToken($this->env, [ 'clients' => [ 'actions' => ['create', 'read', 'delete', 'update' ] ] ]); + $token = $this->getToken(self::$env, [ 'clients' => [ 'actions' => ['create', 'read', 'delete', 'update' ] ] ]); $api = new Management($token, $this->domain); return $api->clients; } diff --git a/tests/API/Management/ConnectionsTest.php b/tests/API/Management/ConnectionsTest.php index 353a407d..16eff94e 100644 --- a/tests/API/Management/ConnectionsTest.php +++ b/tests/API/Management/ConnectionsTest.php @@ -27,7 +27,7 @@ class ConnectionsTest extends BasicCrudTest protected function getApiClient() { $token = $this->getToken( - $this->env, [ + self::$env, [ 'connections' => ['actions' => ['create', 'read', 'delete', 'update']], 'users' => ['actions' => ['delete']], ] diff --git a/tests/API/Management/LogsTest.php b/tests/API/Management/LogsTest.php index d5aa5a72..ce72d185 100644 --- a/tests/API/Management/LogsTest.php +++ b/tests/API/Management/LogsTest.php @@ -65,7 +65,7 @@ public function testLogSearchAndGetById() public function testLogSearchPagination() { $expected_count = 5; - $search_results= self::$api->search([ + $search_results = self::$api->search([ // Fields here to speed up API call. 'fields' => '_id,log_id', 'include_fields' => true, diff --git a/tests/API/Management/UsersTest.php b/tests/API/Management/UsersTest.php index cee5c2e3..4fbafcfd 100644 --- a/tests/API/Management/UsersTest.php +++ b/tests/API/Management/UsersTest.php @@ -40,7 +40,7 @@ class UsersTest extends BasicCrudTest */ protected function getApiClient() { - $token = $this->getToken($this->env, ['users' => ['actions' => ['create', 'read', 'delete', 'update']]]); + $token = $this->getToken(self::$env, ['users' => ['actions' => ['create', 'read', 'delete', 'update']]]); $api = new Management($token, $this->domain); return $api->users; }