[java-plugin-shibd] branch main updated: JSHIBD-25 - Develop necessary CredentialResolvers for SP service
Codeberg
noreply at shibboleth.net
Tue Sep 1 01:18:52 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-plugin-shibd.
View the commit online:
https://codeberg.org/Shibboleth/java-plugin-shibd/commit/33904288dac90cb3643c12c1029e9940a0b810f6
The following commit(s) were added to refs/heads/main by this push:
new 3390428 JSHIBD-25 - Develop necessary CredentialResolvers for SP service
3390428 is described below
commit 33904288dac90cb3643c12c1029e9940a0b810f6
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Mon Aug 31 21:18:36 2026 -0400
JSHIBD-25 - Develop necessary CredentialResolvers for SP service
https://shibboleth.atlassian.net/browse/JSHIBD-25
Embed Cache settings and construction in resolver base class.
---
.../shibboleth/idp/module/conf/sp/credentials.xml | 4 +-
.../AbstractStorageServiceCredentialResolver.java | 115 ++++++++++++++++-----
.../impl/X509CredentialStorageServiceResolver.java | 6 +-
.../X509CredentialStorageServiceResolverTest.java | 8 +-
4 files changed, 94 insertions(+), 39 deletions(-)
diff --git a/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/credentials.xml b/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/credentials.xml
index aabbae3..ee50782 100644
--- a/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/credentials.xml
+++ b/sp-conf-impl/src/main/resources/net/shibboleth/idp/module/conf/sp/credentials.xml
@@ -4,12 +4,10 @@
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
- xmlns:resolver="urn:mace:shibboleth:2.0:resolver"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
- http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
- urn:mace:shibboleth:2.0:resolver http://shibboleth.net/schema/idp/shibboleth-attribute-resolver.xsd"
+ http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"
default-init-method="initialize"
default-destroy-method="destroy">
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/credential/AbstractStorageServiceCredentialResolver.java b/sp-server-api/src/main/java/net/shibboleth/sp/credential/AbstractStorageServiceCredentialResolver.java
index 320a645..073c97d 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/credential/AbstractStorageServiceCredentialResolver.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/credential/AbstractStorageServiceCredentialResolver.java
@@ -16,6 +16,7 @@ package net.shibboleth.sp.credential;
import java.nio.charset.StandardCharsets;
import java.security.NoSuchAlgorithmException;
+import java.time.Duration;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -38,11 +39,13 @@ import org.opensaml.storage.StorageService;
import org.slf4j.Logger;
import com.google.common.cache.Cache;
+import com.google.common.cache.CacheBuilder;
import net.shibboleth.shared.annotation.ParameterName;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NotEmpty;
import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Positive;
import net.shibboleth.shared.annotation.constraint.Unmodifiable;
import net.shibboleth.shared.codec.StringDigester;
import net.shibboleth.shared.codec.StringDigester.OutputFormat;
@@ -133,8 +136,8 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
/** Credential type. */
@Nonnull private final Class<T> credentialType;
- /** Whether to suppress PRC criterion. */
- private boolean supportProfileRequestContextCriterion;
+ /** Whether to enable caching. */
+ private boolean cacheEnabled;
/** Storage service to use. */
@NonnullAfterInit private StorageService storageService;
@@ -145,6 +148,15 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
/** Function to transform the entityID. */
@NonnullAfterInit private Function<String,String> entityIDTransformStrategy;
+ /** Size of cache. */
+ @Nonnull @Positive private Integer maximumCachedElements;
+
+ /** Sets cache expiration based on last access time. */
+ @Nullable @Positive private Duration expireAfterAccess;
+
+ /** Sets cache expiration based on entry creation. */
+ @Nullable @Positive private Duration expireAfterWrite;
+
/** Credential resolution cache. */
@Nullable private Cache<CriteriaSet,List<Credential>> resultsCache;
@@ -170,20 +182,22 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
usageMap = Map.of(UsageType.SIGNING, "-signing", UsageType.ENCRYPTION, "-encryption",
UsageType.UNSPECIFIED, "-signing");
protocolMap = CollectionSupport.emptyMap();
+
+ maximumCachedElements = 500;
+ expireAfterAccess = Duration.ofHours(4);
}
/**
- * Sets whether the {@link ProfileRequestContextCriterion} should be passed in if present.
+ * Sets whether to enable caching.
*
* <p>Defaults to false.</p>
*
- * <p>Most use cases are unlikely to leverage this particular criterion type.
- * Enabling this option will prevent caching so shoould only be enabled when required.</p>
+ * <p>When enabled, any {@link ProfileRequestContextCriterion} is remoed from input criteria.</p>
*
* @param flag flag to set
*/
- public void setSupportProfileRequestContextCriterion(final boolean flag) {
- supportProfileRequestContextCriterion = flag;
+ public void setCacheEnabled(final boolean flag) {
+ cacheEnabled = flag;
}
/**
@@ -240,19 +254,56 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
}
/**
- * Sets the cache used to cache search results.
+ * Sets the maximum cache size.
*
- * <p>All entries in the cache are invalidated prior to use.</p>
+ * <p>Defaults to 500.</p>
*
- * @param cache cache used to cache search results
+ * @param max maximum size
*/
- public void setResultsCache(@Nullable final Cache<CriteriaSet,List<Credential>> cache) {
+ public void setMaximumCachedElements(@Nonnull @Positive final Integer max) {
checkSetterPreconditions();
- if (cache != null) {
- cache.invalidateAll();
+ maximumCachedElements = Constraint.isGreaterThan(0, max, "Maximum cache size must be greater than zero");
+ }
+
+ /**
+ * Sets the cache policy to expire entries after access, with the time reset on each access.
+ *
+ * <p>Defaults to 4 hours.</p>
+ *
+ * <p>Mutually exclusive with {@link #setExpireAfterWrite(Duration)}.</p>
+ *
+ * @param exp threshold
+ */
+ public void setExpireAfterAccess(@Nullable @Positive final Duration exp) {
+ checkSetterPreconditions();
+
+ if (exp != null) {
+ Constraint.isFalse(exp.isNegative() || exp.isZero(), "Thresold must be greater than zero");
+ expireAfterAccess = exp;
+ } else {
+ expireAfterAccess = null;
+ }
+ }
+
+ /**
+ * Sets the cache policy to expire entries after creation.
+ *
+ * <p>Defaults to null.</p>
+ *
+ * <p>Mutually exclusive with {@link #setExpireAfterAccess(Duration)}.</p>
+ *
+ * @param exp threshold
+ */
+ public void setExpireAfterWrite(@Nullable @Positive final Duration exp) {
+ checkSetterPreconditions();
+
+ if (exp != null) {
+ Constraint.isFalse(exp.isNegative() || exp.isZero(), "Thresold must be greater than zero");
+ expireAfterWrite = exp;
+ } else {
+ expireAfterWrite = null;
}
- resultsCache = cache;
}
/**
@@ -296,11 +347,11 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
*
* @param map map of usage types to string values
*/
- public void setProtocolMap(@Nullable final Map<UsageType,String> map) {
+ public void setProtocolMap(@Nullable final Map<String,String> map) {
if (map != null) {
- usageMap = CollectionSupport.copyToMap(map);
+ protocolMap = CollectionSupport.copyToMap(map);
} else {
- usageMap = CollectionSupport.emptyMap();
+ protocolMap = CollectionSupport.emptyMap();
}
}
@@ -315,9 +366,20 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
throw new ComponentInitializationException("VelocityEngine cannot be null");
}
- if (resultsCache != null && supportProfileRequestContextCriterion) {
- throw new ComponentInitializationException(
- "Result caching is incompatible with support for ProfileRequestContextCriterion");
+ if (cacheEnabled) {
+ final CacheBuilder<Object,Object> builder = CacheBuilder.newBuilder();
+ builder.maximumSize(maximumCachedElements);
+ if (expireAfterAccess != null) {
+ if (expireAfterWrite != null) {
+ log.warn("CredentialResolver {}: ignoring expireAfterWrite, expireAfterAccess is set", getId());
+ }
+ builder.expireAfterAccess(expireAfterAccess);
+ } else if (expireAfterWrite != null) {
+ builder.expireAfterWrite(expireAfterWrite);
+ } else {
+ throw new ComponentInitializationException("Cache enabled, but expireAfterAccess/expireAfterWrite both null");
+ }
+ resultsCache = builder.build();
}
if (contextTemplateString == null) {
@@ -340,16 +402,15 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
// Check type.
final ClassCriterion<?> classCriterion = criteria.get(ClassCriterion.class);
if (classCriterion != null && !classCriterion.getClass().isAssignableFrom(credentialType)) {
- log.debug("{}: ClassCriterion specified {}, not compatible with this resolver", getId(),
+ log.debug("CredentialResolver {}: ClassCriterion ({}), not compatible with this resolver", getId(),
classCriterion.getClass());
return CollectionSupport.emptyList();
}
}
- // Suppress PRC if present but unsupported. This is to preserve cache fidelity.
final CriteriaSet manipulatedCriteria;
- if (!supportProfileRequestContextCriterion &&
- criteria != null && criteria.contains(ProfileRequestContextCriterion.class)) {
+ if (resultsCache != null && criteria != null && criteria.contains(ProfileRequestContextCriterion.class)) {
+ log.debug("CredentialResolver (): Cache enabled, suppressing ProfileRequestContextCriterion", getId());
manipulatedCriteria = new CriteriaSet();
criteria.forEach(c -> {
if (!(c instanceof ProfileRequestContextCriterion)) {
@@ -366,7 +427,7 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
if (localCache != null) {
final List<Credential> cached = localCache.getIfPresent(manipulatedCriteria);
if (cached != null) {
- log.debug("{}: Resolved {} credential(s) from cache", getId(), cached.size());
+ log.debug("CredentialResolver {}: Resolved {} credential(s) from cache", getId(), cached.size());
return CollectionSupport.copyToList(cached);
}
}
@@ -382,7 +443,7 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
throw new ResolverException("Error executing context template", e);
}
- log.debug("{}: Resolved storage context ({})", getId(), storageContext);
+ log.debug("CredentialResolver {}: Resolved storage context ({})", getId(), storageContext);
final Iterable<Credential> resolved = doResolve(ctx, manipulatedCriteria, storageContext);
@@ -393,7 +454,7 @@ public abstract class AbstractStorageServiceCredentialResolver<T extends Credent
// iteration is one-time only.
final List<Credential> toCache = new ArrayList<>();
resolved.forEach(toCache::add);
- log.debug("{}: Adding {} resolved credential(s) to cache", getId(), toCache.size());
+ log.debug("CredentialResolver {}: Adding {} resolved credential(s) to cache", getId(), toCache.size());
localCache.put(manipulatedCriteria, toCache);
return CollectionSupport.copyToList(toCache);
}
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/X509CredentialStorageServiceResolver.java b/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/X509CredentialStorageServiceResolver.java
index b89deea..f4e8e2f 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/X509CredentialStorageServiceResolver.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/X509CredentialStorageServiceResolver.java
@@ -171,7 +171,7 @@ public class X509CredentialStorageServiceResolver extends AbstractStorageService
final String keyStorage = privateKeyTemplate.merge(velocityContext);
final String certStorage = certificateTemplate.merge(velocityContext);
- log.debug("{}: Resolved storage keys for key ({}), certificate ({})", getId(), keyStorage, certStorage);
+ log.debug("CredentialResolver {}: Resolved storage keys for key ({}), certificate ({})", getId(), keyStorage, certStorage);
privateKeyData = getStorageService().read(storageContext, keyStorage);
certificateData = getStorageService().read(storageContext, certStorage);
@@ -192,7 +192,7 @@ public class X509CredentialStorageServiceResolver extends AbstractStorageService
return CollectionSupport.emptyList();
}
- log.debug("{}: Resolved data for key and/or certificate, constructing credential", getId());
+ log.debug("CredentialResolver {}: Resolved data for key and/or certificate, constructing credential", getId());
// Populate a factory bean from the resolved material.
@@ -219,7 +219,7 @@ public class X509CredentialStorageServiceResolver extends AbstractStorageService
factory.afterPropertiesSet();
final X509Credential credential = factory.getObject();
if (credential != null) {
- log.debug("{}: Resolved X509Credential for caller", getId());
+ log.debug("CredentialResolver {}: Resolved X509Credential for caller", getId());
return CollectionSupport.singletonList(credential);
}
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/credential/impl/X509CredentialStorageServiceResolverTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/credential/impl/X509CredentialStorageServiceResolverTest.java
index 47d16f6..2d18b49 100644
--- a/sp-server-impl/src/test/java/net/shibboleth/sp/credential/impl/X509CredentialStorageServiceResolverTest.java
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/credential/impl/X509CredentialStorageServiceResolverTest.java
@@ -39,8 +39,6 @@ import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
-import com.google.common.cache.CacheBuilder;
-
import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.resolver.CriteriaSet;
import net.shibboleth.shared.security.impl.SelfSignedCertificateGenerator;
@@ -229,15 +227,13 @@ public class X509CredentialStorageServiceResolverTest {
storage.setStorageBase(testRoot.toString());
storage.initialize();
- final CacheBuilder<Object, Object> builder = CacheBuilder.newBuilder();
- builder.expireAfterWrite(Duration.ofSeconds(10));
-
final X509CredentialStorageServiceResolver resolver = new X509CredentialStorageServiceResolver();
resolver.setId("test");
resolver.setStorageService(storage);
resolver.setVelocityEngine(VelocityEngine.newVelocityEngine());
resolver.setContextTemplate("agents/$agentID/$applicationID");
- resolver.setResultsCache(builder.build());
+ resolver.setCacheEnabled(true);
+ resolver.setExpireAfterAccess(Duration.ofSeconds(10));
resolver.initialize();
X509Credential credential = (X509Credential) resolver.resolveSingle(
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list