diff --git a/src/API/Management.php b/src/API/Management.php index 5ca1debe..054a505a 100644 --- a/src/API/Management.php +++ b/src/API/Management.php @@ -4,9 +4,9 @@ namespace Auth0\SDK\API; -use Auth0\SDK\API\Management\{Actions, AttackProtection, Blacklists, ClientGrants, Clients, Connections, DeviceCredentials, EmailTemplates, Emails, Grants, Guardian, Jobs, Keys, LogStreams, Logs, Organizations, RefreshTokens, ResourceServers, Roles, Rules, Sessions, Stats, Tenants, Tickets, UserBlocks, Users, UsersByEmail}; +use Auth0\SDK\API\Management\{Actions, AttackProtection, Blacklists, ClientGrants, Clients, Connections, DeviceCredentials, EmailTemplates, Emails, Grants, Guardian, Jobs, Keys, LogStreams, Logs, NetworkAcls, Organizations, RefreshTokens, ResourceServers, Roles, Rules, Sessions, Stats, Tenants, Tickets, UserBlocks, Users, UsersByEmail}; use Auth0\SDK\Configuration\SdkConfiguration; -use Auth0\SDK\Contract\API\Management\{ActionsInterface, AttackProtectionInterface, BlacklistsInterface, ClientGrantsInterface, ClientsInterface, ConnectionsInterface, DeviceCredentialsInterface, EmailTemplatesInterface, EmailsInterface, GrantsInterface, GuardianInterface, JobsInterface, KeysInterface, LogStreamsInterface, LogsInterface, OrganizationsInterface, RefreshTokensInterface, ResourceServersInterface, RolesInterface, RulesInterface, SessionsInterface, StatsInterface, TenantsInterface, TicketsInterface, UserBlocksInterface, UsersByEmailInterface, UsersInterface}; +use Auth0\SDK\Contract\API\Management\{ActionsInterface, AttackProtectionInterface, BlacklistsInterface, ClientGrantsInterface, ClientsInterface, ConnectionsInterface, DeviceCredentialsInterface, EmailTemplatesInterface, EmailsInterface, GrantsInterface, GuardianInterface, JobsInterface, KeysInterface, LogStreamsInterface, LogsInterface, NetworkAclsInterface, OrganizationsInterface, RefreshTokensInterface, ResourceServersInterface, RolesInterface, RulesInterface, SessionsInterface, StatsInterface, TenantsInterface, TicketsInterface, UserBlocksInterface, UsersByEmailInterface, UsersInterface}; use Auth0\SDK\Contract\API\{AuthenticationInterface, ManagementInterface}; use Auth0\SDK\Utility\{HttpClient, HttpResponse, HttpResponsePaginator}; use Psr\Cache\CacheItemPoolInterface; @@ -197,6 +197,11 @@ public function logStreams(): LogStreamsInterface return LogStreams::instance($this->getHttpClient()); } + public function networkAcls(): NetworkAclsInterface + { + return NetworkAcls::instance($this->getHttpClient()); + } + public function organizations(): OrganizationsInterface { return Organizations::instance($this->getHttpClient()); diff --git a/src/API/Management/NetworkAcls.php b/src/API/Management/NetworkAcls.php new file mode 100644 index 00000000..cffbd57b --- /dev/null +++ b/src/API/Management/NetworkAcls.php @@ -0,0 +1,149 @@ +string()->trim(); + [$rule, $additional] = Toolkit::filter([$rule, $additional])->array()->trim(); + + Toolkit::assert([ + [$description, \Auth0\SDK\Exception\ArgumentException::missing('description')], + ])->isString(); + + Toolkit::assert([ + [$active, \Auth0\SDK\Exception\ArgumentException::missing('active')], + ])->isBoolean(); + + Toolkit::assert([ + [$priority, \Auth0\SDK\Exception\ArgumentException::missing('priority')], + ])->isInteger(); + + Toolkit::assert([ + [$rule, \Auth0\SDK\Exception\ArgumentException::missing('rule')], + ])->isArray(); + + /** @var array $additional */ + + return $this->getHttpClient() + ->method('post')->addPath(['network-acls']) + ->withBody( + (object) Toolkit::merge([[ + 'description' => $description, + 'active' => $active, + 'priority' => $priority, + 'rule' => (object) $rule, + ], $additional]), + ) + ->withOptions($options) + ->call(); + } + + public function delete( + string $id, + ?RequestOptions $options = null, + ): ResponseInterface { + [$id] = Toolkit::filter([$id])->string()->trim(); + + Toolkit::assert([ + [$id, \Auth0\SDK\Exception\ArgumentException::missing('id')], + ])->isString(); + + return $this->getHttpClient() + ->method('delete')->addPath(['network-acls', $id]) + ->withOptions($options) + ->call(); + } + + public function get( + string $id, + ?RequestOptions $options = null, + ): ResponseInterface { + [$id] = Toolkit::filter([$id])->string()->trim(); + + Toolkit::assert([ + [$id, \Auth0\SDK\Exception\ArgumentException::missing('id')], + ])->isString(); + + return $this->getHttpClient() + ->method('get')->addPath(['network-acls', $id]) + ->withOptions($options) + ->call(); + } + + public function getAll( + ?RequestOptions $options = null, + ): ResponseInterface { + return $this->getHttpClient() + ->method('get') + ->addPath(['network-acls']) + ->withOptions($options) + ->call(); + } + + public function patch( + string $id, + array $body, + ?RequestOptions $options = null, + ): ResponseInterface { + [$id] = Toolkit::filter([$id])->string()->trim(); + [$body] = Toolkit::filter([$body])->array()->trim(); + + Toolkit::assert([ + [$id, \Auth0\SDK\Exception\ArgumentException::missing('id')], + ])->isString(); + + Toolkit::assert([ + [$body, \Auth0\SDK\Exception\ArgumentException::missing('body')], + ])->isArray(); + + return $this->getHttpClient() + ->method('patch')->addPath(['network-acls', $id]) + ->withBody((object) $body) + ->withOptions($options) + ->call(); + } + + public function update( + string $id, + array $body, + ?RequestOptions $options = null, + ): ResponseInterface { + [$id] = Toolkit::filter([$id])->string()->trim(); + [$body] = Toolkit::filter([$body])->array()->trim(); + + Toolkit::assert([ + [$id, \Auth0\SDK\Exception\ArgumentException::missing('id')], + ])->isString(); + + Toolkit::assert([ + [$body, \Auth0\SDK\Exception\ArgumentException::missing('body')], + ])->isArray(); + + return $this->getHttpClient() + ->method('put')->addPath(['network-acls', $id]) + ->withBody((object) $body) + ->withOptions($options) + ->call(); + } +} diff --git a/src/Contract/API/Management/NetworkAclsInterface.php b/src/Contract/API/Management/NetworkAclsInterface.php new file mode 100644 index 00000000..d62b63ba --- /dev/null +++ b/src/Contract/API/Management/NetworkAclsInterface.php @@ -0,0 +1,122 @@ + $rule the rules to apply to the ACL + * @param null|array $additional Optional. Additional body content to pass with the API request. See @see for supported options. + * @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these. See @see for supported options.) + * + * @throws \Auth0\SDK\Exception\ArgumentException when an invalid `type` or `sink` are provided + * @throws \Auth0\SDK\Exception\NetworkException when the API request fails due to a network error + * + * @see https://auth0.com/docs/api/management/v2#!/Network_Acls/post_network_acls + */ + public function create( + string $description, + bool $active, + int $priority, + array $rule, + ?array $additional = null, + ?RequestOptions $options = null, + ): ResponseInterface; + + /** + * Delete an Access Control List. + * Required scope: `delete:network_acls`. + * + * @param string $id ID of the Access Control List to delete + * @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these. See @see for supported options.) + * + * @throws \Auth0\SDK\Exception\ArgumentException when an invalid `id` is provided + * @throws \Auth0\SDK\Exception\NetworkException when the API request fails due to a network error + * + * @see https://auth0.com/docs/api/management/v2#!/Network_Acls/delete_network_acls_by_id + */ + public function delete( + string $id, + ?RequestOptions $options = null, + ): ResponseInterface; + + /** + * Get a Single Access Control List. + * Required scope: `read:network_acls`. + * + * @param string $id log Stream ID to query + * @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these. See @see for supported options.) + * + * @throws \Auth0\SDK\Exception\ArgumentException when an invalid `id` is provided + * @throws \Auth0\SDK\Exception\NetworkException when the API request fails due to a network error + * + * @see https://auth0.com/docs/api/management/v2#!/Network_Acls/get_network_acls_by_id + */ + public function get( + string $id, + ?RequestOptions $options = null, + ): ResponseInterface; + + /** + * Get all Access Control Lists. + * Required scope: `read:network_acls`. + * + * @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these. See @see for supported options.) + * + * @throws \Auth0\SDK\Exception\NetworkException when the API request fails due to a network error + * + * @see https://auth0.com/docs/api/management/v2#!/Network_Acls/get_network_acls + */ + public function getAll( + ?RequestOptions $options = null, + ): ResponseInterface; + + /** + * Update partially an existing Access Control List. + * Required scope: `update:network_acls`. + * + * @param string $id ID of the Access Control List to update + * @param array $body Log Stream data to update. Only certain fields are update-able; see the linked documentation. + * @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these. See @see for supported options.) + * + * @throws \Auth0\SDK\Exception\ArgumentException when an invalid `id` is provided + * @throws \Auth0\SDK\Exception\NetworkException when the API request fails due to a network error + * + * @see https://auth0.com/docs/api/management/v2#!/Network_Acls/patch_network_acls_by_id + */ + public function patch( + string $id, + array $body, + ?RequestOptions $options = null, + ): ResponseInterface; + + /** + * Update an existing Access Control List. + * Required scope: `update:network_acls`. + * + * @param string $id ID of the Access Control List to update + * @param array $body Log Stream data to update. Only certain fields are update-able; see the linked documentation. + * @param null|RequestOptions $options Optional. Additional request options to use, such as a field filtering or pagination. (Not all endpoints support these. See @see for supported options.) + * + * @throws \Auth0\SDK\Exception\ArgumentException when an invalid `id` is provided + * @throws \Auth0\SDK\Exception\NetworkException when the API request fails due to a network error + * + * @see https://auth0.com/docs/api/management/v2#!/Network_Acls/put_network_acls_by_id + */ + public function update( + string $id, + array $body, + ?RequestOptions $options = null, + ): ResponseInterface; +} diff --git a/src/Contract/API/ManagementInterface.php b/src/Contract/API/ManagementInterface.php index 1558b34a..377337b7 100644 --- a/src/Contract/API/ManagementInterface.php +++ b/src/Contract/API/ManagementInterface.php @@ -4,7 +4,7 @@ namespace Auth0\SDK\Contract\API; -use Auth0\SDK\Contract\API\Management\{ActionsInterface, BlacklistsInterface, ClientGrantsInterface, ClientsInterface, ConnectionsInterface, DeviceCredentialsInterface, EmailTemplatesInterface, EmailsInterface, GrantsInterface, GuardianInterface, JobsInterface, LogStreamsInterface, LogsInterface, OrganizationsInterface, ResourceServersInterface, RolesInterface, RulesInterface, StatsInterface, TenantsInterface, TicketsInterface, UserBlocksInterface, UsersByEmailInterface, UsersInterface}; +use Auth0\SDK\Contract\API\Management\{ActionsInterface, BlacklistsInterface, ClientGrantsInterface, ClientsInterface, ConnectionsInterface, DeviceCredentialsInterface, EmailTemplatesInterface, EmailsInterface, GrantsInterface, GuardianInterface, JobsInterface, LogStreamsInterface, LogsInterface, NetworkAclsInterface, OrganizationsInterface, ResourceServersInterface, RolesInterface, RulesInterface, StatsInterface, TenantsInterface, TicketsInterface, UserBlocksInterface, UsersByEmailInterface, UsersInterface}; use Auth0\SDK\Utility\HttpResponsePaginator; interface ManagementInterface extends ClientInterface @@ -40,6 +40,8 @@ public function logs(): LogsInterface; public function logStreams(): LogStreamsInterface; + public function networkAcls(): NetworkAclsInterface; + public function organizations(): OrganizationsInterface; public function resourceServers(): ResourceServersInterface; diff --git a/src/Utility/Toolkit/Assert.php b/src/Utility/Toolkit/Assert.php index 340e361d..0fb77fce 100644 --- a/src/Utility/Toolkit/Assert.php +++ b/src/Utility/Toolkit/Assert.php @@ -8,6 +8,8 @@ use Throwable; use function is_array; +use function is_bool; +use function is_int; use function is_string; final class Assert @@ -40,6 +42,20 @@ public function isArray(): void } } + /** + * Check that a variable is a boolean and is not null. + * + * @throws Exception when subject is not a boolean or is null + */ + public function isBoolean(): void + { + foreach ($this->subjects as [$value, $exception]) { + if (! is_bool($value)) { + throw $exception; + } + } + } + /** * Check that a variable is a non-empty string that contains a valid email address. * @@ -54,6 +70,20 @@ public function isEmail(): void } } + /** + * Check that a variable is an integer and is not null. + * + * @throws Exception when subject is not an integer or is null + */ + public function isInteger(): void + { + foreach ($this->subjects as [$value, $exception]) { + if (! is_int($value)) { + throw $exception; + } + } + } + /** * Check for invalid permissions with an array of permissions. * diff --git a/tests/Unit/API/Management/NetworkAclsTest.php b/tests/Unit/API/Management/NetworkAclsTest.php new file mode 100644 index 00000000..33e0edf7 --- /dev/null +++ b/tests/Unit/API/Management/NetworkAclsTest.php @@ -0,0 +1,170 @@ +group('management', 'management.network_acls'); + +beforeEach(function(): void { + $this->endpoint = $this->api->mock()->networkAcls(); +}); + +test('getAll() issues an appropriate request', function(): void { + $this->endpoint->getAll(); + + expect($this->api->getRequestMethod())->toEqual('GET'); + expect($this->api->getRequestUrl())->toStartWith('https://' . $this->api->mock()->getConfiguration()->getDomain() . '/api/v2/network-acls'); +}); + +test('get() issues an appropriate request', function(): void { + $this->endpoint->get('123'); + + expect($this->api->getRequestMethod())->toEqual('GET'); + expect($this->api->getRequestUrl())->toStartWith('https://' . $this->api->mock()->getConfiguration()->getDomain() . '/api/v2/network-acls/123'); +}); + +test('create() issues an appropriate request', function(): void { + $mock = (object) [ + 'description' => uniqid(), + 'active' => true, + 'priority' => 10, + 'rule' => [ + 'action' => [ + 'block' => true, + 'allow' => true, + 'log' => true, + 'redirect' => true, + 'redirect_uri' => 'https://example.com' + ], + 'match' => [ + 'ipv4_cidrs' => [ + '198.51.100.42', + '10.0.0.0/24' + ], + 'ipv6_cidrs' => [ + '2001:0db8:5b96:0000:0000:426f:8e17:642a', + '2002::1234:abcd:ffff:c0a8:101/64' + ], + 'geo_country_codes' => ['US', 'CA'] + ], + 'scope' => 'management' + ], + 'additional' => [ + 'custom_field' => uniqid() + ] + ]; + + $this->endpoint->create($mock->description, $mock->active, $mock->priority, $mock->rule, $mock->additional); + + expect($this->api->getRequestMethod())->toEqual('POST'); + expect($this->api->getRequestUrl())->toEndWith('/api/v2/network-acls'); + + $headers = $this->api->getRequestHeaders(); + expect($headers['Content-Type'][0])->toEqual('application/json'); + + $body = $this->api->getRequestBody(); + $this->assertArrayHasKey('description', $body); + expect($body['description'])->toEqual($mock->description); + $this->assertArrayHasKey('active', $body); + expect($body['active'])->toEqual($mock->active); + $this->assertArrayHasKey('priority', $body); + expect($body['priority'])->toEqual($mock->priority); + $this->assertArrayHasKey('rule', $body); + expect($body['rule'])->toBeArray(); + + // Verify rule action properties + $this->assertArrayHasKey('action', $body['rule']); + expect($body['rule']['action'])->toBeArray(); + expect($body['rule']['action']['block'])->toEqual($mock->rule['action']['block']); + expect($body['rule']['action']['allow'])->toEqual($mock->rule['action']['allow']); + expect($body['rule']['action']['log'])->toEqual($mock->rule['action']['log']); + expect($body['rule']['action']['redirect'])->toEqual($mock->rule['action']['redirect']); + expect($body['rule']['action']['redirect_uri'])->toEqual($mock->rule['action']['redirect_uri']); + + // Verify rule match properties + $this->assertArrayHasKey('match', $body['rule']); + expect($body['rule']['match'])->toBeArray(); + expect($body['rule']['match']['ipv4_cidrs'])->toEqual($mock->rule['match']['ipv4_cidrs']); + expect($body['rule']['match']['ipv6_cidrs'])->toEqual($mock->rule['match']['ipv6_cidrs']); + expect($body['rule']['match']['geo_country_codes'])->toEqual($mock->rule['match']['geo_country_codes']); + + // Verify rule scope + expect($body['rule']['scope'])->toEqual($mock->rule['scope']); + $this->assertArrayHasKey('custom_field', $body); + expect($body['custom_field'])->toEqual($mock->additional['custom_field']); + + $bodyAsString = $this->api->getRequestBodyAsString(); + $expectedBody = json_encode(array_merge([ + 'description' => $mock->description, + 'active' => $mock->active, + 'priority' => $mock->priority, + 'rule' => $mock->rule, + ], $mock->additional)); + expect($bodyAsString)->toEqual($expectedBody); +}); + +test('update() issues an appropriate request', function(): void { + $mock = (object) [ + 'id' => uniqid(), + 'body' => [ + 'description' => uniqid(), + 'active' => false, + 'priority' => 10 + ] + ]; + + $this->endpoint->update($mock->id, $mock->body); + + expect($this->api->getRequestMethod())->toEqual('PUT'); + expect($this->api->getRequestUrl())->toEndWith('/api/v2/network-acls/' . $mock->id); + + $headers = $this->api->getRequestHeaders(); + expect($headers['Content-Type'][0])->toEqual('application/json'); + + $body = $this->api->getRequestBody(); + $this->assertArrayHasKey('description', $body); + expect($body['description'])->toEqual($mock->body['description']); + $this->assertArrayHasKey('active', $body); + expect($body['active'])->toEqual($mock->body['active']); + $this->assertArrayHasKey('priority', $body); + expect($body['priority'])->toEqual($mock->body['priority']); + + $body = $this->api->getRequestBodyAsString(); + expect($body)->toEqual(json_encode($mock->body)); +}); + +test('patch() issues an appropriate request', function(): void { + $mock = (object) [ + 'id' => uniqid(), + 'body' => [ + 'description' => uniqid(), + 'active' => true + ] + ]; + + $this->endpoint->patch($mock->id, $mock->body); + + expect($this->api->getRequestMethod())->toEqual('PATCH'); + expect($this->api->getRequestUrl())->toEndWith('/api/v2/network-acls/' . $mock->id); + + $headers = $this->api->getRequestHeaders(); + expect($headers['Content-Type'][0])->toEqual('application/json'); + + $body = $this->api->getRequestBody(); + $this->assertArrayHasKey('description', $body); + expect($body['description'])->toEqual($mock->body['description']); + $this->assertArrayHasKey('active', $body); + expect($body['active'])->toEqual($mock->body['active']); + + $body = $this->api->getRequestBodyAsString(); + expect($body)->toEqual(json_encode($mock->body)); +}); + +test('delete() issues an appropriate request', function(): void { + $this->endpoint->delete('123'); + + expect($this->api->getRequestMethod())->toEqual('DELETE'); + expect($this->api->getRequestUrl())->toEndWith('/api/v2/network-acls/123'); + + $headers = $this->api->getRequestHeaders(); + expect($headers['Content-Type'][0])->toEqual('application/json'); +});