diff --git a/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpoint.java b/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpoint.java index 0888a8a505a..012e5d03613 100755 --- a/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpoint.java +++ b/server/src/main/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpoint.java @@ -71,7 +71,6 @@ import java.security.Principal; import java.sql.Timestamp; import java.text.SimpleDateFormat; -import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Date; @@ -85,7 +84,6 @@ import java.util.Properties; import java.util.regex.Matcher; import java.util.regex.Pattern; -import java.util.stream.Stream; import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.Base64.getDecoder; @@ -289,8 +287,8 @@ private String login(Model model, Principal principal, List excludedProm String loginHintParam = extractLoginHintParam(session, request); UaaLoginHint uaaLoginHint = UaaLoginHint.parseRequestParameter(loginHintParam); - Map samlIdentityProviders; - Map oauthIdentityProviders; + Map samlIdentityProviders = null; + Map oauthIdentityProviders = null; Map allIdentityProviders = Map.of(); Map.Entry loginHintProvider = null; @@ -304,32 +302,34 @@ private String login(Model model, Principal principal, List excludedProm ); if (idp != null) { loginHintProvider = Map.entry(idp.getOriginKey(), idp.getConfig()); + oauthIdentityProviders = new HashMap<>(); + oauthIdentityProviders.put(idp.getOriginKey(), (AbstractExternalOAuthIdentityProviderDefinition) idp.getConfig()); } } catch (EmptyResultDataAccessException ignored) { // ignore } } if (loginHintProvider != null) { - oauthIdentityProviders = Map.of(); - samlIdentityProviders = Map.of(); + oauthIdentityProviders = addDefaultOauthMap(oauthIdentityProviders, allowedIdentityProviderKeys, defaultIdentityProviderName); + samlIdentityProviders = addDefaultSamlMap(samlIdentityProviders, allowedIdentityProviderKeys, defaultIdentityProviderName); } else { accountChooserNeeded = false; samlIdentityProviders = getSamlIdentityProviderDefinitions(allowedIdentityProviderKeys); oauthIdentityProviders = getOauthIdentityProviderDefinitions(allowedIdentityProviderKeys); allIdentityProviders = concatenateMaps(samlIdentityProviders, oauthIdentityProviders); } - } else if (!jsonResponse && (accountChooserNeeded || (accountChooserEnabled && !discoveryEnabled && !discoveryPerformed))) { + } else if (!jsonResponse && (accountChooserNeeded || (accountChooserEnabled && !discoveryEnabled && !discoveryPerformed))) { // when `/login` is requested to return html response (as opposed to json response) //Account and origin chooser do not need idp information - oauthIdentityProviders = Map.of(); - samlIdentityProviders = Map.of(); + oauthIdentityProviders = addDefaultOauthMap(oauthIdentityProviders, allowedIdentityProviderKeys, defaultIdentityProviderName); + samlIdentityProviders = addDefaultSamlMap(samlIdentityProviders, allowedIdentityProviderKeys, defaultIdentityProviderName); } else { samlIdentityProviders = getSamlIdentityProviderDefinitions(allowedIdentityProviderKeys); if (jsonResponse) { /* the OAuth IdPs and all IdPs are used for determining the redirect; if jsonResponse is true, the * redirect is ignored anyway */ - oauthIdentityProviders = Map.of(); + oauthIdentityProviders = addDefaultOauthMap(oauthIdentityProviders, allowedIdentityProviderKeys, defaultIdentityProviderName); } else { oauthIdentityProviders = getOauthIdentityProviderDefinitions(allowedIdentityProviderKeys); } @@ -652,6 +652,36 @@ private Map getSamlIdentityProviderDefin return filteredIdps.stream().collect(new MapCollector<>(SamlIdentityProviderDefinition::getIdpEntityAlias, idp -> idp)); } + private Map addDefaultSamlMap(Map list, List allowedIdps, String defaultIdp) { + Map defaultList = list == null ? new HashMap<>() : list; + IdentityProvider samlIdP = getIdentityProvider(allowedIdps, defaultIdp); + if (samlIdP != null && samlIdP.getConfig() instanceof SamlIdentityProviderDefinition samlDefinition) { + defaultList.putIfAbsent(samlDefinition.getIdpEntityAlias(), samlDefinition); + } + return defaultList; + } + + private Map addDefaultOauthMap(Map list, List allowedIdps, String defaultIdp) { + Map defaultList = list == null ? new HashMap<>() : list; + IdentityProvider oauthIdP = getIdentityProvider(allowedIdps, defaultIdp); + if (oauthIdP != null && oauthIdP.getConfig() instanceof AbstractExternalOAuthIdentityProviderDefinition oDefinition) { + defaultList.putIfAbsent(oauthIdP.getOriginKey(), oDefinition); + } + return defaultList; + } + + private IdentityProvider getIdentityProvider(List allowedIdps, String defaultIdp) { + IdentityProvider samlIdP = null; + try { + if (defaultIdp != null && (allowedIdps == null || allowedIdps.contains(defaultIdp))) { + samlIdP = providerProvisioning.retrieveByOrigin(defaultIdp, IdentityZoneHolder.get().getId()); + } + } catch (EmptyResultDataAccessException ignored) { + // ignore + } + return samlIdP; + } + protected Map getOauthIdentityProviderDefinitions(List allowedIdps) { List identityProviders = externalOAuthProviderConfigurator.retrieveActiveByTypes( IdentityZoneHolder.get().getId(), @@ -704,15 +734,18 @@ private void populatePrompts( Map oauthIdentityProviders, boolean returnLoginPrompts ) { + boolean noIdpsPresent = true; for (SamlIdentityProviderDefinition idp : samlIdentityProviders.values()) { if (idp.isShowSamlLink()) { model.addAttribute(SHOW_LOGIN_LINKS, true); + noIdpsPresent = false; break; } } for (AbstractExternalOAuthIdentityProviderDefinition oauthIdp : oauthIdentityProviders.values()) { if (oauthIdp.isShowLinkText()) { model.addAttribute(SHOW_LOGIN_LINKS, true); + noIdpsPresent = false; break; } } @@ -720,6 +753,9 @@ private void populatePrompts( //make the list writeable final List excludedPrompts = new LinkedList<>(exclude); + if (noIdpsPresent) { + excludedPrompts.add(PASSCODE); + } if (!returnLoginPrompts) { excludedPrompts.add(USERNAME_PARAMETER); excludedPrompts.add("password"); diff --git a/server/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointTests.java b/server/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointTests.java index 4de246a2244..cfa8a0c2fa1 100755 --- a/server/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointTests.java +++ b/server/src/test/java/org/cloudfoundry/identity/uaa/login/LoginInfoEndpointTests.java @@ -580,10 +580,10 @@ void promptLogic() throws Exception { assertThat(extendedModelMap).as("prompts attribute should be present").containsKey("prompts"); assertThat(extendedModelMap.get("prompts")).as("prompts should be a Map for JSON content").isInstanceOf(Map.class); mapPrompts = (Map) extendedModelMap.get("prompts"); - assertThat(mapPrompts).as("there should be three prompts for html").hasSize(3) + assertThat(mapPrompts).as("there should be three prompts for html").hasSize(2) .containsKey("username") .containsKey("password") - .containsKey("passcode"); + .doesNotContainKey("passcode"); //add a SAML IDP extendedModelMap.clear(); @@ -832,7 +832,7 @@ void oauth_provider_links_shown() throws Exception { } @Test - void passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider() throws Exception { + void no_passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider() throws Exception { LoginInfoEndpoint endpoint = getEndpoint(IdentityZoneHolder.get()); RawExternalOAuthIdentityProviderDefinition definition = new RawExternalOAuthIdentityProviderDefinition() @@ -847,11 +847,11 @@ void passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider() throws E endpoint.infoForLoginJson(extendedModelMap, null, new MockHttpServletRequest("GET", "http://someurl")); Map mapPrompts = (Map) extendedModelMap.get("prompts"); - assertThat(mapPrompts).containsKey("passcode"); + assertThat(mapPrompts).doesNotContainKey("passcode"); } @Test - void passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider_stillWorksWithAccountChooser() throws Exception { + void no_passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider_stillWorksWithAccountChooser() throws Exception { IdentityZoneHolder.get().getConfig().setAccountChooserEnabled(true); LoginInfoEndpoint endpoint = getEndpoint(IdentityZoneHolder.get()); @@ -867,11 +867,11 @@ void passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider_stillWorks endpoint.infoForLoginJson(extendedModelMap, null, new MockHttpServletRequest("GET", "http://someurl")); Map mapPrompts = (Map) extendedModelMap.get("prompts"); - assertThat(mapPrompts).containsKey("passcode"); + assertThat(mapPrompts).doesNotContainKey("passcode"); } @Test - void passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider_stillWorksWithDiscovery() throws Exception { + void no_passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider_stillWorksWithDiscovery() throws Exception { IdentityZoneHolder.get().getConfig().setIdpDiscoveryEnabled(true); LoginInfoEndpoint endpoint = getEndpoint(IdentityZoneHolder.get()); @@ -886,7 +886,7 @@ void passcode_prompt_present_whenThereIsAtleastOneActiveOauthProvider_stillWorks endpoint.infoForLoginJson(extendedModelMap, null, new MockHttpServletRequest("GET", "http://someurl")); Map mapPrompts = (Map) extendedModelMap.get("prompts"); - assertThat(mapPrompts).containsKey("passcode"); + assertThat(mapPrompts).doesNotContainKey("passcode"); } @Test @@ -1267,13 +1267,13 @@ void getPromptsFromOIDCProvider() { assertThat(extendedModelMap).containsKey("prompts"); assertThat(extendedModelMap.get("prompts")).isInstanceOf(Map.class); Map returnedPrompts = (Map) extendedModelMap.get("prompts"); - assertThat(returnedPrompts).hasSize(3) + assertThat(returnedPrompts).hasSize(2) .containsKey("username") .containsKey("password") - .containsKey("passcode"); + .doesNotContainKey("passcode"); assertThat(returnedPrompts.get("username")[1]).isEqualTo("MyEmail"); assertThat(returnedPrompts.get("password")[1]).isEqualTo("MyPassword"); - assertThat(returnedPrompts.get("passcode")[1]).isEqualTo(passcodePromptText); + assertThat(returnedPrompts.get("passcode")).isNull(); } @Test @@ -1292,7 +1292,7 @@ void getPromptsFromNonOIDCProvider() { assertThat(extendedModelMap).containsKey("prompts"); assertThat(extendedModelMap.get("prompts")).isInstanceOf(Map.class); Map returnedPrompts = (Map) extendedModelMap.get("prompts"); - assertUsernamePasswordAndPasscodePromptsAreReturned(returnedPrompts); + assertUsernamePasswordButNoPasscodePromptsAreReturned(returnedPrompts); } @Test @@ -1308,7 +1308,7 @@ void getPromptsFromNonExistentProvider() { assertThat(extendedModelMap).containsKey("prompts"); assertThat(extendedModelMap.get("prompts")).isInstanceOf(Map.class); Map returnedPrompts = (Map) extendedModelMap.get("prompts"); - assertUsernamePasswordAndPasscodePromptsAreReturned(returnedPrompts); + assertUsernamePasswordButNoPasscodePromptsAreReturned(returnedPrompts); } @Test @@ -1330,7 +1330,7 @@ void getPromptsFromOIDCProviderWithoutPrompts() { assertThat(extendedModelMap).containsKey("prompts"); assertThat(extendedModelMap.get("prompts")).isInstanceOf(Map.class); Map returnedPrompts = (Map) extendedModelMap.get("prompts"); - assertUsernamePasswordAndPasscodePromptsAreReturned(returnedPrompts); + assertUsernamePasswordButNoPasscodePromptsAreReturned(returnedPrompts); } @Test @@ -1733,6 +1733,7 @@ private static void mockOidcProvider(IdentityProviderProvisioning mockIdentityPr when(mockProvider.getConfig()).thenReturn(mockOidcConfig); when(mockOidcConfig.isShowLinkText()).thenReturn(true); when(mockIdentityProviderProvisioning.retrieveActiveByTypes(anyString(), any())).thenReturn(singletonList(mockProvider)); + when(mockIdentityProviderProvisioning.retrieveByOrigin(eq("my-OIDC-idp1"), any())).thenReturn(mockProvider); } private static void mockLoginHintProvider(ExternalOAuthProviderConfigurator mockIdentityProviderProvisioning) @@ -1750,15 +1751,15 @@ private static void mockLoginHintProvider(ExternalOAuthProviderConfigurator mock when(mockIdentityProviderProvisioning.retrieveByOrigin(eq("my-OIDC-idp1"), any())).thenReturn(mockProvider); } - private static void assertUsernamePasswordAndPasscodePromptsAreReturned( + private static void assertUsernamePasswordButNoPasscodePromptsAreReturned( final Map returnedPrompts ) { - assertThat(returnedPrompts).hasSize(3) + assertThat(returnedPrompts).hasSize(2) .containsKey("username") .containsKey("password") - .containsKey("passcode"); + .doesNotContainKey("passcode"); assertThat(returnedPrompts.get("username")[1]).isEqualTo("Email"); assertThat(returnedPrompts.get("password")[1]).isEqualTo("Password"); - assertThat(returnedPrompts.get("passcode")[1]).startsWith("Temporary Authentication Code ( Get one at"); + assertThat(returnedPrompts.get("passcode")).isNull(); } }