From ae98d8ea286c4f507beeb1a91f3c298966f11fd6 Mon Sep 17 00:00:00 2001 From: Luca Leonardo Scorcia Date: Sun, 30 May 2021 06:43:58 -0400 Subject: [PATCH] KEYCLOAK-18315 SAML Client - Add parameter to request specific AttributeConsumingServiceIndex --- .../saml/SAML2AuthnRequestBuilder.java | 9 +- .../broker/saml/SAMLIdentityProvider.java | 3 + .../saml/SAMLIdentityProviderConfig.java | 25 +++++ ...amlAttributeConsumingServiceIndexTest.java | 98 +++++++++++++++++++ .../messages/admin-messages_en.properties | 2 + .../realm-identity-provider-saml.html | 7 ++ 6 files changed, 142 insertions(+), 2 deletions(-) create mode 100644 testsuite/integration-arquillian/tests/base/src/test/java/org/keycloak/testsuite/broker/KcSamlAttributeConsumingServiceIndexTest.java diff --git a/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java b/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java index cf7da5cc6e..a4f7f53cea 100755 --- a/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java +++ b/saml-core/src/main/java/org/keycloak/saml/SAML2AuthnRequestBuilder.java @@ -75,6 +75,11 @@ public class SAML2AuthnRequestBuilder implements SamlProtocolExtensionsAwareBuil return this; } + public SAML2AuthnRequestBuilder attributeConsumingServiceIndex(Integer attributeConsumingServiceIndex) { + this.authnRequestType.setAttributeConsumingServiceIndex(attributeConsumingServiceIndex); + return this; + } + public SAML2AuthnRequestBuilder forceAuthn(boolean forceAuthn) { this.authnRequestType.setForceAuthn(forceAuthn); return this; @@ -85,8 +90,8 @@ public class SAML2AuthnRequestBuilder implements SamlProtocolExtensionsAwareBuil return this; } - public SAML2AuthnRequestBuilder nameIdPolicy(SAML2NameIDPolicyBuilder nameIDPolicy) { - this.authnRequestType.setNameIDPolicy(nameIDPolicy.build()); + public SAML2AuthnRequestBuilder nameIdPolicy(SAML2NameIDPolicyBuilder nameIDPolicyBuilder) { + this.authnRequestType.setNameIDPolicy(nameIDPolicyBuilder.build()); return this; } diff --git a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java index 7b324ec0ce..5ce7c2eed3 100755 --- a/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java +++ b/services/src/main/java/org/keycloak/broker/saml/SAMLIdentityProvider.java @@ -130,6 +130,8 @@ public class SAMLIdentityProvider extends AbstractIdentityProvider No attribute added to AuthnRequest + try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource) + .update()) + { + // Build the login request document + AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST + ".dot/ted", getConsumerRoot() + "/sales-post/saml", null); + Document doc = SAML2Request.convert(loginRep); + new SamlClientBuilder() + .authnRequest(getConsumerSamlEndpoint(bc.consumerRealmName()), doc, Binding.POST) + .build() // Request to consumer IdP + .login().idp(bc.getIDPAlias()).build() + .processSamlResponse(Binding.POST) // AuthnRequest to producer IdP + .targetAttributeSamlRequest() + .transformDocument((document) -> { + try + { + log.infof("Document: %s", DocumentUtil.asString(document)); + + // Find the AuthnRequest AttributeConsumingServiceIndex attribute + Node attrNode = document.getDocumentElement().getAttributes().getNamedItem("AttributeConsumingServiceIndex"); + Assert.assertEquals("Unexpected AttributeConsumingServiceIndex attribute value", null, attrNode); + } + catch (Exception ex) + { + throw new RuntimeException(ex); + } + }) + .build() + .execute(); + } + } + + @Test + public void testAttributeConsumingServiceIndexSet() throws Exception { + // Attribute Consuming Service Index set -> Attribute added to AuthnRequest + try (Closeable idpUpdater = new IdentityProviderAttributeUpdater(identityProviderResource) + .setAttribute(SAMLIdentityProviderConfig.ATTRIBUTE_CONSUMING_SERVICE_INDEX, "15") + .update()) + { + // Build the login request document + AuthnRequestType loginRep = SamlClient.createLoginRequestDocument(AbstractSamlTest.SAML_CLIENT_ID_SALES_POST + ".dot/ted", getConsumerRoot() + "/sales-post/saml", null); + Document doc = SAML2Request.convert(loginRep); + new SamlClientBuilder() + .authnRequest(getConsumerSamlEndpoint(bc.consumerRealmName()), doc, Binding.POST) + .build() // Request to consumer IdP + .login().idp(bc.getIDPAlias()).build() + .processSamlResponse(Binding.POST) // AuthnRequest to producer IdP + .targetAttributeSamlRequest() + .transformDocument((document) -> { + try + { + log.infof("Document: %s", DocumentUtil.asString(document)); + + // Find the AuthnRequest AttributeConsumingServiceIndex attribute + String attrValue = document.getDocumentElement().getAttributes().getNamedItem("AttributeConsumingServiceIndex").getNodeValue(); + Assert.assertEquals("Unexpected AttributeConsumingServiceIndex attribute value", "15", attrValue); + } + catch (Exception ex) + { + throw new RuntimeException(ex); + } + }) + .build() + .execute(); + } + } +} \ No newline at end of file diff --git a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties index bb391adf2e..ccc94bf721 100644 --- a/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties +++ b/themes/src/main/resources/theme/base/admin/messages/admin-messages_en.properties @@ -718,6 +718,8 @@ identity-provider.saml.entity-id=Service Provider Entity ID identity-provider.saml.entity-id.tooltip=The Entity ID that will be used to uniquely identify this SAML Service Provider identity-provider.saml.protocol-endpoints.saml=SAML 2.0 Service Provider Metadata identity-provider.saml.protocol-endpoints.saml.tooltip=Shows the configuration of the Service Provider endpoint +identity-provider.saml.attribute-consuming-service-index=Attribute Consuming Service Index +identity-provider.saml.attribute-consuming-service-index.tooltip=Index of the Attribute Consuming Service profile to request during authentication saml-config=SAML Config identity-provider.saml-config.tooltip=SAML SP and external IDP configuration. single-signon-service-url=Single Sign-On Service URL diff --git a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html index fb1a38d9df..933f5bebfe 100755 --- a/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html +++ b/themes/src/main/resources/theme/base/admin/resources/partials/realm-identity-provider-saml.html @@ -302,6 +302,13 @@ {{:: 'identity-provider.allowed-clock-skew.tooltip' | translate}} +
+ +
+ +
+ {{:: 'identity-provider.saml.attribute-consuming-service-index.tooltip' | translate}} +
{{:: 'identity-provider.saml.requested-authncontext' | translate}} {{:: 'identity-provider.saml.requested-authncontext.tooltip' | translate}}