[java-plugin-shibd-saml] branch main updated: More tests and consumer flow fixes.

Scott Cantor cantor.2 at osu.edu
Mon Oct 7 19:45:14 UTC 2024


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

scantor pushed a commit to branch main
in repository java-plugin-shibd-saml.

View the commit online:
http://git.shibboleth.net/view/?p=java-plugin-shibd-saml.git;a=commit;h=f5353745a8d4f6085006d0d254ac753f57e4d19d

The following commit(s) were added to refs/heads/main by this push:
     new f535374  More tests and consumer flow fixes.
f535374 is described below

commit f5353745a8d4f6085006d0d254ac753f57e4d19d
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Oct 7 15:45:11 2024 -0400

    More tests and consumer flow fixes.
---
 .../idp/flows/sp/consumer/saml2/saml2-beans.xml    |  13 +-
 .../idp/flows/sp/consumer/saml2/saml2-flow.xml     |   1 +
 .../shibboleth/idp/flows/sp/saml2-common-beans.xml |   3 +
 .../flows/saml2/SAML2TokenConsumerFlowTest.java    | 168 +++++++++++++++++++--
 4 files changed, 163 insertions(+), 22 deletions(-)

diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-beans.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-beans.xml
index 4c8290b..3afa58b 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-beans.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-beans.xml
@@ -15,15 +15,8 @@
             <bean class="org.opensaml.saml.common.messaging.context.navigate.SAMLEntityIDFunction" />
         </constructor-arg>
         <constructor-arg name="f">
-            <bean parent="shibboleth.Functions.Compose">
-                <constructor-arg name="g">
-                    <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
-                          c:type="#{ T(org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext) }" />
-                </constructor-arg>
-                <constructor-arg name="f">
-                    <bean class="org.opensaml.messaging.context.navigate.MessageContextLookup" c:direction="INBOUND" />
-                </constructor-arg>
-            </bean>
+            <bean class="org.opensaml.messaging.context.navigate.ChildContextLookup"
+                  c:type="#{ T(org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext) }" />
         </constructor-arg>
     </bean>
 
@@ -114,7 +107,7 @@
         <property name="validIssuers">
             <!-- This is wrapping the valid issuer into a set but via a Function. -->
             <bean parent="shibboleth.ContextFunctions.Expression"
-                c:expression="T(net.shibboleth.shared.collection.CollectionSupport).singleton(#custom.apply(#input))"
+                c:expression="T(net.shibboleth.shared.collection.CollectionSupport).singleton(#custom.apply(#input.ensureInboundMessageContext()))"
                 p:customObject-ref="InboundEntityIDLookup" />
         </property>
         <property name="requireEntityIssuer">
diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-flow.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-flow.xml
index ff88097..06ea41b 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-flow.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/consumer/saml2/saml2-flow.xml
@@ -19,6 +19,7 @@
 
         <evaluate expression="PopulateSignatureValidationParameters" />
         <evaluate expression="PopulateClientTLSValidationParameters" />
+        <evaluate expression="PopulateInboundMessageContextWithSAMLSelf" />
         
         <evaluate expression="HandleResponse" />
         <evaluate expression="PopulateDecryptionParameters" />
diff --git a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/saml2-common-beans.xml b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/saml2-common-beans.xml
index e9cd707..cfc5c60 100644
--- a/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/saml2-common-beans.xml
+++ b/sp-saml-conf-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/sp/saml2-common-beans.xml
@@ -65,4 +65,7 @@
         p:configurationLookupStrategy-ref="shibboleth.ClientTLSValidationConfigurationLookup"
         p:clientTLSValidationParametersResolver-ref="shibboleth.ClientTLSValidationParametersResolver" />
 
+    <bean id="PopulateInboundMessageContextWithSAMLSelf"
+        class="net.shibboleth.idp.saml.profile.impl.PopulateInboundMessageContextWithSAMLSelf" scope="prototype" />
+
 </beans>
diff --git a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2TokenConsumerFlowTest.java b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2TokenConsumerFlowTest.java
index aa6d228..4435048 100644
--- a/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2TokenConsumerFlowTest.java
+++ b/sp-saml-conf-impl/src/test/java/net/shibboleth/sp/saml/flows/saml2/SAML2TokenConsumerFlowTest.java
@@ -28,6 +28,7 @@ import org.opensaml.profile.action.EventIds;
 import org.opensaml.profile.context.ProfileRequestContext;
 import org.opensaml.saml.saml2.core.Assertion;
 import org.opensaml.saml.saml2.core.AuthnContext;
+import org.opensaml.saml.saml2.core.Conditions;
 import org.opensaml.saml.saml2.core.Issuer;
 import org.opensaml.saml.saml2.core.NameID;
 import org.opensaml.saml.saml2.core.NameIDType;
@@ -36,8 +37,14 @@ import org.opensaml.saml.saml2.core.StatusCode;
 import org.opensaml.saml.saml2.core.Subject;
 import org.opensaml.saml.saml2.core.SubjectConfirmation;
 import org.opensaml.saml.saml2.testing.SAML2ActionTestingSupport;
+import org.opensaml.security.SecurityException;
 import org.opensaml.security.credential.Credential;
 import org.opensaml.storage.StorageService;
+import org.opensaml.xmlsec.SignatureSigningParameters;
+import org.opensaml.xmlsec.signature.SignableXMLObject;
+import org.opensaml.xmlsec.signature.support.SignatureConstants;
+import org.opensaml.xmlsec.signature.support.SignatureException;
+import org.opensaml.xmlsec.signature.support.SignatureSupport;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.test.context.ContextConfiguration;
@@ -84,6 +91,9 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
     /** Issuer. */
     @Nonnull public static final String ISSUER = "https://idp.example.org";
 
+    /** Audience. */
+    @Nonnull public static final String AUDIENCE = "https://testsp.example.org";
+
     /** ACS URL. */
     @Nonnull public static final String RESPONSE_URL = "https://sp.example.org/Shibboleth.sso/SAML2/POST";
 
@@ -147,7 +157,8 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
      */
     @Test
     public void testErrorStatus() throws IOException {
-        final Response response = buildSAMLResponse("https://idp.example.org", StatusCode.RESPONDER);
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.RESPONDER);
+        sign(response);
         final DDF input = buildRemotedPOSTResponse(response);
         setApplicationRequest(APPLICATION_ID, input);
 
@@ -164,7 +175,7 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
      */
     @Test
     public void testUnverified() throws IOException {
-        final DDF input = buildRemotedPOSTResponse(buildSAMLResponse("https://unknown.example.org", StatusCode.SUCCESS));
+        final DDF input = buildRemotedPOSTResponse(buildSAMLResponse(ISSUER + "/bad", StatusCode.SUCCESS));
         setApplicationRequest(APPLICATION_ID, input);
 
         final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
@@ -180,8 +191,9 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
      */
     @Test
     public void testExpired() throws IOException {
-        final Response response = buildSAMLResponse("https://idp.example.org", StatusCode.SUCCESS);
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
         response.setIssueInstant(Instant.EPOCH);
+        sign(response);
         final DDF input = buildRemotedPOSTResponse(response);
         setApplicationRequest(APPLICATION_ID, input);
 
@@ -198,8 +210,9 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
      */
     @Test
     public void testBadDestination() throws IOException {
-        final Response response = buildSAMLResponse("https://idp.example.org", StatusCode.SUCCESS);
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
         response.setDestination(RESPONSE_URL + "/bad");
+        sign(response);
         final DDF input = buildRemotedPOSTResponse(response);
         setApplicationRequest(APPLICATION_ID, input);
 
@@ -216,7 +229,9 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
      */
     @Test
     public void testAssertionExpired() throws IOException {
-        final Response response = buildSAMLResponse("https://idp.example.org", StatusCode.SUCCESS);
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
+        response.getAssertions().get(0).setIssueInstant(Instant.now().minusSeconds(1800));
+        sign(response);
         final DDF input = buildRemotedPOSTResponse(response);
         setApplicationRequest(APPLICATION_ID, input);
 
@@ -234,9 +249,9 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
      */
     @Test
     public void testAssertionBadIssuer() throws IOException {
-        final Response response = buildSAMLResponse("https://idp.example.org", StatusCode.SUCCESS);
-        response.getAssertions().get(0).setIssueInstant(Instant.now().minusSeconds(60));
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
         response.getAssertions().get(0).setIssuer(SAML2ActionTestingSupport.buildIssuer(ISSUER + "bad"));
+        sign(response);
         final DDF input = buildRemotedPOSTResponse(response);
         setApplicationRequest(APPLICATION_ID, input);
 
@@ -245,7 +260,8 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
         assertFlowExecutionOutcome(result.getOutcome());
         final DDF output = assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
         validateAssertionError(output,
-                String.format("Issuer of Assertion '%s' did not match any valid issuers", response.getAssertions().get(0).getID()));
+                String.format("Issuer of Assertion '%s' did not match any valid issuers",
+                        response.getAssertions().get(0).getID()));
     }
 
     /**
@@ -255,11 +271,11 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
      */
     @Test
     public void testAssertionBadIssuerFormat() throws IOException {
-        final Response response = buildSAMLResponse("https://idp.example.org", StatusCode.SUCCESS);
-        response.getAssertions().get(0).setIssueInstant(Instant.now().minusSeconds(60));
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
         final Issuer issuer = SAML2ActionTestingSupport.buildIssuer(ISSUER);
         issuer.setFormat(NameIDType.EMAIL);
         response.getAssertions().get(0).setIssuer(issuer);
+        sign(response);
         final DDF input = buildRemotedPOSTResponse(response);
         setApplicationRequest(APPLICATION_ID, input);
 
@@ -270,6 +286,108 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
         validateAssertionError(output, "Issuer had invalid Format: urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress");
     }
 
+    /**
+     * Test flow with no signature.
+     * 
+     * @throws IOException 
+     */
+    @Test
+    public void testUnsigned() throws IOException {
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
+        final DDF input = buildRemotedPOSTResponse(response);
+        setApplicationRequest(APPLICATION_ID, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
+        validateAssertionError(output, "Assertion was required to be signed, but was not");
+    }
+
+    /**
+     * Test flow with no conditions.
+     * 
+     * @throws IOException 
+     */
+    @Test
+    public void testNoConditions() throws IOException {
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
+        response.getAssertions().get(0).setConditions(null);
+        sign(response);
+        final DDF input = buildRemotedPOSTResponse(response);
+        setApplicationRequest(APPLICATION_ID, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
+        validateAssertionError(output, "At least 1 Condition was indicated as required");
+    }
+
+    /**
+     * Test expired condition.
+     * 
+     * @throws IOException 
+     */
+    @Test
+    public void testExpiredCondition() throws IOException {
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
+        final Conditions conditions = response.getAssertions().get(0).getConditions();
+        assert conditions != null;
+        conditions.setNotOnOrAfter(Instant.now().minusSeconds(300));
+        sign(response);
+        final DDF input = buildRemotedPOSTResponse(response);
+        setApplicationRequest(APPLICATION_ID, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
+        validateAssertionError(output, String.format("Assertion '%s' with NotOnOrAfter condition",
+                response.getAssertions().get(0).getID()));
+    }
+
+    /**
+     * Test bad audience.
+     * 
+     * @throws IOException 
+     */
+    @Test
+    public void testBadAudience() throws IOException {
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
+        final Conditions conditions = response.getAssertions().get(0).getConditions();
+        assert conditions != null;
+        conditions.getAudienceRestrictions().get(0).getAudiences().get(0).setURI(AUDIENCE + "/bad");
+        sign(response);
+        final DDF input = buildRemotedPOSTResponse(response);
+        setApplicationRequest(APPLICATION_ID, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageEvent(result, EventIds.INVALID_MESSAGE);
+        validateAssertionError(output, String.format("None of the audiences within Assertion '%s' matched the list of valid audiances",
+                response.getAssertions().get(0).getID()));
+    }
+
+    /**
+     * Test successful flow. 
+     * 
+     * @throws IOException 
+     */
+    @Test
+    public void testSuccess() throws IOException {
+        final Response response = buildSAMLResponse(ISSUER, StatusCode.SUCCESS);
+        sign(response);
+        final DDF input = buildRemotedPOSTResponse(response);
+        setApplicationRequest(APPLICATION_ID, input);
+
+        final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
+        assertFlowExecutionResult(result, FLOW_ID);
+        assertFlowExecutionOutcome(result.getOutcome());
+        final DDF output = assertOutputMessageEvent(result, null);
+    }
+
     /**
      * Decode an encoded response and run sanity checks against it.
      * 
@@ -335,7 +453,9 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
         if (StatusCode.SUCCESS.equals(code)) {
             final Assertion assertion = SAML2ActionTestingSupport.buildAssertion();
             assertion.setID(idGenerator.generateIdentifier());
-
+            assertion.setIssueInstant(Instant.now());
+            assertion.setIssuer(SAML2ActionTestingSupport.buildIssuer(ISSUER));
+            
             final Subject subject = SAML2ActionTestingSupport.buildSubject("jdoe at example.org");
             final NameID nameID = subject.getNameID();
             assert nameID != null;
@@ -351,9 +471,12 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
                     SAML2ActionTestingSupport.buildAuthnStatement(
                             Instant.now().minusSeconds(300), "192.168.1.1", AuthnContext.PPT_AUTHN_CTX));
             
+            assertion.setConditions(
+                    SAML2ActionTestingSupport.buildConditions(Instant.now(), Instant.now().plusSeconds(300), AUDIENCE));
+            
             response.getAssertions().add(assertion);
         }
-
+        
         return response;
     }
     
@@ -391,4 +514,25 @@ public class SAML2TokenConsumerFlowTest extends AbstractSPFlowTest {
         }
     }
 
+    /**
+     * Sign object.
+     * 
+     * @param signable object to sign
+     * 
+     * @throws IOException 
+     */
+    public void sign(@Nonnull final SignableXMLObject signable) throws IOException {
+
+        final SignatureSigningParameters signingParameters = new SignatureSigningParameters();
+        signingParameters.setSigningCredential(idpCredential);
+        signingParameters.setSignatureAlgorithm(SignatureConstants.ALGO_ID_SIGNATURE_RSA_SHA256);
+        signingParameters.setSignatureCanonicalizationAlgorithm(SignatureConstants.ALGO_ID_C14N_EXCL_OMIT_COMMENTS);
+
+        try {
+            SignatureSupport.signObject(signable, signingParameters);
+        } catch (final SecurityException | MarshallingException | SignatureException e) {
+            throw new IOException(e);
+        }
+    }
+
 }
\ 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