[java-plugin-shibd] branch main updated: Initial commit of new code for CredentialResolver service.
Codeberg
noreply at shibboleth.net
Thu Jul 23 15:57:13 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/a013482b395489b3fad291d84304a8c24afb957d
The following commit(s) were added to refs/heads/main by this push:
new a013482 Initial commit of new code for CredentialResolver service.
a013482 is described below
commit a013482b395489b3fad291d84304a8c24afb957d
Author: Scott Cantor <scott at restingparrotsoftware.com>
AuthorDate: Thu Jul 23 11:56:58 2026 -0400
Initial commit of new code for CredentialResolver service.
---
.../shibboleth/sp/AgentCredentialCriterion.java | 4 +-
...dentialCriterion.java => AgentIDCriterion.java} | 18 +-
...lCriterion.java => ApplicationIDCriterion.java} | 18 +-
.../AbstractOrderedCredentialResolver.java | 190 +++++++++++++++++++++
.../sp/credential/OrderedCredentialResolver.java | 26 +++
sp-server-impl/pom.xml | 11 ++
.../credential/impl/CredentialResolverBridge.java | 161 +++++++++++++++++
.../credential/impl/StaticCredentialResolver.java | 58 +++++++
.../impl/StaticCriterionCredentialResolver.java | 85 +++++++++
.../impl/BasicHttpClientSecurityConfiguration.java | 107 ++++++++++++
10 files changed, 658 insertions(+), 20 deletions(-)
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java b/sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java
index efe19f4..34a72eb 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java
@@ -22,11 +22,11 @@ import net.shibboleth.shared.primitive.StringSupport;
import net.shibboleth.shared.resolver.Criterion;
/**
- * Resolver {@link Criterion} identifying an agent by means of a "credential" name.
+ * Resolver {@link Criterion} identifying an {@link Agent} by means of a "credential" name.
*/
public class AgentCredentialCriterion implements Criterion {
- /** Application ID. */
+ /** Agent credential ID/Username. */
@Nonnull @NotEmpty final String credentialId;
/**
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java b/sp-server-api/src/main/java/net/shibboleth/sp/AgentIDCriterion.java
similarity index 65%
copy from sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java
copy to sp-server-api/src/main/java/net/shibboleth/sp/AgentIDCriterion.java
index efe19f4..35f6a86 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/AgentIDCriterion.java
@@ -22,29 +22,29 @@ import net.shibboleth.shared.primitive.StringSupport;
import net.shibboleth.shared.resolver.Criterion;
/**
- * Resolver {@link Criterion} identifying an agent by means of a "credential" name.
+ * Resolver {@link Criterion} identifying an {@link Agent} by means of its unique ID.
*/
-public class AgentCredentialCriterion implements Criterion {
+public class AgentIDCriterion implements Criterion {
- /** Application ID. */
- @Nonnull @NotEmpty final String credentialId;
+ /** Agent ID. */
+ @Nonnull @NotEmpty final String agentId;
/**
* Constructor.
*
* @param id agent ID
*/
- public AgentCredentialCriterion(@Nonnull @NotEmpty final String id) {
- credentialId = Constraint.isNotNull(StringSupport.trimOrNull(id), "Agent credential name cannot be null or empty");
+ public AgentIDCriterion(@Nonnull @NotEmpty final String id) {
+ agentId = Constraint.isNotNull(StringSupport.trimOrNull(id), "Agent ID cannot be null or empty");
}
/**
- * Get the agent credential ID specified.
+ * Get the agent ID specified.
*
- * @return agent credential ID
+ * @return agent ID
*/
@Nonnull @NotEmpty public String getId() {
- return credentialId;
+ return agentId;
}
}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java b/sp-server-api/src/main/java/net/shibboleth/sp/ApplicationIDCriterion.java
similarity index 65%
copy from sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java
copy to sp-server-api/src/main/java/net/shibboleth/sp/ApplicationIDCriterion.java
index efe19f4..d4dcc10 100644
--- a/sp-server-api/src/main/java/net/shibboleth/sp/AgentCredentialCriterion.java
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/ApplicationIDCriterion.java
@@ -22,29 +22,29 @@ import net.shibboleth.shared.primitive.StringSupport;
import net.shibboleth.shared.resolver.Criterion;
/**
- * Resolver {@link Criterion} identifying an agent by means of a "credential" name.
+ * Resolver {@link Criterion} identifying an {@link Application} by means of its {@link Agent}-specific ID.
*/
-public class AgentCredentialCriterion implements Criterion {
+public class ApplicationIDCriterion implements Criterion {
/** Application ID. */
- @Nonnull @NotEmpty final String credentialId;
+ @Nonnull @NotEmpty final String applicationId;
/**
* Constructor.
*
- * @param id agent ID
+ * @param id application ID
*/
- public AgentCredentialCriterion(@Nonnull @NotEmpty final String id) {
- credentialId = Constraint.isNotNull(StringSupport.trimOrNull(id), "Agent credential name cannot be null or empty");
+ public ApplicationIDCriterion(@Nonnull @NotEmpty final String id) {
+ applicationId = Constraint.isNotNull(StringSupport.trimOrNull(id), "Application ID cannot be null or empty");
}
/**
- * Get the agent credential ID specified.
+ * Get the application ID specified.
*
- * @return agent credential ID
+ * @return application ID
*/
@Nonnull @NotEmpty public String getId() {
- return credentialId;
+ return applicationId;
}
}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/credential/AbstractOrderedCredentialResolver.java b/sp-server-api/src/main/java/net/shibboleth/sp/credential/AbstractOrderedCredentialResolver.java
new file mode 100644
index 0000000..bffe302
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/credential/AbstractOrderedCredentialResolver.java
@@ -0,0 +1,190 @@
+/*
+ * 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.credential;
+
+import java.util.Collection;
+import java.util.Set;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.saml.criterion.ProtocolCriterion;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.slf4j.Logger;
+import org.springframework.core.Ordered;
+
+import com.google.common.collect.Iterables;
+
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.ResolverException;
+
+/**
+ * Simple base class for resolvers to supply an {@link Ordered} implementation if they don't
+ * need a dedicated base class.
+ *
+ * <p>Additional "value-adds" include the component handling, the resolveSingle method, and
+ * built-in support for {@link UsageCriterion} and {@link ProtocolCriterion} filtering.</p>
+ */
+public abstract class AbstractOrderedCredentialResolver extends AbstractIdentifiableInitializableComponent
+ implements OrderedCredentialResolver {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(AbstractOrderedCredentialResolver.class);
+
+ /** Order. */
+ private int order;
+
+ /** Whether to apply usage filteing. */
+ private boolean filterByUsage;
+
+ /** Protocol values supported by these credentials. */
+ private Set<String> supportedProtocols;
+
+ /** {@inheritDoc} */
+ public int getOrder() {
+ return order;
+ }
+
+ /**
+ * Set component order.
+ *
+ * @param i order value
+ */
+ public void setOrder(final int i) {
+ checkSetterPreconditions();
+ order = i;
+ }
+
+ /**
+ * Sets whether to apply {@link UsageCriterion} if supplied to the results.
+ *
+ * <p>Defaults to true.</p>
+ *
+ * @param flag
+ */
+ public void setFilterByUsage(final boolean flag) {
+ checkSetterPreconditions();
+ filterByUsage = true;
+ }
+
+ /**
+ * Sets the protocol values supported by this resolver.
+ *
+ * <p>If set, any {@link ProtocolCriterion} will be enforced apriori before returning any credentials.</p>
+ *
+ * @param protocols
+ */
+ public void setProtocols(@Nullable final Collection<String> protocols) {
+ checkSetterPreconditions();
+ if (protocols != null) {
+ supportedProtocols = CollectionSupport.copyToSet(StringSupport.normalizeStringCollection(protocols));
+ } else {
+ supportedProtocols = CollectionSupport.emptySet();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Nullable public Credential resolveSingle(@Nullable final CriteriaSet criteriaSet) throws ResolverException {
+ final Iterable<Credential> creds = resolve(criteriaSet);
+ if (creds.iterator().hasNext()) {
+ return creds.iterator().next();
+ }
+ return null;
+ }
+
+ /** {@inheritDoc} */
+ @Nonnull public Iterable<Credential> resolve(@Nullable final CriteriaSet criteria) throws ResolverException {
+ checkComponentActive();
+
+ // Check for protocol support.
+ final ProtocolCriterion protocolCriterion = criteria != null ? criteria.get(ProtocolCriterion.class) : null;
+ if (protocolCriterion != null && !supportedProtocols.contains(protocolCriterion.getProtocol())) {
+ log.debug("CredentialResolver {}: Unsupported protocol ({}), filtering out all credentials", getId(),
+ protocolCriterion.getProtocol());
+ return CollectionSupport.emptyList();
+ }
+
+ final Iterable<Credential> creds = doResolve(criteria);
+
+ if (!filterByUsage) {
+ return creds;
+ }
+
+ final UsageCriterion usage = criteria != null ? criteria.get(UsageCriterion.class) : null;
+ if (usage == null) {
+ return creds;
+ }
+
+ log.debug("CredentialResolver {}: Filtering results by usage ({})", getId(), usage.getUsage());
+
+ final UsageCriterionPredicate condition = new UsageCriterionPredicate(usage.getUsage());
+ return Iterables.filter(creds, condition::test);
+ }
+
+ /**
+ * Subclasses implement this method to resolve the underlying objects.
+ *
+ * @param criteria criteria for resolution
+ *
+ * @return resolved credential iterator
+ *
+ * @throws ResolverException if an error occurs
+ */
+ @Nonnull public abstract Iterable<Credential> doResolve(@Nullable final CriteriaSet criteria)
+ throws ResolverException;
+
+ /**
+ * This is more or less a duplicate of an OpenSAML implementation class to filter by usage.
+ */
+ public static class UsageCriterionPredicate implements Predicate<Credential> {
+
+ /** Usage to check for. */
+ @Nonnull UsageType usageType;
+
+ /**
+ * Constructor.
+ *
+ * @param type usage type tp filter against
+ */
+ public UsageCriterionPredicate(@Nonnull final UsageType type) {
+ usageType = Constraint.isNotNull(type, "Usage type cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ public boolean test(@Nullable final Credential input) {
+ if (input == null) {
+ return false;
+ }
+
+ final UsageType credUsage = input.getUsageType();
+
+ if (credUsage == null || UsageType.UNSPECIFIED.equals(credUsage) || UsageType.UNSPECIFIED.equals(usageType)) {
+ return true;
+ }
+
+ return usageType.equals(credUsage);
+ }
+
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-api/src/main/java/net/shibboleth/sp/credential/OrderedCredentialResolver.java b/sp-server-api/src/main/java/net/shibboleth/sp/credential/OrderedCredentialResolver.java
new file mode 100644
index 0000000..55a8a69
--- /dev/null
+++ b/sp-server-api/src/main/java/net/shibboleth/sp/credential/OrderedCredentialResolver.java
@@ -0,0 +1,26 @@
+/*
+ * 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.credential;
+
+import org.opensaml.security.credential.CredentialResolver;
+import org.springframework.core.Ordered;
+
+/**
+ * Marker interface for a {@link CredentialResolver} providing control over order
+ * during Spring auto-wiring.
+ */
+public interface OrderedCredentialResolver extends CredentialResolver, Ordered {
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/pom.xml b/sp-server-impl/pom.xml
index 49f4585..05cd9b2 100644
--- a/sp-server-impl/pom.xml
+++ b/sp-server-impl/pom.xml
@@ -91,6 +91,17 @@
<artifactId>opensaml-profile-impl</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>${opensaml.groupId}</groupId>
+ <artifactId>opensaml-security-api</artifactId>
+ <scope>provided</scope>
+ </dependency>
+ <!-- TODO: get various classes moved into -api -->
+ <dependency>
+ <groupId>${opensaml.groupId}</groupId>
+ <artifactId>opensaml-security-impl</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>${opensaml.groupId}</groupId>
<artifactId>opensaml-storage-api</artifactId>
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/CredentialResolverBridge.java b/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/CredentialResolverBridge.java
new file mode 100644
index 0000000..254a1fb
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/CredentialResolverBridge.java
@@ -0,0 +1,161 @@
+/*
+ * 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.credential.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.core.criterion.EntityIdCriterion;
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.opensaml.profile.criterion.ProfileRequestContextCriterion;
+import org.opensaml.saml.criterion.ProtocolCriterion;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.slf4j.Logger;
+
+import jakarta.servlet.ServletRequest;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.NonnullSupplier;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.ResolverException;
+import net.shibboleth.shared.service.ReloadableService;
+import net.shibboleth.shared.service.ServiceableComponent;
+import net.shibboleth.sp.Agent;
+import net.shibboleth.sp.AgentIDCriterion;
+import net.shibboleth.sp.Application;
+import net.shibboleth.sp.ApplicationIDCriterion;
+import net.shibboleth.sp.context.AgentRequestContext;
+
+/**
+ * Support object for bridging legacy OpenSAML interfaces to new credential resolver service.
+ *
+ * <p>This is hackery along the lines of the original IdP transition to accepting the
+ * {@link ProfileRequestContext} into the getters aross much of the configuration by
+ * obtaining it "under the covers" from the {@link ServletRequest}, which is itself
+ * accessed implicitly via our epic filter hackery. It's hackery turtles!</p>
+ */
+public class CredentialResolverBridge implements CredentialResolver {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(CredentialResolverBridge.class);
+
+ /** Credential resolver service. */
+ @Nonnull private final ReloadableService<CredentialResolver> credentialResolver;
+
+ /** Access to servlet request. */
+ @Nonnull private final NonnullSupplier<ServletRequest> servletRequestSupplier;
+
+ /**
+ * Constructor.
+ *
+ * @param resolver credential resolver service
+ * @param requestSupplier supplier for {@link ServletRequest}
+ */
+ public CredentialResolverBridge(
+ @Nonnull @ParameterName(name="resolver") final ReloadableService<CredentialResolver> resolver,
+ @Nonnull @ParameterName(name="requestSupplier") final NonnullSupplier<ServletRequest> requestSupplier) {
+ credentialResolver = Constraint.isNotNull(resolver, "CredentialResolver cannot be null");
+ servletRequestSupplier = Constraint.isNotNull(requestSupplier, "ServletRequest supplier cannot be null");
+ }
+
+ /**
+ * {@inheritDoc}
+ *
+ * <p>In addition to the input, additional criteria will be added based on the existance of an active
+ * {@link ProfileRequestContext} and any state located via that object such as the active Agent, Application,
+ * RelyingParty identity, etc.</p>
+ *
+ * <p>Typically the caller is expected to supply {@link UsageCriterion} and {@link ProtocolCriterion} if desired
+ * to limit what sorts of credentials will be resolved based on the caller's use case, and of course may
+ * supply others.</p>
+ */
+ @Nonnull public Iterable<Credential> resolve(@Nullable final CriteriaSet criteriaSet) throws ResolverException {
+ try (final ServiceableComponent<CredentialResolver> component = credentialResolver.getServiceableComponent()) {
+ return component.getComponent().resolve(buildCriteriaSet(criteriaSet));
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Nullable public Credential resolveSingle(@Nullable final CriteriaSet criteriaSet) throws ResolverException {
+ final Iterable<Credential> creds = resolve(criteriaSet);
+ if (creds.iterator().hasNext()) {
+ return creds.iterator().next();
+ }
+ return null;
+ }
+
+ /**
+ * Add or construct final {@link CriteriaSet} to supply to resolver.
+ *
+ * @param criteria existing criteria to add to, if any
+ *
+ * @return the final set, if any
+ */
+ @Nullable private CriteriaSet buildCriteriaSet(@Nullable final CriteriaSet criteria) {
+
+ final ProfileRequestContext prc = getProfileRequestContext();
+ if (prc == null) {
+ log.debug("CredentialResolverBridge found no ProfileRequestContext to process");
+ return criteria;
+ }
+
+ final CriteriaSet finalCriteria = criteria != null ? criteria : new CriteriaSet();
+ finalCriteria.add(new ProfileRequestContextCriterion(prc));
+
+ // Add Agent/Application info.
+ final AgentRequestContext arc = prc.getSubcontext(AgentRequestContext.class);
+ if (arc != null) {
+ final Agent agent = arc.getAgent();
+ if (agent != null) {
+ final String id = agent.getId();
+ if (id != null) {
+ finalCriteria.add(new AgentIDCriterion(id));
+ }
+ }
+ final Application app = arc.getApplication();
+ if (app != null) {
+ finalCriteria.add(new ApplicationIDCriterion(app.getApplicationId()));
+ }
+ }
+
+ final RelyingPartyContext rpc = prc.getSubcontext(RelyingPartyContext.class);
+ if (rpc != null) {
+ final String rpid = rpc.getRelyingPartyId();
+ if (rpid != null) {
+ finalCriteria.add(new EntityIdCriterion(rpid));
+ }
+ }
+
+ return finalCriteria;
+ }
+
+ /**
+ * Get the current {@link ProfileRequestContext}.
+ *
+ * @return current profile request context or null
+ */
+ @Nullable private ProfileRequestContext getProfileRequestContext() {
+ if (servletRequestSupplier.get().getAttribute(ProfileRequestContext.BINDING_KEY)
+ instanceof ProfileRequestContext prc) {
+ return prc;
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/StaticCredentialResolver.java b/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/StaticCredentialResolver.java
new file mode 100644
index 0000000..95643af
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/StaticCredentialResolver.java
@@ -0,0 +1,58 @@
+/*
+ * 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.credential.impl;
+
+import java.util.List;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.ResolverException;
+import net.shibboleth.sp.credential.AbstractOrderedCredentialResolver;
+
+/**
+ * A static resolver that subclasses the functionality needed for SP usage.
+ */
+public class StaticCredentialResolver extends AbstractOrderedCredentialResolver {
+
+ /** List of credentials held by this resolver. */
+ @Nonnull private final List<Credential> creds;
+
+ /**
+ * Constructor.
+ *
+ * @param credentials static credentials
+ */
+ public StaticCredentialResolver(@Nonnull @ParameterName(name="credentials") final List<Credential> credentials) {
+ Constraint.isNotNull(credentials, "Input credentials list cannot be null");
+
+ creds = CollectionSupport.copyToList(credentials);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull @NotLive @Unmodifiable public Iterable<Credential> doResolve(@Nullable final CriteriaSet criteria) throws ResolverException {
+ return creds;
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/StaticCriterionCredentialResolver.java b/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/StaticCriterionCredentialResolver.java
new file mode 100644
index 0000000..e3a8a35
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/credential/impl/StaticCriterionCredentialResolver.java
@@ -0,0 +1,85 @@
+/*
+ * 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.credential.impl;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCredentialResolver;
+import org.opensaml.xmlsec.keyinfo.KeyInfoCriterion;
+
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.Criterion;
+import net.shibboleth.shared.resolver.ResolverException;
+
+/**
+ * A resolver that adds statically-defined {@link Criterion} objects to the {@link CriteriaSet}
+ * fed into an underlying resolver.
+ *
+ * <p>It exports the additional {@link KeyInfoCredentialResolver} interface so it can be used in that
+ * context, but does not itself process a {@link KeyInfoCriterion}.</p>
+ */
+public class StaticCriterionCredentialResolver implements KeyInfoCredentialResolver {
+
+ /** List of credentials held by this resolver. */
+ @Nonnull private final CredentialResolver credentialResolver;
+
+ /** Static criteria to add. */
+ @Nonnull private final Collection<Criterion> staticCriteria;
+
+ /**
+ * Constructor.
+ *
+ * @param resolver underlying resolver
+ * @param criteria static criteria to add to all resolutions
+ */
+ public StaticCriterionCredentialResolver(@Nonnull @ParameterName(name="resolver") final CredentialResolver resolver,
+ @Nullable @ParameterName(name="criteria") final Collection<Criterion> criteria) {
+ credentialResolver = Constraint.isNotNull(resolver, "CredentialResolver cannot be null");
+ if (criteria != null) {
+ staticCriteria = CollectionSupport.copyToSet(criteria);
+ } else {
+ staticCriteria = CollectionSupport.emptySet();
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nonnull @NotLive @Unmodifiable public Iterable<Credential> resolve(@Nullable final CriteriaSet criteriaSet) throws ResolverException {
+ final CriteriaSet finalCriteria = criteriaSet != null ? criteriaSet : new CriteriaSet();
+ finalCriteria.addAll(staticCriteria);
+ return credentialResolver.resolve(finalCriteria);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public Credential resolveSingle(@Nullable final CriteriaSet criteriaSet) throws ResolverException {
+ final Iterable<Credential> creds = resolve(criteriaSet);
+ if (creds.iterator().hasNext()) {
+ return creds.iterator().next();
+ }
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/sp-server-impl/src/main/java/net/shibboleth/sp/security/config/impl/BasicHttpClientSecurityConfiguration.java b/sp-server-impl/src/main/java/net/shibboleth/sp/security/config/impl/BasicHttpClientSecurityConfiguration.java
new file mode 100644
index 0000000..7fec068
--- /dev/null
+++ b/sp-server-impl/src/main/java/net/shibboleth/sp/security/config/impl/BasicHttpClientSecurityConfiguration.java
@@ -0,0 +1,107 @@
+/*
+ * 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.security.config.impl;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.saml.criterion.ProtocolCriterion;
+import org.opensaml.security.credential.Credential;
+import org.opensaml.security.credential.CredentialResolver;
+import org.opensaml.security.credential.UsageType;
+import org.opensaml.security.criteria.UsageCriterion;
+import org.opensaml.security.x509.X509Credential;
+import org.slf4j.Logger;
+
+import net.shibboleth.shared.annotation.ParameterName;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.resolver.ClassCriterion;
+import net.shibboleth.shared.resolver.CriteriaSet;
+import net.shibboleth.shared.resolver.ResolverException;
+
+/**
+ * Subclass of OpenSAML version to provide support for CredentialResolver service
+ * through a bridging class.
+ */
+public class BasicHttpClientSecurityConfiguration
+ extends org.opensaml.security.httpclient.impl.BasicHttpClientSecurityConfiguration {
+
+ /** Class logger. */
+ @Nonnull private final Logger log = LoggerFactory.getLogger(BasicHttpClientSecurityConfiguration.class);
+
+ /** Bridge to resolve credentials. */
+ @Nonnull private final CredentialResolver credentialResolver;
+
+ /** Protocol designation. */
+ @Nullable private String protocol;
+
+ /**
+ * Constructor.
+ *
+ * @param resolver a credential resolver service to use
+ */
+ public BasicHttpClientSecurityConfiguration(
+ @Nonnull @ParameterName(name="resolver") final CredentialResolver resolver) {
+ credentialResolver = Constraint.isNotNull(resolver, "CredentialResolver cannot be null");
+ }
+
+ /**
+ * Sets the protocol designation for credential resolution, if any.
+ *
+ * @param proto protocol support value
+ */
+ public void setProtocol(@Nullable final String proto) {
+ protocol = StringSupport.trimOrNull(protocol);
+ }
+
+ /**
+ * Get the optional client TLS credential.
+ *
+ * @return the client TLS credential, or null
+ */
+ @Nullable public X509Credential getClientTLSCredential() {
+ final X509Credential hardwired = super.getClientTLSCredential();
+ if (hardwired != null) {
+ return hardwired;
+ }
+
+ // Resolve if possible using:
+ // * UsageType = SIGNING
+ // * Class = X509Credential
+ // * Protocol = if set
+
+ final CriteriaSet criteria =
+ new CriteriaSet(new UsageCriterion(UsageType.SIGNING), new ClassCriterion<>(X509Credential.class));
+ if (protocol != null) {
+ criteria.add(new ProtocolCriterion(protocol));
+ }
+
+ try {
+ final Credential cred = credentialResolver.resolveSingle(criteria);
+ if (cred instanceof X509Credential x509) {
+ return x509;
+ }
+
+ log.warn("Non-X509Credential returned, ignoring it");
+ } catch (final ResolverException e) {
+ log.error("Exception resolving X509Credential for client TLS", e);
+ }
+
+ return null;
+ }
+
+}
\ 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