[java-oidc-common] branch main updated: Add OR support to chaining JWT validator.

Scott Cantor cantor.2 at osu.edu
Mon Jan 24 17:38:46 UTC 2022


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

scantor pushed a commit to branch main
in repository java-oidc-common.

View the commit online:
http://git.shibboleth.net/view/?p=java-oidc-common.git;a=commit;h=a9cb31dd97b6cda59b2abab80b8ac929bce3102e

The following commit(s) were added to refs/heads/main by this push:
     new a9cb31d  Add OR support to chaining JWT validator.
a9cb31d is described below

commit a9cb31dd97b6cda59b2abab80b8ac929bce3102e
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Jan 24 12:38:42 2022 -0500

    Add OR support to chaining JWT validator.
---
 .../claims/impl/ChainingJWTClaimsValidator.java    |  64 ++++++++++--
 .../claims/impl/JWTIdentifierClaimsValidator.java  |   6 +-
 .../impl/ChainingJWTClaimsValidatorTest.java       | 109 +++++++++++++++++----
 3 files changed, 151 insertions(+), 28 deletions(-)

diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidator.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidator.java
index 5346221..df148f8 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidator.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidator.java
@@ -41,8 +41,12 @@ import net.shibboleth.utilities.java.support.component.ComponentSupport;
 
 /**
  * A {@link ClaimsValidator} implementation that validates a JWT claims set from a chain 
- * of configured validators. Validation terminates when one of the validators throws a {@link JWTValidationException}. 
- * If no {@link JWTValidationException} is thrown, the claims set is 'valid'.
+ * of configured validators.
+ * 
+ * <p>Normally, validation terminates when one of the validators throws a {@link JWTValidationException}. 
+ * If no {@link JWTValidationException} is thrown, the claims set is 'valid'.</p>
+ * 
+ * <p>Optionally this can be toggled such that any of the validators succeeeding implies overall success.</p>
  * 
  * <p>Note, does not represent a chain of responsibility pattern despite the name.</p>
  */
@@ -55,6 +59,13 @@ public class ChainingJWTClaimsValidator extends AbstractIdentifiableInitializabl
     /** List of claim validators. Ordering is not important.*/
     @NonnullAfterInit @NonnullElements private List<ClaimsValidator> claimValidators;
     
+    /** Require all of the validators to be successful. */
+    private boolean requireAll;
+
+    /** Constructor. */
+    public ChainingJWTClaimsValidator() {
+        requireAll = true;
+    }
     
     @Override
     protected void doInitialize() throws ComponentInitializationException {
@@ -65,6 +76,19 @@ public class ChainingJWTClaimsValidator extends AbstractIdentifiableInitializabl
         }
     }
     
+    /**
+     * Set whether to require all validators to be successful.
+     * 
+     * <p>Defaults to true.</p>
+     * 
+     * @param flag flag to set
+     */
+    public void setRequireAll(final boolean flag) {
+        ComponentSupport.ifInitializedThrowUnmodifiabledComponentException(this);
+        
+        requireAll = flag;
+    }
+    
     /**
      * Set the list of validators to use.
      * 
@@ -85,17 +109,41 @@ public class ChainingJWTClaimsValidator extends AbstractIdentifiableInitializabl
         throws JWTValidationException {       
         
         if (claims == null) {
-            log.trace("No claims to verify, nothing to do");
+            log.trace("{}: No claims to validation, nothing to do", getId());
+            return;
+        } else if (claimValidators.isEmpty()) {
+            log.trace("{}: No validators to check, nothing to do", getId());
             return;
         }
-        log.debug("Attempting JWT claims verification for subject '{}'",claims.getSubject());
+        
+        log.debug("{}: Attempting JWT claims validation for subject '{}'", getId(), claims.getSubject());
+        
+        JWTValidationException failure = null;
         
         for (final ClaimsValidator validator : claimValidators) {
-            log.trace("Attempting JWT claims validator '{}'", validator.getId());
-            validator.validate(claims, context);
+            log.trace("{}: Attempting JWT claims validation '{}'", getId(), validator.getId());
+            try {
+                validator.validate(claims, context);
+                if (!requireAll) {
+                    log.debug("{}: JWT claims validation for subject '{}' succeeded", getId(), claims.getSubject());
+                    return;
+                }
+            } catch (final JWTValidationException e) {
+                if (requireAll) {
+                    throw e;
+                } else {
+                    failure = e;
+                }
+            }
+        }
+
+        if (!requireAll) {
+            log.debug("{}: JWT claims validation for subject '{}' failed, all validators failed", getId(),
+                    claims.getSubject());
+            throw failure;
         }
-        log.debug("JWT claims verification for subject '{}' succeeded",claims.getSubject());
         
+        log.debug("{}: JWT claims validation for subject '{}' succeeded", getId(), claims.getSubject());
     }   
 
-}
+}
\ No newline at end of file
diff --git a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java
index ab45f20..1572545 100644
--- a/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java
+++ b/oidc-common-crypto-impl/src/main/java/net/shibboleth/oidc/security/jwt/claims/impl/JWTIdentifierClaimsValidator.java
@@ -37,8 +37,10 @@ import net.shibboleth.utilities.java.support.logic.Constraint;
 import net.shibboleth.utilities.java.support.primitive.StringSupport;
 
 /**
- * Verifies the identifier (jit) from the JWT against repla via configurable {@link ReplayCache}. Single identifier is
- * stored in the cache for the lifetime of the JWT: expiration instant is taken from the 'exp' -claim.
+ * Verifies the identifier (jit) from the JWT against replay via configurable {@link ReplayCache}.
+ * 
+ * <p>Single identifier is stored in the cache for the lifetime of the JWT (expiration instant is
+ * taken from the 'exp' -claim).</p>
  */
 public class JWTIdentifierClaimsValidator extends AbstractClaimsValidator {
 
diff --git a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidatorTest.java b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidatorTest.java
index b23e4df..356c695 100644
--- a/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidatorTest.java
+++ b/oidc-common-crypto-impl/src/test/java/net/shibboleth/oidc/security/jwt/claims/impl/ChainingJWTClaimsValidatorTest.java
@@ -26,11 +26,13 @@ import java.util.Set;
 
 import javax.annotation.Nonnull;
 
+import org.opensaml.profile.context.ProfileRequestContext;
 import org.testng.annotations.BeforeMethod;
 import org.testng.annotations.Test;
 
 import com.nimbusds.jwt.JWTClaimsSet;
 
+import net.shibboleth.oidc.jwt.claims.AbstractClaimsValidator;
 import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
 import net.shibboleth.oidc.jwt.claims.JWTValidationException;
 import net.shibboleth.utilities.java.support.component.ComponentInitializationException;
@@ -128,6 +130,95 @@ public class ChainingJWTClaimsValidatorTest extends AbstractClaimsValidatorTest{
         validationNew.setClaimValidators(null);
         validationNew.initialize();
     }
+
+    @Test(expectedExceptions = JWTValidationException.class)
+    public void testOptionalValidatorsFailure() throws ComponentInitializationException, JWTValidationException {
+
+        final ClaimsValidator good = new AbstractClaimsValidator() {
+            @Override
+            protected void doValidate(JWTClaimsSet claims, ProfileRequestContext context)
+                    throws JWTValidationException {
+            }
+        };
+        
+        final ClaimsValidator bad = new AbstractClaimsValidator() {
+            @Override
+            protected void doValidate(JWTClaimsSet claims, ProfileRequestContext context)
+                    throws JWTValidationException {
+                throw new JWTValidationException("failure");
+            }
+        };
+        
+        final ChainingJWTClaimsValidator validationNew = new ChainingJWTClaimsValidator();
+        validationNew.setId("test");
+        validationNew.setClaimValidators(List.of(bad, good));
+        validationNew.initialize();
+        
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().build();
+        validationNew.validate(claimsSet, prc);
+    }
+
+    @Test
+    public void testOptionalValidatorsSuccess() throws ComponentInitializationException, JWTValidationException {
+
+        final ClaimsValidator good = new AbstractClaimsValidator() {
+            @Override
+            protected void doValidate(JWTClaimsSet claims, ProfileRequestContext context)
+                    throws JWTValidationException {
+            }
+        };
+        
+        final ClaimsValidator bad = new AbstractClaimsValidator() {
+            @Override
+            protected void doValidate(JWTClaimsSet claims, ProfileRequestContext context)
+                    throws JWTValidationException {
+                throw new JWTValidationException("failure");
+            }
+        };
+        
+        final ChainingJWTClaimsValidator validationNew = new ChainingJWTClaimsValidator();
+        validationNew.setId("test");
+        validationNew.setClaimValidators(List.of(bad, good));
+        validationNew.setRequireAll(false);
+        validationNew.initialize();
+        
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().build();
+        validationNew.validate(claimsSet, prc);
+    }
+
+    @Test
+    public void testNestedValidatorsSuccess() throws ComponentInitializationException, JWTValidationException {
+
+        final ClaimsValidator good = new AbstractClaimsValidator() {
+            @Override
+            protected void doValidate(JWTClaimsSet claims, ProfileRequestContext context)
+                    throws JWTValidationException {
+            }
+        };
+        
+        final ClaimsValidator bad = new AbstractClaimsValidator() {
+            @Override
+            protected void doValidate(JWTClaimsSet claims, ProfileRequestContext context)
+                    throws JWTValidationException {
+                throw new JWTValidationException("failure");
+            }
+        };
+        
+        final ChainingJWTClaimsValidator nested = new ChainingJWTClaimsValidator();
+        nested.setId("nested");
+        nested.setClaimValidators(List.of(bad, good));
+        nested.setRequireAll(false);
+        nested.initialize();
+        
+        final ChainingJWTClaimsValidator validationNew = new ChainingJWTClaimsValidator();
+        validationNew.setId("test");
+        validationNew.setClaimValidators(List.of(bad, nested));
+        validationNew.setRequireAll(false);
+        validationNew.initialize();
+        
+        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().build();
+        validationNew.validate(claimsSet, prc);
+    }
     
     @Test
     public void testNullClaims() throws ComponentInitializationException, JWTValidationException {
@@ -154,24 +245,6 @@ public class ChainingJWTClaimsValidatorTest extends AbstractClaimsValidatorTest{
         validation.validate(claimsSet, prc);        
     }
     
-    @Test(expectedExceptions = JWTValidationException.class)
-    public void validationFailedNoSubject() throws ComponentInitializationException, JWTValidationException {
-        
-        final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
-                .issuer("issuer")
-                .expirationTime(Date.from(Instant.now().plus(Duration.ofMinutes(10))))
-                .audience("audience")
-                .notBeforeTime(Date.from(Instant.now().minus(Duration.ofMinutes(1))))
-                .issueTime(Date.from(Instant.now()))
-                .claim(IDTokenClaims.AUTHENTICATION_TIME.getClaimName(), 
-                        Instant.now().minus(Duration.ofSeconds(30)).getEpochSecond())
-                .claim("username", "jdoe")
-                .claim(IDTokenClaims.NONCE.getClaimName(), "nonce")
-                .build();        
-
-        validation.validate(claimsSet, prc);        
-    }
-    
     @Test(expectedExceptions = JWTValidationException.class)
     public void validationFailedExpired() throws ComponentInitializationException, JWTValidationException {
         

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


More information about the commits mailing list