diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpoints.java b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpoints.java index ec127fcee47..3e57f54642e 100644 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpoints.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpoints.java @@ -46,6 +46,7 @@ import org.cloudfoundry.identity.uaa.scim.ScimGroupProvisioning; import org.cloudfoundry.identity.uaa.util.JsonUtils; import org.cloudfoundry.identity.uaa.util.ObjectUtils; +import org.cloudfoundry.identity.uaa.util.UaaStringUtils; import org.cloudfoundry.identity.uaa.zone.beans.IdentityZoneManager; import org.opensaml.saml2.metadata.provider.MetadataProviderException; import org.slf4j.Logger; @@ -293,10 +294,19 @@ public ResponseEntity updateIdentityProviderStatus(@Path } @GetMapping() - public ResponseEntity> retrieveIdentityProviders(@RequestParam(value = "active_only", required = false) String activeOnly, @RequestParam(required = false, defaultValue = "false") boolean rawConfig) { + public ResponseEntity> retrieveIdentityProviders( + @RequestParam(value = "active_only", required = false) String activeOnly, + @RequestParam(required = false, defaultValue = "false") boolean rawConfig, + @RequestParam(required = false, defaultValue = "") String originKey) + { boolean retrieveActiveOnly = Boolean.parseBoolean(activeOnly); - List identityProviderList = identityProviderProvisioning.retrieveAll(retrieveActiveOnly, identityZoneManager.getCurrentIdentityZoneId()); - for(IdentityProvider idp : identityProviderList) { + List identityProviderList; + if (UaaStringUtils.isNotEmpty(originKey)) { + identityProviderList = List.of(identityProviderProvisioning.retrieveByOrigin(originKey, identityZoneManager.getCurrentIdentityZoneId())); + } else { + identityProviderList = identityProviderProvisioning.retrieveAll(retrieveActiveOnly, identityZoneManager.getCurrentIdentityZoneId()); + } + for(IdentityProvider idp : identityProviderList) { idp.setSerializeConfigRaw(rawConfig); setAuthMethod(idp); redactSensitiveData(idp); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpointsTest.java b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpointsTest.java index ecc122a4045..cd70f259025 100644 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpointsTest.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/provider/IdentityProviderEndpointsTest.java @@ -296,7 +296,7 @@ void patch_bind_password_non_ldap() { void retrieve_all_providers_redacts_data() { when(mockIdentityProviderProvisioning.retrieveAll(anyBoolean(), anyString())) .thenReturn(Arrays.asList(getLdapDefinition(), getExternalOAuthProvider())); - ResponseEntity> ldapList = identityProviderEndpoints.retrieveIdentityProviders("false", true); + ResponseEntity> ldapList = identityProviderEndpoints.retrieveIdentityProviders("false", true, ""); assertNotNull(ldapList); assertNotNull(ldapList.getBody()); assertEquals(2, ldapList.getBody().size()); @@ -313,6 +313,22 @@ void retrieve_all_providers_redacts_data() { assertNull(oauth.getConfig().getRelyingPartySecret()); } + @Test + void retrieve_by_origin_providers_redacts_data() { + when(mockIdentityProviderProvisioning.retrieveByOrigin(anyString(), anyString())) + .thenReturn(getExternalOAuthProvider()); + ResponseEntity> puppyList = identityProviderEndpoints.retrieveIdentityProviders("false", true, "puppy"); + assertNotNull(puppyList); + assertNotNull(puppyList.getBody()); + assertEquals(1, puppyList.getBody().size()); + IdentityProvider oidc = puppyList.getBody().get(0); + assertNotNull(oidc); + assertNotNull(oidc.getConfig()); + assertTrue(oidc.getConfig() instanceof AbstractExternalOAuthIdentityProviderDefinition); + assertNull(oidc.getConfig().getRelyingPartySecret()); + assertEquals(ClientAuthentication.CLIENT_SECRET_BASIC, oidc.getConfig().getAuthMethod()); + } + @Test void update_ldap_provider_patches_password() throws Exception { IdentityProvider provider = retrieve_ldap_provider_by_id("id"); diff --git a/uaa/slateCustomizations/source/index.html.md.erb b/uaa/slateCustomizations/source/index.html.md.erb index 490ae2f633f..792a2ce97b3 100644 --- a/uaa/slateCustomizations/source/index.html.md.erb +++ b/uaa/slateCustomizations/source/index.html.md.erb @@ -1115,6 +1115,31 @@ _Error Codes_ |------------|-----------------------------------------------------------------------| | 403 | Forbidden - Insufficient scope | +## Retrieve with Filtering + +<%= render('IdentityProviderEndpointDocs/getFilteredIdentityProviders/curl-request.md') %> +<%= render('IdentityProviderEndpointDocs/getFilteredIdentityProviders/http-request.md') %> +<%= render('IdentityProviderEndpointDocs/getFilteredIdentityProviders/http-response.md') %> + +_Request Headers_ + +<%= render('IdentityProviderEndpointDocs/getFilteredIdentityProviders/request-headers.md') %> + +_Request Parameters_ + +<%= render('IdentityProviderEndpointDocs/getFilteredIdentityProviders/request-parameters.md') %> + +_Response Fields_ + +<%= render('IdentityProviderEndpointDocs/getFilteredIdentityProviders/response-fields.md') %> + +_Error Codes_ + +| Error Code | Description | +|------------|-----------------------------------------------------------------------| +| 403 | Forbidden - Insufficient scope | + + ## Retrieve <%= render('IdentityProviderEndpointDocs/getIdentityProvider/curl-request.md') %> diff --git a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointDocs.java b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointDocs.java index a1e164683a9..100b2ae51e9 100644 --- a/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointDocs.java +++ b/uaa/src/test/java/org/cloudfoundry/identity/uaa/mock/providers/IdentityProviderEndpointDocs.java @@ -967,6 +967,47 @@ void getAllIdentityProviders() throws Exception { responseFields)); } + @Test + void getFilteredIdentityProviders() throws Exception { + Snippet responseFields = responseFields( + fieldWithPath("[].type").description("Type of the identity provider."), + fieldWithPath("[].originKey").description("Unique identifier for the identity provider."), + fieldWithPath("[].name").description(NAME_DESC), + fieldWithPath("[].config").description(CONFIG_DESCRIPTION), + fieldWithPath("[]." + FIELD_ALIAS_ID).description(ALIAS_ID_DESC).attributes(key("constraints").value("Optional")).optional().type(STRING), + fieldWithPath("[]." + FIELD_ALIAS_ZID).description(ALIAS_ZID_DESC).attributes(key("constraints").value("Optional")).optional().type(STRING), + + fieldWithPath("[].version").description(VERSION_DESC), + fieldWithPath("[].active").description(ACTIVE_DESC), + + fieldWithPath("[].id").description(ID_DESC), + fieldWithPath("[].identityZoneId").description(IDENTITY_ZONE_ID_DESC), + fieldWithPath("[].created").description(CREATED_DESC), + fieldWithPath("[].last_modified").description(LAST_MODIFIED_DESC) + ); + + mockMvc.perform(get("/identity-providers") + .param("rawConfig", "false") + .param("active_only", "false") + .param("originKey", "my-oauth2-provider") + .header("Authorization", "Bearer " + adminToken) + .contentType(APPLICATION_JSON)) + .andExpect(status().isOk()) + .andDo(document("{ClassName}/{methodName}", + preprocessResponse(prettyPrint()), + requestHeaders( + headerWithName("Authorization").description("Bearer token containing `zones..admin` or `zones..idps.read` or `uaa.admin` or `idps.read` (only in the same zone that you are a user of)"), + headerWithName("X-Identity-Zone-Id").description("May include this header to administer another zone if using `zones..admin` or `zones..idps.read` or `uaa.admin` scope against the default UAA zone.").optional(), + IDENTITY_ZONE_SUBDOMAIN_HEADER + ), + requestParameters( + parameterWithName("rawConfig").optional("false").type(BOOLEAN).description("Flag indicating whether the response should use raw, unescaped JSON for the `config` field of the IDP, rather than the default behavior of encoding the JSON as a string."), + parameterWithName("active_only").optional("false").type(BOOLEAN).description("Flag indicating whether only active IdPs should be returned or all."), + parameterWithName("originKey").optional(null).type(STRING).description("UAA 77.10.0 Return only IdPs with specific origin.") + ), + responseFields)); + } + @Test void getIdentityProvider() throws Exception { IdentityProvider identityProvider = JsonUtils.readValue(mockMvc.perform(post("/identity-providers")