[java-opensaml] 10/11: Enhance SAML 2 Assertion validator to validate Issuer.
Brent Putman
putmanb at georgetown.edu
Thu Jan 30 02:17:35 EST 2020
This is an automated email from the git hooks/post-receive script.
putmanb pushed a commit to branch master
in repository java-opensaml.
View the commit online:
http://git.shibboleth.net/view/?p=java-opensaml.git;a=commit;h=259899aec241d036c881f1cbec9cb6b7a24c8384
commit 259899aec241d036c881f1cbec9cb6b7a24c8384
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Thu Jan 30 01:50:26 2020 -0500
Enhance SAML 2 Assertion validator to validate Issuer.
---
.../saml2/assertion/SAML20AssertionValidator.java | 63 +++++++++++++++++++
.../SAML2AssertionValidationParameters.java | 7 +++
.../DefaultAssertionValidationContextBuilder.java | 70 ++++++++++++++++++++++
.../assertion/BaseAssertionValidationTest.java | 2 +
.../assertion/SAML20AssertionValidatorTest.java | 32 ++++++++++
5 files changed, 174 insertions(+)
diff --git a/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/assertion/SAML20AssertionValidator.java b/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/assertion/SAML20AssertionValidator.java
index b3f9185..1b70dea 100644
--- a/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/assertion/SAML20AssertionValidator.java
+++ b/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/assertion/SAML20AssertionValidator.java
@@ -44,6 +44,7 @@ import org.opensaml.saml.common.assertion.ValidationResult;
import org.opensaml.saml.saml2.core.Assertion;
import org.opensaml.saml.saml2.core.Condition;
import org.opensaml.saml.saml2.core.Conditions;
+import org.opensaml.saml.saml2.core.Issuer;
import org.opensaml.saml.saml2.core.Statement;
import org.opensaml.saml.saml2.core.Subject;
import org.opensaml.saml.saml2.core.SubjectConfirmation;
@@ -236,6 +237,11 @@ public class SAML20AssertionValidator {
return result;
}
+ result = validateIssuer(assertion, context);
+ if (result != ValidationResult.VALID) {
+ return result;
+ }
+
result = validateSignature(assertion, context);
if (result != ValidationResult.VALID) {
return result;
@@ -295,6 +301,63 @@ public class SAML20AssertionValidator {
}
/**
+ * Validates the Assertion {@link Issuer}.
+ *
+ * @param assertion the assertion to validate
+ * @param context current validation context
+ *
+ * @return the result of the validation evaluation
+ *
+ * @throws AssertionValidationException if there is a problem validating the Issuer
+ */
+ protected ValidationResult validateIssuer(@Nonnull final Assertion assertion,
+ @Nonnull final ValidationContext context) throws AssertionValidationException {
+
+ String issuer = null;
+ if (assertion.getIssuer() != null) {
+ issuer = StringSupport.trimOrNull(assertion.getIssuer().getValue());
+ }
+ if (issuer == null) {
+ log.warn("Assertion Issuer was missing and was required");
+ context.setValidationFailureMessage("Assertion Issuer was missing and was required");
+ return ValidationResult.INVALID;
+ }
+
+ log.debug("Evaluating Assertion Issuer of : {}", issuer);
+
+ final Set<String> validIssuers;
+ try {
+ validIssuers = (Set<String>) context.getStaticParameters()
+ .get(SAML2AssertionValidationParameters.VALID_ISSUERS);
+ } catch (final ClassCastException e) {
+ log.warn("The value of the static validation parameter '{}' was not java.util.Set<String>",
+ SAML2AssertionValidationParameters.VALID_ISSUERS);
+ context.setValidationFailureMessage("Unable to determine list of valid issuers");
+ return ValidationResult.INDETERMINATE;
+ }
+ if (validIssuers == null || validIssuers.isEmpty()) {
+ log.warn("Set of valid issuers was not available from the validation context, unable to evaluate Issuer");
+ return ValidationResult.VALID;
+ /* TODO this should really be indeterminate, but would change the behavior for older code.
+ Need to update that first.
+ context.setValidationFailureMessage("Unable to determine list of valid issuers");
+ return ValidationResult.INDETERMINATE;
+ */
+ }
+
+ if (validIssuers.contains(issuer)) {
+ log.debug("Matched valid issuer: {}", issuer);
+ return ValidationResult.VALID;
+ }
+
+ log.debug("Failed to match Issuer to any supplied valid issuers: {}", validIssuers);
+
+ context.setValidationFailureMessage(String.format(
+ "Issuer of Assertion '%s' did not match any valid issuers", assertion.getID()));
+ return ValidationResult.INVALID;
+ }
+
+ /**
* Validates the signature of the assertion, if it is signed.
*
* @param token assertion whose signature will be validated
diff --git a/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/assertion/SAML2AssertionValidationParameters.java b/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/assertion/SAML2AssertionValidationParameters.java
index 73ac07b..795977b 100644
--- a/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/assertion/SAML2AssertionValidationParameters.java
+++ b/opensaml-saml-api/src/main/java/org/opensaml/saml/saml2/assertion/SAML2AssertionValidationParameters.java
@@ -47,6 +47,13 @@ public final class SAML2AssertionValidationParameters {
* Carries the {@link org.opensaml.saml.saml2.core.SubjectConfirmation} that confirmed the subject.
*/
public static final String CONFIRMED_SUBJECT_CONFIRMATION = STD_PREFIX + ".ConfirmedSubjectConfirmation";
+
+ /**
+ * Carries a {@link java.util.Set}<code><</code>{@link java.lang.String}<code>></code>
+ * whose values are the acceptable
+ * {@link org.opensaml.saml.saml2.core.Issuer} values.
+ */
+ public static final String VALID_ISSUERS = STD_PREFIX + ".ValidIssuers";
/**
* Carries a {@link java.lang.Boolean} flag which indicates whether the Assertion is required to be signed.
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/profile/impl/DefaultAssertionValidationContextBuilder.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/profile/impl/DefaultAssertionValidationContextBuilder.java
index b8afff4..183dd05 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/profile/impl/DefaultAssertionValidationContextBuilder.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/profile/impl/DefaultAssertionValidationContextBuilder.java
@@ -50,6 +50,8 @@ import org.opensaml.profile.context.ProfileRequestContext;
import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
import org.opensaml.saml.common.assertion.ValidationContext;
import org.opensaml.saml.common.binding.SAMLBindingSupport;
+import org.opensaml.saml.common.binding.security.impl.MessageContextEntityIDLookup;
+import org.opensaml.saml.common.binding.security.impl.MessageContextEntityIDLookup.Direction;
import org.opensaml.saml.common.messaging.context.SAMLMetadataContext;
import org.opensaml.saml.common.messaging.context.SAMLPeerEntityContext;
import org.opensaml.saml.common.messaging.context.SAMLProtocolContext;
@@ -100,6 +102,9 @@ public class DefaultAssertionValidationContextBuilder
/** Function for determining additional valid audience values. */
private Function<ProfileRequestContext, Set<String>> additionalAudiences;
+ /** Function for determining additional valid Issuer values. */
+ private Function<ProfileRequestContext, Set<String>> validIssuers;
+
/** Function for determining the valid InResponseTo value. */
private Function<ProfileRequestContext, String> inResponseTo;
@@ -138,6 +143,7 @@ public class DefaultAssertionValidationContextBuilder
notBeforeRequired = Predicates.alwaysFalse();
addressRequired = Predicates.alwaysFalse();
requiredConditions = Collections.emptySet();
+ validIssuers = new DefaultValidIssuersLookupFunction();
securityParametersLookupStrategy = new ChildContextLookup<>(SecurityParametersContext.class)
.compose(new InboundMessageContextLookup());
@@ -446,6 +452,32 @@ public class DefaultAssertionValidationContextBuilder
}
/**
+ * Get the function for determining the valid Issuer values
+ *
+ * <p>
+ * Defaults to an implementation which resolves the outbound SAML peer entityID.
+ * </p>
+ *
+ * @return the function
+ */
+ public Function<ProfileRequestContext,Set<String>> getValidIssuers() {
+ return validIssuers;
+ }
+
+ /**
+ * Set the function for determining the valid Issuer values
+ *
+ * <p>
+ * Defaults to an implementation which resolves the outbound SAML peer entityID.
+ * </p>
+ *
+ * @param function the function, may be null
+ */
+ public void setValidIssuers(final @Nonnull Function<ProfileRequestContext,Set<String>> function) {
+ validIssuers = Constraint.isNotNull(function, "Valied Issuers function was null");
+ }
+
+ /**
* Get the function for determining the max allowed time since authentication.
*
* <p>
@@ -519,6 +551,10 @@ public class DefaultAssertionValidationContextBuilder
final TreeMap<String, Object> staticParams = new TreeMap<>();
+ // For Issuer
+ staticParams.put(SAML2AssertionValidationParameters.VALID_ISSUERS,
+ getValidIssuers().apply(input.getProfileRequestContext()));
+
//For signature validation
staticParams.put(SAML2AssertionValidationParameters.SIGNATURE_REQUIRED,
Boolean.valueOf(getSignatureRequired().test(input.getProfileRequestContext())));
@@ -903,5 +939,39 @@ public class DefaultAssertionValidationContextBuilder
}
}
+
+ /**
+ * Default strategy for resolving the valid Issuers.
+ *
+ * <p>
+ * Resolves the entityID from the {@link SAMLPeerEntityContext} child of the outbound {@link MessageContext}.
+ * </p>
+ * */
+ public static class DefaultValidIssuersLookupFunction implements Function<ProfileRequestContext, Set<String>> {
+
+ /** The lookup delegate. */
+ private MessageContextEntityIDLookup delegate;
+
+ /** Constructor. */
+ public DefaultValidIssuersLookupFunction() {
+ delegate = new MessageContextEntityIDLookup(SAMLPeerEntityContext.class);
+ delegate.setDirection(Direction.OUTBOUND);
+ }
+
+ /** {@inheritDoc} */
+ public Set<String> apply(ProfileRequestContext prc) {
+ if (prc == null || prc.getInboundMessageContext() == null) {
+ return null;
+ }
+
+ // Note: Doesn't matter whether we apply to inbound or outbound
+ final String entityID = delegate.apply(prc.getInboundMessageContext());
+ if (entityID != null) {
+ return Collections.singleton(entityID);
+ }
+ return Collections.emptySet();
+ }
+
+ }
}
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/BaseAssertionValidationTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/BaseAssertionValidationTest.java
index 779f8f6..6fa4d87 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/BaseAssertionValidationTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/BaseAssertionValidationTest.java
@@ -128,6 +128,8 @@ public class BaseAssertionValidationTest extends XMLObjectBaseTestCase {
params.put(SAML2AssertionValidationParameters.CLOCK_SKEW, CLOCK_SKEW);
+ params.put(SAML2AssertionValidationParameters.VALID_ISSUERS, Collections.singleton(ISSUER));
+
params.put(SAML2AssertionValidationParameters.SC_VALID_IN_RESPONSE_TO, SUBJECT_CONFIRMATION_IN_RESPONSE_TO);
params.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS,
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/SAML20AssertionValidatorTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/SAML20AssertionValidatorTest.java
index 8e887e5..95311f8 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/SAML20AssertionValidatorTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/SAML20AssertionValidatorTest.java
@@ -550,6 +550,38 @@ public class SAML20AssertionValidatorTest extends BaseAssertionValidationTest {
Assert.assertEquals(validator.validate(assertion, validationContext), ValidationResult.INVALID);
}
+ @Test
+ public void testInvalidIssuer() throws AssertionValidationException {
+ getAssertion().getIssuer().setValue("invalid");
+
+ validator = getCurrentValidator();
+
+ Map<String,Object> staticParams = buildBasicStaticParameters();
+ staticParams.put(SAML2AssertionValidationParameters.SIGNATURE_REQUIRED, false);
+
+ ValidationContext validationContext = new ValidationContext(staticParams);
+
+ Assertion assertion = getAssertion();
+
+ Assert.assertEquals(validator.validate(assertion, validationContext), ValidationResult.INVALID);
+ }
+
+ @Test
+ public void testNoIssuer() throws AssertionValidationException {
+ getAssertion().setIssuer(null);
+
+ validator = getCurrentValidator();
+
+ Map<String,Object> staticParams = buildBasicStaticParameters();
+ staticParams.put(SAML2AssertionValidationParameters.SIGNATURE_REQUIRED, false);
+
+ ValidationContext validationContext = new ValidationContext(staticParams);
+
+ Assertion assertion = getAssertion();
+
+ Assert.assertEquals(validator.validate(assertion, validationContext), ValidationResult.INVALID);
+ }
+
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list