[java-opensaml] 05/11: Support additional required checks over SubjectConfirmationData.

Brent Putman putmanb at georgetown.edu
Thu Jan 30 02:17:30 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=44ac147db7c51f025ccd22a64ab1bec37c5d0d35

commit 44ac147db7c51f025ccd22a64ab1bec37c5d0d35
Author: Brent Putman <putmanb at georgetown.edu>
AuthorDate: Wed Jan 29 21:07:22 2020 -0500

    Support additional required checks over SubjectConfirmationData.
---
 .../SAML2AssertionValidationParameters.java        |  24 +++
 .../impl/AbstractSubjectConfirmationValidator.java | 168 +++++++++++++++++--
 .../DefaultAssertionValidationContextBuilder.java  | 177 ++++++++++++++++++---
 .../AbstractSubjectConfirmationValidatorTest.java  |  96 +++++++++++
 4 files changed, 426 insertions(+), 39 deletions(-)

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 6e0b0b4..7166303 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
@@ -73,6 +73,24 @@ public final class SAML2AssertionValidationParameters {
     public static final String SIGNATURE_VALIDATION_PREVALIDATOR = STD_PREFIX + ".SignatureValidationPrevalidator";
 
     /**
+     * Carries a {@link java.lang.Boolean} flag which indicates whether
+     * {@link org.opensaml.saml.saml2.core.SubjectConfirmationData} NotBefore is required.
+     */
+    public static final String SC_NOT_BEFORE_REQUIRED = STD_PREFIX + SC_INFIX + ".NotBeforeRequired";
+    
+    /**
+     * Carries a {@link java.lang.Boolean} flag which indicates whether
+     * {@link org.opensaml.saml.saml2.core.SubjectConfirmationData} NotOnOrAfter is required.
+     */
+    public static final String SC_NOT_ON_OR_AFTER_REQUIRED = STD_PREFIX + SC_INFIX + ".NoOnOrAfterRequired";
+    
+    /**
+     * Carries a {@link java.lang.Boolean} flag which indicates whether
+     * {@link org.opensaml.saml.saml2.core.SubjectConfirmationData} Recipient is required.
+     */
+    public static final String SC_RECIPIENT_REQUIRED = STD_PREFIX + SC_INFIX + ".RecipientRequired";
+    
+    /**
      * 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.SubjectConfirmationData} recipients.
@@ -81,6 +99,12 @@ public final class SAML2AssertionValidationParameters {
 
     /**
      * Carries a {@link java.lang.Boolean} flag which indicates whether the
+     * {@link org.opensaml.saml.saml2.core.SubjectConfirmationData} Address is required.
+     */
+    public static final String SC_ADDRESS_REQUIRED = STD_PREFIX + SC_INFIX + ".AddressRequired";
+
+    /**
+     * Carries a {@link java.lang.Boolean} flag which indicates whether the
      * {@link org.opensaml.saml.saml2.core.SubjectConfirmationData} address should be checked.
      */
     public static final String SC_CHECK_ADDRESS = STD_PREFIX + SC_INFIX + ".CheckAddress";
diff --git a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/assertion/impl/AbstractSubjectConfirmationValidator.java b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/assertion/impl/AbstractSubjectConfirmationValidator.java
index a4203b6..3c4a069 100644
--- a/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/assertion/impl/AbstractSubjectConfirmationValidator.java
+++ b/opensaml-saml-impl/src/main/java/org/opensaml/saml/saml2/assertion/impl/AbstractSubjectConfirmationValidator.java
@@ -51,6 +51,10 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
  * </p>
  * <ul>
  * <li>
+ * {@link SAML2AssertionValidationParameters#SC_ADDRESS_REQUIRED}:
+ * Optional.
+ * </li>
+ * <li>
  * {@link SAML2AssertionValidationParameters#SC_CHECK_ADDRESS}:
  * Optional.
  * </li>
@@ -60,13 +64,29 @@ import net.shibboleth.utilities.java.support.primitive.StringSupport;
  * otherwise optional.
  * </li>
  * <li>
+ * {@link SAML2AssertionValidationParameters#SC_RECIPIENT_REQUIRED}:
+ * Optional.
+ * </li>
+ * <li>
  * {@link SAML2AssertionValidationParameters#SC_VALID_RECIPIENTS}:
  * Required.
  * </li>
  * <li>
+ * {@link SAML2AssertionValidationParameters#SC_IN_RESPONSE_TO_REQUIRED}:
+ * Optional.
+ * </li>
+ * <li>
  * {@link SAML2AssertionValidationParameters#SC_VALID_IN_RESPONSE_TO}:
  * Required.
  * </li>
+ * <li>
+ * {@link SAML2AssertionValidationParameters#SC_NOT_BEFORE_REQUIRED}:
+ * Optional.
+ * </li>
+ * <li>
+ * {@link SAML2AssertionValidationParameters#SC_NOT_ON_OR_AFTER_REQUIRED}:
+ * Optional.
+ * </li>
  * </ul>
  * 
  * <p>
@@ -87,29 +107,34 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
     }
 
     /** {@inheritDoc} */
+    // Checkstyle: CyclomaticComplexity OFF
     @Nonnull public ValidationResult validate(@Nonnull final SubjectConfirmation confirmation, 
             @Nonnull final Assertion assertion, @Nonnull final ValidationContext context)
             throws AssertionValidationException {
         
+        final boolean addressRequired = isAddressRequired(context);
         final boolean inResponseToRequired = isInResponseToRequired(context);
+        final boolean recipientRequired = isRecipientRequired(context);
+        final boolean notOnOrAfterRequired = isNotOnOrAfterRequired(context);
+        final boolean notBeforeRequired = isNotBeforeRequired(context);
 
         if (confirmation.getSubjectConfirmationData() != null) {
-            ValidationResult result = validateNotBefore(confirmation, assertion, context);
+            ValidationResult result = validateNotBefore(confirmation, assertion, context, notBeforeRequired);
             if (result != ValidationResult.VALID) {
                 return result;
             }
 
-            result = validateNotOnOrAfter(confirmation, assertion, context);
+            result = validateNotOnOrAfter(confirmation, assertion, context, notOnOrAfterRequired);
             if (result != ValidationResult.VALID) {
                 return result;
             }
 
-            result = validateRecipient(confirmation, assertion, context);
+            result = validateRecipient(confirmation, assertion, context, recipientRequired);
             if (result != ValidationResult.VALID) {
                 return result;
             }
 
-            result = validateAddress(confirmation, assertion, context);
+            result = validateAddress(confirmation, assertion, context, addressRequired);
             if (result != ValidationResult.VALID) {
                 return result;
             }
@@ -119,14 +144,82 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
                 return result;
             }
         } else {
-            if (inResponseToRequired) {
+            if (inResponseToRequired || recipientRequired || notOnOrAfterRequired || notBeforeRequired 
+                    || addressRequired) {
+                log.warn("SubjectConfirmationData was null, and one of more data elements were required");
+                context.setValidationFailureMessage(
+                        "SubjectConfirmationData was null and one or more data elements were required");
                 return ValidationResult.INVALID;
             }
         }
 
         return doValidate(confirmation, assertion, context);
     }
+    // Checkstyle: CyclomaticComplexity ON
+
+    /**
+     * Determine whether Address is required.
+     * 
+     * @param context current validation context
+     * 
+     * @return true if required, false if not
+     */
+    protected boolean isAddressRequired(final ValidationContext context) {
+        return ObjectSupport.firstNonNull(
+                (Boolean) context.getStaticParameters().get(
+                        SAML2AssertionValidationParameters.SC_ADDRESS_REQUIRED),
+                Boolean.FALSE);
+    }
+
+    /**
+     * Determine whether Recipient is required.
+     * 
+     * @param context current validation context
+     * 
+     * @return true if required, false if not
+     */
+    protected boolean isRecipientRequired(final ValidationContext context) {
+        return ObjectSupport.firstNonNull(
+                (Boolean) context.getStaticParameters().get(
+                        SAML2AssertionValidationParameters.SC_RECIPIENT_REQUIRED),
+                Boolean.FALSE);
+    }
+
+    /**
+     * Determine whether NotBefore is required.
+     * 
+     * @param context current validation context
+     * 
+     * @return true if required, false if not
+     */
+    protected boolean isNotBeforeRequired(final ValidationContext context) {
+        return ObjectSupport.firstNonNull(
+                (Boolean) context.getStaticParameters().get(
+                        SAML2AssertionValidationParameters.SC_NOT_BEFORE_REQUIRED),
+                Boolean.FALSE);
+    }
+
+    /**
+     * Determine whether NotOnOrAfter is required.
+     * 
+     * @param context current validation context
+     * 
+     * @return true if required, false if not
+     */
+    protected boolean isNotOnOrAfterRequired(final ValidationContext context) {
+        return ObjectSupport.firstNonNull(
+                (Boolean) context.getStaticParameters().get(
+                        SAML2AssertionValidationParameters.SC_NOT_ON_OR_AFTER_REQUIRED),
+                Boolean.FALSE);
+    }
 
+    /**
+     * Determine whether InResponseTo is required.
+     * 
+     * @param context current validation context
+     * 
+     * @return true if required, false if not
+     */
     protected boolean isInResponseToRequired(final ValidationContext context) {
         return ObjectSupport.firstNonNull(
                 (Boolean) context.getStaticParameters().get(
@@ -142,7 +235,7 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
      *  being validated
      * @param assertion assertion bearing the confirmation method
      * @param context current validation context
-     * @param required whether the inResponseTo value is required
+     * @param required whether the InResponseTo value is required
      * 
      * @return the result of the validation evaluation
      * 
@@ -205,16 +298,28 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
      *  being validated
      * @param assertion assertion bearing the confirmation method
      * @param context current validation context
+     * @param required 
      * 
      * @return the result of the validation evaluation
      * 
      * @throws AssertionValidationException thrown if there is a problem determining the validity of the NotBefore
      */
     @Nonnull protected ValidationResult validateNotBefore(@Nonnull final SubjectConfirmation confirmation, 
-            @Nonnull final Assertion assertion, @Nonnull final ValidationContext context) 
-                    throws AssertionValidationException {
-        final Instant skewedNow = Instant.now().plus(SAML20AssertionValidator.getClockSkew(context));
+            @Nonnull final Assertion assertion, @Nonnull final ValidationContext context,
+            final boolean required) throws AssertionValidationException {
+        
         final Instant notBefore = confirmation.getSubjectConfirmationData().getNotBefore();
+        if (notBefore == null) {
+            if (required) {
+                log.warn("SubjectConfirmationData/@NotBefore was missing and was required");
+                context.setValidationFailureMessage(
+                        "SubjectConfirmationData/@NotBefore was missing and was required");
+                return ValidationResult.INVALID;
+            }
+            return ValidationResult.VALID;
+        }
+        
+        final Instant skewedNow = Instant.now().plus(SAML20AssertionValidator.getClockSkew(context));
         
         log.debug("Evaluating SubjectConfirmationData NotBefore '{}' against 'skewed now' time '{}'",
                 notBefore, skewedNow);
@@ -236,16 +341,28 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
      *  being validated
      * @param assertion assertion bearing the confirmation method
      * @param context current validation context
+     * @param required whether the NotOnOrAfter value is required
      * 
      * @return the result of the validation evaluation
      * 
      * @throws AssertionValidationException thrown if there is a problem determining the validity of the NotOnOrAFter
      */
     @Nonnull protected ValidationResult validateNotOnOrAfter(@Nonnull final SubjectConfirmation confirmation, 
-            @Nonnull final Assertion assertion, @Nonnull final ValidationContext context) 
+            @Nonnull final Assertion assertion, @Nonnull final ValidationContext context, final boolean required) 
                     throws AssertionValidationException {
-        final Instant skewedNow = Instant.now().minus(SAML20AssertionValidator.getClockSkew(context));
+        
         final Instant notOnOrAfter = confirmation.getSubjectConfirmationData().getNotOnOrAfter();
+        if (notOnOrAfter == null) {
+            if (required) {
+                log.warn("SubjectConfirmationData/@NotOnOrAfter was missing and was required");
+                context.setValidationFailureMessage(
+                        "SubjectConfirmationData/@NotOnOrAfter was missing and was required");
+                return ValidationResult.INVALID;
+            }
+            return ValidationResult.VALID;
+        }
+        
+        final Instant skewedNow = Instant.now().minus(SAML20AssertionValidator.getClockSkew(context));
         
         log.debug("Evaluating SubjectConfirmationData NotOnOrAfter '{}' against 'skewed now' time '{}'",
                 notOnOrAfter, skewedNow);
@@ -266,16 +383,25 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
      * @param confirmation confirmation method being validated
      * @param assertion assertion bearing the confirmation method
      * @param context current validation context
+     * @param required whether the Recipient value is required
      * 
      * @return the result of the validation evaluation
      * 
      * @throws AssertionValidationException thrown if there is a problem determining the validity of the recipient
      */
     @Nonnull protected ValidationResult validateRecipient(@Nonnull final SubjectConfirmation confirmation, 
-            @Nonnull final Assertion assertion, @Nonnull final ValidationContext context) 
+            @Nonnull final Assertion assertion, @Nonnull final ValidationContext context, final boolean required)
                     throws AssertionValidationException {
-        final String recipient = StringSupport.trimOrNull(confirmation.getSubjectConfirmationData().getRecipient());
+        
+        final String recipient = 
+                StringSupport.trimOrNull(confirmation.getSubjectConfirmationData().getRecipient());
         if (recipient == null) {
+            if (required) {
+                log.warn("SubjectConfirmationData/@Recipient was missing and was required");
+                context.setValidationFailureMessage(
+                        "SubjectConfirmationData/@Recipient was missing and was required");
+                return ValidationResult.INVALID;
+            }
             return ValidationResult.VALID;
         }
         
@@ -299,8 +425,6 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
                     "Unable to determine list of valid subject confirmation recipient endpoints");
             return ValidationResult.INDETERMINATE;
         }
-        
-        
 
         if (validRecipients.contains(recipient)) {
             log.debug("Matched valid recipient: {}", recipient);
@@ -311,7 +435,7 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
                 validRecipients);
 
         context.setValidationFailureMessage(String.format(
-                "Subject confirmation recipient for asertion '%s' did not match any valid recipients", assertion
+                "Subject confirmation recipient for assertion '%s' did not match any valid recipients", assertion
                         .getID()));
         return ValidationResult.INVALID;
     }
@@ -323,13 +447,14 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
      * @param confirmation confirmation method being validated
      * @param assertion assertion bearing the confirmation method
      * @param context current validation context
+     * @param required whether the Address value is required
      * 
      * @return the result of the validation evaluation
      * 
      * @throws AssertionValidationException thrown if there is a problem determining the validity of the address
      */
     @Nonnull protected ValidationResult validateAddress(@Nonnull final SubjectConfirmation confirmation, 
-            @Nonnull final Assertion assertion, @Nonnull final ValidationContext context) 
+            @Nonnull final Assertion assertion, @Nonnull final ValidationContext context, final boolean required) 
                     throws AssertionValidationException {
 
         final Boolean checkAddress =
@@ -341,6 +466,15 @@ public abstract class AbstractSubjectConfirmationValidator implements SubjectCon
         }
 
         final String address = StringSupport.trimOrNull(confirmation.getSubjectConfirmationData().getAddress());
+        if (address == null) {
+            if (required) {
+                log.warn("SubjectConfirmationData/@Address was missing and was required");
+                context.setValidationFailureMessage(
+                        "SubjectConfirmationData/@Address was missing and was required");
+                return ValidationResult.INVALID;
+            }
+            return ValidationResult.VALID;
+        }
         
         return AssertionValidationSupport.checkAddress(context, address, 
                 SAML2AssertionValidationParameters.SC_VALID_ADDRESSES,
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 7c8c5b6..96c11be 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
@@ -98,11 +98,25 @@ public class DefaultAssertionValidationContextBuilder
     /** Function for determining additional valid audience values. */
     private Function<ProfileRequestContext, Set<String>> additionalAudiences;
     
-    /** Predicate for determining whether an Assertion's InResponseTo is required. */
-    private Predicate<ProfileRequestContext> inResponseToRequired;
-    
     /** Function for determining the valid InResponseTo value. */
     private Function<ProfileRequestContext, String> inResponseTo;
+    
+    /** Predicate for determining whether an Assertion SubjectConfirmationData InResponseTo is required. */
+    private Predicate<ProfileRequestContext> inResponseToRequired;
+    
+    /** Predicate for determining whether an Assertion SubjectConfirmationData Recipient is required. */
+    private Predicate<ProfileRequestContext> recipientRequired;
+    
+    /** Predicate for determining whether an Assertion SubjectConfirmationData NotBefore is required. */
+    private Predicate<ProfileRequestContext> notBeforeRequired;
+    
+    /** Predicate for determining whether an Assertion SubjectConfirmationData NotOnOrAfter is required. */
+    private Predicate<ProfileRequestContext> notOnOrAfterRequired;
+    
+    /** Predicate for determining whether an Assertion SubjectConfirmationData Address is required. */
+    private Predicate<ProfileRequestContext> addressRequired;
+    
+    
 
     /** Resolver for security parameters context. */
     private Function<ProfileRequestContext, SecurityParametersContext> securityParametersLookupStrategy;
@@ -114,8 +128,12 @@ public class DefaultAssertionValidationContextBuilder
         signatureRequired = Predicates.alwaysTrue();
         includeSelfEntityIDAsRecipient = Predicates.alwaysFalse();
         checkAddress = Predicates.alwaysTrue();
-        inResponseToRequired = Predicates.alwaysFalse();
         inResponseTo = new DefaultValidInResponseToLookupFunction();
+        inResponseToRequired = Predicates.alwaysFalse();
+        recipientRequired = Predicates.alwaysFalse();
+        notOnOrAfterRequired = Predicates.alwaysFalse();
+        notBeforeRequired = Predicates.alwaysFalse();
+        addressRequired = Predicates.alwaysFalse();
 
         securityParametersLookupStrategy = new ChildContextLookup<>(SecurityParametersContext.class)
                 .compose(new InboundMessageContextLookup());
@@ -194,7 +212,33 @@ public class DefaultAssertionValidationContextBuilder
     }
 
     /**
-     * Get the predicate which determines whether an Assertion's InResponseTo is required.
+     * Set the function for determining the valid InResponseTo.
+     *
+     * <p>
+     * Defaults to null.
+     * </p>
+     *
+     * @param function the function, may be null
+     */
+    public void setInResponseTo(final @Nonnull Function<ProfileRequestContext,String> function) {
+        inResponseTo = function;
+    }
+    
+    /**
+     * Get the function for determining the valid InResponseTo.
+     *
+     * <p>
+     * Defaults to null.
+     * </p>
+     *
+     * @return the function
+     */
+    public Function<ProfileRequestContext,String> getInResponseTo() {
+        return inResponseTo;
+    }
+    
+    /**
+     * Get the predicate which determines whether an Assertion SubjectConfirmationData InResponseTo is required.
      * 
      * <p>
      * Defaults to an always false predicate;
@@ -207,7 +251,7 @@ public class DefaultAssertionValidationContextBuilder
     }
 
     /**
-     * Set the predicate which determines whether an Assertion's InResponseTo is required.
+     * Set the predicate which determines whether an Assertion SubjectConfirmationData InResponseTo is required.
      * 
      * <p>
      * Defaults to an always false predicate.
@@ -218,31 +262,109 @@ public class DefaultAssertionValidationContextBuilder
     public void setInResponseToRequired(final @Nonnull Predicate<ProfileRequestContext> predicate) {
         inResponseToRequired = Constraint.isNotNull(predicate, "InResponseTo required predicate was null");
     }
-    
+
     /**
-     * Set the function for determining the valid InResponseTo.
-     *
+     * Get the predicate which determines whether an Assertion SubjectConfirmationData Recipient is required.
+     * 
      * <p>
-     * Defaults to null.
+     * Defaults to an always false predicate;
      * </p>
-     *
-     * @param function the function, may be null
+     * 
+     * @return the predicate
      */
-    public void setInResponseTo(final @Nonnull Function<ProfileRequestContext,String> function) {
-        inResponseTo = function;
+    public Predicate<ProfileRequestContext> getRecipientRequired() {
+        return recipientRequired;
     }
-    
+
     /**
-     * Get the function for determining the valid InResponseTo.
-     *
+     * Set the predicate which determines whether an Assertion SubjectConfirmationData Recipient is required.
+     * 
      * <p>
-     * Defaults to null.
+     * Defaults to an always false predicate.
      * </p>
-     *
-     * @return the function
+     * 
+     * @param predicate the predicate, must be non-null
      */
-    public Function<ProfileRequestContext,String> getInResponseTo() {
-        return inResponseTo;
+    public void setRecipientRequired(final @Nonnull Predicate<ProfileRequestContext> predicate) {
+        recipientRequired = Constraint.isNotNull(predicate, "Recipient required predicate was null");
+    }
+
+    /**
+     * Get the predicate which determines whether an Assertion SubjectConfirmationData NotBefore is required.
+     * 
+     * <p>
+     * Defaults to an always false predicate;
+     * </p>
+     * 
+     * @return the predicate
+     */
+    public Predicate<ProfileRequestContext> getNotBeforeRequired() {
+        return notBeforeRequired;
+    }
+
+    /**
+     * Set the predicate which determines whether an Assertion SubjectConfirmationData NotBefore is required.
+     * 
+     * <p>
+     * Defaults to an always false predicate.
+     * </p>
+     * 
+     * @param predicate the predicate, must be non-null
+     */
+    public void setNotBeforeRequired(final @Nonnull Predicate<ProfileRequestContext> predicate) {
+        notBeforeRequired = Constraint.isNotNull(predicate, "NotBefore required predicate was null");
+    }
+
+    /**
+     * Get the predicate which determines whether an Assertion SubjectConfirmationData NotOnOrAfter is required.
+     * 
+     * <p>
+     * Defaults to an always false predicate;
+     * </p>
+     * 
+     * @return the predicate
+     */
+    public Predicate<ProfileRequestContext> getNotOnOrAfterRequired() {
+        return notOnOrAfterRequired;
+    }
+
+    /**
+     * Set the predicate which determines whether an Assertion SubjectConfirmationData NotOnOrAfter is required.
+     * 
+     * <p>
+     * Defaults to an always false predicate.
+     * </p>
+     * 
+     * @param predicate the predicate, must be non-null
+     */
+    public void setNotOnOrAfterRequired(final @Nonnull Predicate<ProfileRequestContext> predicate) {
+        notOnOrAfterRequired = Constraint.isNotNull(predicate, "NotOnOrAfter required predicate was null");
+    }
+
+    /**
+     * Get the predicate which determines whether an Assertion SubjectConfirmationData Address is required.
+     * 
+     * <p>
+     * Defaults to an always false predicate;
+     * </p>
+     * 
+     * @return the predicate
+     */
+    public Predicate<ProfileRequestContext> getAddressRequired() {
+        return addressRequired;
+    }
+
+    /**
+     * Set the predicate which determines whether an Assertion SubjectConfirmationData Address is required.
+     * 
+     * <p>
+     * Defaults to an always false predicate.
+     * </p>
+     * 
+     * @param predicate the predicate, must be non-null
+     */
+    public void setAddressRequired(final @Nonnull Predicate<ProfileRequestContext> predicate) {
+        addressRequired = Constraint.isNotNull(predicate, "Address required predicate was null");
     }
 
     /**
@@ -397,9 +519,15 @@ public class DefaultAssertionValidationContextBuilder
         final Boolean checkAddressEnabled = Boolean.valueOf(getCheckAddress().test(input.getProfileRequestContext()));
         
         // For SubjectConfirmationData
+        staticParams.put(SAML2AssertionValidationParameters.SC_RECIPIENT_REQUIRED,
+                Boolean.valueOf(getRecipientRequired().test(input.getProfileRequestContext())));
         staticParams.put(SAML2AssertionValidationParameters.SC_VALID_RECIPIENTS, getValidRecipients(input));
+        
+        staticParams.put(SAML2AssertionValidationParameters.SC_ADDRESS_REQUIRED,
+                Boolean.valueOf(getAddressRequired().test(input.getProfileRequestContext())));
         staticParams.put(SAML2AssertionValidationParameters.SC_VALID_ADDRESSES, validAddresses);
         staticParams.put(SAML2AssertionValidationParameters.SC_CHECK_ADDRESS, checkAddressEnabled);
+        
         staticParams.put(SAML2AssertionValidationParameters.SC_IN_RESPONSE_TO_REQUIRED,
                 Boolean.valueOf(getInResponseToRequired().test(input.getProfileRequestContext())));
         if (getInResponseTo() != null) {
@@ -407,6 +535,11 @@ public class DefaultAssertionValidationContextBuilder
                     getInResponseTo().apply(input.getProfileRequestContext()));
         }
         
+        staticParams.put(SAML2AssertionValidationParameters.SC_NOT_BEFORE_REQUIRED,
+                Boolean.valueOf(getNotBeforeRequired().test(input.getProfileRequestContext())));
+        staticParams.put(SAML2AssertionValidationParameters.SC_NOT_ON_OR_AFTER_REQUIRED,
+                Boolean.valueOf(getNotOnOrAfterRequired().test(input.getProfileRequestContext())));
+        
         // For Audience Condition
         staticParams.put(SAML2AssertionValidationParameters.COND_VALID_AUDIENCES, getValidAudiences(input));
         
diff --git a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/impl/AbstractSubjectConfirmationValidatorTest.java b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/impl/AbstractSubjectConfirmationValidatorTest.java
index 3464b41..a7ed5f8 100644
--- a/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/impl/AbstractSubjectConfirmationValidatorTest.java
+++ b/opensaml-saml-impl/src/test/java/org/opensaml/saml/saml2/assertion/impl/AbstractSubjectConfirmationValidatorTest.java
@@ -124,6 +124,30 @@ public class AbstractSubjectConfirmationValidatorTest extends BaseAssertionValid
     }
     
     @Test
+    public void testNoAddressAndRequired() throws AssertionValidationException {
+        subjectConfirmation.getSubjectConfirmationData().setAddress(null);
+        
+        Map<String,Object> staticParams = buildBasicStaticParameters();
+        staticParams.put(SAML2AssertionValidationParameters.SC_ADDRESS_REQUIRED, Boolean.TRUE);
+        ValidationContext validationContext = new ValidationContext(staticParams);
+        
+        Assert.assertEquals(validator.validate(subjectConfirmation, getAssertion(), validationContext), 
+                ValidationResult.INVALID);        
+    }
+    
+    @Test
+    public void testNoConfirmationDataAndAddressRequired() throws AssertionValidationException {
+        subjectConfirmation.setSubjectConfirmationData(null);
+        
+        Map<String,Object> staticParams = buildBasicStaticParameters();
+        staticParams.put(SAML2AssertionValidationParameters.SC_ADDRESS_REQUIRED, Boolean.TRUE);
+        ValidationContext validationContext = new ValidationContext(staticParams);
+        
+        Assert.assertEquals(validator.validate(subjectConfirmation, getAssertion(), validationContext), 
+                ValidationResult.INVALID);        
+    }
+    
+    @Test
     public void testInvalidRecipient() throws AssertionValidationException {
         subjectConfirmation.getSubjectConfirmationData().setRecipient("https://bogussp.example.com");
         
@@ -167,6 +191,30 @@ public class AbstractSubjectConfirmationValidatorTest extends BaseAssertionValid
     }
     
     @Test
+    public void testNoRecipientAndRequired() throws AssertionValidationException {
+        subjectConfirmation.getSubjectConfirmationData().setRecipient(null);
+        
+        Map<String,Object> staticParams = buildBasicStaticParameters();
+        staticParams.put(SAML2AssertionValidationParameters.SC_RECIPIENT_REQUIRED, Boolean.TRUE);
+        ValidationContext validationContext = new ValidationContext(staticParams);
+        
+        Assert.assertEquals(validator.validate(subjectConfirmation, getAssertion(), validationContext), 
+                ValidationResult.INVALID);
+    }
+    
+    @Test
+    public void testNoConfirmationDataAndRecipientRequired() throws AssertionValidationException {
+        subjectConfirmation.setSubjectConfirmationData(null);
+        
+        Map<String,Object> staticParams = buildBasicStaticParameters();
+        staticParams.put(SAML2AssertionValidationParameters.SC_RECIPIENT_REQUIRED, Boolean.TRUE);
+        ValidationContext validationContext = new ValidationContext(staticParams);
+        
+        Assert.assertEquals(validator.validate(subjectConfirmation, getAssertion(), validationContext), 
+                ValidationResult.INVALID);
+    }
+    
+    @Test
     public void testInvalidNotBefore() throws AssertionValidationException {
         // Adjust them both just so they make sense
         subjectConfirmation.getSubjectConfirmationData().setNotBefore(Instant.now().plus(30, ChronoUnit.MINUTES));
@@ -189,6 +237,30 @@ public class AbstractSubjectConfirmationValidatorTest extends BaseAssertionValid
     }
     
     @Test
+    public void testNoNotBeforeAndRequired() throws AssertionValidationException {
+        subjectConfirmation.getSubjectConfirmationData().setNotBefore(null);
+        
+        Map<String,Object> staticParams = buildBasicStaticParameters();
+        staticParams.put(SAML2AssertionValidationParameters.SC_NOT_BEFORE_REQUIRED, Boolean.TRUE);
+        ValidationContext validationContext = new ValidationContext(staticParams);
+        
+        Assert.assertEquals(validator.validate(subjectConfirmation, getAssertion(), validationContext), 
+                ValidationResult.INVALID);        
+    }
+    
+    @Test
+    public void testNoConfirmationDataAndNotBeforeRequired() throws AssertionValidationException {
+        subjectConfirmation.setSubjectConfirmationData(null);
+        
+        Map<String,Object> staticParams = buildBasicStaticParameters();
+        staticParams.put(SAML2AssertionValidationParameters.SC_NOT_BEFORE_REQUIRED, Boolean.TRUE);
+        ValidationContext validationContext = new ValidationContext(staticParams);
+        
+        Assert.assertEquals(validator.validate(subjectConfirmation, getAssertion(), validationContext), 
+                ValidationResult.INVALID);        
+    }
+    
+    @Test
     public void testInvalidNotOnOrAfter() throws AssertionValidationException {
         // Adjust them both just so they make sense
         subjectConfirmation.getSubjectConfirmationData().setNotBefore(Instant.now().minus(60, ChronoUnit.MINUTES));
@@ -211,6 +283,30 @@ public class AbstractSubjectConfirmationValidatorTest extends BaseAssertionValid
     }
     
     @Test
+    public void testNoNotOnOrAfterAndRequired() throws AssertionValidationException {
+        subjectConfirmation.getSubjectConfirmationData().setNotOnOrAfter(null);
+        
+        Map<String,Object> staticParams = buildBasicStaticParameters();
+        staticParams.put(SAML2AssertionValidationParameters.SC_NOT_ON_OR_AFTER_REQUIRED, Boolean.TRUE);
+        ValidationContext validationContext = new ValidationContext(staticParams);
+        
+        Assert.assertEquals(validator.validate(subjectConfirmation, getAssertion(), validationContext), 
+                ValidationResult.INVALID);        
+    }
+    
+    @Test
+    public void testNoConfirmationDataAndNotOnOrAfterRequired() throws AssertionValidationException {
+        subjectConfirmation.setSubjectConfirmationData(null);
+        
+        Map<String,Object> staticParams = buildBasicStaticParameters();
+        staticParams.put(SAML2AssertionValidationParameters.SC_NOT_ON_OR_AFTER_REQUIRED, Boolean.TRUE);
+        ValidationContext validationContext = new ValidationContext(staticParams);
+        
+        Assert.assertEquals(validator.validate(subjectConfirmation, getAssertion(), validationContext), 
+                ValidationResult.INVALID);        
+    }
+    
+    @Test
     public void testInvalidInResponseTo() throws AssertionValidationException {
         subjectConfirmation.getSubjectConfirmationData().setInResponseTo("invalid");
         

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


More information about the commits mailing list