From 625b068aecd113ac1e1f1597aa3b620ec565de19 Mon Sep 17 00:00:00 2001 From: Tanya Sinha Date: Wed, 16 Oct 2024 14:46:57 +0530 Subject: [PATCH] Added client credentials changes (#670) --- .../client/mgmt/filter/ClientFilter.java | 11 ++ .../OrganizationClientGrantsFilter.java | 11 ++ .../mgmt/filter/ResourceServersFilter.java | 24 ++++ .../com/auth0/json/mgmt/client/Client.java | 18 +++ .../client/ClientDefaultOrganization.java | 57 ++++++++ .../auth0/client/mgmt/ClientsEntityTest.java | 19 +++ .../client/mgmt/OrganizationEntityTest.java | 136 +++++++++++++++++- .../client/mgmt/ResourceServerEntityTest.java | 46 ++++++ .../auth0/json/mgmt/client/ClientTest.java | 12 +- 9 files changed, 329 insertions(+), 5 deletions(-) create mode 100644 src/main/java/com/auth0/json/mgmt/client/ClientDefaultOrganization.java diff --git a/src/main/java/com/auth0/client/mgmt/filter/ClientFilter.java b/src/main/java/com/auth0/client/mgmt/filter/ClientFilter.java index 00e53e2f..0ce98687 100644 --- a/src/main/java/com/auth0/client/mgmt/filter/ClientFilter.java +++ b/src/main/java/com/auth0/client/mgmt/filter/ClientFilter.java @@ -71,4 +71,15 @@ public ClientFilter withFields(String fields, boolean includeFields) { super.withFields(fields, includeFields); return this; } + + /** + * Filter by custom query + * + * @param query the query string using Lucene query syntax + * @return this filter instance + */ + public ClientFilter withQuery(String query) { + parameters.put("q", query); + return this; + } } diff --git a/src/main/java/com/auth0/client/mgmt/filter/OrganizationClientGrantsFilter.java b/src/main/java/com/auth0/client/mgmt/filter/OrganizationClientGrantsFilter.java index e607da02..5230a414 100644 --- a/src/main/java/com/auth0/client/mgmt/filter/OrganizationClientGrantsFilter.java +++ b/src/main/java/com/auth0/client/mgmt/filter/OrganizationClientGrantsFilter.java @@ -51,4 +51,15 @@ public OrganizationClientGrantsFilter withTotals(boolean includeTotals) { parameters.put("include_totals", includeTotals); return this; } + + /** + * Filter by grant IDs + * + * @param grantIds comma-separated list of grant IDs to filter results on. + * @return this filter instance + */ + public OrganizationClientGrantsFilter withGrantIds(String grantIds) { + parameters.put("grant_ids", grantIds); + return this; + } } diff --git a/src/main/java/com/auth0/client/mgmt/filter/ResourceServersFilter.java b/src/main/java/com/auth0/client/mgmt/filter/ResourceServersFilter.java index 4ff64843..ddf81daf 100644 --- a/src/main/java/com/auth0/client/mgmt/filter/ResourceServersFilter.java +++ b/src/main/java/com/auth0/client/mgmt/filter/ResourceServersFilter.java @@ -33,4 +33,28 @@ public ResourceServersFilter withTotals(boolean includeTotals) { return this; } + /** + * Filter by specific identifier IDs (i.e. audience) + * + * @param identifiers the identifier IDs to filter by + * @return this filter instance + */ + public ResourceServersFilter withIdentifiers(String identifiers) { + parameters.put("identifiers", identifiers); + return this; + } + + /** + * Filter by checkpoint pagination support + * + * @param from the starting index identifier + * @param take the number of items to retrieve + * @return this filter instance + */ + public ResourceServersFilter withCheckpointPagination(String from, int take) { + parameters.put("from", from); + parameters.put("take", take); + return this; + } + } diff --git a/src/main/java/com/auth0/json/mgmt/client/Client.java b/src/main/java/com/auth0/json/mgmt/client/Client.java index 99ffeefa..0b0e705e 100644 --- a/src/main/java/com/auth0/json/mgmt/client/Client.java +++ b/src/main/java/com/auth0/json/mgmt/client/Client.java @@ -102,6 +102,8 @@ public class Client { private String complianceLevel; @JsonProperty("require_proof_of_possession") private Boolean requireProofOfPossession; + @JsonProperty("default_organization") + private ClientDefaultOrganization defaultOrganization; /** * Getter for the name of the tenant this client belongs to. @@ -889,5 +891,21 @@ public Boolean getRequireProofOfPossession() { public void setRequireProofOfPossession(Boolean requireProofOfPossession) { this.requireProofOfPossession = requireProofOfPossession; } + + /** + * Getter for the default organization configuration. + * @return the default organization configuration. + */ + public ClientDefaultOrganization getDefaultOrganization() { + return defaultOrganization; + } + + /** + * Setter for the default organization configuration. + * @param defaultOrganization the default organization configuration to set. + */ + public void setDefaultOrganization(ClientDefaultOrganization defaultOrganization) { + this.defaultOrganization = defaultOrganization; + } } diff --git a/src/main/java/com/auth0/json/mgmt/client/ClientDefaultOrganization.java b/src/main/java/com/auth0/json/mgmt/client/ClientDefaultOrganization.java new file mode 100644 index 00000000..3512f6b8 --- /dev/null +++ b/src/main/java/com/auth0/json/mgmt/client/ClientDefaultOrganization.java @@ -0,0 +1,57 @@ +package com.auth0.json.mgmt.client; + +import com.fasterxml.jackson.annotation.JsonIgnoreProperties; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; + +import java.util.List; + +@JsonIgnoreProperties(ignoreUnknown = true) +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ClientDefaultOrganization { + @JsonProperty("flows") + private List flows; + @JsonProperty("organization_id") + private String organizationId; + + public ClientDefaultOrganization() { + + } + + public ClientDefaultOrganization(List flows, String organizationId) { + this.flows = flows; + this.organizationId = organizationId; + } + + /** + * Getter for the supported flows. + * @return the supported flows. + */ + public List getFlows() { + return flows; + } + + /** + * Setter for the supported flows. + * @param flows the supported flows to set. + */ + public void setFlows(List flows) { + this.flows = flows; + } + + /** + * Getter for the organization_id. + * @return the organization_id. + */ + public String getOrganizationId() { + return organizationId; + } + + /** + * Setter for the organization_id. + * @param organizationId the organization_id to set. + */ + public void setOrganizationId(String organizationId) { + this.organizationId = organizationId; + } +} diff --git a/src/test/java/com/auth0/client/mgmt/ClientsEntityTest.java b/src/test/java/com/auth0/client/mgmt/ClientsEntityTest.java index a04ecfc3..4e574254 100644 --- a/src/test/java/com/auth0/client/mgmt/ClientsEntityTest.java +++ b/src/test/java/com/auth0/client/mgmt/ClientsEntityTest.java @@ -125,6 +125,25 @@ public void shouldListClientsWithAdditionalProperties() throws Exception { assertThat(response.getItems(), hasSize(2)); } + @Test + public void shouldListClientsWithQuery() throws Exception { + ClientFilter filter = new ClientFilter().withQuery("client_grant.organization_id:" + "org_123"); + Request request = api.clients().list(filter); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_CLIENTS_PAGED_LIST, 200); + ClientsPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/clients")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + assertThat(recordedRequest, hasQueryParameter("q", "client_grant.organization_id:" + "org_123")); + + assertThat(response, is(notNullValue())); + assertThat(response.getItems(), hasSize(2)); + } + @Test public void shouldThrowOnGetClientWithNullId() { verifyThrows(IllegalArgumentException.class, diff --git a/src/test/java/com/auth0/client/mgmt/OrganizationEntityTest.java b/src/test/java/com/auth0/client/mgmt/OrganizationEntityTest.java index 7dc06479..bfa20956 100644 --- a/src/test/java/com/auth0/client/mgmt/OrganizationEntityTest.java +++ b/src/test/java/com/auth0/client/mgmt/OrganizationEntityTest.java @@ -1,11 +1,15 @@ package com.auth0.client.mgmt; import com.auth0.client.MockServer; -import com.auth0.client.mgmt.filter.FieldsFilter; -import com.auth0.client.mgmt.filter.InvitationsFilter; -import com.auth0.client.mgmt.filter.OrganizationClientGrantsFilter; -import com.auth0.client.mgmt.filter.PageFilter; +import com.auth0.client.mgmt.filter.*; +import com.auth0.exception.Auth0Exception; +import com.auth0.json.mgmt.client.Client; +import com.auth0.json.mgmt.client.ClientDefaultOrganization; +import com.auth0.json.mgmt.client.ClientsPage; +import com.auth0.json.mgmt.clientgrants.ClientGrant; +import com.auth0.json.mgmt.clientgrants.ClientGrantsPage; import com.auth0.json.mgmt.organizations.*; +import com.auth0.json.mgmt.resourceserver.ResourceServer; import com.auth0.json.mgmt.roles.RolesPage; import com.auth0.net.Request; import com.auth0.net.client.HttpMethod; @@ -1082,6 +1086,32 @@ public void shouldListClientGrantsWithFilter() throws Exception { assertThat(response.getItems(), hasSize(1)); } + @Test + public void shouldListClientGrantsWithGrantIds() throws Exception { + OrganizationClientGrantsFilter filter = new OrganizationClientGrantsFilter(); + filter + .withClientId("clientId") + .withAudience("https://api-identifier/") + .withGrantIds("cgr_123456789012,cgr_abcdefghijkl"); + + Request request = api.organizations().listClientGrants("orgId", filter); + assertThat(request, is(notNullValue())); + + server.jsonResponse(ORGANIZATION_CLIENT_GRANTS_PAGED_LIST, 200); + OrganizationClientGrantsPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/organizations/orgId/client-grants")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + assertThat(recordedRequest, hasQueryParameter("grant_ids", "cgr_123456789012,cgr_abcdefghijkl")); + assertThat(recordedRequest, hasQueryParameter("audience", "https://api-identifier/")); + assertThat(recordedRequest, hasQueryParameter("client_id", "clientId")); + + assertThat(response, is(notNullValue())); + assertThat(response.getItems(), hasSize(1)); + } + @Test public void shouldThrowOnGetClientGrantsWithNullOrgId() { verifyThrows(IllegalArgumentException.class, @@ -1153,4 +1183,102 @@ public void shouldThrowOnDeleteClientGreatWithNullGrant() { () -> api.organizations().deleteClientGrant("org_1213", null), "'client grant ID' cannot be null!"); } + + @Test + public void testClientGrantsWithOrg() throws Auth0Exception { + + Organization organization = null; + ResourceServer resourceServer = null; + Client client = null; + ClientGrant clientGrant = null; + OrganizationClientGrant organizationClientGrant = null; + + try { + //Create organization + organization = givenAnOrganization(); + + //Create resource server + resourceServer = givenAResourceServer(); + + //Create client + client = createNewClient(organization.getId()); + + //Create client grants + clientGrant = createNewClientGrant(client, resourceServer); + + //Associates the grant with an organization. + organizationClientGrant = api.organizations().addClientGrant(organization.getId(), new CreateOrganizationClientGrantRequestBody(clientGrant.getId())).execute().getBody(); + + ClientFilter clientFilter = new ClientFilter(); + clientFilter.withQuery("client_grant.organization_id:" + organization.getId()); + + // List all clients associated with a ClientGrant given an organizationID as query param + ClientsPage clientsPage = api.clients().list(clientFilter).execute().getBody(); + + for (Client c : clientsPage.getItems()) { + assertThat(organization.getId(), is(c.getDefaultOrganization().getOrganizationId())); + } + + OrganizationClientGrantsFilter filter = new OrganizationClientGrantsFilter(); + filter.withGrantIds(clientGrant.getId()); + + // List all ClientGrants given a list of grant_ids as query param + OrganizationClientGrantsPage organizationClientGrantsPage = api.organizations().listClientGrants(organization.getId(), filter).execute().getBody(); + + assertThat(organizationClientGrantsPage.getItems().size(), is(1)); + assertThat(organizationClientGrantsPage.getItems().get(0).getClientId(), is(clientGrant.getClientId())); + + // Remove the associated ClientGrants + api.organizations().deleteClientGrant(organization.getId(), organizationClientGrant.getId()).execute(); + + // List all ClientGrants which should be an empty list since grant has been removed from the organization. + OrganizationClientGrantsPage organizationClientGrantsPage1 = api.organizations().listClientGrants(organization.getId(), filter).execute().getBody(); + assertThat(organizationClientGrantsPage1.getItems().size(), is(0)); + + // Delete the ClientGrant. + api.clientGrants().delete(clientGrant.getId()).execute(); + + // Retrieve the ClientGrant and ensure error is return since grant has been deleted. + ClientGrantsPage clientGrantsPage = api.clientGrants().list(new ClientGrantsFilter().withClientId(client.getClientId())).execute().getBody(); + assertThat(clientGrantsPage.getItems().size(), is(0)); + } + catch (Exception ex){ + ex.printStackTrace(); + } + } + + private ClientGrant createNewClientGrant(Client client, ResourceServer resourceServer) throws Auth0Exception { + ClientGrant clientGrant = new ClientGrant(); + clientGrant.setClientId(client.getClientId()); + clientGrant.setAudience(resourceServer.getIdentifier()); + clientGrant.setScope(Arrays.asList("create:resource", "create:organization_client_grants")); + clientGrant.setAllowAnyOrganization(true); + clientGrant.setOrganizationUsage("allow"); + + return api.clientGrants().create(client.getClientId(), resourceServer.getIdentifier(), new String[]{"create:resource", "create:organization_client_grants"}).execute().getBody(); + } + + private Client createNewClient(String orgId) throws Auth0Exception { + Client client = new Client("Test Client (" + System.currentTimeMillis() + ")"); + client.setDescription("This is just a test client."); + client.setOrganizationUsage("allow"); + client.setDefaultOrganization(new ClientDefaultOrganization(Arrays.asList("client_credentials"), orgId)); + + return api.clients().create(client).execute().getBody(); + } + + private Organization givenAnOrganization() throws Auth0Exception { + Organization organization = new Organization(); + organization.setName("test-organization"); + organization.setDisplayName("test-organization"); + + return api.organizations().create(organization).execute().getBody(); + } + + private ResourceServer givenAResourceServer() throws Auth0Exception { + ResourceServer resourceServer = new ResourceServer("https://www.tanyaisawesome.com"); + resourceServer.setName("tanyaisawesome"); + + return api.resourceServers().create(resourceServer).execute().getBody(); + } } diff --git a/src/test/java/com/auth0/client/mgmt/ResourceServerEntityTest.java b/src/test/java/com/auth0/client/mgmt/ResourceServerEntityTest.java index e88c7a0f..e4f7d9fe 100644 --- a/src/test/java/com/auth0/client/mgmt/ResourceServerEntityTest.java +++ b/src/test/java/com/auth0/client/mgmt/ResourceServerEntityTest.java @@ -80,6 +80,52 @@ public void shouldListResourceServerWithTotals() throws Exception { assertThat(response.getLimit(), is(50)); } + @Test + public void shouldListResourceServerWithIdentifiers() throws Exception { + ResourceServersFilter filter = new ResourceServersFilter().withIdentifiers("identifier"); + Request request = api.resourceServers().list(filter); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_RESOURCE_SERVERS_PAGED_LIST, 200); + ResourceServersPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/resource-servers")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + assertThat(recordedRequest, hasQueryParameter("identifiers", "identifier")); + + assertThat(response, is(notNullValue())); + assertThat(response.getItems(), hasSize(2)); + assertThat(response.getStart(), is(0)); + assertThat(response.getLength(), is(14)); + assertThat(response.getTotal(), is(14)); + assertThat(response.getLimit(), is(50)); + } + + @Test + public void shouldListResourceServerWithCheckpointPagination() throws Exception { + ResourceServersFilter filter = new ResourceServersFilter().withCheckpointPagination("tokenId2", 5); + Request request = api.resourceServers().list(filter); + assertThat(request, is(notNullValue())); + + server.jsonResponse(MGMT_RESOURCE_SERVERS_PAGED_LIST, 200); + ResourceServersPage response = request.execute().getBody(); + RecordedRequest recordedRequest = server.takeRequest(); + + assertThat(recordedRequest, hasMethodAndPath(HttpMethod.GET, "/api/v2/resource-servers")); + assertThat(recordedRequest, hasHeader("Content-Type", "application/json")); + assertThat(recordedRequest, hasHeader("Authorization", "Bearer apiToken")); + assertThat(recordedRequest, hasQueryParameter("from", "tokenId2")); + assertThat(recordedRequest, hasQueryParameter("take", "5")); + assertThat(response, is(notNullValue())); + assertThat(response.getItems(), hasSize(2)); + assertThat(response.getStart(), is(0)); + assertThat(response.getLength(), is(14)); + assertThat(response.getTotal(), is(14)); + assertThat(response.getLimit(), is(50)); + } + @Test public void shouldUpdateResourceServer() throws Exception { ResourceServer resourceServer = new ResourceServer("https://api.my-company.com/api/v2/"); diff --git a/src/test/java/com/auth0/json/mgmt/client/ClientTest.java b/src/test/java/com/auth0/json/mgmt/client/ClientTest.java index 11f901ba..05bbd465 100644 --- a/src/test/java/com/auth0/json/mgmt/client/ClientTest.java +++ b/src/test/java/com/auth0/json/mgmt/client/ClientTest.java @@ -136,7 +136,11 @@ public class ClientTest extends JsonTest { " ]\n" + " },\n" + " \"require_proof_of_possession\": true,\n" + - " \"compliance_level\": \"fapi1_adv_pkj_par\"\n" + + " \"compliance_level\": \"fapi1_adv_pkj_par\",\n" + + " \"default_organization\": {\n" + + " \"flows\": [\"client_credentials\"],\n" + + " \"organizations_id\": \"org_id\"\n" + + " }\n" + "}"; @Test @@ -214,6 +218,11 @@ public void shouldSerialize() throws Exception { client.setSignedRequest(signedRequest); client.setComplianceLevel("fapi1_adv_pkj_par"); + ClientDefaultOrganization defaultOrganization = new ClientDefaultOrganization(); + defaultOrganization.setFlows(Collections.singletonList("client_credentials")); + defaultOrganization.setOrganizationId("org_id"); + client.setDefaultOrganization(defaultOrganization); + String serialized = toJSON(client); assertThat(serialized, is(notNullValue())); @@ -254,6 +263,7 @@ public void shouldSerialize() throws Exception { assertThat(serialized, JsonMatcher.hasEntry("signed_request_object", containsString("{\"required\":true,\"credentials\":[{\"credential_type\":\"public_key\",\"name\":\"cred name\",\"pem\":\"pem\"}]}"))); assertThat(serialized, JsonMatcher.hasEntry("compliance_level", "fapi1_adv_pkj_par")); assertThat(serialized, JsonMatcher.hasEntry("require_proof_of_possession", true)); + assertThat(serialized, JsonMatcher.hasEntry("default_organization", notNullValue())); } @Test