[java-identity-provider] branch main updated: IDP-2076 - Implement new SAML profile settings

Scott Cantor cantor.2 at osu.edu
Thu Feb 23 20:31:17 UTC 2023


This is an automated email from the git hooks/post-receive script.

scantor pushed a commit to branch main
in repository java-identity-provider.

View the commit online:
http://git.shibboleth.net/view/?p=java-identity-provider.git;a=commit;h=1bce56014d0272342e4c78dc34af464149869f0c

The following commit(s) were added to refs/heads/main by this push:
     new 1bce56014 IDP-2076 - Implement new SAML profile settings
1bce56014 is described below

commit 1bce56014d0272342e4c78dc34af464149869f0c
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Thu Feb 23 15:31:14 2023 -0500

    IDP-2076 - Implement new SAML profile settings
    
    https://shibboleth.atlassian.net/browse/IDP-2076
    
    Add enforcement check for simple feature blockage.
    Implement new subclass to handle profile-driven SPNameQualifier.
---
 .../idp/flows/saml/saml2/sso-abstract-beans.xml    |   5 +-
 .../idp/flows/saml/saml2/sso-abstract-flow.xml     |   1 +
 .../saml/nameid/AbstractSAML2NameIDGenerator.java  | 110 ++++++++++++++
 .../impl/AttributeSourcedSAML2NameIDGenerator.java |   2 +-
 .../impl/PersistentSAML2NameIDGenerator.java       |   5 +-
 .../nameid/impl/TransientSAML2NameIDGenerator.java |   2 +-
 .../profile/impl/EnforceDisallowedSSOFeatures.java | 160 +++++++++++++++++++++
 .../impl/PersistentSAML2NameIDGeneratorTest.java   |  14 +-
 .../impl/EnforceDisallowedSSOFeaturesTest.java     | 144 +++++++++++++++++++
 9 files changed, 437 insertions(+), 6 deletions(-)

diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/saml/saml2/sso-abstract-beans.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/saml/saml2/sso-abstract-beans.xml
index 10bf66aa9..2f1e0b28a 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/saml/saml2/sso-abstract-beans.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/saml/saml2/sso-abstract-beans.xml
@@ -22,6 +22,9 @@
         class="org.opensaml.profile.action.impl.StaticMessageChannelSecurity" scope="prototype"
         p:confidentialityActive="false" p:integrityActive="false" />
         
+    <bean id="EnforceDisallowedSSOFeatures" scope="prototype"
+        class="net.shibboleth.idp.saml.saml2.profile.impl.EnforceDisallowedSSOFeatures" />
+
     <bean id="VerifyChannelBindings"
         class="org.opensaml.saml.common.profile.impl.VerifyChannelBindings" scope="prototype" />
     
@@ -41,7 +44,7 @@
                 c:f-ref="shibboleth.ChildLookup.RelyingParty" />
         </property>
     </bean>
-    
+
     <!-- Default list of context classes or declarations to ignore if an SP requests them. -->
     <util:list id="shibboleth.DefaultIgnoredContexts">
         <value>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</value>
diff --git a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/saml/saml2/sso-abstract-flow.xml b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/saml/saml2/sso-abstract-flow.xml
index e949cb35c..f8dd3b5fc 100644
--- a/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/saml/saml2/sso-abstract-flow.xml
+++ b/idp-conf-impl/src/main/resources/net/shibboleth/idp/flows/saml/saml2/sso-abstract-flow.xml
@@ -17,6 +17,7 @@
     </action-state>
 
     <action-state id="DoProfileWork">
+        <evaluate expression="EnforceDisallowedSSOFeatures" />
         <evaluate expression="VerifyChannelBindings" />
         <evaluate expression="PopulateECPContext" />
         <evaluate expression="ExtractProxiedRequesters" />
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/nameid/AbstractSAML2NameIDGenerator.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/nameid/AbstractSAML2NameIDGenerator.java
new file mode 100644
index 000000000..fec155a6a
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/nameid/AbstractSAML2NameIDGenerator.java
@@ -0,0 +1,110 @@
+/*
+ * 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.nameid;
+
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.MessageLookup;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+import org.opensaml.saml.saml2.core.AuthnRequest;
+
+import com.google.common.base.Strings;
+
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * IdP-specific base class for SAML 2.0 NameID generation that extends the OpenSAML base class with support for
+ * {@link BrowserSSOProfileConfiguration#getSPNameQualifier(org.opensaml.profile.context.ProfileRequestContext)}.
+ * 
+ * @since 5.0.0
+ */
+public class AbstractSAML2NameIDGenerator extends org.opensaml.saml.saml2.profile.AbstractSAML2NameIDGenerator {
+
+    /** Strategy function to lookup RelyingPartyContext. */
+    @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+
+    /** Strategy used to locate an {@link AuthnRequest} to check. */
+    @Nonnull private Function<ProfileRequestContext,AuthnRequest> requestLookupStrategy;
+
+    /**
+     * Constructor.
+     */
+    public AbstractSAML2NameIDGenerator() {
+        requestLookupStrategy = new MessageLookup<>(AuthnRequest.class).compose(new InboundMessageContextLookup());
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class); 
+    }
+
+    /**
+     * Set the strategy used to locate the {@link AuthnRequest} to check for a
+     * {@link org.opensaml.saml.saml2.core.NameIDPolicy}.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setRequestLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,AuthnRequest> strategy) {
+        checkSetterPreconditions();
+    
+        requestLookupStrategy = Constraint.isNotNull(strategy, "AuthnRequest lookup strategy cannot be null");
+    }
+
+    /**
+     * Set the lookup strategy to use to locate the {@link RelyingPartyContext}.
+     * 
+     * @param strategy lookup function to use
+     */
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+        checkSetterPreconditions();
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    @Nullable protected String getEffectiveSPNameQualifier(@Nonnull final ProfileRequestContext profileRequestContext) {
+        
+        // Override the default behavior if the SP specifies a qualifier in its request,
+        // matching the original base class behavior. SP request trumps local configuration.
+        final AuthnRequest request = requestLookupStrategy.apply(profileRequestContext);
+        if (request != null && request.getNameIDPolicy() != null) {
+            final String qual = request.getNameIDPolicy().getSPNameQualifier();
+            if (!Strings.isNullOrEmpty(qual)) {
+                return qual;
+            }
+        }
+        
+        final RelyingPartyContext rpContext = relyingPartyContextLookupStrategy.apply(profileRequestContext);
+        if (rpContext != null) {
+            if (rpContext.getProfileConfig() instanceof BrowserSSOProfileConfiguration sso) {
+                final String qual = sso.getSPNameQualifier(profileRequestContext);
+                if (qual != null) {
+                    return qual;
+                }
+            }
+        }
+        
+        return super.getEffectiveSPNameQualifier(profileRequestContext);
+    }
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/AttributeSourcedSAML2NameIDGenerator.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/AttributeSourcedSAML2NameIDGenerator.java
index 88000b27b..8226aadcc 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/AttributeSourcedSAML2NameIDGenerator.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/AttributeSourcedSAML2NameIDGenerator.java
@@ -28,7 +28,6 @@ import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.SAMLException;
 import org.opensaml.saml.saml2.core.NameID;
-import org.opensaml.saml.saml2.profile.AbstractSAML2NameIDGenerator;
 import org.opensaml.saml.saml2.profile.SAML2ObjectSupport;
 import org.slf4j.Logger;
 
@@ -40,6 +39,7 @@ import net.shibboleth.idp.attribute.StringAttributeValue;
 import net.shibboleth.idp.attribute.XMLObjectAttributeValue;
 import net.shibboleth.idp.attribute.context.AttributeContext;
 import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
+import net.shibboleth.idp.saml.nameid.AbstractSAML2NameIDGenerator;
 import net.shibboleth.profile.context.RelyingPartyContext;
 import net.shibboleth.profile.context.navigate.RelyingPartyIdLookupFunction;
 import net.shibboleth.shared.annotation.constraint.NonnullElements;
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/PersistentSAML2NameIDGenerator.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/PersistentSAML2NameIDGenerator.java
index 561ea2df0..8abf7304c 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/PersistentSAML2NameIDGenerator.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/PersistentSAML2NameIDGenerator.java
@@ -30,7 +30,6 @@ import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.SAMLException;
 import org.opensaml.saml.saml2.core.NameID;
-import org.opensaml.saml.saml2.profile.AbstractSAML2NameIDGenerator;
 import org.slf4j.Logger;
 
 import net.shibboleth.idp.attribute.IdPAttribute;
@@ -43,6 +42,7 @@ import net.shibboleth.idp.attribute.context.AttributeContext;
 import net.shibboleth.idp.attribute.impl.JDBCPairwiseIdStore;
 import net.shibboleth.idp.authn.context.SubjectContext;
 import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
+import net.shibboleth.idp.saml.nameid.AbstractSAML2NameIDGenerator;
 import net.shibboleth.profile.context.RelyingPartyContext;
 import net.shibboleth.profile.context.navigate.RelyingPartyIdLookupFunction;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
@@ -125,7 +125,8 @@ public class PersistentSAML2NameIDGenerator extends AbstractSAML2NameIDGenerator
      */
     public void setAttributeSourceIds(@Nonnull @NonnullElements final List<String> ids) {
         checkSetterPreconditions();
-        attributeSourceIds = CollectionSupport.copyToList(Constraint.isNotNull(ids, "Attribute ID collection cannot be null"));
+        attributeSourceIds = CollectionSupport.copyToList(
+                Constraint.isNotNull(ids, "Attribute ID collection cannot be null"));
     }
 
     /**
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/TransientSAML2NameIDGenerator.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/TransientSAML2NameIDGenerator.java
index 0cf6fa8d7..2186e89f3 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/TransientSAML2NameIDGenerator.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/nameid/impl/TransientSAML2NameIDGenerator.java
@@ -26,11 +26,11 @@ import org.opensaml.messaging.context.navigate.ChildContextLookup;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.common.SAMLException;
 import org.opensaml.saml.saml2.core.NameID;
-import org.opensaml.saml.saml2.profile.AbstractSAML2NameIDGenerator;
 import org.slf4j.Logger;
 
 import net.shibboleth.idp.authn.context.SubjectContext;
 import net.shibboleth.idp.profile.context.navigate.ResponderIdLookupFunction;
+import net.shibboleth.idp.saml.nameid.AbstractSAML2NameIDGenerator;
 import net.shibboleth.profile.context.navigate.RelyingPartyIdLookupFunction;
 import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
 import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/EnforceDisallowedSSOFeatures.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/EnforceDisallowedSSOFeatures.java
new file mode 100644
index 000000000..3ee972a97
--- /dev/null
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/EnforceDisallowedSSOFeatures.java
@@ -0,0 +1,160 @@
+/*
+ * 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 java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.messaging.context.navigate.MessageLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.NameIDPolicy;
+import org.opensaml.saml.saml2.core.NameIDType;
+import org.slf4j.Logger;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * An action that processes a SAML 2 {@link AuthnRequest} and blocks the use of any "simple"
+ * disallowed features.
+ * 
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_MSG_CTX}
+ * @event {@link EventIds#ACCESS_DENIED}
+ */
+public class EnforceDisallowedSSOFeatures extends AbstractProfileAction {
+
+    /** Class logger. */
+    @Nonnull private final Logger log = LoggerFactory.getLogger(EnforceDisallowedSSOFeatures.class);
+
+    /** Strategy used to look up a {@link RelyingPartyContext} for configuration options. */
+    @Nonnull private Function<ProfileRequestContext,RelyingPartyContext> relyingPartyContextLookupStrategy;
+
+    /** Lookup strategy function for obtaining {@link AuthnRequest}. */
+    @Nonnull private Function<ProfileRequestContext,AuthnRequest> authnRequestLookupStrategy;
+    
+    /** The request message to read from. */
+    @Nullable private AuthnRequest authnRequest;
+    
+    /** Constructor. */
+    public EnforceDisallowedSSOFeatures() {
+        relyingPartyContextLookupStrategy = new ChildContextLookup<>(RelyingPartyContext.class);
+        authnRequestLookupStrategy = new MessageLookup<>(AuthnRequest.class).compose(new InboundMessageContextLookup());
+    }
+
+    /**
+     * Set the strategy used to return the {@link RelyingPartyContext} for configuration options.
+     * 
+     * @param strategy lookup strategy
+     * 
+     * @since 3.3.0
+     */
+    public void setRelyingPartyContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,RelyingPartyContext> strategy) {
+        checkSetterPreconditions();
+        
+        relyingPartyContextLookupStrategy =
+                Constraint.isNotNull(strategy, "RelyingPartyContext lookup strategy cannot be null");
+    }
+
+    /**
+     * Set the strategy used to locate the {@link AuthnRequest} to read from.
+     * 
+     * @param strategy lookup strategy
+     */
+    public void setAuthnRequestLookupStrategy(@Nonnull final Function<ProfileRequestContext,AuthnRequest> strategy) {
+        checkSetterPreconditions();
+        
+        authnRequestLookupStrategy = Constraint.isNotNull(strategy, "AuthnRequest lookup strategy cannot be null");
+    }
+    
+    /** {@inheritDoc} */
+    @Override
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        checkComponentActive();
+        
+        if (!super.doPreExecute(profileRequestContext)) {
+            return false;
+        }
+        
+        authnRequest = authnRequestLookupStrategy.apply(profileRequestContext);
+        if (authnRequest == null) {
+            log.debug("{} AuthnRequest message was not returned by lookup strategy", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+            return false;
+        }
+        
+        return true;
+    }
+
+// Checkstyle: CyclomaticComplexity OFF
+    /** {@inheritDoc} */
+    @Override protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+        final RelyingPartyContext rpContext = relyingPartyContextLookupStrategy.apply(profileRequestContext);
+        if (rpContext == null || !(rpContext.getProfileConfig() instanceof BrowserSSOProfileConfiguration)) {
+            log.debug("{} No BrowserSSOProfileConfiguration available, skipping feature enforcement", getLogPrefix());
+            return;
+        }
+        
+        final BrowserSSOProfileConfiguration profileConfiguration =
+                (BrowserSSOProfileConfiguration) rpContext.getProfileConfig();
+        
+        final Boolean forceAuthn = authnRequest.isForceAuthn();
+        if (forceAuthn != null && forceAuthn &&
+                profileConfiguration.isFeatureDisallowed(profileRequestContext,
+                        BrowserSSOProfileConfiguration.FEATURE_FORCEAUTHN)) {
+            log.warn("{} Use of ForceAuthn disallowed by profile configuration", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+            return;
+        }
+        
+        final NameIDPolicy nidPolicy = authnRequest.getNameIDPolicy();
+        if (nidPolicy != null) {
+            if (nidPolicy.getFormat() != null &&
+                    profileConfiguration.isFeatureDisallowed(profileRequestContext,
+                            BrowserSSOProfileConfiguration.FEATURE_NAMEIDFORMAT)) {
+                if (!NameIDType.UNSPECIFIED.equals(nidPolicy.getFormat()) &&
+                        !NameIDType.ENCRYPTED.equals(nidPolicy.getFormat())) {
+                    log.warn("{} Incoming NameID Format disallowed by profile configuration", getLogPrefix());
+                    ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+                    return;
+                }
+            }
+
+            if (nidPolicy.getSPNameQualifier() != null &&
+                    profileConfiguration.isFeatureDisallowed(profileRequestContext,
+                            BrowserSSOProfileConfiguration.FEATURE_SPNAMEQUALIFIER)) {
+                log.warn("{} Incoming SPNameQualifier disallowed by profile configuration", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, EventIds.ACCESS_DENIED);
+                return;
+            }
+        }        
+    }
+// Checkstyle: CyclomaticComplexity ON
+    
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/nameid/impl/PersistentSAML2NameIDGeneratorTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/nameid/impl/PersistentSAML2NameIDGeneratorTest.java
index 7ed49a3a7..9711dde3c 100644
--- a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/nameid/impl/PersistentSAML2NameIDGeneratorTest.java
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/nameid/impl/PersistentSAML2NameIDGeneratorTest.java
@@ -30,8 +30,10 @@ import net.shibboleth.idp.attribute.impl.JDBCPairwiseIdStore;
 import net.shibboleth.idp.authn.context.SubjectContext;
 import net.shibboleth.idp.profile.testing.RequestContextBuilder;
 import net.shibboleth.idp.saml.impl.testing.TestSources;
+import net.shibboleth.idp.saml.saml2.profile.config.impl.BrowserSSOProfileConfiguration;
 import net.shibboleth.profile.context.RelyingPartyContext;
 import net.shibboleth.shared.testing.DatabaseTestingSupport;
+import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.component.ComponentInitializationException;
 
 import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
@@ -72,7 +74,9 @@ public class PersistentSAML2NameIDGeneratorTest extends OpenSAMLInitBaseTestCase
         testSource = DatabaseTestingSupport.GetMockDataSource(INIT_FILE, "StoredIDDataConnectorStore");
         prc = new RequestContextBuilder()
                 .setInboundMessageIssuer(TestSources.SP_ENTITY_ID)
-                .setOutboundMessageIssuer(TestSources.IDP_ENTITY_ID).buildProfileRequestContext();
+                .setOutboundMessageIssuer(TestSources.IDP_ENTITY_ID)
+                .setRelyingPartyProfileConfigurations(CollectionSupport.singletonList(new BrowserSSOProfileConfiguration()))
+                .buildProfileRequestContext();
         generator = new PersistentSAML2NameIDGenerator();
         generator.setId("test");
         generator.setOmitQualifiers(false);
@@ -242,6 +246,14 @@ public class PersistentSAML2NameIDGeneratorTest extends OpenSAMLInitBaseTestCase
         Assert.assertEquals(id.getValue(), storedvalue);
         Assert.assertEquals(id.getNameQualifier(), TestSources.IDP_ENTITY_ID);
         Assert.assertEquals(id.getSPNameQualifier(), "https://affiliation.org");
+        
+        prc.getInboundMessageContext().setMessage(null);
+        ((BrowserSSOProfileConfiguration) prc.getSubcontext(RelyingPartyContext.class).getProfileConfig()).setSPNameQualifier("https://affiliation.org");
+        id = generator.generate(prc, NameID.PERSISTENT);
+        Assert.assertNotNull(id);
+        Assert.assertEquals(id.getValue(), storedvalue);
+        Assert.assertEquals(id.getNameQualifier(), TestSources.IDP_ENTITY_ID);
+        Assert.assertEquals(id.getSPNameQualifier(), "https://affiliation.org");
     }
     
     private void testComputedAndStoredIdLogic() throws Exception {
diff --git a/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/EnforceDisallowedSSOFeaturesTest.java b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/EnforceDisallowedSSOFeaturesTest.java
new file mode 100644
index 000000000..f3a8522a4
--- /dev/null
+++ b/idp-saml-impl/src/test/java/net/shibboleth/idp/saml/saml2/profile/impl/EnforceDisallowedSSOFeaturesTest.java
@@ -0,0 +1,144 @@
+/*
+ * 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 org.opensaml.profile.action.EventIds;
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.profile.testing.ActionTestingSupport;
+import net.shibboleth.idp.profile.testing.RequestContextBuilder;
+import net.shibboleth.idp.saml.saml2.profile.config.impl.BrowserSSOProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.component.ComponentInitializationException;
+
+import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
+import org.opensaml.core.xml.config.XMLObjectProviderRegistrySupport;
+import org.opensaml.saml.common.SAMLObjectBuilder;
+import org.opensaml.saml.saml2.core.AuthnRequest;
+import org.opensaml.saml.saml2.core.NameIDPolicy;
+import org.opensaml.saml.saml2.core.NameIDType;
+import org.springframework.webflow.execution.Event;
+import org.springframework.webflow.test.MockRequestContext;
+import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.Test;
+
+/** {@link EnforceDisallowedSSOFeatures} unit test. */
+ at SuppressWarnings("javadoc")
+public class EnforceDisallowedSSOFeaturesTest extends OpenSAMLInitBaseTestCase {
+
+    private MockRequestContext src; 
+    
+    private ProfileRequestContext prc;
+    
+    private BrowserSSOProfileConfiguration profileConfig;
+    
+    private EnforceDisallowedSSOFeatures action;
+    
+    private SAMLObjectBuilder<NameIDPolicy> nidBuilder;
+    
+    @BeforeMethod public void setUp() throws ComponentInitializationException {
+        nidBuilder = (SAMLObjectBuilder<NameIDPolicy>)
+                XMLObjectProviderRegistrySupport.getBuilderFactory().<NameIDPolicy>getBuilderOrThrow(
+                        NameIDPolicy.DEFAULT_ELEMENT_NAME);
+        
+        src = (MockRequestContext) new RequestContextBuilder().buildRequestContext();
+        prc = (ProfileRequestContext) src.getConversationScope().get(ProfileRequestContext.BINDING_KEY);
+        profileConfig = new BrowserSSOProfileConfiguration();
+        prc.getOrCreateSubcontext(RelyingPartyContext.class).setProfileConfig(profileConfig);
+        
+        action = new EnforceDisallowedSSOFeatures();
+        action.initialize();
+    }
+    
+    @Test public void testNoRequest() {
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, EventIds.INVALID_MSG_CTX);
+    }
+
+    @Test public void testGeneric() {
+        prc.getInboundMessageContext().setMessage(SAML2ActionTestingSupport.buildAuthnRequest());
+        
+        final Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+    }
+
+    @Test public void testForceAuthn() {
+        prc.getInboundMessageContext().setMessage(SAML2ActionTestingSupport.buildAuthnRequest());
+        ((AuthnRequest) prc.getInboundMessageContext().getMessage()).setForceAuthn(true);
+
+        Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+
+        profileConfig.setDisallowedFeatures(BrowserSSOProfileConfiguration.FEATURE_FORCEAUTHN);
+        event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+        
+        ((AuthnRequest) prc.getInboundMessageContext().getMessage()).setForceAuthn(false);
+        event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+    }
+
+    @Test public void testFormat() {
+        prc.getInboundMessageContext().setMessage(SAML2ActionTestingSupport.buildAuthnRequest());
+        
+        final NameIDPolicy policy = nidBuilder.buildObject();
+        policy.setFormat(NameIDType.EMAIL);
+        
+        ((AuthnRequest) prc.getInboundMessageContext().getMessage()).setNameIDPolicy(policy);
+
+        Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+
+        profileConfig.setDisallowedFeatures(BrowserSSOProfileConfiguration.FEATURE_NAMEIDFORMAT);
+        event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+
+        policy.setFormat(NameIDType.UNSPECIFIED);
+        event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+
+        policy.setFormat(NameIDType.ENCRYPTED);
+        event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+
+        policy.setFormat(null);
+        event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+}
+
+    @Test public void testSPNameQualifier() {
+        prc.getInboundMessageContext().setMessage(SAML2ActionTestingSupport.buildAuthnRequest());
+        
+        final NameIDPolicy policy = nidBuilder.buildObject();
+        policy.setSPNameQualifier(ActionTestingSupport.OUTBOUND_MSG_ISSUER);
+        
+        ((AuthnRequest) prc.getInboundMessageContext().getMessage()).setNameIDPolicy(policy);
+
+        Event event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+
+        profileConfig.setDisallowedFeatures(BrowserSSOProfileConfiguration.FEATURE_SPNAMEQUALIFIER);
+        event = action.execute(src);
+        ActionTestingSupport.assertEvent(event, EventIds.ACCESS_DENIED);
+        
+        policy.setSPNameQualifier(null);
+        event = action.execute(src);
+        ActionTestingSupport.assertProceedEvent(event);
+    }
+
+}
\ 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