[java-identity-provider] 02/02: Profile and flow support for SAML 2 authentication

Brent Putman putmanb at georgetown.edu
Fri Jan 24 23:06:14 EST 2020


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

putmanb 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=df1dafeab3ce07e2f681c0001c70cb39245c9c07

commit df1dafeab3ce07e2f681c0001c70cb39245c9c07
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Tue Dec 17 15:17:29 2019 -0500

    Profile and flow support for SAML 2 authentication
---
 .../system/flows/authn/saml-authn-beans.xml        |  68 +++++
 .../system/flows/authn/saml-authn-flow.xml         |   3 +
 .../config/logic/CheckAddressPredicate.java        |  49 ++++
 .../logic/MaximumTimeSinceAuthnLookupFunction.java |  53 ++++
 .../impl/ProcessAssertionsForAuthentication.java   | 308 +++++++++++++++++++++
 .../profile/impl/ValidateSAMLAuthentication.java   |   7 -
 6 files changed, 481 insertions(+), 7 deletions(-)

diff --git a/idp-conf/src/main/resources/system/flows/authn/saml-authn-beans.xml b/idp-conf/src/main/resources/system/flows/authn/saml-authn-beans.xml
index 7d5556b..89615ff 100644
--- a/idp-conf/src/main/resources/system/flows/authn/saml-authn-beans.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/saml-authn-beans.xml
@@ -117,6 +117,12 @@
         </constructor-arg>
     </bean>
 
+    <bean id="PopulateInboundMessageContextWithSAMLSelf" parent="NestedWebFlowProfileActionAdaptor" scope="prototype">
+        <constructor-arg>
+            <bean class="net.shibboleth.idp.saml.profile.impl.PopulateInboundMessageContextWithSAMLSelf" scope="prototype" />
+        </constructor-arg>
+    </bean>
+
     <util:constant id="shibboleth.EndpointType"
         static-field="org.opensaml.saml.saml2.metadata.SingleSignOnService.DEFAULT_ELEMENT_NAME" />
 
@@ -286,6 +292,68 @@
         </constructor-arg>
     </bean>
 
+    <bean id="AssertionValidator" class="org.opensaml.saml.saml2.assertion.SAML20AssertionValidator">
+        <!-- Condition validators. -->
+        <constructor-arg index="0">
+            <util:list>
+                <bean class="org.opensaml.saml.saml2.assertion.impl.AudienceRestrictionConditionValidator" />
+                <bean class="org.opensaml.saml.saml2.assertion.impl.DelegationRestrictionConditionValidator" />
+                <bean class="org.opensaml.saml.saml2.assertion.impl.OneTimeUseConditionValidator">
+                    <constructor-arg ref="shibboleth.ReplayCache" />
+                    <constructor-arg value="#{null}" />
+                </bean>
+                <bean class="org.opensaml.saml.saml2.assertion.impl.ProxyRestrictionConditionValidator" />
+            </util:list>
+        </constructor-arg>
+        <!-- SubjectConfirmation validators. -->
+        <constructor-arg index="1">
+            <util:list>
+                <bean class="org.opensaml.saml.saml2.assertion.impl.BearerSubjectConfirmationValidator" />
+                <bean class="org.opensaml.saml.saml2.assertion.impl.HolderOfKeySubjectConfirmationValidator" />
+            </util:list>
+        </constructor-arg>
+        <!-- Statement validators. -->
+        <constructor-arg index="2">
+            <util:list>
+                <bean class="org.opensaml.saml.saml2.assertion.impl.AuthnStatementValidator" />
+            </util:list>
+        </constructor-arg>
+        <!-- This is null b/c in this case we use a dynamically-resolved engine in the ValidationContext -->
+        <constructor-arg index="3" value="#{null}" />
+        <constructor-arg index="4">
+            <bean class="org.opensaml.saml.security.impl.SAMLSignatureProfileValidator" />
+        </constructor-arg>
+    </bean>
+
+    <bean id="AssertionValidationContextBuilder" class="org.opensaml.saml.saml2.profile.impl.DefaultAssertionValidationContextBuilder">
+        <property name="checkAddress">
+            <bean class="net.shibboleth.idp.saml.saml2.profile.config.logic.CheckAddressPredicate" />
+        </property>
+        <property name="maximumTimeSinceAuthn">
+            <bean class="net.shibboleth.idp.saml.saml2.profile.config.logic.MaximumTimeSinceAuthnLookupFunction" />
+        </property>
+        <property name="signatureRequired">
+            <bean parent="shibboleth.Conditions.NOT">
+                <constructor-arg>
+                    <bean class="org.opensaml.saml.common.profile.logic.InboundMessageSignedPredicate" />
+                </constructor-arg>
+            </bean>
+        </property>
+    </bean>
+
+    <bean id="ValidateAssertions" parent="NestedWebFlowProfileActionAdaptor" scope="prototype">
+        <constructor-arg>
+            <!-- TODO: should invalid be fatal, or let a later action decide what to do? -->
+            <bean class="org.opensaml.saml.saml2.profile.impl.ValidateAssertions" scope="prototype"
+                  p:invalidFatal="true"
+                  p:httpServletRequest-ref="shibboleth.HttpServletRequest"
+                  p:validationContextBuilder-ref="AssertionValidationContextBuilder"
+                  p:assertionValidator-ref="AssertionValidator" />
+        </constructor-arg>
+    </bean>
+
+    <bean id="ProcessAssertionsForAuthentication" class="net.shibboleth.idp.saml.saml2.profile.impl.ProcessAssertionsForAuthentication" scope="prototype" />
+
     <bean id="DecryptNameIDs" parent="NestedWebFlowProfileActionAdaptor" scope="prototype">
         <constructor-arg>
             <bean class="org.opensaml.saml.saml2.profile.impl.DecryptNameIDs" />
diff --git a/idp-conf/src/main/resources/system/flows/authn/saml-authn-flow.xml b/idp-conf/src/main/resources/system/flows/authn/saml-authn-flow.xml
index 53d8d5e..5be18e0 100644
--- a/idp-conf/src/main/resources/system/flows/authn/saml-authn-flow.xml
+++ b/idp-conf/src/main/resources/system/flows/authn/saml-authn-flow.xml
@@ -59,11 +59,14 @@
         <evaluate expression="ContinueSAMLAuthentication" />
         <evaluate expression="PopulateSignatureValidationParameters" />
         <evaluate expression="PopulateClientTLSValidationParameters" />
+        <evaluate expression="PopulateInboundMessageContextWithSAMLSelf" />
         <evaluate expression="HandleResponse" />
         <evaluate expression="PopulateDecryptionParameters" />
         <evaluate expression="DecryptAssertions" />
+        <evaluate expression="ValidateAssertions" />
         <evaluate expression="DecryptNameIDs" />
         <evaluate expression="DecryptAttributes" />
+        <evaluate expression="ProcessAssertionsForAuthentication" />
         <evaluate expression="ValidateSAMLAuthentication" />
         <evaluate expression="PostAssertionPopulateAuditContext" />
         <evaluate expression="PostResponsePopulateAuditContext" />
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/logic/CheckAddressPredicate.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/logic/CheckAddressPredicate.java
new file mode 100644
index 0000000..057fae5
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/logic/CheckAddressPredicate.java
@@ -0,0 +1,49 @@
+/*
+ * 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.config.logic;
+
+import javax.annotation.Nullable;
+
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.logic.AbstractRelyingPartyPredicate;
+import net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+/**
+ * A predicate that evaluates a {@link ProfileRequestContext} and extracts the effective
+ * setting of {@link BrowserSSOProfileConfiguration#isCheckAddress(ProfileRequestContext)}.
+ * 
+ * <p>Defaults to true.</p>
+ * 
+ * @since 4.0.0
+ */
+public class CheckAddressPredicate extends AbstractRelyingPartyPredicate {
+    
+    /** {@inheritDoc} */
+    public boolean test(@Nullable final ProfileRequestContext input) {
+        
+        final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+        if (rpc != null && rpc.getProfileConfig() instanceof BrowserSSOProfileConfiguration) {
+            return ((BrowserSSOProfileConfiguration) rpc.getProfileConfig()).isCheckAddress(input);
+        }
+        
+        return true;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/logic/MaximumTimeSinceAuthnLookupFunction.java b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/logic/MaximumTimeSinceAuthnLookupFunction.java
new file mode 100644
index 0000000..6de2aa8
--- /dev/null
+++ b/idp-saml-api/src/main/java/net/shibboleth/idp/saml/saml2/profile/config/logic/MaximumTimeSinceAuthnLookupFunction.java
@@ -0,0 +1,53 @@
+/*
+ * 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.config.logic;
+
+import java.time.Duration;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.idp.profile.config.ProfileConfiguration;
+import net.shibboleth.idp.profile.context.RelyingPartyContext;
+import net.shibboleth.idp.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+import net.shibboleth.idp.saml.saml2.profile.config.BrowserSSOProfileConfiguration;
+
+/**
+ * A function that returns {@link BrowserSSOProfileConfiguration#getMaximumTimeSinceAuthn(ProfileRequestContext)}
+ * if such a profile is available from a {@link RelyingPartyContext} obtained via a lookup function,
+ * by default a child of the {@link ProfileRequestContext}.
+ * 
+ * <p>If a specific setting is unavailable, a null value is returned.</p>
+ */
+public class MaximumTimeSinceAuthnLookupFunction extends AbstractRelyingPartyLookupFunction<Duration> {
+
+    /** {@inheritDoc} */
+    @Nullable public Duration apply(@Nullable final ProfileRequestContext input) {
+        final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+        if (rpc != null) {
+            final ProfileConfiguration pc = rpc.getProfileConfig();
+            if (pc != null && pc instanceof BrowserSSOProfileConfiguration) {
+                return ((BrowserSSOProfileConfiguration) pc).getMaximumTimeSinceAuthn(input);
+            }
+        }
+        
+        return null;
+    }
+
+}
\ No newline at end of file
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessAssertionsForAuthentication.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessAssertionsForAuthentication.java
new file mode 100644
index 0000000..139727e
--- /dev/null
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ProcessAssertionsForAuthentication.java
@@ -0,0 +1,308 @@
+/*
+ * 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.List;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.saml.common.SAMLObject;
+import org.opensaml.saml.common.assertion.ValidationContext;
+import org.opensaml.saml.common.assertion.ValidationProcessingData;
+import org.opensaml.saml.common.assertion.ValidationResult;
+import org.opensaml.saml.saml2.assertion.SAML2AssertionValidationParameters;
+import org.opensaml.saml.saml2.core.Assertion;
+import org.opensaml.saml.saml2.core.AuthnStatement;
+import org.opensaml.saml.saml2.core.Response;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import net.shibboleth.idp.authn.AbstractAuthenticationAction;
+import net.shibboleth.idp.authn.AuthnEventIds;
+import net.shibboleth.idp.authn.context.AuthenticationContext;
+import net.shibboleth.utilities.java.support.component.ComponentSupport;
+import net.shibboleth.utilities.java.support.logic.Constraint;
+
+/**
+ * Perform processing of SAML 2 Assertions that have been validated by earlier actions
+ * for use in finalization of SAML-based authentication by later actions. 
+ */
+public class ProcessAssertionsForAuthentication extends AbstractAuthenticationAction {
+    
+    /** Logger. */
+    private final Logger log = LoggerFactory.getLogger(ProcessAssertionsForAuthentication.class);
+    
+    /** The resolver for the list of assertions to be processed. */
+    @Nonnull private Function<ProfileRequestContext, List<Assertion>> assertionResolver;
+    
+    /** Lookup strategy to locate the SAML context. */
+    @Nonnull private Function<ProfileRequestContext,SAMLAuthnContext> samlContextLookupStrategy;
+    
+    /** Selection strategy for multiple valid authn Assertions. */
+    @Nonnull private Function<List<Assertion>,Assertion> authnAssertionSelectionStrategy;
+    
+    /** Selection strategy for multiple AuthnStatements. */
+    @Nonnull private Function<Assertion,AuthnStatement> authnStatementSelectionStrategy;
+    
+    /** The list of initial candidate Assertions to process. */
+    private List<Assertion> candidates;
+    
+    /** The SAML authentication context. */
+    private SAMLAuthnContext samlAuthnContext;
+    
+    /**
+     * Constructor.
+     */
+    public ProcessAssertionsForAuthentication() {
+        super();
+        
+        assertionResolver = new DefaultAssertionResolver().compose(
+                new ChildContextLookup<>(ProfileRequestContext.class).compose(
+                        new ChildContextLookup<>(AuthenticationContext.class)));
+        
+        // PRC -> AC -> SAMLAuthnContext
+        samlContextLookupStrategy = new ChildContextLookup<>(SAMLAuthnContext.class).compose(
+                new ChildContextLookup<>(AuthenticationContext.class));
+        
+        //TODO replace with better default logic based on Scott review and SP behavior
+        authnAssertionSelectionStrategy = assertions -> {
+            return assertions.get(0);
+        };
+            
+        //TODO replace with better default logic based on Scott review and SP behavior 
+        authnStatementSelectionStrategy = assertion -> {
+            return assertion.getAuthnStatements().get(0);
+        };
+    }
+
+    /**
+     * Set the strategy function for selecting which of multiple valid Assertions to use.
+     * 
+     * @param strategy the new strategy function
+     */
+    public void setAuthnAssertionSelectionStrategy(@Nonnull final Function<List<Assertion>, Assertion> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        authnAssertionSelectionStrategy = Constraint.isNotNull(strategy, 
+                "The Assertion selection strategy may not be null");
+    }
+    
+    /**
+     * Set the strategy function for selecting which of multiple AuthnStatements to use.
+     * 
+     * @param strategy the new strategy function
+     */
+    public void setAuthnStatementSelectionStrategy(@Nonnull final Function<Assertion, AuthnStatement> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        authnStatementSelectionStrategy = Constraint.isNotNull(strategy, 
+                "The AuthnStatement selection strategy may not be null");
+    }
+    
+    /**
+     * Set the strategy function which resolves the list of assertions to process.
+     * 
+     * @param strategy the new strategy function
+     */
+    public void setAssertionResolver(@Nonnull final Function<ProfileRequestContext, List<Assertion>> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        assertionResolver = Constraint.isNotNull(strategy, "The Assertion resolver strategy may not be null");
+    }
+    
+    /**
+     * Set the lookup strategy used to locate the {@link SAMLAuthnContext}.
+     * 
+     * @param strategy the new strategy function
+     */
+    public void setSAMLAuthnContextLookupStrategy(
+            @Nonnull final Function<ProfileRequestContext,SAMLAuthnContext> strategy) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        samlContextLookupStrategy = Constraint.isNotNull(strategy, "SAMLAuthnContext lookup strategy may not be null");
+    }
+
+    /** {@inheritDoc} */
+    protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        if (!super.doPreExecute(profileRequestContext, authenticationContext)) {
+            return false;
+        }
+
+        candidates = assertionResolver.apply(profileRequestContext);
+        if (candidates == null || candidates.isEmpty()) {
+            log.info("{} Profile context contained no candidate Assertions to process. Skipping further processing",
+                    getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_CREDENTIALS);
+            return false;
+        }
+        
+        samlAuthnContext = samlContextLookupStrategy.apply(profileRequestContext);
+        if (samlAuthnContext == null) {
+            log.debug("{} No SAMLAuthnContext available within authentication context", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_CREDENTIALS);
+            return false;
+        }
+
+        return true;
+    }
+
+    /** {@inheritDoc} */
+    protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext,
+            @Nonnull final AuthenticationContext authenticationContext) {
+        
+        // Select only valid Assertions which contain at least 1 AuthnStatement and a confirmed Subject
+        final Predicate<Assertion> selector = new AssertionIsValid()
+                .and(new AssertionContainsAuthenticationStatement())
+                .and(new AssertionContainsConfirmedSubject());
+        
+        final List<Assertion> assertions = candidates.stream().filter(selector).collect(Collectors.toList());
+        if (assertions.isEmpty()) {
+            log.debug("{} No valid SAML Assertions meeting the criteria for authentication were found", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_CREDENTIALS);
+            return;
+        }
+        
+        Assertion authnAssertion = null;
+        if (assertions.size() == 1) {
+            authnAssertion = assertions.get(0);
+            log.debug("{} Saw single valid SAML Assertion, selecting for authentication", getLogPrefix());
+        } else {
+            log.debug("{} Attempting to select from multiple valid SAML Assertions for authentication", getLogPrefix());
+            authnAssertion = authnAssertionSelectionStrategy.apply(assertions);
+        }
+        if (authnAssertion == null) {
+            log.debug("{} Could not select a single valid SAML Assertion for authentication", getLogPrefix());
+            ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_CREDENTIALS);
+            return;
+        }
+        
+        log.debug("{} Selected valid SAML Assertion for authentication: {}", getLogPrefix(), authnAssertion.getID());
+        
+        AuthnStatement authnStatement = null;
+        if (authnAssertion.getAuthnStatements().size() == 1) {
+            authnStatement = authnAssertion.getAuthnStatements().get(0);
+            log.debug("{} Saw single AuthnStatement, selecting for authentication", getLogPrefix());
+        } else {
+            log.debug("{} Attempting to select from multiple AuthnStatements for authentication", getLogPrefix());
+            authnStatement = authnStatementSelectionStrategy.apply(authnAssertion);
+            if (authnStatement == null) {
+                log.debug("{} Could not select a single AuthnStatement for authentication", getLogPrefix());
+                ActionSupport.buildEvent(profileRequestContext, AuthnEventIds.INVALID_CREDENTIALS);
+                return;
+            }
+        }
+        
+        samlAuthnContext.setAuthnStatement(authnStatement);
+        samlAuthnContext.setSubject(authnAssertion.getSubject());
+    }
+
+    /**
+     * The default assertion resolver function. NOTE: this is relative to the nested profile request context.
+     * Need to compose with other lookup function against the main/outer profile request context.
+     */
+    private class DefaultAssertionResolver implements Function<ProfileRequestContext, List<Assertion>> {
+
+        /** {@inheritDoc} */
+        public List<Assertion> apply(@Nonnull final ProfileRequestContext profileContext) {
+            final SAMLObject message = (SAMLObject) profileContext.getInboundMessageContext().getMessage();
+            if (message instanceof Response) {
+                return ((Response) message).getAssertions();
+            }
+            
+            return null;
+        }
+        
+    }
+    
+    /**
+     * Predicate for valid assertions.
+     */
+    private class AssertionIsValid implements Predicate<Assertion> {
+
+        /** {@inheritDoc} */
+        public boolean test(@Nullable final Assertion assertion) {
+            if (assertion == null) {
+                return false;
+            }
+            
+            final Optional<ValidationProcessingData> validationData = assertion.getObjectMetadata()
+                    .get(ValidationProcessingData.class).stream().findFirst();
+            if (validationData.isEmpty()) {
+                return false;
+            }
+            
+            return validationData.get().getResult() == ValidationResult.VALID;
+        }
+        
+    }
+    
+    /**
+     * Predicate for assertions containing at least 1 AuthenticationStatement.
+     */
+    private class AssertionContainsAuthenticationStatement implements Predicate<Assertion> {
+
+        /** {@inheritDoc} */
+        public boolean test(@Nullable final Assertion assertion) {
+            if (assertion == null) {
+                return false;
+            }
+            
+            return ! assertion.getAuthnStatements().isEmpty();
+        }
+        
+    }
+
+    /**
+     * Predicate for assertions which have been validated and have a confirmed Subject.
+     */
+    private class AssertionContainsConfirmedSubject implements Predicate<Assertion> {
+
+        /** {@inheritDoc} */
+        public boolean test(@Nullable final Assertion assertion) {
+            if (assertion == null) {
+                return false;
+            }
+            
+            final Optional<ValidationProcessingData> validationData = assertion.getObjectMetadata()
+                    .get(ValidationProcessingData.class).stream().findFirst();
+            if (validationData.isEmpty()) {
+                return false;
+            }
+            
+            final ValidationContext validationContext = validationData.get().getContext();
+            if (validationContext == null) {
+                return false;
+            }
+            
+            return validationContext.getDynamicParameters()
+                    .get(SAML2AssertionValidationParameters.CONFIRMED_SUBJECT_CONFIRMATION) != null;
+        }
+        
+    }
+
+}
diff --git a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java
index 1e0f13c..b3ab4dd 100644
--- a/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java
+++ b/idp-saml-impl/src/main/java/net/shibboleth/idp/saml/saml2/profile/impl/ValidateSAMLAuthentication.java
@@ -240,13 +240,6 @@ public class ValidateSAMLAuthentication extends AbstractValidationAction {
         
         profileConfiguration = (BrowserSSOProfileConfiguration) rpContext.getProfileConfig();
         
-        // TODO: Dummy code until we have something processing the results properly.
-        final Response response = (Response) profileRequestContext.getInboundMessageContext().getMessage();
-        Constraint.isTrue(response.getAssertions().size() == 1, "Wrong assertion count");
-        Constraint.isTrue(response.getAssertions().get(0).getAuthnStatements().size() == 1, "Wrong statement count");
-        authenticationContext.getSubcontext(SAMLAuthnContext.class)
-            .setSubject(response.getAssertions().get(0).getSubject())
-            .setAuthnStatement(response.getAssertions().get(0).getAuthnStatements().get(0));        
         return true;
     }
     

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list