[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