[java-plugin-shibd] branch main updated: Bug fixes amd unit tests for flat file Agent loader.
Codeberg
noreply at shibboleth.net
Tue Nov 3 23:57:20 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/3b140c0569472362e3725671295993248eef58bd
The following commit(s) were added to refs/heads/main by this push:
new 3b140c0 Bug fixes amd unit tests for flat file Agent loader.
3b140c0 is described below
commit 3b140c0569472362e3725671295993248eef58bd
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Tue Nov 3 18:57:06 2026 -0500
Bug fixes amd unit tests for flat file Agent loader.
---
.../sp/impl/AbstractExternalAgentResolver.java | 57 +++++++-
.../sp/impl/FlatFileExternalAgentResolver.java | 64 +++++++--
.../sp/impl/FlatFileExternalAgentResolverTest.java | 143 +++++++++++++++++++++
.../ResourceBackedMemoryStorageServiceTest.java | 3 +-
.../resources/net/shibboleth/sp/impl/empty.txt | 0
.../resources/net/shibboleth/sp/impl/flatfile.txt | 12 ++
6 files changed, 263 insertions(+), 16 deletions(-)
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/AbstractExternalAgentResolver.java b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/AbstractExternalAgentResolver.java
index 3a58d0a..edb20fb 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/AbstractExternalAgentResolver.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/AbstractExternalAgentResolver.java
@@ -43,6 +43,7 @@ import net.shibboleth.idp.attribute.resolver.AttributeResolver;
import net.shibboleth.idp.attribute.transcoding.AttributeTranscoderRegistry;
import net.shibboleth.profile.config.ProfileConfiguration;
import net.shibboleth.profile.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.profile.relyingparty.VerifiedProfileCriterion;
import net.shibboleth.shared.annotation.ParameterName;
import net.shibboleth.shared.annotation.constraint.NonNegative;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
@@ -179,6 +180,15 @@ public abstract class AbstractExternalAgentResolver extends AbstractIdentifiable
@Nonnull
private Function<ProfileRequestContext,Map<String,ProfileConfiguration>> profileConfigurationsLookupStrategy;
+ /** Unverified RP config. */
+ @Nullable private RelyingPartyConfiguration unverifiedRelyingPartyConfiguration;
+
+ /** RP config overrides. */
+ @Nonnull private Collection<RelyingPartyConfiguration> relyingPartyConfigurationOverrides;
+
+ /** Metrics name. */
+ @Nullable private String metricName;
+
/**
* Constructor.
*
@@ -210,6 +220,8 @@ public abstract class AbstractExternalAgentResolver extends AbstractIdentifiable
securityConfigurationLookupStrategy = FunctionSupport.constant(null);
profileConfigurationsLookupStrategy = FunctionSupport.constant(null);
+ relyingPartyConfigurationOverrides = CollectionSupport.emptyList();
+
}
/**
@@ -717,6 +729,44 @@ public abstract class AbstractExternalAgentResolver extends AbstractIdentifiable
profileConfigurationsLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
}
+ /**
+ * Set the verified relying party configurations.
+ *
+ * @param configs list of verified relying party configurations
+ */
+ public void setRelyingPartyConfigurations(@Nullable final Collection<? extends RelyingPartyConfiguration> configs) {
+ checkSetterPreconditions();
+
+ if (configs != null) {
+ relyingPartyConfigurationOverrides = CollectionSupport.copyToList(configs);
+ } else {
+ relyingPartyConfigurationOverrides = CollectionSupport.emptyList();
+ }
+ }
+
+ /**
+ * Set the {@link RelyingPartyConfiguration} to use if the configuration is found to be "unverified"
+ * (via use of {@link VerifiedProfileCriterion}).
+ *
+ * @param configuration unverified configuration
+ */
+ public void setUnverifiedConfiguration(@Nullable final RelyingPartyConfiguration configuration) {
+ checkSetterPreconditions();
+
+ unverifiedRelyingPartyConfiguration = configuration;
+ }
+
+ /**
+ * Set name of metric to use for counters to track use of configurations.
+ *
+ * @param name name for counter metrics
+ */
+ public void setMetricName(@Nullable final String name) {
+ checkSetterPreconditions();
+
+ metricName = StringSupport.trimOrNull(name);
+ }
+
/** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
@@ -834,6 +884,10 @@ public abstract class AbstractExternalAgentResolver extends AbstractIdentifiable
agent.setSecurityConfigurationLookupStrategy(securityConfigurationLookupStrategy);
agent.setProfileConfigurationsLookupStrategy(profileConfigurationsLookupStrategy);
+ agent.setUnverifiedConfiguration(unverifiedRelyingPartyConfiguration);
+ agent.setRelyingPartyConfigurations(relyingPartyConfigurationOverrides);
+ agent.setMetricName(metricName);
+
agent.initialize();
return agent;
@@ -902,8 +956,7 @@ public abstract class AbstractExternalAgentResolver extends AbstractIdentifiable
authority = StringSupport.trimOrNull(auth);
if (ranges != null) {
- addressRanges = new HashSet<>();
- ranges.stream()
+ addressRanges = ranges.stream()
.map(IPRange::parseCIDRBlock)
.collect(CollectionSupport.nonnullCollector(Collectors.toUnmodifiableSet())).get();
} else {
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/FlatFileExternalAgentResolver.java b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/FlatFileExternalAgentResolver.java
index a961c59..681b217 100644
--- a/sp-server-impl/src/main/java/net/shibboleth/sp/impl/FlatFileExternalAgentResolver.java
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/impl/FlatFileExternalAgentResolver.java
@@ -44,6 +44,7 @@ import net.shibboleth.shared.annotation.constraint.Unmodifiable;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
import net.shibboleth.shared.primitive.TimerSupport;
import net.shibboleth.shared.resolver.CriteriaSet;
import net.shibboleth.shared.resolver.ResolverException;
@@ -221,9 +222,11 @@ public class FlatFileExternalAgentResolver extends AbstractExternalAgentResolver
AgentTemplate template = null;
- final Lock readLock = lock.readLock();
+ final Lock readLock = lock != null ? lock.readLock() : null;
try {
- readLock.lock();
+ if (readLock != null) {
+ readLock.lock();
+ }
final AgentCredentialCriterion credentialCrit = criteria.get(AgentCredentialCriterion.class);
if (credentialCrit != null) {
@@ -237,7 +240,9 @@ public class FlatFileExternalAgentResolver extends AbstractExternalAgentResolver
}
}
} finally {
- readLock.unlock();
+ if (readLock != null) {
+ readLock.unlock();
+ }
}
return template;
@@ -319,7 +324,9 @@ public class FlatFileExternalAgentResolver extends AbstractExternalAgentResolver
if (line.isEmpty() || line.isBlank()) {
if (id != null) {
- theMap.put(id, new AgentTemplate(id, issuer, authority, ranges, usernames));
+ installMappings(theMap, new AgentTemplate(id, issuer, authority, ranges, usernames));
+ } else {
+ log.error("Ignoring agent definition with no '{}' field", ID_FIELD);
}
id = null;
@@ -331,22 +338,30 @@ public class FlatFileExternalAgentResolver extends AbstractExternalAgentResolver
} else if (line.startsWith("#")) {
continue;
} else if (line.startsWith(ID_FIELD)) {
- id = line.substring(ID_FIELD.length());
+ id = StringSupport.trimOrNull(line.substring(ID_FIELD.length()));
} else if (line.startsWith(ALLOWED_RANGE_FIELD)) {
- ranges.add(line.substring(ALLOWED_RANGE_FIELD.length()));
+ final String temp = StringSupport.trimOrNull(line.substring(ALLOWED_RANGE_FIELD.length()));
+ if (temp != null) {
+ ranges.add(temp);
+ }
} else if (line.startsWith(USERNAME_FIELD)) {
- usernames.add(line.substring(USERNAME_FIELD.length()));
+ final String temp = StringSupport.trimOrNull(line.substring(USERNAME_FIELD.length()));
+ if (temp != null) {
+ usernames.add(temp);
+ }
} else if (line.startsWith(ISSUER_FIELD)) {
- issuer = line.substring(ISSUER_FIELD.length());
+ issuer = StringSupport.trimOrNull(line.substring(ISSUER_FIELD.length()));
} else if (line.startsWith(AUTHORITY_FIELD)) {
- authority = line.substring(AUTHORITY_FIELD.length());
+ authority = StringSupport.trimOrNull(line.substring(AUTHORITY_FIELD.length()));
} else {
- log.warn("Invalid line ({}) found", lineno);
+ log.info("Ignoring unrecognized line ({})", lineno);
}
}
if (id != null) {
- theMap.put(id, new AgentTemplate(id, issuer, authority, ranges, usernames));
+ installMappings(theMap, new AgentTemplate(id, issuer, authority, ranges, usernames));
+ } else if (lineno > 0) {
+ log.error("Ignoring agent definition with no '{}' field", ID_FIELD);
}
}
@@ -354,4 +369,29 @@ public class FlatFileExternalAgentResolver extends AbstractExternalAgentResolver
return theMap;
}
-}
+ /**
+ * Install mappings while guarding against duplicates.
+ *
+ * @param map map of templates
+ * @param template template to add
+ */
+ private void installMappings(@Nonnull final Map<String,AgentTemplate> map, @Nonnull final AgentTemplate template) {
+
+ if (map.containsKey(template.getId())) {
+ log.warn("Ignoring duplicate mapping for Agent ID: {}", template.getId());
+ } else {
+ map.put(template.getId(), template);
+ }
+
+ for (final String altname : template.getUsernames()) {
+ if (altname != null) {
+ if (map.containsKey(altname)) {
+ log.warn("Ignoring duplicate mapping for Agent username: {}", altname);
+ } else {
+ map.put(altname, template);
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/impl/FlatFileExternalAgentResolverTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/impl/FlatFileExternalAgentResolverTest.java
new file mode 100644
index 0000000..7689f07
--- /dev/null
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/impl/FlatFileExternalAgentResolverTest.java
@@ -0,0 +1,143 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.sp.impl;
+
+import java.time.Duration;
+
+import javax.annotation.Nonnull;
+
+import org.springframework.core.io.ClassPathResource;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import net.shibboleth.profile.relyingparty.RelyingPartyConfiguration;
+import net.shibboleth.profile.relyingparty.VerifiedProfileCriterion;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.net.IPRange;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.ResolverException;
+import net.shibboleth.shared.resource.Resource;
+import net.shibboleth.shared.spring.resource.ResourceHelper;
+import net.shibboleth.shared.testing.MockReloadableService;
+import net.shibboleth.sp.Agent;
+import net.shibboleth.sp.Application;
+import net.shibboleth.sp.criteria.AgentCredentialCriterion;
+import net.shibboleth.sp.state.impl.PassthroughStateManager;
+
+/**
+ * Unit tests for {@link FlatFileExternalAgentResolver}.
+ */
+ at SuppressWarnings("javadoc")
+public class FlatFileExternalAgentResolverTest {
+
+ @Nonnull @NotEmpty private static final String DATA_PATH = "/net/shibboleth/sp/impl/";
+
+ @Test
+ public void testEmpty() throws ComponentInitializationException, ResolverException {
+
+ final var resolver = getResolver(ResourceHelper.of(new ClassPathResource(DATA_PATH + "empty.txt")));
+
+ Assert.assertNull(resolver.resolveSingle(null));
+ Assert.assertNull(resolver.resolveSingle(new CriteriaSet(new AgentCredentialCriterion("foo"))));
+ }
+
+ @Test
+ public void testFile() throws ComponentInitializationException, ResolverException {
+
+ final var resolver = getResolver(ResourceHelper.of(new ClassPathResource(DATA_PATH + "flatfile.txt")));
+
+ Assert.assertNull(resolver.resolveSingle(null));
+ Assert.assertNull(resolver.resolveSingle(new CriteriaSet(new AgentCredentialCriterion("zorkmid"))));
+
+ Agent agent = resolver.resolveSingle(new CriteriaSet(new AgentCredentialCriterion("foo")));
+ assert agent != null;
+ Assert.assertEquals(agent.getId(), "foo");
+ Assert.assertEquals(agent.getAuthenticationMethod(), Agent.AUTH_METHOD_BASIC);
+ Assert.assertTrue(agent.getAllowedAddressRanges().isEmpty());
+ Assert.assertTrue(agent.getCredentials().isEmpty());
+ Assert.assertFalse(agent.isSupportsCachedAuthentication());
+ Assert.assertTrue(agent.isSupportsPostPreservation());
+ Application app = agent.getApplication("default");
+ assert app != null;
+ Assert.assertNull(app.getAuthenticatingAuthority(null));
+ RelyingPartyConfiguration rpc = app.resolveSingle(new CriteriaSet(new VerifiedProfileCriterion(true)));
+ assert rpc != null;
+ Assert.assertNull(rpc.getIssuer(null));
+
+ agent = resolver.resolveSingle(new CriteriaSet(new AgentCredentialCriterion("bar")));
+ assert agent != null;
+ Assert.assertEquals(agent.getId(), "bar");
+ Assert.assertEquals(agent.getAuthenticationMethod(), Agent.AUTH_METHOD_BASIC);
+ Assert.assertTrue(agent.getAllowedAddressRanges().isEmpty());
+ Assert.assertTrue(agent.getCredentials().isEmpty());
+ Assert.assertFalse(agent.isSupportsCachedAuthentication());
+ Assert.assertTrue(agent.isSupportsPostPreservation());
+ app = agent.getApplication("default");
+ assert app != null;
+ Assert.assertEquals(app.getAuthenticatingAuthority(null), "https://idp.example.org");
+ rpc = app.resolveSingle(new CriteriaSet(new VerifiedProfileCriterion(true)));
+ assert rpc != null;
+ Assert.assertEquals(rpc.getIssuer(null), "https://bar.example.org");
+
+ agent = resolver.resolveSingle(new CriteriaSet(new AgentCredentialCriterion("sa-baz2")));
+ assert agent != null;
+ Assert.assertEquals(agent.getId(), "baz");
+ Assert.assertEquals(agent.getAuthenticationMethod(), Agent.AUTH_METHOD_BASIC);
+ Assert.assertEqualsNoOrder(agent.getAllowedAddressRanges(),
+ CollectionSupport.setOf(IPRange.parseCIDRBlock("127.0.0.1/32"), IPRange.parseCIDRBlock("192.168.0.0/16")));
+ Assert.assertEquals(agent.getCredentials().size(), 2);
+ Assert.assertFalse(agent.isSupportsCachedAuthentication());
+ Assert.assertTrue(agent.isSupportsPostPreservation());
+ app = agent.getApplication("default");
+ assert app != null;
+ Assert.assertNull(app.getAuthenticatingAuthority(null));
+ rpc = app.resolveSingle(new CriteriaSet(new VerifiedProfileCriterion(true)));
+ assert rpc != null;
+ Assert.assertNull(rpc.getIssuer(null));
+ }
+
+ /**
+ * Create a resolver with a specific resource.
+ *
+ * @param resource resource to load
+ *
+ * @return resolver to test
+ *
+ * @throws ComponentInitializationException
+ */
+ @Nonnull private FlatFileExternalAgentResolver getResolver(@Nonnull final Resource resource) throws ComponentInitializationException {
+ final var resolver = new FlatFileExternalAgentResolver(new ProtocolSupportServiceManager(null));
+
+ resolver.setId("test");
+ resolver.setResource(resource);
+ resolver.setReloadInterval(Duration.ZERO);
+
+ resolver.setAuthenticationMethod(Agent.AUTH_METHOD_BASIC);
+ resolver.setSupportsCachedAuthentication(false);
+ resolver.setSupportsPostPreservation(true);
+
+ resolver.setStateManager(new PassthroughStateManager());
+ resolver.setAttributeTranscoderRegistry(new MockReloadableService<>());
+ resolver.setAttributeResolver(new MockReloadableService<>());
+ resolver.setAttributeFilter(new MockReloadableService<>());
+
+ resolver.initialize();
+
+ return resolver;
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/test/java/net/shibboleth/sp/storage/impl/ResourceBackedMemoryStorageServiceTest.java b/sp-server-impl/src/test/java/net/shibboleth/sp/storage/impl/ResourceBackedMemoryStorageServiceTest.java
index 3b5acf5..f699b47 100644
--- a/sp-server-impl/src/test/java/net/shibboleth/sp/storage/impl/ResourceBackedMemoryStorageServiceTest.java
+++ b/sp-server-impl/src/test/java/net/shibboleth/sp/storage/impl/ResourceBackedMemoryStorageServiceTest.java
@@ -20,7 +20,6 @@ import javax.annotation.Nonnull;
import org.opensaml.storage.StorageRecord;
import org.opensaml.storage.StorageService;
-import org.opensaml.storage.VersionMismatchException;
import org.springframework.core.io.ClassPathResource;
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
@@ -32,7 +31,7 @@ import net.shibboleth.shared.resource.Resource;
import net.shibboleth.shared.spring.resource.ResourceHelper;
/**
- * Unit tests for {@link FilesystemStorageService}.
+ * Unit tests for {@link ResourceBackedMemoryStorageService}.
*/
@SuppressWarnings("javadoc")
public class ResourceBackedMemoryStorageServiceTest {
diff --git a/sp-server-impl/src/test/resources/net/shibboleth/sp/impl/empty.txt b/sp-server-impl/src/test/resources/net/shibboleth/sp/impl/empty.txt
new file mode 100644
index 0000000..e69de29
diff --git a/sp-server-impl/src/test/resources/net/shibboleth/sp/impl/flatfile.txt b/sp-server-impl/src/test/resources/net/shibboleth/sp/impl/flatfile.txt
new file mode 100644
index 0000000..e72c67b
--- /dev/null
+++ b/sp-server-impl/src/test/resources/net/shibboleth/sp/impl/flatfile.txt
@@ -0,0 +1,12 @@
+# Comment
+id: foo
+
+id: bar
+issuer: https://bar.example.org
+authority: https://idp.example.org
+
+id: baz
+allowedRange: 127.0.0.1/32
+allowedRange: 192.168.0.0/16
+username: sa-baz
+username: sa-baz2
\ 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