[java-shib-profile] branch main updated: JSPROF-10 - Enable loop detection via Condition

Scott Cantor cantor.2 at osu.edu
Mon Feb 17 15:37:26 UTC 2025


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

scantor pushed a commit to branch main
in repository java-shib-profile.

View the commit online:
http://git.shibboleth.net/view/?p=java-shib-profile.git;a=commit;h=4a6abcf06ca7533e198b4abb496cc37a5dd36020

The following commit(s) were added to refs/heads/main by this push:
     new 4a6abcf  JSPROF-10 - Enable loop detection via Condition
4a6abcf is described below

commit 4a6abcf06ca7533e198b4abb496cc37a5dd36020
Author: Scott Cantor <cantor.2 at osu.edu>
AuthorDate: Mon Feb 17 10:37:24 2025 -0500

    JSPROF-10 - Enable loop detection via Condition
    
    https://shibboleth.atlassian.net/browse/JSPROF-10
---
 .../context/logic/LoopDetectionPredicate.java      | 85 +++++++++++++++++++++-
 .../context/logic/LoopDetectionPredicateTest.java  | 17 ++++-
 2 files changed, 98 insertions(+), 4 deletions(-)

diff --git a/shib-profile-api/src/main/java/net/shibboleth/profile/context/logic/LoopDetectionPredicate.java b/shib-profile-api/src/main/java/net/shibboleth/profile/context/logic/LoopDetectionPredicate.java
index b14b8c2..cd4d060 100644
--- a/shib-profile-api/src/main/java/net/shibboleth/profile/context/logic/LoopDetectionPredicate.java
+++ b/shib-profile-api/src/main/java/net/shibboleth/profile/context/logic/LoopDetectionPredicate.java
@@ -14,8 +14,10 @@
 
 package net.shibboleth.profile.context.logic;
 
+import java.security.NoSuchAlgorithmException;
 import java.util.Map;
 import java.util.function.Function;
+import java.util.function.Predicate;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -30,9 +32,12 @@ import com.codahale.metrics.SlidingTimeWindowMovingAverages;
 
 import net.shibboleth.profile.context.RelyingPartyContext;
 import net.shibboleth.shared.annotation.constraint.Positive;
+import net.shibboleth.shared.codec.StringDigester;
+import net.shibboleth.shared.codec.StringDigester.OutputFormat;
 import net.shibboleth.shared.collection.CollectionSupport;
 import net.shibboleth.shared.logic.Constraint;
 import net.shibboleth.shared.logic.FunctionSupport;
+import net.shibboleth.shared.logic.PredicateSupport;
 import net.shibboleth.shared.primitive.LoggerFactory;
 
 /**
@@ -54,14 +59,26 @@ public class LoopDetectionPredicate extends AbstractRelyingPartyPredicate {
     /** Map of RP names to meter names. */
     @Nonnull private Map<String,String> relyingPartyMap;
     
+    /** Generalized condition in place of dedicated map. */
+    @Nonnull private Predicate<ProfileRequestContext> activationCondition; 
+    
+    /** Used with generalized condition to compute name to use in loop metric. */
+    @Nonnull private Function<ProfileRequestContext,String> tagGenerationStrategy; 
+    
     /** Lookup strategy to obtain subject name. */
     @Nonnull private Function<ProfileRequestContext,String> usernameLookupStrategy;
     
-    /** Constructor. */
-    public LoopDetectionPredicate() {
+    /**
+     * Constructor.
+     * 
+     * @throws NoSuchAlgorithmException if unable to create digester
+     */
+    public LoopDetectionPredicate() throws NoSuchAlgorithmException {
         privateRegistry = new MetricRegistry();
         threshold = 20;
         relyingPartyMap = CollectionSupport.emptyMap();
+        activationCondition = PredicateSupport.alwaysFalse();
+        tagGenerationStrategy = new DefaultTagGenerator();
         usernameLookupStrategy = FunctionSupport.constant(null);
     }
     
@@ -98,6 +115,32 @@ public class LoopDetectionPredicate extends AbstractRelyingPartyPredicate {
         usernameLookupStrategy = Constraint.isNotNull(strategy, "Username lookup strategy cannot be null");
     }
     
+    /**
+     * Sets a generalized activation condition to apply in addition to the hardcoded map of RPs.
+     * 
+     * <p>Defaults to false.</p>
+     * 
+     * @param condition condition to set
+     *  
+     * @since 5.2.0
+     */
+    public void setActivationCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+        activationCondition = Constraint.isNotNull(condition, "Activation condition cannot be null");
+    }
+
+    /**
+     * Sets a generalized tag generation strategy to produce the loop detection metric name.
+     * 
+     * <p>Defaults to the SHA-1 hash of the RP name.</p>
+     * 
+     * @param strategy function to produce metric name
+     * 
+     * @since 5.2.0
+     */
+    public void setTagGenerationStrategy(@Nonnull final Function<ProfileRequestContext,String> strategy) {
+        tagGenerationStrategy = Constraint.isNotNull(strategy, "Tag generation function cannot be null");
+    }
+    
     /** {@inheritDoc} */
     public boolean test(@Nullable final ProfileRequestContext input) {
         
@@ -106,6 +149,13 @@ public class LoopDetectionPredicate extends AbstractRelyingPartyPredicate {
         
         if (username != null && rpCtx != null && rpCtx.getRelyingPartyId() != null) {
             String meterName = relyingPartyMap.get(rpCtx.getRelyingPartyId());
+            if (meterName == null) {
+                // Check generalized predicate.
+                if (activationCondition.test(input)) {
+                    meterName = tagGenerationStrategy.apply(input);
+                }
+            }
+            
             if (meterName != null) {
                 try {
                     meterName = MetricRegistry.name("net.shibboleth.idp.loopDetection", meterName,
@@ -134,4 +184,35 @@ public class LoopDetectionPredicate extends AbstractRelyingPartyPredicate {
         return false;
     }
 
+    /**
+     * Default tag generator that hashes the RP ID.
+     */
+    private class DefaultTagGenerator implements Function<ProfileRequestContext,String> {
+
+        /** Digester to prooduce tag. */
+        @Nonnull private final StringDigester digester;
+        
+        /**
+         * Constructor.
+         * 
+         * @throws NoSuchAlgorithmException if unable to obtain digester
+         */
+        public DefaultTagGenerator() throws NoSuchAlgorithmException {
+            digester = new StringDigester("SHA1", OutputFormat.HEX_LOWER);
+        }
+        
+        
+        /** {@inheritDoc} */
+        @Nullable public String apply(@Nullable final ProfileRequestContext input) {
+
+            final RelyingPartyContext rpCtx = getRelyingPartyContextLookupStrategy().apply(input);
+            
+            if (rpCtx != null && rpCtx.getRelyingPartyId() != null) {
+                return digester.apply(rpCtx.getRelyingPartyId());
+            }
+            
+            return null;
+        }
+    }
+    
 }
\ No newline at end of file
diff --git a/shib-profile-api/src/test/java/net/shibboleth/profile/context/logic/LoopDetectionPredicateTest.java b/shib-profile-api/src/test/java/net/shibboleth/profile/context/logic/LoopDetectionPredicateTest.java
index 4903d7e..8ae7f9e 100644
--- a/shib-profile-api/src/test/java/net/shibboleth/profile/context/logic/LoopDetectionPredicateTest.java
+++ b/shib-profile-api/src/test/java/net/shibboleth/profile/context/logic/LoopDetectionPredicateTest.java
@@ -14,10 +14,12 @@
 
 package net.shibboleth.profile.context.logic;
 
+import java.security.NoSuchAlgorithmException;
 import java.util.Map;
 
 import net.shibboleth.profile.context.RelyingPartyContext;
 import net.shibboleth.shared.logic.FunctionSupport;
+import net.shibboleth.shared.logic.PredicateSupport;
 
 import org.opensaml.core.testing.OpenSAMLInitBaseTestCase;
 import org.opensaml.profile.context.ProfileRequestContext;
@@ -34,7 +36,7 @@ public class LoopDetectionPredicateTest extends OpenSAMLInitBaseTestCase {
     private LoopDetectionPredicate pred;
     
     @BeforeMethod
-    public void setUp() {
+    public void setUp() throws NoSuchAlgorithmException {
         prc = new ProfileRequestContext();
         rpCtx = prc.ensureSubcontext(RelyingPartyContext.class);
         pred = new LoopDetectionPredicate();
@@ -56,7 +58,7 @@ public class LoopDetectionPredicateTest extends OpenSAMLInitBaseTestCase {
         rpCtx.setRelyingPartyId("foo");
         Assert.assertFalse(pred.test(prc));
     }
-
+    
     @Test
     public void testMatch() {
         pred.setRelyingPartyMap(Map.of("foo", "foo"));
@@ -76,4 +78,15 @@ public class LoopDetectionPredicateTest extends OpenSAMLInitBaseTestCase {
         Assert.assertTrue(pred.test(prc));
     }
 
+    @Test
+    public void testExceedUsingActivationCondition() throws InterruptedException {
+        pred.setActivationCondition(PredicateSupport.alwaysTrue());
+        
+        rpCtx.setRelyingPartyId("bar");
+        for (int i=0; i<20; ++i) {
+            Assert.assertFalse(pred.test(prc));
+        }
+        Assert.assertTrue(pred.test(prc));
+    }
+
 }
\ 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