[java-identity-provider] branch master updated: IDP-1494 - Login flow for proxied SAML authentication
Scott Cantor
cantor.2 at osu.edu
Thu Nov 14 19:41:19 EST 2019
This is an automated email from the git hooks/post-receive script.
scantor pushed a commit to branch master
in repository java-identity-provider.
View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=bebfd4fcca364715d0f6f2214aa70739e17c5cc6
The following commit(s) were added to refs/heads/master by this push:
new bebfd4f IDP-1494 - Login flow for proxied SAML authentication
bebfd4f is described below
commit bebfd4fcca364715d0f6f2214aa70739e17c5cc6
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Nov 14 19:41:11 2019 -0500
IDP-1494 - Login flow for proxied SAML authentication
https://issues.shibboleth.net/jira/browse/IDP-1494
Add RequestedAuthnContext support and unit test for request action.
---
.../system/conf/relying-party-mddriven.xml | 12 +-
...tMetadataDrivenConfigurationLookupStrategy.java | 37 +++-
...yAwareAuthnContextComparisonLookupFunction.java | 57 ++++++
...DefaultAuthenticationMethodsLookupFunction.java | 66 ++++++
.../config/BrowserSSOProfileConfiguration.java | 6 +-
.../saml/saml2/profile/impl/AddAuthnRequest.java | 70 +++++++
.../saml2/profile/impl/AddAuthnRequestTest.java | 225 +++++++++++++++++++++
7 files changed, 459 insertions(+), 14 deletions(-)
diff --git a/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml b/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml
index 3a1ce24..3fe3353 100644
--- a/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml
+++ b/idp-conf/src/main/resources/system/conf/relying-party-mddriven.xml
@@ -411,11 +411,19 @@
</bean>
</property>
<property name="authnContextComparisonLookupStrategy">
- <bean parent="shibboleth.MDDrivenStringProperty" p:propertyName="authnContextComparison" />
+ <bean parent="shibboleth.MDDrivenStringProperty" p:propertyName="authnContextComparison">
+ <property name="defaultValueStrategy">
+ <bean class="net.shibboleth.idp.saml.profile.config.navigate.ProxyAwareAuthnContextComparisonLookupFunction" />
+ </property>
+ </bean>
</property>
<property name="defaultAuthenticationMethodsLookupStrategy">
<bean parent="shibboleth.MDDrivenListProperty" p:propertyName="defaultAuthenticationMethods"
- p:propertyType="#{T(net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal)}" />
+ p:propertyType="#{T(net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal)}">
+ <property name="defaultValueStrategy">
+ <bean class="net.shibboleth.idp.saml.profile.config.navigate.ProxyAwareDefaultAuthenticationMethodsLookupFunction" />
+ </property>
+ </bean>
</property>
<property name="postAuthenticationFlowsLookupStrategy">
<bean parent="shibboleth.MDDrivenListProperty" p:propertyName="postAuthenticationFlows" />
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractMetadataDrivenConfigurationLookupStrategy.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractMetadataDrivenConfigurationLookupStrategy.java
index 4ee321e..03a877b 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractMetadataDrivenConfigurationLookupStrategy.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/AbstractMetadataDrivenConfigurationLookupStrategy.java
@@ -57,6 +57,7 @@ import net.shibboleth.utilities.java.support.component.AbstractInitializableComp
import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
+import net.shibboleth.utilities.java.support.logic.FunctionSupport;
import net.shibboleth.utilities.java.support.primitive.StringSupport;
/**
@@ -107,7 +108,7 @@ public abstract class AbstractMetadataDrivenConfigurationLookupStrategy<T> exten
@NonnullAfterInit @NonnullElements private Collection<String> propertyAliases;
/** Optional default to return in the absence of a property. */
- @Nullable private T defaultValue;
+ @Nullable private Function<BaseContext,T> defaultValueStrategy;
/** Strategy for obtaining metadata to check. */
@Nullable private Function<BaseContext,EntityDescriptor> metadataLookupStrategy;
@@ -118,6 +119,7 @@ public abstract class AbstractMetadataDrivenConfigurationLookupStrategy<T> exten
/** Constructor. */
public AbstractMetadataDrivenConfigurationLookupStrategy() {
enableCaching = true;
+ defaultValueStrategy = FunctionSupport.constant(null);
}
/**
@@ -195,7 +197,20 @@ public abstract class AbstractMetadataDrivenConfigurationLookupStrategy<T> exten
public void setDefaultValue(@Nullable final T value) {
ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
- defaultValue = value;
+ defaultValueStrategy = FunctionSupport.constant(value);
+ }
+
+ /**
+ * Sets a default value function to apply in the absence of an explicit property.
+ *
+ * @param strategy default function to apply
+ *
+ * @since 4.0.0
+ */
+ public void setDefaultValueStrategy(@Nonnull final Function<BaseContext,T> strategy) {
+ ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+
+ defaultValueStrategy = Constraint.isNotNull(strategy, "Default value strategy cannot be null");
}
/**
@@ -261,8 +276,8 @@ public abstract class AbstractMetadataDrivenConfigurationLookupStrategy<T> exten
}
if (entity == null) {
- log.debug("No metadata available for relying party, default returned for '{}'", propertyName);
- return defaultValue;
+ log.debug("No metadata available for relying party, applying default strategy for '{}'", propertyName);
+ return defaultValueStrategy.apply(input);
}
if (profileIdLookupStrategy != null) {
@@ -300,11 +315,12 @@ public abstract class AbstractMetadataDrivenConfigurationLookupStrategy<T> exten
}
if (ignoreUnmappedEntityAttributes) {
- log.debug("No applicable mapped tag, default returned for '{}'", propertyName);
+ log.debug("No applicable mapped tag, applying default strategy for '{}'", propertyName);
+ final T ret = defaultValueStrategy.apply(input);
if (enableCaching) {
- cacheContext.getPropertyMap().put(propertyName, defaultValue);
+ cacheContext.getPropertyMap().put(propertyName, ret);
}
- return defaultValue;
+ return ret;
}
// Look for "primary" tag name based on profile/property.
@@ -331,11 +347,12 @@ public abstract class AbstractMetadataDrivenConfigurationLookupStrategy<T> exten
}
}
- log.debug("No applicable tag, default returned for '{}'", propertyName);
+ log.debug("No applicable tag, applying default strategy for '{}'", propertyName);
+ final T ret = defaultValueStrategy.apply(input);
if (enableCaching) {
- cacheContext.getPropertyMap().put(propertyName, defaultValue);
+ cacheContext.getPropertyMap().put(propertyName, ret);
}
- return defaultValue;
+ return ret;
}
// Checkstyle: CyclomaticComplexity|MethodLength ON
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/navigate/ProxyAwareAuthnContextComparisonLookupFunction.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/navigate/ProxyAwareAuthnContextComparisonLookupFunction.java
new file mode 100644
index 0000000..83986f9
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/navigate/ProxyAwareAuthnContextComparisonLookupFunction.java
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config.navigate;
+
+import java.util.function.Function;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
+
+/**
+ * Implements a set of default logic for determining the {@link org.opensaml.saml.saml2.core.RequestedAuthnContext}
+ * operator to use.
+ *
+ * <p>This operates in two different scenarios: ordinary use and proxy SAML authentication use, detectable
+ * by whether the input context is parent-less (the former), or the child of an {@link AuthenticationContext}.</p>
+ *
+ * <p>In normal use, the value returned is null.</p>
+ *
+ * <p>In proxy use, the value returned is null unless the parent context itself contains a child context carrying
+ * a particular value. In other words, the proxy default is "passthrough" of the value.</p>
+ *
+ * @since 4.0.0
+ */
+public class ProxyAwareAuthnContextComparisonLookupFunction implements Function<ProfileRequestContext,String> {
+
+ /** {@inheritDoc} */
+ @Nullable public String apply(@Nullable final ProfileRequestContext input) {
+ if (input != null && input.getParent() instanceof AuthenticationContext) {
+ final RequestedPrincipalContext rpc = input.getParent().getSubcontext(RequestedPrincipalContext.class);
+ if (rpc != null) {
+ return rpc.getOperator();
+ }
+ }
+
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/navigate/ProxyAwareDefaultAuthenticationMethodsLookupFunction.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/navigate/ProxyAwareDefaultAuthenticationMethodsLookupFunction.java
new file mode 100644
index 0000000..4941ded
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/profile/config/navigate/ProxyAwareDefaultAuthenticationMethodsLookupFunction.java
@@ -0,0 +1,66 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.profile.config.navigate;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
+import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
+
+/**
+ * Implements a set of default logic for determining the custom principals to derive the
+ * {@link org.opensaml.saml.saml2.core.RequestedAuthnContext} from.
+ *
+ * <p>This operates in two different scenarios: ordinary use and proxy SAML authentication use, detectable
+ * by whether the input context is parent-less (the former), or the child of an {@link AuthenticationContext}.</p>
+ *
+ * <p>In normal use, the value returned is empty.</p>
+ *
+ * <p>In proxy use, the value returned is empty unless the parent context itself contains a child context carrying
+ * particular values. In other words, the proxy default is "passthrough" of the values.</p>
+ *
+ * @since 4.0.0
+ */
+public class ProxyAwareDefaultAuthenticationMethodsLookupFunction
+ implements Function<ProfileRequestContext,Collection<AuthnContextClassRefPrincipal>> {
+
+ /** {@inheritDoc} */
+ @Nullable public Collection<AuthnContextClassRefPrincipal> apply(@Nullable final ProfileRequestContext input) {
+ if (input != null && input.getParent() instanceof AuthenticationContext) {
+ final RequestedPrincipalContext rpc = input.getParent().getSubcontext(RequestedPrincipalContext.class);
+ if (rpc != null) {
+ return rpc.getRequestedPrincipals()
+ .stream()
+ .filter(AuthnContextClassRefPrincipal.class::isInstance)
+ .map(AuthnContextClassRefPrincipal.class::cast)
+ .collect(Collectors.toUnmodifiableList());
+ }
+ }
+
+ return Collections.emptyList();
+ }
+
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
index 3f9765d..697dbe6 100644
--- a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/BrowserSSOProfileConfiguration.java
@@ -32,6 +32,8 @@ import javax.annotation.Nullable;
import net.shibboleth.idp.authn.config.AuthenticationProfileConfiguration;
import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
import net.shibboleth.idp.saml.profile.config.logic.ProxyAwareForceAuthnPredicate;
+import net.shibboleth.idp.saml.profile.config.navigate.ProxyAwareAuthnContextComparisonLookupFunction;
+import net.shibboleth.idp.saml.profile.config.navigate.ProxyAwareDefaultAuthenticationMethodsLookupFunction;
import net.shibboleth.utilities.java.support.annotation.constraint.NonNegative;
import net.shibboleth.utilities.java.support.annotation.constraint.NonnullElements;
import net.shibboleth.utilities.java.support.annotation.constraint.NotEmpty;
@@ -134,8 +136,8 @@ public class BrowserSSOProfileConfiguration extends AbstractSAML2ArtifactAwarePr
allowDelegationPredicate = Predicates.alwaysFalse();
authenticationFlowsLookupStrategy = FunctionSupport.constant(null);
postAuthenticationFlowsLookupStrategy = FunctionSupport.constant(null);
- authnContextComparisonLookupStrategy = FunctionSupport.constant(null);
- defaultAuthenticationContextsLookupStrategy = FunctionSupport.constant(null);
+ authnContextComparisonLookupStrategy = new ProxyAwareAuthnContextComparisonLookupFunction();
+ defaultAuthenticationContextsLookupStrategy = new ProxyAwareDefaultAuthenticationMethodsLookupFunction();
nameIDFormatPrecedenceLookupStrategy = FunctionSupport.constant(null);
}
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
index 32777eb..676dd3b 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequest.java
@@ -17,9 +17,11 @@
package net.shibboleth.idp.saml.saml2.profile.impl;
+import java.security.Principal;
import java.time.Instant;
import java.util.List;
import java.util.function.Function;
+import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -28,6 +30,8 @@ import net.shibboleth.idp.authn.AbstractAuthenticationAction;
import net.shibboleth.idp.authn.context.AuthenticationContext;
import net.shibboleth.idp.profile.IdPEventIds;
import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
+import net.shibboleth.idp.saml.authn.principal.AuthnContextDeclRefPrincipal;
import net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
import net.shibboleth.utilities.java.support.component.ComponentSupport;
import net.shibboleth.utilities.java.support.logic.Constraint;
@@ -43,9 +47,11 @@ import org.opensaml.profile.action.EventIds;
import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.saml.common.SAMLObjectBuilder;
import org.opensaml.saml.common.SAMLVersion;
+import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
import org.opensaml.saml.saml2.core.AuthnRequest;
import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.NameIDPolicy;
+import org.opensaml.saml.saml2.core.RequestedAuthnContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -215,6 +221,9 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
final SAMLObjectBuilder<NameIDPolicy> nipBuilder =
(SAMLObjectBuilder<NameIDPolicy>) bf.<NameIDPolicy>getBuilderOrThrow(
NameIDPolicy.DEFAULT_ELEMENT_NAME);
+ final SAMLObjectBuilder<RequestedAuthnContext> racBuilder =
+ (SAMLObjectBuilder<RequestedAuthnContext>) bf.<RequestedAuthnContext>getBuilderOrThrow(
+ RequestedAuthnContext.DEFAULT_ELEMENT_NAME);
final AuthnRequest object = requestBuilder.buildObject();
@@ -257,8 +266,69 @@ public class AddAuthnRequest extends AbstractAuthenticationAction {
}
object.setNameIDPolicy(nip);
+
+ final RequestedAuthnContext rac = getRequestedAuthnContext(profileRequestContext, racBuilder);
+ if (rac != null) {
+ final AuthnContextComparisonTypeEnumeration operator =
+ profileConfiguration.getAuthnContextComparison(profileRequestContext);
+ if (operator != null) {
+ rac.setComparison(operator);
+ }
+ object.setRequestedAuthnContext(rac);
+ }
profileRequestContext.getOutboundMessageContext().setMessage(object);
}
+ /**
+ * Build a {@link RequestedAuthnContext} if warranted.
+ *
+ * @param profileRequestContext current profile request context
+ * @param builder object builder
+ *
+ * @return the object to include in the request, or null
+ */
+ @Nullable private RequestedAuthnContext getRequestedAuthnContext(
+ @Nullable final ProfileRequestContext profileRequestContext,
+ @Nonnull final SAMLObjectBuilder<RequestedAuthnContext> builder) {
+
+ // RequestedAuthnContext also based on profile configuration.
+ final List<Principal> principals = profileConfiguration.getDefaultAuthenticationMethods(profileRequestContext);
+ if (principals.isEmpty()) {
+ return null;
+ }
+
+ // Check for class refs.
+ final List<AuthnContextClassRefPrincipal> classRefPrincipals = principals.stream()
+ .filter(AuthnContextClassRefPrincipal.class::isInstance)
+ .map(AuthnContextClassRefPrincipal.class::cast)
+ .collect(Collectors.toUnmodifiableList());
+ if (!classRefPrincipals.isEmpty()) {
+ final RequestedAuthnContext rac = builder.buildObject();
+
+ rac.getAuthnContextClassRefs().addAll(
+ classRefPrincipals.stream()
+ .map(AuthnContextClassRefPrincipal::getAuthnContextClassRef)
+ .collect(Collectors.toUnmodifiableList()));
+ return rac;
+ }
+
+ // Check for decl refs.
+ final List<AuthnContextDeclRefPrincipal> declRefPrincipals = principals.stream()
+ .filter(AuthnContextDeclRefPrincipal.class::isInstance)
+ .map(AuthnContextDeclRefPrincipal.class::cast)
+ .collect(Collectors.toUnmodifiableList());
+ if (!declRefPrincipals.isEmpty()) {
+ final RequestedAuthnContext rac = builder.buildObject();
+
+ rac.getAuthnContextDeclRefs().addAll(
+ declRefPrincipals.stream()
+ .map(AuthnContextDeclRefPrincipal::getAuthnContextDeclRef)
+ .collect(Collectors.toUnmodifiableList()));
+ return rac;
+ }
+
+ return null;
+ }
+
}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequestTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequestTest.java
new file mode 100644
index 0000000..e01926a
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/AddAuthnRequestTest.java
@@ -0,0 +1,225 @@
+/*
+ * Licensed to the University Corporation for Advanced Internet Development,
+ * Inc. (UCAID) under one or more contributor license agreements. See the
+ * NOTICE file distributed with this work for additional information regarding
+ * copyright ownership. The UCAID licenses this file to You under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file except in
+ * compliance with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.saml.saml2.profile.impl;
+
+import static org.testng.Assert.*;
+
+import java.util.Arrays;
+
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.idp.authn.context.RequestedPrincipalContext;
+import net.shibboleth.idp.profile.ActionTestingSupport;
+import net.shibboleth.idp.profile.IdPEventIds;
+
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.profile.RequestContextBuilder;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
+import net.shibboleth.idp.profile.context.navigate.WebflowRequestContextProfileRequestContextLookup;
+import net.shibboleth.idp.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.idp.saml.authn.principal.AuthenticationMethodPrincipal;
+import net.shibboleth.idp.saml.authn.principal.AuthnContextClassRefPrincipal;
+import net.shibboleth.idp.saml.saml2.profile.SAML2ActionTestingSupport;
+import net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
+import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
+
+import org.opensaml.core.OpenSAMLInitBaseTestCase;
+import org.opensaml.messaging.context.MessageContext;
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.ParentContextLookup;
+import org.opensaml.saml.saml1.core.AuthenticationStatement;
+import org.opensaml.saml.saml2.core.AuthnContext;
+import org.opensaml.saml.saml2.core.AuthnContextComparisonTypeEnumeration;
+import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.NameIDPolicy;
+import org.opensaml.saml.saml2.core.NameIDType;
+import org.opensaml.saml.saml2.core.RequestedAuthnContext;
+import org.springframework.webflow.execution.Event;
+import org.springframework.webflow.execution.RequestContext;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/** {@link AddAuthnRequest} unit test. */
+public class AddAuthnRequestTest extends OpenSAMLInitBaseTestCase {
+
+ private RequestContext rc;
+ private AuthenticationContext ac;
+ private ProfileRequestContext prc1,prc2;
+ private RelyingPartyContext rpc;
+ private AddAuthnRequest action;
+
+ @BeforeMethod public void setUp() throws ComponentInitializationException {
+ rc = new RequestContextBuilder().buildRequestContext();
+ prc1 = new WebflowRequestContextProfileRequestContextLookup().apply(rc);
+ ac = prc1.getSubcontext(AuthenticationContext.class, true);
+ prc2 = ac.getSubcontext(ProfileRequestContext.class, true);
+ prc2.setOutboundMessageContext(new MessageContext());
+
+ rpc = prc2.getSubcontext(RelyingPartyContext.class, true);
+ rpc.setRelyingPartyId(ActionTestingSupport.INBOUND_MSG_ISSUER);
+ final RelyingPartyConfiguration rp = new RelyingPartyConfiguration();
+ rp.setId("mock");
+ rp.setResponderId(ActionTestingSupport.OUTBOUND_MSG_ISSUER);
+ rp.setDetailedErrors(true);
+ rp.initialize();
+ rpc.setConfiguration(rp);
+ rpc.setProfileConfig(new BrowserSSOProfileConfiguration());
+
+ action = new AddAuthnRequest();
+ action.setProfileContextLookupStrategy(
+ new ChildContextLookup<>(ProfileRequestContext.class).compose(
+ new ChildContextLookup<>(AuthenticationContext.class).compose(
+ new WebflowRequestContextProfileRequestContextLookup())));
+ action.setAuthenticationContextLookupStrategy(new ParentContextLookup<>(AuthenticationContext.class));
+ action.setIssuerLookupStrategy(new ResponderIdLookupFunction());
+ action.initialize();
+ }
+
+ /** Test that the action errors out properly if there is no relying party context. */
+ @Test public void testNoRelyingPartyContext() {
+ prc2.removeSubcontext(RelyingPartyContext.class);
+
+ final Event event = action.execute(rc);
+ ActionTestingSupport.assertEvent(event, IdPEventIds.INVALID_PROFILE_CONFIG);
+ }
+
+ /** Test that the action errors out properly if there is no context. */
+ @Test public void testNoMessageContext() {
+ prc2.setOutboundMessageContext(null);
+
+ final Event event = action.execute(rc);
+ ActionTestingSupport.assertEvent(event, EventIds.INVALID_MSG_CTX);
+ }
+
+ /** Test that the action errors out properly if there is a message there. */
+ @Test public void testExistingMessage() {
+ prc2.getOutboundMessageContext().setMessage(SAML2ActionTestingSupport.buildAuthnRequest());
+
+ final Event event = action.execute(rc);
+ ActionTestingSupport.assertEvent(event, EventIds.INVALID_MSG_CTX);
+ }
+
+ /** Test that the action works in vanilla form. */
+ @Test public void testSimple() {
+ final Event event = action.execute(rc);
+ ActionTestingSupport.assertProceedEvent(event);
+
+ assertNotNull(prc2.getOutboundMessageContext().getMessage());
+ assertTrue(prc2.getOutboundMessageContext().getMessage() instanceof AuthnRequest);
+
+ final AuthnRequest request = (AuthnRequest) prc2.getOutboundMessageContext().getMessage();
+ assertEquals(request.getIssuer().getValue(), ActionTestingSupport.OUTBOUND_MSG_ISSUER);
+ assertFalse(request.isForceAuthn());
+ assertFalse(request.isPassive());
+
+ final NameIDPolicy nid = request.getNameIDPolicy();
+ assertNotNull(nid);
+ assertNull(nid.getFormat());
+ assertTrue(nid.getAllowCreate());
+
+ assertNull(request.getRequestedAuthnContext());
+ }
+
+ /** Test that the action works for ForceAuthn/IsPassive. */
+ @Test public void testFlags() {
+ ac.setIsPassive(true);
+ ac.setForceAuthn(true);
+
+ Event event = action.execute(rc);
+ ActionTestingSupport.assertProceedEvent(event);
+
+ assertNotNull(prc2.getOutboundMessageContext().getMessage());
+ assertTrue(prc2.getOutboundMessageContext().getMessage() instanceof AuthnRequest);
+
+ final AuthnRequest request = (AuthnRequest) prc2.getOutboundMessageContext().getMessage();
+ assertEquals(request.getIssuer().getValue(), ActionTestingSupport.OUTBOUND_MSG_ISSUER);
+ assertTrue(request.isForceAuthn());
+ assertTrue(request.isPassive());
+
+ prc2.getOutboundMessageContext().setMessage(null);
+ ((BrowserSSOProfileConfiguration) rpc.getProfileConfig()).setForceAuthn(false);
+
+ event = action.execute(rc);
+ ActionTestingSupport.assertProceedEvent(event);
+ assertFalse(((AuthnRequest) prc2.getOutboundMessageContext().getMessage()).isForceAuthn());
+ }
+
+ /** Test that the action works with a NameID format set. */
+ @Test public void testNameIDFormat() {
+ ((BrowserSSOProfileConfiguration) rpc.getProfileConfig()).setNameIDFormatPrecedence(
+ Arrays.asList(NameIDType.EMAIL, NameIDType.KERBEROS));
+
+ Event event = action.execute(rc);
+ ActionTestingSupport.assertProceedEvent(event);
+
+ assertNotNull(prc2.getOutboundMessageContext().getMessage());
+ assertTrue(prc2.getOutboundMessageContext().getMessage() instanceof AuthnRequest);
+
+ final AuthnRequest request = (AuthnRequest) prc2.getOutboundMessageContext().getMessage();
+ final NameIDPolicy nid = request.getNameIDPolicy();
+ assertNotNull(nid);
+ assertEquals(nid.getFormat(), NameIDType.EMAIL);
+ assertTrue(nid.getAllowCreate());
+ }
+
+ /** Test that the action works for RequestedAuthnContext. */
+ @Test public void testAuthnContext() {
+
+ final RequestedPrincipalContext reqctx = ac.getSubcontext(RequestedPrincipalContext.class, true);
+ reqctx.setOperator("exact");
+ reqctx.setRequestedPrincipals(
+ Arrays.asList(new AuthnContextClassRefPrincipal(AuthnContext.KERBEROS_AUTHN_CTX),
+ new AuthenticationMethodPrincipal(AuthenticationStatement.KERBEROS_AUTHN_METHOD),
+ new AuthnContextClassRefPrincipal(AuthnContext.X509_AUTHN_CTX)));
+
+ Event event = action.execute(rc);
+ ActionTestingSupport.assertProceedEvent(event);
+
+ assertNotNull(prc2.getOutboundMessageContext().getMessage());
+ assertTrue(prc2.getOutboundMessageContext().getMessage() instanceof AuthnRequest);
+
+ AuthnRequest request = (AuthnRequest) prc2.getOutboundMessageContext().getMessage();
+ RequestedAuthnContext rac = request.getRequestedAuthnContext();
+ assertNotNull(rac);
+ assertEquals(rac.getComparison(), AuthnContextComparisonTypeEnumeration.EXACT);
+ assertEquals(rac.getAuthnContextClassRefs().size(), 2);
+ assertEquals(rac.getAuthnContextClassRefs().get(0).getAuthnContextClassRef(), AuthnContext.KERBEROS_AUTHN_CTX);
+ assertEquals(rac.getAuthnContextClassRefs().get(1).getAuthnContextClassRef(), AuthnContext.X509_AUTHN_CTX);
+
+ ((BrowserSSOProfileConfiguration) rpc.getProfileConfig()).setAuthnContextComparison(
+ AuthnContextComparisonTypeEnumeration.EXACT);
+ ((BrowserSSOProfileConfiguration) rpc.getProfileConfig()).setDefaultAuthenticationMethods(
+ Arrays.asList(new AuthnContextClassRefPrincipal(AuthnContext.KERBEROS_AUTHN_CTX),
+ new AuthnContextClassRefPrincipal(AuthnContext.X509_AUTHN_CTX)));
+
+ prc2.getOutboundMessageContext().setMessage(null);
+ event = action.execute(rc);
+ ActionTestingSupport.assertProceedEvent(event);
+
+ request = (AuthnRequest) prc2.getOutboundMessageContext().getMessage();
+ rac = request.getRequestedAuthnContext();
+ assertNotNull(rac);
+ assertEquals(rac.getComparison(), AuthnContextComparisonTypeEnumeration.EXACT);
+ assertEquals(rac.getAuthnContextClassRefs().size(), 2);
+ assertEquals(rac.getAuthnContextClassRefs().get(0).getAuthnContextClassRef(), AuthnContext.KERBEROS_AUTHN_CTX);
+ assertEquals(rac.getAuthnContextClassRefs().get(1).getAuthnContextClassRef(), AuthnContext.X509_AUTHN_CTX);
+ }
+
+}
\ No newline at end of file
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list