[java-idp-plugin-oidc-op-oidfed] branch main updated: Move resolve-entity flow and its dependencies into oidfed-common
Codeberg
noreply at shibboleth.net
Tue Sep 22 15:22:42 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-idp-plugin-oidc-op-oidfed.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-oidc-op-oidfed/commit/fa315923d040ebf6fcff0c5a1dcaf25eeff40f92
The following commit(s) were added to refs/heads/main by this push:
new fa31592 Move resolve-entity flow and its dependencies into oidfed-common
fa31592 is described below
commit fa315923d040ebf6fcff0c5a1dcaf25eeff40f92
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Tue Sep 22 18:22:03 2026 +0300
Move resolve-entity flow and its dependencies into oidfed-common
---
.../context/RelyingPartyConfigurationSupport.java | 61 ----
.../context/logic/TrustAnchorIdPredicate.java | 97 ------
.../navigate/TrustAnchorIdLookupFunction.java | 69 ----
idp-oidfed-op-impl/pom.xml | 5 +
.../decoding/impl/ResolveEntityRequestDecoder.java | 113 -------
.../BuildResolveEntityErrorResponseFromEvent.java | 270 ---------------
.../profile/impl/BuildResolveEntityResponse.java | 204 -----------
.../impl/FormOutboundResolveEntityResponse.java | 230 -------------
.../impl/LookupCachedResolveEntityResponse.java | 157 ---------
.../oidfed/profile/impl/OidFederationEventIds.java | 84 -----
.../profile/impl/ValidateProvidedTrustChain.java | 1 +
.../profile/impl/ValidateResolveEntityRequest.java | 186 -----------
.../profile/impl/ValidateSelectedTrustChain.java | 231 -------------
.../navigate/DefaultEntityTypesLookupFunction.java | 46 ---
.../META-INF/net.shibboleth.idp/postconfig.xml | 48 ---
.../oidfed/resolve-entity/resolve-entity-beans.xml | 218 ------------
.../oidfed/resolve-entity/resolve-entity-flow.xml | 112 -------
.../idp/service/relying-party/postconfig.xml | 11 -
.../op/oidfed/conf/oidfed/oidfed-op.properties | 22 --
.../profile/flow/oidfed/ResolveEntityFlowTest.java | 371 ---------------------
.../shibboleth/idp/module/conf/relying-party.xml | 10 -
21 files changed, 6 insertions(+), 2540 deletions(-)
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/RelyingPartyConfigurationSupport.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/RelyingPartyConfigurationSupport.java
deleted file mode 100644
index 8d246c5..0000000
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/RelyingPartyConfigurationSupport.java
+++ /dev/null
@@ -1,61 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.context;
-
-import java.util.Collection;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.logic.TrustAnchorIdPredicate;
-import net.shibboleth.profile.relyingparty.BasicRelyingPartyConfiguration;
-import net.shibboleth.profile.relyingparty.RelyingPartyConfiguration;
-import net.shibboleth.shared.logic.Constraint;
-
-/**
- * Support functions for building {@link RelyingPartyConfiguration} objects with activation conditions.
- */
-public class RelyingPartyConfigurationSupport {
-
- /**
- * A shorthand method for constructing a {@link BasicRelyingPartyConfiguration} with an activation condition
- * based on one or more trust anchor IDs.
- *
- * <p>If a single ID is supplied, then the ID is also set as the identifier for the configuration.</p>
- *
- * @param trustAnchorIds the trust anchors for which the configuration should be active
- *
- * @return a default-constructed configuration with the appropriate condition set
- */
- @Nonnull
- public static BasicRelyingPartyConfiguration byTrustAnchor(@Nonnull final Collection<String> trustAnchorIds) {
-
- Constraint.isNotNull(trustAnchorIds, "Trust Anchor ID list cannot be null");
-
- final BasicRelyingPartyConfiguration config = new BasicRelyingPartyConfiguration();
- config.setActivationCondition(new TrustAnchorIdPredicate(trustAnchorIds));
-
- final StringBuffer name = new StringBuffer("TrustAnchorIDs[");
- for (final String taId: trustAnchorIds) {
- name.append(taId).append(',');
-
- }
- name.append(']');
- final String id = name.toString();
- assert id != null;
- config.setId(id);
- return config;
- }
-
-}
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/logic/TrustAnchorIdPredicate.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/logic/TrustAnchorIdPredicate.java
deleted file mode 100644
index 020b197..0000000
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/logic/TrustAnchorIdPredicate.java
+++ /dev/null
@@ -1,97 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.context.logic;
-
-import java.util.Collection;
-import java.util.function.Predicate;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.navigate.TrustAnchorIdLookupFunction;
-import net.shibboleth.shared.annotation.ParameterName;
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.logic.StrategyIndirectedPredicate;
-import net.shibboleth.shared.primitive.StringSupport;
-
-/**
- * Predicate that evaluates a {@link ProfileRequestContext} by looking for a trust anchor ID that matches one of a
- * designated set, or a generic predicate.
- */
-public class TrustAnchorIdPredicate extends StrategyIndirectedPredicate<ProfileRequestContext,String> {
-
- /**
- * Constructor.
- *
- * @param candidates hardwired set of values to check against
- */
- public TrustAnchorIdPredicate(@Nonnull @ParameterName(name="candidates") final Collection<String> candidates) {
- super(new TrustAnchorIdLookupFunction(), StringSupport.normalizeStringCollection(candidates));
- }
-
- /**
- * Constructor.
- *
- * @param candidate a single value to check against
- */
- public TrustAnchorIdPredicate(@Nonnull @NotEmpty @ParameterName(name="candidate") final String candidate) {
- this(CollectionSupport.singleton(candidate));
- }
-
- /**
- * Constructor.
- *
- * @param pred generalized predicate
- */
- public TrustAnchorIdPredicate(@Nonnull @ParameterName(name="pred") final Predicate<String> pred) {
- super(new TrustAnchorIdLookupFunction(), pred);
- }
-
- /**
- * Workaround for Spring type conversion ambiguities.
- *
- * @param candidates hardwired set of values to check against
- *
- * @return the predicate
- */
- @Nonnull public static TrustAnchorIdPredicate fromCandidates(@Nonnull final Collection<String> candidates) {
- return new TrustAnchorIdPredicate(candidates);
- }
-
- /**
- * Workaround for Spring type conversion ambiguities.
- *
- * @param candidate a single value to check against
- *
- * @return the predicate
- */
- @Nonnull public static TrustAnchorIdPredicate fromCandidate(@Nonnull @NotEmpty final String candidate) {
- return new TrustAnchorIdPredicate(candidate);
- }
-
- /**
- * Workaround for Spring type conversion ambiguities.
- *
- * @param pred generalized predicate
- *
- * @return the predicate
- */
- @Nonnull public static TrustAnchorIdPredicate fromPredicate(@Nonnull final Predicate<String> pred) {
- return new TrustAnchorIdPredicate(pred);
- }
-
-}
diff --git a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/navigate/TrustAnchorIdLookupFunction.java b/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/navigate/TrustAnchorIdLookupFunction.java
deleted file mode 100644
index b346e11..0000000
--- a/idp-oidfed-op-api/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/context/navigate/TrustAnchorIdLookupFunction.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.context.navigate;
-
-import java.util.Optional;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-import javax.annotation.concurrent.ThreadSafe;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.idp.plugin.oidc.op.profile.context.navigate.DefaultOIDCMetadataContextLookupFunction;
-import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
-import net.shibboleth.oidfed.support.ClientInformationExtensionSupport;
-import net.shibboleth.shared.logic.Constraint;
-
-/**
- * A function that returns {@link ClientInformationExtensionSupport#KEY_VALIDATED_TRUST_ANCHOR} if found from the
- * client information resolved via {@link OIDCMetadataContext}.
- *
- * <p>If a specific setting is unavailable, a null value is returned.</p>
- */
- at ThreadSafe
-public class TrustAnchorIdLookupFunction implements Function<ProfileRequestContext, String> {
-
- /** Strategy used to lookup the OIDC metadata context. */
- @Nonnull private Function<ProfileRequestContext, OIDCMetadataContext> oidcMetadataContextLookupStrategy;
-
- /**
- * Constructor.
- */
- public TrustAnchorIdLookupFunction() {
- oidcMetadataContextLookupStrategy = new DefaultOIDCMetadataContextLookupFunction();
- }
-
- /**
- * Constructor.
- *
- * @param oidcMetadataStrategy strategy used to lookup the OIDC metadata context
- */
- public TrustAnchorIdLookupFunction(
- @Nonnull final Function<ProfileRequestContext, OIDCMetadataContext> oidcMetadataStrategy) {
- oidcMetadataContextLookupStrategy = Constraint.isNotNull(oidcMetadataStrategy,
- "OIDC metadata context lookup strategy cannot be null");
- }
-
- /** {@inheritDoc} */
- @Nullable public String apply(@Nullable final ProfileRequestContext profileRequestContext) {
- return Optional.ofNullable(oidcMetadataContextLookupStrategy.apply(profileRequestContext))
- .map(oidcContext -> oidcContext.getClientInformation())
- .map(clientInfo -> clientInfo != null ?
- ClientInformationExtensionSupport.parseValidatedTrustAnchor(clientInfo) : null)
- .orElse(null);
- }
-}
diff --git a/idp-oidfed-op-impl/pom.xml b/idp-oidfed-op-impl/pom.xml
index ac345b0..2f66a8b 100644
--- a/idp-oidfed-op-impl/pom.xml
+++ b/idp-oidfed-op-impl/pom.xml
@@ -70,6 +70,11 @@
<artifactId>oidc-common-profile-impl</artifactId>
<scope>provided</scope>
</dependency>
+ <dependency>
+ <groupId>${oidc-common.groupId}</groupId>
+ <artifactId>oidc-common-conf-impl</artifactId>
+ <scope>provided</scope>
+ </dependency>
<dependency>
<groupId>${oidfed-common.groupId}</groupId>
<artifactId>oidfed-common-api</artifactId>
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ResolveEntityRequestDecoder.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ResolveEntityRequestDecoder.java
deleted file mode 100644
index ad03e4d..0000000
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/decoding/impl/ResolveEntityRequestDecoder.java
+++ /dev/null
@@ -1,113 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.decoding.impl;
-
-import java.io.IOException;
-import java.net.URI;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.decoder.MessageDecodingException;
-import org.slf4j.Logger;
-
-import com.google.common.base.MoreObjects;
-import com.nimbusds.oauth2.sdk.ParseException;
-import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
-import com.nimbusds.oauth2.sdk.http.HTTPRequest;
-import com.nimbusds.oauth2.sdk.http.JakartaServletUtils;
-
-import jakarta.servlet.http.HttpServletRequest;
-import net.shibboleth.idp.plugin.oidc.op.decoding.impl.RequestUtil;
-import net.shibboleth.idp.plugin.oidc.op.oauth2.decoding.impl.BaseOAuth2RequestDecoder;
-import net.shibboleth.oidfed.messaging.impl.ResolveEntityRequest;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * Message decoder decoding OpenID Federation Resolve Entity request {@link ResolveEntityRequest}.
- */
-public class ResolveEntityRequestDecoder extends BaseOAuth2RequestDecoder<ResolveEntityRequest> {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(ResolveEntityRequestDecoder.class);
-
- /** {@inheritDoc} */
- @Override
- protected ResolveEntityRequest parseMessage() throws MessageDecodingException {
- final HttpServletRequest request = getHttpServletRequest();
- assert request != null;
- if (!"GET".equalsIgnoreCase(request.getMethod()) && !"POST".equalsIgnoreCase(request.getMethod())) {
- throw new MessageDecodingException("This message decoder only supports the HTTP GET and POST methods");
- }
- if (!"application/x-www-form-urlencoded".equals(request.getContentType())) {
- throw new MessageDecodingException("Invalid content type: " + request.getContentType());
- }
- try {
- final HTTPRequest httpRequest = JakartaServletUtils.createHTTPRequest(request);
- getProtocolMessageLogger().trace("Inbound request {}", RequestUtil.toString(httpRequest, null));
- final URI uri = httpRequest.getURI();
- if (uri == null) {
- throw new MessageDecodingException("Could not parse request URI");
- }
- final ClientAuthentication clientAuthentication = ClientAuthentication.parse(httpRequest);
- if (clientAuthentication != null && !"POST".equalsIgnoreCase(request.getMethod())) {
- throw new MessageDecodingException(
- "This message decoder requires use of POST method when client authentication is involved");
- }
- if (clientAuthentication == null && !"GET".equalsIgnoreCase(request.getMethod())) {
- throw new MessageDecodingException(
- "This message decoder requires use of GET method when client authentication is not involved");
- }
- final Map<String, List<String>> parameters = httpRequest.getQueryParameters();
- final String subject = Optional.ofNullable(parameters.get("sub"))
- .filter(Objects::nonNull)
- .filter(list -> list.size() == 1)
- .map(list -> list.get(0))
- .orElse(null);
- if (subject == null) {
- throw new MessageDecodingException("No single sub value in the request");
- }
- final List<String> trustAnchors = Optional.ofNullable(parameters.get("trust_anchor"))
- .filter(Objects::nonNull)
- .filter(list -> list.size() > 0)
- .orElse(null);
- if (trustAnchors == null) {
- throw new MessageDecodingException("No trust_anchor included in the request");
- }
- return new ResolveEntityRequest(uri, subject, trustAnchors, parameters.get("entity_type"),
- clientAuthentication);
- } catch (final IOException | ParseException e) {
- log.error("Could not create HTTP request from the request", e);
- throw new MessageDecodingException(e);
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected String getMessageToLog(@Nullable final ResolveEntityRequest message) {
- return message == null ? null : MoreObjects.toStringHelper(this).omitNullValues()
- .add("subject", message.getSubject())
- .add("trustAnchors", message.getTrustAnchors())
- .add("entityTypes", message.getEntityTypes())
- .add("endpointURI", getEndpointURI(message))
- .add("clientAuthentication", RequestUtil.getClientAuthenticationLog(message.getClientAuthentication()))
- .toString();
- }
-
-}
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityErrorResponseFromEvent.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityErrorResponseFromEvent.java
deleted file mode 100644
index 527f119..0000000
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityErrorResponseFromEvent.java
+++ /dev/null
@@ -1,270 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.impl;
-
-import java.time.Duration;
-import java.time.Instant;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.context.EventContext;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.CurrentOrPreviousEventLookup;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
-import org.slf4j.Logger;
-
-import com.nimbusds.oauth2.sdk.ErrorObject;
-import com.nimbusds.oauth2.sdk.ErrorResponse;
-import com.nimbusds.oauth2.sdk.http.HTTPResponse;
-
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.oidc.metadata.cache.MetadataCache;
-import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
-import net.shibboleth.oidc.profile.messaging.JSONErrorResponse;
-import net.shibboleth.oidfed.messaging.impl.ResolveEntityRequest;
-import net.shibboleth.oidfed.metadata.cache.ResponseContainerExpirationCriterion;
-import net.shibboleth.oidfed.metadata.cache.local.NimbusResponseCriterion;
-import net.shibboleth.oidfed.metadata.cache.local.ResolveEntityRequestCriterion;
-import net.shibboleth.oidfed.metadata.cache.local.ResolveEntityResponseContainer;
-import net.shibboleth.oidfed.profile.config.navigate.CachedErrorResponseLifetimeLookupFunction;
-import net.shibboleth.oidfed.profile.impl.RelyingPartyCachedMessageContext;
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.resolver.CriteriaSet;
-
-/**
- * This action reads an event from the configured {@link EventContext} lookup strategy, constructs a JSON error response
- * message and attaches it as the outbound message. If {@link RelyingPartyCachedMessageContext} is found, it's exploited
- * for storing the response message in the configured {@link #responseCache}.
- */
-public class BuildResolveEntityErrorResponseFromEvent extends AbstractProfileAction {
-
- /** Default value for the error code in the error response messages. */
- public static final String DEFAULT_ERROR_CODE = "invalid_request";
-
- /** Default value for the HTTP response status code in the HTTP responses. */
- public static final int DEFAULT_HTTP_STATUS_CODE = HTTPResponse.SC_BAD_REQUEST;
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(BuildResolveEntityErrorResponseFromEvent.class);
-
- /** Strategy function for access to {@link EventContext} to check. */
- @Nonnull
- private Function<ProfileRequestContext, EventContext> eventContextLookupStrategy;
-
- /** Map of eventIds to pre-configured error objects. */
- private Map<String, ErrorObject> mappedErrors;
-
- /** The status code for unmapped events. */
- private int defaultStatusCode;
-
- /** The code for unmapped events. */
- private String defaultCode;
-
- /** Metadata cache for cached response containers. */
- @NonnullAfterInit private MetadataCache<ResolveEntityResponseContainer> responseCache;
-
- /** Strategy used to locate the lifetime for the cached response record. */
- @Nonnull private Function<ProfileRequestContext,Duration> cachedResponseLifetimeLookupStrategy;
-
- /** Strategy used to locate the resolve entity context. */
- @Nonnull
- private Function<ProfileRequestContext, RelyingPartyCachedMessageContext> resolveEntityContextLookupStrategy;
-
- /**
- * Constructor.
- */
- public BuildResolveEntityErrorResponseFromEvent() {
- eventContextLookupStrategy = new CurrentOrPreviousEventLookup();
- mappedErrors = new HashMap<>();
- defaultStatusCode = DEFAULT_HTTP_STATUS_CODE;
- defaultCode = DEFAULT_ERROR_CODE;
- cachedResponseLifetimeLookupStrategy = new CachedErrorResponseLifetimeLookupFunction();
- final Function<ProfileRequestContext, RelyingPartyCachedMessageContext> recls =
- new ChildContextLookup<>(RelyingPartyCachedMessageContext.class).compose(
- new InboundMessageContextLookup());
- assert recls != null;
- resolveEntityContextLookupStrategy = recls;
-
- }
-
- /**
- * Set the status code for unmapped events.
- *
- * @param code The default status code for unmapped events.
- */
- public void setDefaultStatusCode(final int code) {
- defaultStatusCode = code;
- }
-
- /**
- * Set the code for unmapped events.
- *
- * @param code The default status code for unmapped events.
- */
- public void setDefaultCode(@Nonnull final String code) {
- defaultCode = Constraint.isNotNull(code, "Default code cannot be null");
- }
-
- /**
- * Set lookup strategy for {@link EventContext} to check.
- *
- * @param strategy lookup strategy
- */
- public void setEventContextLookupStrategy(@Nonnull final Function<ProfileRequestContext, EventContext> strategy) {
- ifInitializedThrowUnmodifiabledComponentException();
-
- eventContextLookupStrategy = Constraint.isNotNull(strategy, "EventContext lookup strategy cannot be null");
- }
-
- /**
- * Set map of eventIds to pre-configured error objects.
- *
- * @param errors map of eventIds to pre-configured error objects.
- */
- public void setMappedErrors(@Nonnull final Map<String, ErrorObject> errors) {
- ifInitializedThrowUnmodifiabledComponentException();
-
- mappedErrors = Constraint.isNotNull(errors, "Mapped errors cannot be null");
- }
-
- /**
- * Set the metadata cache for cached response containers.
- *
- * @param cache What to set.
- */
- public void setResponseCache(@Nonnull final MetadataCache<ResolveEntityResponseContainer> cache) {
- checkSetterPreconditions();
- responseCache = Constraint.isNotNull(cache, "Response metadata cache cannot be null");
- }
-
- /**
- * Set the strategy used to locate the lifetime for the cached response record.
- *
- * @param strategy What to set.
- */
- public void setCachedResponseLifetimeLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, Duration> strategy) {
- checkSetterPreconditions();
- cachedResponseLifetimeLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
- }
-
- /**
- * Set the strategy used to locate the resolve entity context
- *
- * @param strategy What to set.
- */
- public void setResolveEntityContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,RelyingPartyCachedMessageContext> strategy) {
- checkSetterPreconditions();
- resolveEntityContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null!");
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (responseCache == null) {
- throw new ComponentInitializationException("Response metadata cache cannot be null");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- if (profileRequestContext.getOutboundMessageContext() == null) {
- log.debug("{} No outbound message context initialized, nothing to do", getLogPrefix());
- return false;
- }
-
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- final EventContext eventCtx = eventContextLookupStrategy.apply(profileRequestContext);
- if (eventCtx == null || eventCtx.getEvent() == null) {
- log.error("{} No event to be included in the response, nothing to do", getLogPrefix());
- return;
- }
- assert eventCtx != null;
- final Object event = eventCtx.getEvent();
- assert event != null;
- final String eventValue = event.toString();
- final ErrorObject error;
- if (mappedErrors.containsKey(eventValue)) {
- log.debug("{} Found mapped event for {}", getLogPrefix(), eventValue);
- error = mappedErrors.get(eventValue);
- } else {
- log.debug("{} No mapped event found for {}, creating general {}", getLogPrefix(), eventValue, defaultCode);
- error = new ErrorObject(defaultCode, eventValue, defaultStatusCode);
- }
- assert error != null;
- final ErrorResponse errorResponse = buildErrorResponse(error, profileRequestContext);
- if (errorResponse != null) {
- profileRequestContext.ensureOutboundMessageContext().setMessage(errorResponse);
- log.debug("{} ErrorResponse successfully set as the outbound message", getLogPrefix());
- } else {
- log.debug("{} Error response not formed", getLogPrefix());
- }
- }
-
- protected JSONErrorResponse buildErrorResponse(@Nonnull final ErrorObject error,
- @Nonnull final ProfileRequestContext profileRequestContext) {
- final JSONErrorResponse response = new JSONErrorResponse(error);
- final RelyingPartyCachedMessageContext resolveEntityContext =
- resolveEntityContextLookupStrategy.apply(profileRequestContext);
-
- final Duration cachedResponseLifetime = cachedResponseLifetimeLookupStrategy.apply(profileRequestContext);
- if (resolveEntityContext != null && cachedResponseLifetime != null &&
- resolveEntityContext.getValidatedRequest() instanceof ResolveEntityRequest resolveEntityRequest) {
- final NimbusResponseCriterion responseCriterion = new NimbusResponseCriterion(response);
- final Instant expiration = Instant.now().plus(cachedResponseLifetime);
- assert expiration != null;
- final ResponseContainerExpirationCriterion expirationCriterion =
- new ResponseContainerExpirationCriterion(expiration);
- final ResolveEntityRequestCriterion requestCriterion =
- new ResolveEntityRequestCriterion(resolveEntityRequest);
- final CriteriaSet criteria = new CriteriaSet(requestCriterion, responseCriterion, expirationCriterion);
- try {
- final List<ResolveEntityResponseContainer> result = responseCache.get(criteria);
- if (result.size() != 1) {
- log.error("{} Unexpected result (size={}) when storing response record into the metadata cache",
- getLogPrefix(), result.size());
- } else {
- log.debug("{} Response stored into the cache", getLogPrefix());
- }
- } catch (final MetadataCacheException e) {
- log.error("{} Could not store the response record into the metadata cache", getLogPrefix(), e);
- }
- }
-
- return response;
- }
-}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityResponse.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityResponse.java
deleted file mode 100644
index dae1be2..0000000
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/BuildResolveEntityResponse.java
+++ /dev/null
@@ -1,204 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.impl;
-
-import java.time.Instant;
-import java.util.Date;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
-import java.util.Optional;
-import java.util.function.Function;
-import java.util.stream.Collectors;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
-import org.slf4j.Logger;
-
-import com.nimbusds.jwt.JWTClaimsSet;
-
-import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultEntityTypesLookupFunction;
-import net.shibboleth.oidfed.metadata.EntityStatement;
-import net.shibboleth.oidfed.metadata.TrustMark;
-import net.shibboleth.oidfed.metadata.payload.claim.Metadata;
-import net.shibboleth.oidfed.profile.context.RelyingPartyTrustChainContext;
-import net.shibboleth.oidfed.profile.context.VerifiedTrustChain;
-import net.shibboleth.oidfed.profile.impl.AbstractBuildEntityStatementAction;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.collection.Pair;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-
-/**
- * An action that uses the information from {@link RelyingPartyTrustChainContext} for creating a new JWT to be used for
- * creating a response to OpenID Federation Resolve Entity API.
- */
-public class BuildResolveEntityResponse extends AbstractBuildEntityStatementAction {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(BuildResolveEntityResponse.class);
-
- /** Strategy used to lookup the trust chain context. */
- @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
-
- /** Strategy used to lookup the entity types included in the response metadata. */
- @Nonnull private Function<ProfileRequestContext, List<String>> entityTypesLookupStrategy;
-
- /** Trust chain context to operate on. */
- @NonnullBeforeExec private RelyingPartyTrustChainContext trustChainContext;
-
- /** Constructor. */
- public BuildResolveEntityResponse() {
- final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
- new ChildContextLookup<>(RelyingPartyTrustChainContext.class).compose(
- new InboundMessageContextLookup());
- assert tcls != null;
- trustChainContextLookupStrategy = tcls;
- entityTypesLookupStrategy = new DefaultEntityTypesLookupFunction();
- }
-
- /**
- * Set the strategy used to lookup the trust chain context.
- *
- * @param strategy lookup strategy
- */
- public void setTrustChainContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, RelyingPartyTrustChainContext> strategy) {
- checkSetterPreconditions();
- trustChainContextLookupStrategy =
- Constraint.isNotNull(strategy, "TrustChainContextLookupStrategy cannot be null");
- }
-
- /**
- * Set the strategy used to lookup the entity types included in the response metadata.
- *
- * @param strategy lookup strategy
- */
- public void setEntityTypesLookupStrategy(@Nonnull final Function<ProfileRequestContext, List<String>> strategy) {
- checkSetterPreconditions();
- entityTypesLookupStrategy = Constraint.isNotNull(strategy, "EntityTypesLookupStrategy cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- trustChainContext = trustChainContextLookupStrategy.apply(profileRequestContext);
- if (trustChainContext == null) {
- log.error("{} Unable to locate trust chain context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
- return false;
- }
-
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean populateClaimsSetBuilder(@Nonnull final JWTClaimsSet.Builder builder,
- @Nonnull final ProfileRequestContext profileRequestContext) {
- final VerifiedTrustChain selectedTrustChain = trustChainContext.getSelectedTrustChain();
- if (selectedTrustChain == null) {
- log.debug("{} No selected trust chain found form the context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.INVALID_METADATA);
- return false;
- }
- final Metadata metadata = selectedTrustChain.getMetadata();
- final List<String> entityTypes = entityTypesLookupStrategy.apply(profileRequestContext);
- log.trace("{} The following entity types were requested: {}", getLogPrefix(), entityTypes);
- if (entityTypes != null && !entityTypes.isEmpty()) {
- final Map<String,Object> filteredMetadata = metadata.getAllClaims().entrySet()
- .stream()
- .filter(entry -> entityTypes.contains(entry.getKey()))
- .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
- if (filteredMetadata.isEmpty()) {
- log.warn("{} No metadata for entity types {} found for the selected trust chain", getLogPrefix(),
- entityTypes);
- ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.INVALID_METADATA);
- return false;
- }
- builder.claim("metadata", filteredMetadata);
- } else {
- builder.claim("metadata", metadata);
- }
-
- final List<EntityStatement<?>> trustChain = selectedTrustChain.getTrustChain();
- builder.claim("trust_chain", trustChain.stream()
- .map(statement -> statement.getJwt().serialize())
- .toList());
-
- final Instant expirationTime = resolveTrustChainExpiration(trustChain);
- if (expirationTime == null) {
- log.error("{} Coud not resolve expiration time from the selected trust chain context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
- return false;
- }
- builder.expirationTime(Date.from(expirationTime));
-
- final String entityId = trustChain.get(0).getSubject();
- assert entityId != null;
- final Map<String, String> trustMarks = buildTrustMarks(entityId, trustChainContext.getVerifiedTrustMarks());
- if (trustMarks != null && !trustMarks.isEmpty()) {
- builder.claim("trust_marks", trustMarks);
- }
- return true;
- }
-
- /**
- * Builds value for the trust_marks claim in the resolve entity response entity statement.
- *
- * @param entityId the subject entity ID
- * @param trustMarks trust marks for the selected trust chain
- * @return map of trust marks, keyed with trust mark IDs
- */
- @Nullable private Map<String, String> buildTrustMarks(@Nonnull final String entityId,
- @Nullable final Map<String, List<TrustMark>> trustMarks) {
- return Optional.ofNullable(trustMarks)
- .map(marks -> marks.get(entityId))
- .filter(Objects::nonNull)
- .map(list -> list.stream()
- .map(trustMark -> new Pair<String, String>(
- trustMark.getParsedPayload().getTrustMarkType(), trustMark.getJwt().serialize()))
- .filter(Objects::nonNull)
- .collect(Collectors.toMap(pair -> pair.getFirst(), pair -> pair.getSecond())))
- .orElse(null);
- }
-
- /**
- * Resolve expiration time for the given trust chain.
- *
- * @param trustChain trust chain
- * @return expiration time
- */
- @Nullable private Instant resolveTrustChainExpiration(@Nonnull final List<EntityStatement<?>> trustChain) {
- Instant metadataExpiration = null;
- for (final EntityStatement<?> statement : trustChain) {
- final Instant statementExpiration = statement.getParsedPayload().getExpiration();
- metadataExpiration = metadataExpiration == null ? statementExpiration :
- statementExpiration.isBefore(metadataExpiration) ? statementExpiration : metadataExpiration;
- }
- return metadataExpiration;
- }
-
-}
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/FormOutboundResolveEntityResponse.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/FormOutboundResolveEntityResponse.java
deleted file mode 100644
index 99d1ecb..0000000
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/FormOutboundResolveEntityResponse.java
+++ /dev/null
@@ -1,230 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.impl;
-
-import java.time.Duration;
-import java.time.Instant;
-import java.util.List;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
-import org.opensaml.profile.context.navigate.OutboundMessageContextLookup;
-import org.slf4j.Logger;
-
-import com.nimbusds.jwt.SignedJWT;
-import com.nimbusds.oauth2.sdk.Response;
-
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.oidc.metadata.cache.MetadataCache;
-import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
-import net.shibboleth.oidfed.messaging.impl.ResolveEntityRequest;
-import net.shibboleth.oidfed.messaging.impl.ResolveEntityResponse;
-import net.shibboleth.oidfed.metadata.cache.ResponseContainerExpirationCriterion;
-import net.shibboleth.oidfed.metadata.cache.local.NimbusResponseCriterion;
-import net.shibboleth.oidfed.metadata.cache.local.ResolveEntityRequestCriterion;
-import net.shibboleth.oidfed.metadata.cache.resolver.ResolveEntityResponseContainer;
-import net.shibboleth.oidfed.profile.config.navigate.CachedSuccessResponseLifetimeLookupFunction;
-import net.shibboleth.oidfed.profile.impl.EntityStatementContext;
-import net.shibboleth.oidfed.profile.impl.RelyingPartyCachedMessageContext;
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.resolver.CriteriaSet;
-
-/**
- * This action builds a response for the OpenID federation resolve entity request. The response contains an
- * {@link SignedJWT}. The response is put in the {@link #responseCache} using the lifetime resolved via
- * {@link #cachedResponseLifetimeLookupStrategy}.
- */
-public class FormOutboundResolveEntityResponse extends AbstractProfileAction {
-
- /** Class logger. */
- @Nonnull private final Logger log = LoggerFactory.getLogger(FormOutboundResolveEntityResponse.class);
-
- /** Metadata cache for cached response containers. */
- @NonnullAfterInit private MetadataCache<ResolveEntityResponseContainer> responseCache;
-
- /** Strategy used to locate the resolve entity context. */
- @Nonnull
- private Function<ProfileRequestContext, RelyingPartyCachedMessageContext> cachedMessageContextLookupStrategy;
-
- /** Strategy used to locate the subcontext to hold the statement. */
- @Nonnull private Function<ProfileRequestContext,EntityStatementContext> entityStatementContextLookupStrategy;
-
- /** Strategy used to locate the lifetime for the cached response record. */
- @Nonnull private Function<ProfileRequestContext,Duration> cachedResponseLifetimeLookupStrategy;
-
- /** JWT used to build entity statement. */
- @NonnullBeforeExec private SignedJWT jwt;
-
- /** The resolve entity context to operate on. */
- @NonnullBeforeExec private RelyingPartyCachedMessageContext cachedMessageContext;
-
- /**
- * Constructor.
- */
- public FormOutboundResolveEntityResponse() {
- final Function<ProfileRequestContext,EntityStatementContext> escls =
- new ChildContextLookup<>(EntityStatementContext.class).compose(
- new OutboundMessageContextLookup());
- assert escls != null;
- entityStatementContextLookupStrategy = escls;
- final Function<ProfileRequestContext, RelyingPartyCachedMessageContext> recls =
- new ChildContextLookup<>(RelyingPartyCachedMessageContext.class).compose(
- new InboundMessageContextLookup());
- assert recls != null;
- cachedMessageContextLookupStrategy = recls;
- cachedResponseLifetimeLookupStrategy = new CachedSuccessResponseLifetimeLookupFunction();
- }
-
- /**
- * Set the strategy used to locate the subcontext to hold the statement
- *
- * @param strategy What to set.
- */
- public void setEntityStatementContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,EntityStatementContext> strategy) {
- checkSetterPreconditions();
- entityStatementContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null!");
- }
-
- /**
- * Set the strategy used to locate the cached message context
- *
- * @param strategy What to set.
- */
- public void setCachedMessageContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,RelyingPartyCachedMessageContext> strategy) {
- checkSetterPreconditions();
- cachedMessageContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null!");
- }
-
- /**
- * Set the metadata cache for cached response containers.
- *
- * @param cache What to set.
- */
- public void setResponseCache(@Nonnull final MetadataCache<ResolveEntityResponseContainer> cache) {
- checkSetterPreconditions();
- responseCache = Constraint.isNotNull(cache, "Response metadata cache cannot be null");
- }
-
- /**
- * Set the strategy used to locate the lifetime for the cached response record.
- *
- * @param strategy What to set.
- */
- public void setCachedResponseLifetimeLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, Duration> strategy) {
- checkSetterPreconditions();
- cachedResponseLifetimeLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (responseCache == null) {
- throw new ComponentInitializationException("Response metadata cache cannot be null");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
-
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
- cachedMessageContext = cachedMessageContextLookupStrategy.apply(profileRequestContext);
- if (cachedMessageContext == null) {
- log.error("{} Could not resolve cached message context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
-
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- final Response cachedResponse = cachedMessageContext.getCachedResponse();
- if (cachedResponse != null) {
- log.debug("{} Cached response found, storing in to the outbound message context", getLogPrefix());
- profileRequestContext.ensureOutboundMessageContext().setMessage(cachedResponse);
- return;
- }
- log.debug("{} No cached response found, resolving the response JWT from the context", getLogPrefix());
- final EntityStatementContext entityStatementContext =
- entityStatementContextLookupStrategy.apply(profileRequestContext);
- if (entityStatementContext == null) {
- log.error("{} Could not resolve entity statement context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return;
- }
- if (entityStatementContext.getJWT() instanceof SignedJWT signedJwt) {
- jwt = signedJwt;
- } else {
- log.error("{} No signed JWT found from the entity statement context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return;
- }
-
- assert jwt != null;
- final ResolveEntityResponse response = new ResolveEntityResponse(jwt);
- final NimbusResponseCriterion responseCriterion = new NimbusResponseCriterion(response);
- final Duration lifetime = cachedResponseLifetimeLookupStrategy.apply(profileRequestContext);
- if (lifetime == null) {
- log.error("{} Could not resolve lifetime for the cached response record", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return;
- }
- final Instant expiration = Instant.now().plus(lifetime);
- assert expiration != null;
- final ResponseContainerExpirationCriterion expirationCriterion =
- new ResponseContainerExpirationCriterion(expiration);
- if (cachedMessageContext.getValidatedRequest() instanceof ResolveEntityRequest validatedRequest) {
- final ResolveEntityRequestCriterion requestCriterion = new ResolveEntityRequestCriterion(validatedRequest);
- final CriteriaSet criteria = new CriteriaSet(requestCriterion, responseCriterion, expirationCriterion);
- try {
- final List<ResolveEntityResponseContainer> result = responseCache.get(criteria);
- if (result.size() != 1) {
- log.error("{} Unexpected result (size={}) when storing response record into the metadata cache",
- getLogPrefix(), result.size());
- } else {
- log.debug("{} Response stored into the cache", getLogPrefix());
- }
- } catch (final MetadataCacheException e) {
- log.error("{} Could not store the response record into tht metadata cache", getLogPrefix(), e);
- }
- } else {
- log.error("{} No validated request found from the resolve entity context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return;
- }
-
- profileRequestContext.ensureOutboundMessageContext().setMessage(response);
- }
-}
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/LookupCachedResolveEntityResponse.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/LookupCachedResolveEntityResponse.java
deleted file mode 100644
index e6a5632..0000000
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/LookupCachedResolveEntityResponse.java
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.impl;
-
-import java.util.List;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
-import org.slf4j.Logger;
-
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.oidc.metadata.cache.MetadataCache;
-import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
-import net.shibboleth.oidfed.messaging.impl.ResolveEntityRequest;
-import net.shibboleth.oidfed.metadata.cache.local.ResolveEntityRequestCriterion;
-import net.shibboleth.oidfed.metadata.cache.local.ResolveEntityResponseContainer;
-import net.shibboleth.oidfed.profile.impl.RelyingPartyCachedMessageContext;
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.resolver.CriteriaSet;
-
-/**
- * Lookup if a cached response already exists for the validated resolve entity API request. If yes, the response is
- * stored into {@link RelyingPartyCachedMessageContext} and a corresponding event ID is published.
- *
- * @event {@link EventIds#PROCEED_EVENT_ID}
- * @event {@link EventIds#INVALID_PROFILE_CTX}
- * @event {@link OidFederationEventIds#CACHED_RESPONSE_FOUND}
- */
-public class LookupCachedResolveEntityResponse extends AbstractProfileAction {
-
- /** Class logger. */
- @Nonnull private Logger log = LoggerFactory.getLogger(LookupCachedResolveEntityResponse.class);
-
- /** Strategy used to locate the cached message context. */
- @Nonnull
- private Function<ProfileRequestContext, RelyingPartyCachedMessageContext> cachedMessageContextLookupStrategy;
-
- /** Metadata cache for cached response containers. */
- @NonnullAfterInit private MetadataCache<ResolveEntityResponseContainer> responseCache;
-
- /** Cached message context to operate on. */
- @NonnullBeforeExec private RelyingPartyCachedMessageContext cachedMessageContext;
-
- /** Request message to operate on. */
- @NonnullBeforeExec private ResolveEntityRequest validatedRequest;
-
- /**
- * Constructor.
- */
- public LookupCachedResolveEntityResponse() {
- final Function<ProfileRequestContext, RelyingPartyCachedMessageContext> recls =
- new ChildContextLookup<>(RelyingPartyCachedMessageContext.class).compose(
- new InboundMessageContextLookup());
- assert recls != null;
- cachedMessageContextLookupStrategy = recls;
- }
-
- /**
- * Set the strategy used to locate the cached message context
- *
- * @param strategy What to set.
- */
- public void setCachedMessageContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,RelyingPartyCachedMessageContext> strategy) {
- checkSetterPreconditions();
- cachedMessageContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null!");
- }
-
- /**
- * Set the metadata cache for cached response containers.
- *
- * @param cache What to set.
- */
- public void setResponseCache(@Nonnull final MetadataCache<ResolveEntityResponseContainer> cache) {
- checkSetterPreconditions();
- responseCache = Constraint.isNotNull(cache, "Response metadata cache cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
- if (responseCache == null) {
- throw new ComponentInitializationException("Response metadata cache cannot be null");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- cachedMessageContext = cachedMessageContextLookupStrategy.apply(profileRequestContext);
- if (cachedMessageContext == null) {
- log.error("{} Could not resolve cached response context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
-
- if (cachedMessageContext.getValidatedRequest() instanceof ResolveEntityRequest resolveEntityRequest) {
- validatedRequest = resolveEntityRequest;
- } else {
- log.error("{} Could not resolve validated resolve entity request", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
-
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- assert validatedRequest != null;
- final ResolveEntityRequestCriterion requestCriterion = new ResolveEntityRequestCriterion(validatedRequest);
- final CriteriaSet criteria = new CriteriaSet(requestCriterion);
- try {
- final List<ResolveEntityResponseContainer> result = responseCache.get(criteria);
- if (result.size() != 1) {
- log.debug("{} No cached response record found from the metadata cache", getLogPrefix(), result.size());
- } else {
- final ResolveEntityResponseContainer cachedResponse = result.get(0);
- cachedMessageContext.setCachedResponse(cachedResponse.getResponse());
- log.debug("{} Response found from the cache, publishing event {}", getLogPrefix(),
- OidFederationEventIds.CACHED_RESPONSE_FOUND);
- ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.CACHED_RESPONSE_FOUND);
- return;
- }
- } catch (final MetadataCacheException e) {
- log.error("{} Could not fetch response record from the metadata cache", getLogPrefix(), e);
- }
- }
-}
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/OidFederationEventIds.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/OidFederationEventIds.java
deleted file mode 100644
index 9c5752c..0000000
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/OidFederationEventIds.java
+++ /dev/null
@@ -1,84 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.impl;
-
-import javax.annotation.Nonnull;
-
-import net.shibboleth.shared.annotation.constraint.NotEmpty;
-
-/**
- * OpenID Federation -specific constants to use for {@link org.opensaml.profile.action.ProfileAction}
- * {@link org.opensaml.profile.context.EventContext}s.
- */
-public class OidFederationEventIds {
-
- /**
- * ID of event returned if a flow wishes to indicate that another trust chain should be selected instead
- */
- @Nonnull @NotEmpty public static final String RESELECT_TRUST_CHAIN = "ReselectTrustChain";
-
- /**
- * ID of event returned if cached response was found and set to the context.
- */
- @Nonnull @NotEmpty public static final String CACHED_RESPONSE_FOUND = "CachedResponseFound";
-
- /**
- * ID of event returned if no trust chains were resolved for the client.
- */
- @Nonnull @NotEmpty public static final String NO_TRUST_CHAINS_RESOLVED = "NoTrustChainsResolved";
-
- /**
- * ID of event returned if the given trust anchor is invalid.
- */
- @Nonnull @NotEmpty public static final String INVALID_TRUST_ANCHOR = "InvalidTrustAnchor";
-
- /**
- * ID of event returned if the given subject is invalid.
- */
- @Nonnull @NotEmpty public static final String INVALID_SUBJECT = "InvalidSubject";
-
- /**
- * ID of event returned if the given metadata is invalid.
- */
- @Nonnull @NotEmpty public static final String INVALID_METADATA = "InvalidMetadata";
-
- /**
- * ID of event returned if the given metadata policy is invalid.
- */
- @Nonnull @NotEmpty public static final String INVALID_METADATA_POLICY = "InvalidMetadataPolicy";
-
- /**
- * ID of event returned if the given metadata is invalid against policy.
- */
- @Nonnull @NotEmpty public static final String INVALID_METADATA_AGAINST_POLICY = "InvalidMetadataAgainstPolicy";
-
- /**
- * ID of event returned if the trust chain is invalid against constraints.
- */
- @Nonnull @NotEmpty public static final String INVALID_TRUST_CHAIN_AGAINST_CONSTRAINTS =
- "InvalidTrustChainAgainstConstraints";
-
- /**
- * ID of event returned if the mandatory provided trust chain could not be fetched.
- */
- @Nonnull @NotEmpty public static final String MISSING_MANDATORY_PROVIDED_TRUST_CHAIN =
- "MissingMandatoryProvidedTrustChain";
-
- /**
- * ID of event returned if the provided trust chain could not be verified.
- */
- @Nonnull @NotEmpty public static final String INVALID_PROVIDED_TRUST_CHAIN = "InvalidProvidedTrustChain";
-
-}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedTrustChain.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedTrustChain.java
index 57d089f..8a40d65 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedTrustChain.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateProvidedTrustChain.java
@@ -27,6 +27,7 @@ import org.opensaml.profile.context.ProfileRequestContext;
import org.slf4j.Logger;
import net.shibboleth.oidfed.metadata.EntityStatement;
+import net.shibboleth.oidfed.profile.OidFederationEventIds;
import net.shibboleth.oidfed.profile.ProvidedTrustChainResolver;
import net.shibboleth.oidfed.profile.context.RelyingPartyTrustChainContext;
import net.shibboleth.oidfed.profile.context.VerifiedTrustChain;
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateResolveEntityRequest.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateResolveEntityRequest.java
deleted file mode 100644
index 683e6b7..0000000
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateResolveEntityRequest.java
+++ /dev/null
@@ -1,186 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.impl;
-
-import java.util.List;
-import java.util.Map;
-import java.util.Optional;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
-import org.slf4j.Logger;
-
-import com.nimbusds.oauth2.sdk.Request;
-
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.oidc.metadata.cache.MetadataCache;
-import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
-import net.shibboleth.oidfed.messaging.impl.ResolveEntityRequest;
-import net.shibboleth.oidfed.metadata.cache.SubjectEntityIDCriterion;
-import net.shibboleth.oidfed.metadata.cache.local.LocalKeyContainer;
-import net.shibboleth.oidfed.profile.impl.RelyingPartyCachedMessageContext;
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-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.resolver.CriteriaSet;
-
-/**
- * Validates the resolve entity request against the profile configuration and stores the validated (possibly modified)
- * request into {@link RelyingPartyCachedMessageContext#setValidatedRequest(Request)}.
- *
- * @event {@link EventIds#PROCEED_EVENT_ID}
- * @event {@link EventIds#INVALID_MSG_CTX}
- */
-public class ValidateResolveEntityRequest extends AbstractProfileAction {
-
- /** Class logger. */
- @Nonnull private Logger log = LoggerFactory.getLogger(ValidateResolveEntityRequest.class);
-
- /** Strategy used to create the cached message context. */
- @Nonnull
- private Function<ProfileRequestContext, RelyingPartyCachedMessageContext> cachedMessageContextCreationStrategy;
-
- /** Cache containing local copies of trusted trust anchor keys. */
- @NonnullAfterInit private MetadataCache<Map<String, LocalKeyContainer>> localTrustAnchorsCache;
-
- /** Request message to operate on. */
- @NonnullBeforeExec private ResolveEntityRequest requestMessage;
-
- /** Cached message context to operate on. */
- @NonnullBeforeExec private RelyingPartyCachedMessageContext cachedMessageContext;
-
- /**
- * Constructor.
- */
- public ValidateResolveEntityRequest() {
- final Function<ProfileRequestContext, RelyingPartyCachedMessageContext> reccs =
- new ChildContextLookup<>(RelyingPartyCachedMessageContext.class, true).compose(
- new InboundMessageContextLookup());
- assert reccs != null;
- cachedMessageContextCreationStrategy = reccs;
- }
-
- /**
- * Set the strategy used to return or create the resolve entity context.
- *
- * @param strategy creation strategy
- */
- public void setResolveEntityContextCreationStrategy(
- @Nonnull final Function<ProfileRequestContext, RelyingPartyCachedMessageContext> strategy) {
- checkSetterPreconditions();
- cachedMessageContextCreationStrategy = Constraint.isNotNull(strategy,
- "RelyingPartyResolveEntityContext creation strategy cannot be null");
- }
-
- /**
- * Set the strategy used to lookup the trust chain context.
- *
- * @param cache lookup strategy
- */
- public void setLocalTrustAnchorsCache(
- @Nonnull final MetadataCache<Map<String, LocalKeyContainer>> cache) {
- checkSetterPreconditions();
- localTrustAnchorsCache =
- Constraint.isNotNull(cache, "LocalTrustAnchorsCache cannot be null");
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
- if (localTrustAnchorsCache == null) {
- throw new ComponentInitializationException("LocalTrustAnchorsCache cannot be null");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- requestMessage = Optional.ofNullable(profileRequestContext.getInboundMessageContext())
- .map(messageContext -> messageContext.getMessage())
- .filter(ResolveEntityRequest.class::isInstance)
- .map(ResolveEntityRequest.class::cast)
- .orElse(null);
- if (requestMessage == null) {
- log.error("{} Unable to fetch the request message to operate on", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
-
- cachedMessageContext = cachedMessageContextCreationStrategy.apply(profileRequestContext);
- if (cachedMessageContext == null) {
- log.error("{} Unable to create resolve entity context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- final List<String> validatedAnchors = requestMessage.getTrustAnchors().stream()
- .filter(anchor -> isLocallyTrusted(anchor))
- .toList();
- if (validatedAnchors.isEmpty()) {
- log.info("{} No locally trusted anchors left after filtering", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.INVALID_TRUST_ANCHOR);
- return;
- }
- log.debug("{} The following trust anchors were validated: {}", getLogPrefix(), validatedAnchors);
- cachedMessageContext.setValidatedRequest(
- new ResolveEntityRequest(requestMessage.getEndpointURI(), requestMessage.getSubject(),
- validatedAnchors, requestMessage.getEntityTypes(), requestMessage.getClientAuthentication()));
- }
-
- /**
- * Verifies whether the given trust anchor candidate is locally trusted.
- *
- * @param candidate the trust anchor candidate
- * @return true if locally trusted, false otherwise
- */
- protected boolean isLocallyTrusted(@Nullable final String candidate) {
- if (StringSupport.trimOrNull(candidate) == null) {
- return false;
- }
- assert candidate != null;
- final SubjectEntityIDCriterion criterion = new SubjectEntityIDCriterion(candidate);
- try {
- final List<Map<String,LocalKeyContainer>> result = localTrustAnchorsCache.get(new CriteriaSet(criterion));
- if (result.isEmpty() || result.get(0).isEmpty()) {
- log.debug("{} No locally trusted keys found for {}", getLogPrefix(), candidate);
- return false;
- }
- return result.get(0).containsKey(candidate);
- } catch (final MetadataCacheException e) {
- log.error("{} Could not fetch value for {} from the metadata cache", getLogPrefix(), candidate, e);
- }
- return false;
- }
-}
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateSelectedTrustChain.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateSelectedTrustChain.java
deleted file mode 100644
index d585f54..0000000
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateSelectedTrustChain.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.impl;
-
-import java.util.ArrayList;
-import java.util.List;
-import java.util.function.Function;
-
-import javax.annotation.Nonnull;
-
-import org.opensaml.profile.action.EventIds;
-import org.opensaml.profile.context.ProfileRequestContext;
-import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
-import org.slf4j.Logger;
-
-import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.oidc.metadata.cache.MetadataCache;
-import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
-import net.shibboleth.oidfed.messaging.impl.ResolveEntityRequest;
-import net.shibboleth.oidfed.metadata.EntityStatement;
-import net.shibboleth.oidfed.metadata.cache.SubjectEntityIDCriterion;
-import net.shibboleth.oidfed.metadata.cache.configuration.EntityConfigurationContainer;
-import net.shibboleth.oidfed.profile.context.RelyingPartyTrustChainContext;
-import net.shibboleth.oidfed.profile.context.VerifiedTrustChain;
-import net.shibboleth.oidfed.profile.impl.RelyingPartyCachedMessageContext;
-import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
-import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.component.ComponentInitializationException;
-import net.shibboleth.shared.logic.Constraint;
-import net.shibboleth.shared.primitive.LoggerFactory;
-import net.shibboleth.shared.resolver.CriteriaSet;
-
-import org.opensaml.messaging.context.navigate.ChildContextLookup;
-import org.opensaml.profile.action.ActionSupport;
-
-/**
- * Validates that the currenty selected trust chain meets the trust anchor requirements in the resolve entity request.
- * If not and if other candidates remains, {@link OidFederationEventIds#RESELECT_TRUST_CHAIN} is published. If no other
- * candidates are available, {@link OidFederationEventIds#INVALID_TRUST_ANCHOR} or
- * {@link OidFederationEventIds#INVALID_SUBJECT} is published.
- *
- * @event {@link EventIds#PROCEED_EVENT_ID}
- * @event {@link EventIds#INVALID_MSG_CTX}
- * @event {@link OidFederationEventIds#RESELECT_TRUST_CHAIN}
- * @event {@link OidFederationEventIds#INVALID_TRUST_ANCHOR}
- * @event {@link OidFederationEventIds#INVALID_SUBJECT}
- */
-public class ValidateSelectedTrustChain extends AbstractProfileAction {
-
- /** Class logger. */
- @Nonnull private Logger log = LoggerFactory.getLogger(ValidateSelectedTrustChain.class);
-
- /** Metadata cache for entity configurations. */
- @NonnullAfterInit private MetadataCache<EntityConfigurationContainer> entityConfigurationCache;
-
- /** Strategy used to lookup the trust chain context. */
- @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
-
- /** Strategy used to locate the resolve entity context. */
- @Nonnull
- private Function<ProfileRequestContext, RelyingPartyCachedMessageContext> resolveEntityContextLookupStrategy;
-
- /** The validated request to operate on. */
- @NonnullBeforeExec private ResolveEntityRequest validatedRequest;
-
- /**
- * Constructor.
- */
- public ValidateSelectedTrustChain() {
- final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
- new ChildContextLookup<>(RelyingPartyTrustChainContext.class).compose(
- new InboundMessageContextLookup());
- assert tcls != null;
- trustChainContextLookupStrategy = tcls;
- final Function<ProfileRequestContext, RelyingPartyCachedMessageContext> recls =
- new ChildContextLookup<>(RelyingPartyCachedMessageContext.class).compose(
- new InboundMessageContextLookup());
- assert recls != null;
- resolveEntityContextLookupStrategy = recls;
- }
-
- /**
- * Set the metadata cache for entity configurations.
- *
- * @param cache What to set.
- */
- public void setEntityConfigurationCache(@Nonnull final MetadataCache<EntityConfigurationContainer> cache) {
- checkSetterPreconditions();
- entityConfigurationCache = Constraint.isNotNull(cache, "Entity configuration metadata cache cannot be null");
- }
-
- /**
- * Set the strategy used to lookup the trust chain context.
- *
- * @param strategy lookup strategy
- */
- public void setTrustChainContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext, RelyingPartyTrustChainContext> strategy) {
- checkSetterPreconditions();
- trustChainContextLookupStrategy =
- Constraint.isNotNull(strategy, "TrustChainContextLookupStrategy cannot be null");
- }
-
- /**
- * Set the strategy used to locate the resolve entity context
- *
- * @param strategy What to set.
- */
- public void setResolveEntityContextLookupStrategy(
- @Nonnull final Function<ProfileRequestContext,RelyingPartyCachedMessageContext> strategy) {
- checkSetterPreconditions();
- resolveEntityContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null!");
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doInitialize() throws ComponentInitializationException {
- super.doInitialize();
-
- if (entityConfigurationCache == null) {
- throw new ComponentInitializationException("Entity configuration metadata cache cannot be null");
- }
- }
-
- /** {@inheritDoc} */
- @Override
- protected boolean doPreExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- if (!super.doPreExecute(profileRequestContext)) {
- return false;
- }
-
- final RelyingPartyCachedMessageContext cachedResponseContext =
- resolveEntityContextLookupStrategy.apply(profileRequestContext);
- if (cachedResponseContext == null) {
- log.error("{} Could not resolve cached message context", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
-
- if (cachedResponseContext.getValidatedRequest() instanceof ResolveEntityRequest resolveEntityRequest) {
- validatedRequest = resolveEntityRequest;
- } else {
- log.error("{} Could not resolve request message", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_PROFILE_CTX);
- return false;
- }
-
- return true;
- }
-
- /** {@inheritDoc} */
- @Override
- protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
- final RelyingPartyTrustChainContext trustChainContext =
- trustChainContextLookupStrategy.apply(profileRequestContext);
- final VerifiedTrustChain selectedTrustChain =
- trustChainContext != null ? trustChainContext.getSelectedTrustChain() : null;
- if (selectedTrustChain == null) {
- final List<VerifiedTrustChain> allChains =
- trustChainContext != null ? trustChainContext.getPolicyCompliantTrustChains() : null;
- if (allChains == null || allChains.isEmpty()) {
- if (isSubjectValid(validatedRequest.getSubject())) {
- log.debug("{} No trust chains were resolved, subject is valid", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.INVALID_TRUST_ANCHOR);
- return;
- } else {
- log.debug("{} No trust chains were resolved, subject is not valid", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.INVALID_SUBJECT);
- return;
- }
- } else {
- log.debug("{} No trust chains left to choose from", getLogPrefix());
- ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.INVALID_TRUST_ANCHOR);
- return;
- }
- }
-
- final List<String> trustAnchors = validatedRequest.getTrustAnchors();
- final List<EntityStatement<?>> candidateChain = selectedTrustChain.getTrustChain();
- assert candidateChain != null;
- final String candidateAnchor = candidateChain.get(candidateChain.size() - 1).getSubject();
- if (!trustAnchors.contains(candidateAnchor)) {
- log.debug("{} Selected trust chain candidate has unrequested trust anchor {}", getLogPrefix(),
- candidateAnchor);
- assert trustChainContext != null;
- final List<List<EntityStatement<?>>> rejectedTrustChains = trustChainContext.getRejectedTrustChains();
- if (rejectedTrustChains == null) {
- trustChainContext.setRejectedTrustChains(List.of(selectedTrustChain.getTrustChain()));
- } else {
- final List<List<EntityStatement<?>>> rejectedChains = new ArrayList<>(rejectedTrustChains);
- rejectedChains.add(selectedTrustChain.getTrustChain());
- trustChainContext.setRejectedTrustChains(CollectionSupport.copyToList(rejectedChains));
- }
- ActionSupport.buildEvent(profileRequestContext, OidFederationEventIds.RESELECT_TRUST_CHAIN);
- return;
- }
- }
-
- /**
- * Checks if an entity configuration can be resolved for the given subject and it's thus valid for federation.
- *
- * @param subject the subject to be verified
- * @return true if the given subject is valid, false otherwise.
- */
- protected boolean isSubjectValid(@Nonnull final String subject) {
- final SubjectEntityIDCriterion subjectCriterion = new SubjectEntityIDCriterion(subject);
- try {
- final List<EntityConfigurationContainer> result =
- entityConfigurationCache.get(new CriteriaSet(subjectCriterion));
- if (result.size() == 1 && result.get(0).getStatement() != null) {
- return true;
- }
- } catch (final MetadataCacheException e) {
- log.debug("{} Exception catched when resolving entty configuration", e);
- }
- return false;
- }
-}
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultEntityTypesLookupFunction.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultEntityTypesLookupFunction.java
deleted file mode 100644
index e2bd6d2..0000000
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultEntityTypesLookupFunction.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.oidfed.profile.navigate;
-
-import java.util.List;
-import java.util.Optional;
-import java.util.function.Function;
-
-import javax.annotation.Nullable;
-
-import org.opensaml.profile.context.ProfileRequestContext;
-
-import net.shibboleth.oidfed.messaging.impl.ResolveEntityRequest;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.shibboleth.shared.primitive.NonnullSupplier;
-
-/**
- * Default function to lookup entity types to be included to the response metadata.
- */
-public class DefaultEntityTypesLookupFunction implements Function<ProfileRequestContext, List<String>> {
-
- /** {@inheritDoc} */
- @Override @Nullable
- public List<String> apply(@Nullable final ProfileRequestContext profileRequestContext) {
- return Optional.ofNullable(profileRequestContext)
- .map(prc -> prc.getInboundMessageContext())
- .map(msgCtx -> msgCtx.getMessage())
- .filter(ResolveEntityRequest.class::isInstance)
- .map(ResolveEntityRequest.class::cast)
- .map(req -> req.getEntityTypes())
- .orElseGet(NonnullSupplier.of(CollectionSupport.emptyList()));
- }
-
-}
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
index 3f1d26f..a45f92a 100644
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
+++ b/idp-oidfed-op-impl/src/main/resources/META-INF/net.shibboleth.idp/postconfig.xml
@@ -51,39 +51,6 @@
<bean p:id="oidc/metadata-lookup-ext/oidfed" parent="shibboleth.oidc.MetadataLookupExtensionFlow" />
- <bean id="shibboleth.oidc.DefaultResolveEntityApiMappedErrors"
- parent="shibboleth.oidc.DefaultApiMappedErrors"
- class="org.springframework.beans.factory.config.MapFactoryBean">
- <property name="sourceMap">
- <map merge="true" value-type="com.nimbusds.oauth2.sdk.ErrorObject">
- <entry>
- <key>
- <util:constant static-field="org.opensaml.profile.action.EventIds.INVALID_MSG_CTX"/>
- </key>
- <bean class="com.nimbusds.oauth2.sdk.ErrorObject" c:_0="server_error" c:_1="Internal server error" c:_2="500" />
- </entry>
- <entry>
- <key>
- <util:constant static-field="net.shibboleth.oidfed.profile.OidFederationEventIds.INVALID_TRUST_ANCHOR"/>
- </key>
- <bean class="com.nimbusds.oauth2.sdk.ErrorObject" c:_0="invalid_trust_anchor" c:_1="Trust anchor in the request is invalid" c:_2="404" />
- </entry>
- <entry>
- <key>
- <util:constant static-field="net.shibboleth.oidfed.profile.OidFederationEventIds.INVALID_SUBJECT"/>
- </key>
- <bean class="com.nimbusds.oauth2.sdk.ErrorObject" c:_0="invalid_subject" c:_1="Subject in the request is invalid" c:_2="404" />
- </entry>
- <entry>
- <key>
- <util:constant static-field="net.shibboleth.oidfed.profile.OidFederationEventIds.INVALID_METADATA"/>
- </key>
- <bean class="com.nimbusds.oauth2.sdk.ErrorObject" c:_0="invalid_metadata" c:_1="Metadata is invalid or not found for the requested entity types" c:_2="400" />
- </entry>
- </map>
- </property>
- </bean>
-
<bean id="shibboleth.oidfed.register.DefaultMappedErrors"
parent="shibboleth.oidc.register.DefaultMappedErrors"
class="org.springframework.beans.factory.config.MapFactoryBean">
@@ -186,9 +153,6 @@
<entry key="#{T(net.shibboleth.oidc.profile.config.OIDCUserInfoConfiguration).PROFILE_ID}">
<ref bean="shibboleth.oidfed.userinfo.DefaultAutomaticRegistrationCondition"/>
</entry>
- <entry key="#{T(net.shibboleth.oidfed.profile.config.OIDFederationResolveEntityProfileConfiguration).PROFILE_ID}">
- <ref bean="shibboleth.oidfed.resolve-entity.DefaultAutomaticRegistrationCondition"/>
- </entry>
</util:map>
</property>
</bean>
@@ -283,14 +247,6 @@
</constructor-arg>
</bean>
- <bean id="shibboleth.oidfed.resolve-entity.DefaultAutomaticRegistrationCondition" parent="shibboleth.Conditions.AND">
- <constructor-arg>
- <list>
- <ref bean="%{idp.oidfed.resolve-entity.automaticRegistrationCondition:shibboleth.Conditions.TRUE}"/>
- </list>
- </constructor-arg>
- </bean>
-
<bean class="net.shibboleth.idp.plugin.oidc.op.security.jwt.claims.RequestObjectClaimsValidator">
<constructor-arg>
<bean id="shibboleth.oidfed.DefaultRequestObjectClaimsValidation"
@@ -411,10 +367,6 @@
</property>
</bean>
- <!-- Property-based definition of login flows for the resolve-entity endpoint. -->
- <bean id="shibboleth.oidfed.resolver.PotentialFlows" class="org.springframework.beans.factory.config.ListFactoryBean"
- p:sourceList="#{getObject('shibboleth.AuthenticationFlowDescriptorManager').getComponents().?[id matches 'authn/(' + '%{idp.oidfed.resolve-entity.authn.flows:OAuth2Client}'.trim() + ')']}" />
-
<bean id="ProviderMetadataEntityConfigurationMetadataDecorator"
class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.ProviderMetadataEntityConfigurationMetadataDecorator"
p:metadataResolver-ref="#{'%{idp.oidfed.op.metadata.resolver:shibboleth.oidfed.DefaultOpenIdConfigurationResolver}'.trim()}"/>
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/resolve-entity/resolve-entity-beans.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/resolve-entity/resolve-entity-beans.xml
deleted file mode 100644
index 4c41d7a..0000000
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/resolve-entity/resolve-entity-beans.xml
+++ /dev/null
@@ -1,218 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<beans xmlns="http://www.springframework.org/schema/beans" xmlns:c="http://www.springframework.org/schema/c"
- xmlns:context="http://www.springframework.org/schema/context" xmlns:p="http://www.springframework.org/schema/p"
- xmlns:util="http://www.springframework.org/schema/util" 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"
- default-init-method="initialize" default-destroy-method="destroy">
-
- <bean id="shibboleth.oidc.profileId" class="java.lang.String"
- c:_0="#{T(net.shibboleth.oidfed.profile.config.OIDFederationResolveEntityProfileConfiguration).PROFILE_ID}" />
-
- <bean id="shibboleth.oidc.loggingId" class="java.lang.String" c:_0="%{idp.service.logging.oidfedresolve:OIDFED.ResolveEntity}" />
-
- <bean id="shibboleth.oidc.browserProfile" class="java.lang.Boolean" c:_0="false" />
-
- <util:constant id="shibboleth.metrics.ProfileCounter"
- static-field="net.shibboleth.oidfed.profile.config.impl.DefaultOIDFederationResolveEntityProfileConfiguration.PROFILE_COUNTER" />
-
- <bean id="DecodeMessage" class="org.opensaml.profile.action.impl.DecodeMessage" scope="prototype">
- <constructor-arg>
- <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.decoding.impl.ResolveEntityRequestDecoder" scope="prototype"
- p:httpServletRequestSupplier-ref="shibboleth.HttpServletRequestSupplier"
- p:removeIpAddressFromEndpointUri="%{idp.oidc.logging.removeIpAddressFromProtocolMessage:false}"/>
- <!-- TODO: support custom parsing? p:customRequestParser="#{getObject('%{idp.oidfed.requestParser.ResolveEntityRequest:}'.trim())}"/>-->
- </constructor-arg>
- </bean>
-
- <bean id="InitializeOutboundMessageContext"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeOutboundResponseMessageContext"
- scope="prototype" />
-
- <bean id="shibboleth.ClientIDLookupStrategy" parent="shibboleth.Functions.Expression" c:expression="#input.getMessage().getClientAuthentication() != null ? #input.getMessage().getClientAuthentication().getClientID() : new com.nimbusds.oauth2.sdk.id.ClientID(#input.getMessage().getSubject())" />
-
- <bean id="InitializeRelyingPartyContext"
- class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeRelyingPartyContext" scope="prototype"
- p:clientIDLookupStrategy-ref="shibboleth.ClientIDLookupStrategy"
- p:inbound="true" />
-
- <bean id="InitializeAuthenticationContext"
- class="net.shibboleth.idp.saml.profile.impl.InitializeAuthenticationContext" scope="prototype" />
-
- <bean id="shibboleth.oidfed.ResolveEntityResponseMetadataCache" parent="shibboleth.oidc.CacheBuilder">
- <constructor-arg>
- <bean p:cacheId="DefaultResolveEntityResponseMetadataCache" parent="shibboleth.oidfed.ResolveEntityResponseMetadataCacheBuilderSpec"
- p:cleanupTaskInterval="PT30S"/>
- </constructor-arg>
- </bean>
-
- <bean id="shibboleth.oidfed.ResolveEntityResponseMetadataCacheBuilderSpec"
- class="net.shibboleth.oidc.metadata.cache.impl.DynamicMetadataCacheBuilderSpec"
- p:minCacheDuration="%{idp.oidfed.cache.resolveEntity.minRefreshDelay:PT1S}"
- p:maxCacheDuration="%{idp.oidfed.cache.resolveEntity.maxRefreshDelay:PT30S}">
- <property name="criteriaToIdentifierStrategy">
- <bean parent="shibboleth.Functions.Expression"
- c:expression="#input?.get(T(net.shibboleth.oidfed.metadata.cache.local.ResolveEntityRequestCriterion))?.getRequest().toString()"/>
- </property>
- <property name="identifierExtractionStrategy">
- <bean parent="shibboleth.Functions.Expression"
- c:expression="#input?.getRequest()?.toString()"/>
- </property>
- <property name="metadataExpirationTimeStrategy">
- <bean class="net.shibboleth.oidfed.metadata.cache.local.DefaultNimbusResponseContainerExpirationTimeStrategy"/>
- </property>
- <property name="metadataFilterStrategy">
- <bean parent="shibboleth.BiFunctions.Expression" c:expression="#input1"/>
- </property>
- <property name="fetchStrategy">
- <bean class="net.shibboleth.oidfed.metadata.cache.local.DefaultResolveEntityResponseFetchingStrategy" />
- </property>
- </bean>
-
- <bean id="ValidateRequest" class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.ValidateResolveEntityRequest"
- scope="prototype"
- p:localTrustAnchorsCache-ref="#{'%{idp.oidfed.resolve-entity.LocalTrustAnchorsMetadataCache:shibboleth.oidfed.LocalTrustAnchorsMetadataCache}'.trim()}" />
-
- <bean id="LookupCachedResolveEntityResponse"
- class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.LookupCachedResolveEntityResponse"
- scope="prototype"
- p:responseCache-ref="shibboleth.oidfed.ResolveEntityResponseMetadataCache" />
-
- <bean id="shibboleth.oidfed.trust-chain-resolver.EntityIDLookupStrategy"
- parent="shibboleth.Functions.Expression"
- c:expression="#custom.apply(#input)">
- <property name="customObject">
- <bean parent="shibboleth.Functions.Expression" c:expression="#input.getInboundMessageContext()?.getMessage()?.getSubject()" />
- </property>
- </bean>
-
- <bean id="DefaultMetadataPolicyEnforcer"
- class="net.shibboleth.oidfed.metadata.policy.impl.DefaultFederationMetadataPolicyEnforcer"
- p:metadataPolicyOperators-ref="#{'%{idp.oidfed.resolve-entity.MetadataPolicyOperatorsy:shibboleth.oidfed.StandardMetadataPolicyOperators}'.trim()}"/>
-
- <bean id="DefaultTrustChainMetadataPolicyMergingStrategy"
- class="net.shibboleth.oidfed.profile.navigate.DefaultTrustChainMetadataPolicyMergingStrategy"
- p:metadataPolicyMergingStrategy-ref="#{'%{idp.oidfed.resolve-entity.MetadataPolicMergingyStrategy:MetadataPolicMergingyStrategy}'.trim()}"
- p:localMetadataPolicyStrategy-ref="#{'%{idp.oidfed.resolve-entity.LocalMetadataPolicyStrategy:DefaultLocalMetadataPolicyStrategy}'.trim()}"/>
-
- <bean id="MetadataPolicMergingyStrategy"
- class="net.shibboleth.oidfed.metadata.policy.impl.DefaultFederationMetadataPolicyMergingStrategy"
- p:metadataPolicyOperators-ref="#{'%{idp.oidfed.resolve-entity.MetadataPolicyOperatorsy:shibboleth.oidfed.StandardMetadataPolicyOperators}'.trim()}"/>
-
- <bean id="DefaultLocalMetadataPolicyStrategy"
- parent="shibboleth.Functions.Constant">
- <constructor-arg name="target">
- <util:map/>
- </constructor-arg>
- </bean>
-
- <bean id="SelectTrustChain" class="net.shibboleth.oidfed.profile.impl.SelectTrustChain"
- scope="prototype">
- <property name="activationCondition">
- <bean parent="shibboleth.Conditions.Expression"
- c:expression="#input.ensureInboundMessageContext().containsSubcontext(T(net.shibboleth.oidfed.profile.context.RelyingPartyTrustChainContext))" />
- </property>
- </bean>
-
- <bean id="ValidateSelectedTrustChain" class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.ValidateSelectedTrustChain"
- scope="prototype"
- p:entityConfigurationCache-ref="shibboleth.oidfed.EntityConfigurationMetadataCache"/>
-
- <bean id="ResolveTrustMarks" class="net.shibboleth.oidfed.profile.impl.ResolveTrustMarks"
- scope="prototype"
- p:trustChainCache-ref="#{'%{idp.oidfed.resolve-entity.TrustChainMetadataCache:shibboleth.oidfed.TrustChainMetadataCache}'.trim()}"
- p:trustMarkClaimsValidationLookupStrategy="#{getObject('shibboleth.oidfed.TrustMarkClaimsValidationLookupStrategy') ?: getObject('DefaultTrustMarkClaimsValidationLookupStrategy')}"
- p:delegatedTrustMarkClaimsValidationLookupStrategy="#{getObject('shibboleth.oidfed.DelegatedTrustMarkClaimsValidationLookupStrategy') ?: getObject('DefaultDelegatedTrustMarkClaimsValidationLookupStrategy')}"
- p:trustEngine-ref="shibboleth.oidfed.DefaultEntityConfigurationTrustEngine"
- p:delegationTrustEngine-ref="shibboleth.oidfed.DefaultDelegatedTrustMarkTrustEngine">
- <property name="trustChainTrustMarksParsingStrategy">
- <bean class="net.shibboleth.oidfed.profile.navigate.DefaultTrustChainTrustMarksParsingStrategy"
- p:objectMapper-ref="shibboleth.oidfed.JWTPayloadJSONObjectMapper"/>
- </property>
- <property name="trustedTrustMarkIssuersLookupStrategy">
- <bean class="net.shibboleth.oidfed.profile.navigate.DefaultTrustChainTrustedTrustMarkIssuersLookupStrategy"/>
- </property>
- <property name="trustedTrustMarkOwnersLookupStrategy">
- <bean class="net.shibboleth.oidfed.profile.navigate.DefaultTrustChainTrustedTrustMarkOwnersLookupStrategy"/>
- </property>
- </bean>
-
- <bean id="ValidateTrustMarks" class="net.shibboleth.oidfed.profile.impl.ValidateTrustMarks"
- scope="prototype"
- p:trustMarkStatusCache-ref="#{'%{idp.oidfed.resolve-entity.TrustMarkStatusMetadataCache:shibboleth.oidfed.TrustMarkStatusMetadataCache}'.trim()}">
- </bean>
-
- <bean id="PopulateResolveResponseSignatureSigningParameters"
- class="net.shibboleth.oidc.profile.impl.PopulateJWTSignatureSigningParameters" scope="prototype"
- c:strategy-ref="shibboleth.MessageContextLookup.Outbound"
- p:securityParametersContextLookupStrategy-ref="ResolveResponseSecurityParametersContextLookupStrategy">
- <property name="configurationLookupStrategy">
- <bean lazy-init="true"
- class="net.shibboleth.oidc.profile.config.navigate.JWTSignatureSigningConfigurationLookupFunction" />
- </property>
- <property name="signatureSigningParametersResolver">
- <bean class="net.shibboleth.oidc.security.jose.impl.ClientInformationSignatureSigningParametersResolver">
- <constructor-arg name="signatureAlgorithmLookupStrategy">
- <bean parent="shibboleth.Functions.Constant" c:target="" />
- </constructor-arg>
- <constructor-arg name="defaultAlgorithmValue" value="%{idp.oidfed.entity.sigalg:RS256}" />
- </bean>
- </property>
- </bean>
-
- <bean id="ResolveResponseSecurityParametersContextLookupStrategy" parent="shibboleth.Functions.Compose"
- c:g-ref="shibboleth.oidc.ChildLookupOrCreate.JWTSecurityParameters"
- c:f-ref="shibboleth.ChildLookup.RelyingParty" />
-
- <bean id="ResolveResponseSecurityParametersCreationViaMessageContextStrategy" parent="shibboleth.Functions.Compose">
- <constructor-arg name="g" ref="ResolveResponseSecurityParametersContextLookupStrategy" />
- <constructor-arg name="f">
- <bean parent="shibboleth.Functions.Expression" c:expression="#input.getParent()" />
- </constructor-arg>
- </bean>
-
- <bean id="BuildResolveResponse"
- class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.BuildResolveEntityResponse" scope="prototype"
- p:identifierGeneratorLookupStrategy-ref="shibboleth.oidc.DefaultIdentifierGenerationStrategy"
- p:objectMapper-ref="#{'%{idp.oidc.logging.objectMapper:shibboleth.oidc.JSONObjectMapper}'.trim()}">
- <property name="subjectLookupStrategy">
- <bean parent="shibboleth.Functions.Expression"
- c:expression="#input.ensureInboundMessageContext().getMessage().getSubject()" />
- </property>
- </bean>
-
- <bean id="SignResolveResponse" class="net.shibboleth.idp.profile.impl.WebFlowMessageHandlerAdaptor"
- scope="prototype" c:executionDirection="OUTBOUND ">
- <constructor-arg name="messageHandler">
- <bean id="SignResolveResponseHandler"
- class="net.shibboleth.oidc.security.impl.SignJWTHandler" scope="prototype" p:logName="Resolve Response"
- p:securityParametersLookupStrategy-ref="ResolveResponseSecurityParametersCreationViaMessageContextStrategy"
- p:typeHeader="resolve-response+jwt">
- <property name="claimsToSignLookupStrategy">
- <bean
- class="net.shibboleth.oidfed.profile.impl.JWTClaimsSetFromEntityStatementLookupFunction" />
- </property>
- <property name="jwtUpdateConsumer">
- <bean
- class="net.shibboleth.oidfed.profile.impl.EntityStatementUpdateStrategy" />
- </property>
- </bean>
- </constructor-arg>
- </bean>
-
- <bean id="FormOutboundMessage"
- class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.FormOutboundResolveEntityResponse" scope="prototype"
- p:responseCache-ref="shibboleth.oidfed.ResolveEntityResponseMetadataCache" />
-
- <bean id="BuildErrorResponseFromEvent"
- class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.BuildResolveEntityErrorResponseFromEvent" scope="prototype"
- p:httpServletResponseSupplier-ref="shibboleth.HttpServletResponseSupplier"
- p:mappedErrors="#{getObject('shibboleth.oidfed.resolve-entity.MappedErrors') ?: getObject('shibboleth.oidc.DefaultResolveEntityApiMappedErrors')}"
- p:responseCache-ref="shibboleth.oidfed.ResolveEntityResponseMetadataCache">
- <property name="eventContextLookupStrategy">
- <bean class="net.shibboleth.idp.profile.context.navigate.WebFlowCurrentEventLookupFunction" />
- </property>
- </bean>
-
-</beans>
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/resolve-entity/resolve-entity-flow.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/resolve-entity/resolve-entity-flow.xml
deleted file mode 100644
index 38665e3..0000000
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidfed/resolve-entity/resolve-entity-flow.xml
+++ /dev/null
@@ -1,112 +0,0 @@
-<flow xmlns="http://www.springframework.org/schema/webflow"
- xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
- xsi:schemaLocation="http://www.springframework.org/schema/webflow http://www.springframework.org/schema/webflow/spring-webflow.xsd"
- parent="oidc/abstract, oidc/metadata-lookup, oidfed/resolve-trust-chains">
-
- <action-state id="InitializeMandatoryContexts">
- <evaluate expression="InitializeProfileRequestContext" />
- <evaluate expression="PopulateMetricContext" />
- <evaluate expression="FlowStartPopulateAuditContext" />
- <evaluate expression="InitializeOutboundMessageContext" />
- <evaluate expression="'proceed'" />
-
- <transition on="proceed" to="DecodeMessage">
- <set name="flowScope.transitionAfterDecode" value="'PostDecode'" />
- </transition>
- </action-state>
-
- <action-state id="PostDecode">
- <on-entry>
- <set name="flowScope.skipOAuth2ClientAuth" value="opensamlProfileRequestContext.getInboundMessageContext().getMessage().getClientAuthentication() == null" />
- </on-entry>
- <evaluate expression="'proceed'"/>
- <transition on="proceed" to="#{skipOAuth2ClientAuth ? 'SelectConfiguration' : 'DoMetadataLookup'}" />
- </action-state>
-
- <action-state id="SelectConfiguration">
- <evaluate expression="InitializeRelyingPartyContext" />
- <evaluate expression="SelectRelyingPartyConfiguration" />
- <evaluate expression="SelectProfileConfiguration" />
- <evaluate expression="CallInboundMessageHandler" />
- <evaluate expression="PostLookupPopulateAuditContext" />
- <evaluate expression="PopulateInboundInterceptContext" />
- <evaluate expression="'proceed'" />
-
- <transition on="proceed" to="CheckInboundInterceptContext" />
- </action-state>
-
- <decision-state id="CheckInboundInterceptContext">
- <if test="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.profile.context.ProfileInterceptorContext)).getAvailableFlows().isEmpty()"
- then="#{skipOAuth2ClientAuth ? 'ResumeAfterAuthentication' : 'AuthenticationSetup'}" else="DoInboundInterceptSubflow" />
- </decision-state>
-
- <subflow-state id="DoInboundInterceptSubflow" subflow="intercept">
- <input name="calledAsSubflow" value="true" />
- <transition on="proceed" to="#{skipOAuth2ClientAuth ? 'ResumeAfterAuthentication' : 'AuthenticationSetup'}" />
- </subflow-state>
-
- <action-state id="AuthenticationSetup">
- <evaluate expression="CallInboundMessageHandler" />
- <evaluate expression="InitializeAuthenticationContext" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="DoAuthenticationSubflow" />
- </action-state>
-
- <subflow-state id="DoAuthenticationSubflow" subflow="authn">
- <input name="calledAsSubflow" value="true" />
- <input name="bypassSessionActions" value="true" />
- <input name="potentialFlows" value="getActiveFlow().getApplicationContext().getBean('shibboleth.oidfed.resolver.PotentialFlows')" />
- <transition on="proceed" to="ResumeAfterAuthentication" />
- <transition on="RestartAuthentication" to="AuthenticationSetup" />
- </subflow-state>
-
- <!-- Authentication subflow happens here. -->
-
- <action-state id="ResumeAfterAuthentication">
- <evaluate expression="ValidateRequest" />
- <evaluate expression="LookupCachedResolveEntityResponse" />
- <evaluate expression="'proceed'" />
- <transition on="CachedResponseFound" to="BuildResponseMessage" />
- <transition on="proceed" to="ResolveTrustChains" />
- </action-state>
-
- <action-state id="ResolveTrustChains">
- <evaluate expression="ResolveTrustChains" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="SelectTrustChain" />
- </action-state>
-
- <action-state id="SelectTrustChain">
- <evaluate expression="SelectTrustChain" />
- <evaluate expression="ValidateSelectedTrustChain" />
- <evaluate expression="ResolveTrustMarks" />
- <evaluate expression="ValidateTrustMarks" />
- <evaluate expression="'proceed'" />
- <transition on="ReselectTrustChain" to="SelectTrustChain" />
- <transition on="proceed" to="BuildResponse" />
- </action-state>
-
-
- <action-state id="BuildResponse">
- <evaluate expression="PopulateResolveResponseSignatureSigningParameters" />
- <evaluate expression="BuildResolveResponse" />
- <evaluate expression="SignResolveResponse" />
- <evaluate expression="'proceed'" />
-
- <transition on="proceed" to="BuildResponseMessage" />
- </action-state>
-
- <action-state id="HandleError">
- <on-entry>
- <evaluate
- expression="opensamlProfileRequestContext.ensureSubcontext(T(net.shibboleth.idp.profile.context.SpringRequestContext)).setRequestContext(flowRequestContext)" />
- <evaluate expression="LogEvent" />
- </on-entry>
- <evaluate expression="BuildErrorResponseFromEvent" />
- <evaluate expression="'proceed'" />
- <transition on="proceed" to="PopulateOutboundInterceptContext"/>
- </action-state>
-
- <bean-import resource="resolve-entity-beans.xml" />
-
-</flow>
\ No newline at end of file
diff --git a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index 80ad2a6..042d770 100644
--- a/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidfed-op-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -48,15 +48,4 @@
p:remoteTrustMarkValidation="%{idp.oidfed.op.explicitRegistration.remoteTrustMarkValidation:true}"
p:tokenEndpointAuthMethods="%{idp.oidfed.op.explicitRegistration.tokenEndpointAuthMethods:client_secret_basic,client_secret_post,client_secret_jwt,private_key_jwt}"/>
- <bean id="OIDFED.ResolveEntity" parent="AbstractOIDFederationProfile" lazy-init="true"
- class="net.shibboleth.oidfed.profile.config.impl.DefaultOIDFederationResolveEntityProfileConfiguration"
- p:issuer-ref="shibboleth.oidc.issuer"
- p:tokenEndpointAuthMethods="%{idp.oidfed.resolve-entity.endpointAuthMethods:private_key_jwt}"
- p:claimsValidator="#{getObject('DefaultJWTClaimsValidator')}"
- p:useTargetedEndpointAsJWTAudience="%{idp.oidfed.resolve-entity.targetedEndpointAsJWTAudience:false}"
- p:requireSingleJWTAudience="%{idp.oidfed.resolve-entity.requireSingleJWTAudience:true}"/>
-
- <bean id="RelyingPartyByTrustAnchor" abstract="true" parent="RelyingParty"
- class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.context.RelyingPartyConfigurationSupport" factory-method="byTrustAnchor" />
-
</beans>
diff --git a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-op.properties b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-op.properties
index 303bf80..40f23fe 100644
--- a/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-op.properties
+++ b/idp-oidfed-op-impl/src/main/resources/net/shibboleth/idp/plugin/oidc/op/oidfed/conf/oidfed/oidfed-op.properties
@@ -62,25 +62,3 @@ idp.oidfed.op.explicitRegistration.FederationPolicyConstraints = shibboleth.oidf
idp.service.logging.oidfedconfig = OIDFED.SignedKeyset
idp.oidfed.op.cache.signedKeyset.minRefreshDelay = PT1S
idp.oidfed.op.cache.signedKeyset.maxRefreshDelay = PT30S
-
-# resolve-entity-beans, TODO: move these to oidfed-common once the feature is moved there
-#idp.oidfed.resolve-entity.authn.flows = OAuth2Client
-
-
-#idp.service.logging.oidfedresolve = OIDFED.ResolveEntity
-#idp.oidfed.requestParser.ResolveEntityRequest =
-#idp.oidfed.cache.resolveEntity.minRefreshDelay = PT1S
-#idp.oidfed.cache.resolveEntity.maxRefreshDelay = PT30S
-#idp.oidfed.resolve-entity.LocalTrustAnchorsMetadataCache = shibboleth.oidfed.LocalTrustAnchorsMetadataCache
-#idp.oidfed.resolve-entity.MetadataPolicyOperatorsy = shibboleth.oidfed.StandardMetadataPolicyOperators
-#idp.oidfed.resolve-entity.MetadataPolicMergingyStrategy = MetadataPolicMergingyStrategy
-#idp.oidfed.resolve-entity.LocalMetadataPolicyStrategy = DefaultLocalMetadataPolicyStrategy
-#idp.oidfed.resolve-entity.TrustChainMetadataCache = shibboleth.oidfed.TrustChainMetadataCache
-#idp.oidfed.resolve-entity.TrustMarkStatusMetadataCache = shibboleth.oidfed.TrustMarkStatusMetadataCache
-#idp.oidfed.entity.sigalg = RS256
-#idp.oidfed.resolve-entity.automaticRegistrationCondition = shibboleth.Conditions.TRUE
-
-# relying-party/postconfig.xml
-#idp.oidfed.resolve-entity.endpointAuthMethods = private_key_jwt
-#idp.oidfed.resolve-entity.targetedEndpointAsJWTAudience = false
-#idp.oidfed.resolve-entity.requireSingleJWTAudience = true
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/ResolveEntityFlowTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/ResolveEntityFlowTest.java
deleted file mode 100644
index c6bde81..0000000
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/ResolveEntityFlowTest.java
+++ /dev/null
@@ -1,371 +0,0 @@
-/*
- * 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.idp.plugin.oidc.op.profile.flow.oidfed;
-
-import java.io.IOException;
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.time.Instant;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-import org.opensaml.storage.StorageService;
-import org.springframework.beans.factory.annotation.Autowired;
-import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.webflow.executor.FlowExecutionResult;
-import org.testng.Assert;
-import org.testng.annotations.Test;
-
-import com.fasterxml.jackson.databind.ObjectMapper;
-import com.nimbusds.jose.JOSEObjectType;
-import com.nimbusds.jose.JWSAlgorithm;
-import com.nimbusds.jose.jwk.JWKSet;
-import com.nimbusds.jwt.JWTClaimsSet;
-import com.nimbusds.jwt.SignedJWT;
-import com.nimbusds.oauth2.sdk.Response;
-import com.nimbusds.oauth2.sdk.Scope;
-import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
-
-import net.shibboleth.oidc.profile.messaging.JSONErrorResponse;
-import net.shibboleth.oidfed.messaging.impl.ResolveEntityResponse;
-import net.shibboleth.oidfed.metadata.EntityStatement;
-import net.shibboleth.oidfed.metadata.impl.SubordinateStatementImpl;
-import net.shibboleth.oidfed.testing.FederationJwtSupport;
-import net.shibboleth.shared.collection.CollectionSupport;
-import net.minidev.json.JSONObject;
-
-/**
- * Flow tests for the OpenID federation resolve entity flow.
- */
-public class ResolveEntityFlowTest extends AbstractFederationFlowTest {
-
- public static final String FLOW_ID = "oidfed/resolve-entity";
-
- @Autowired
- @Qualifier("shibboleth.StorageService")
- StorageService storageService;
-
- public ResolveEntityFlowTest() {
- super(FLOW_ID);
- }
-
- @Test
- public void testInvalidMethod() throws Exception {
- setJsonRequest("POST", "{}");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "invalid_request");
- }
-
- @Test
- public void testNoContentType() throws Exception {
- request.setMethod("GET");
- request.setQueryString("sub=mockClientId&trust_anchor=" + anchorId + "&entity_type=openid_relying_party");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "invalid_request");
- }
-
- @Test
- public void testInvalidSubject() throws Exception {
- request.setMethod("GET");
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=mockClientId&trust_anchor=" + anchorId + "&entity_type=openid_relying_party");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "invalid_subject");
- }
-
- @Test
- public void testUntrustedAnchor() throws Exception {
- request.setMethod("GET");
- final String clientId = uniqueClientId();
- rpConfigureMockHttpClient(clientId);
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=" + clientId + "&trust_anchor=mockAnchors&entity_type=openid_relying_party");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "invalid_trust_anchor");
- }
-
- @Test
- public void testRPWithTrustedTrustAnchor() throws Exception {
- request.setMethod("GET");
- final String clientId = uniqueClientId();
- rpConfigureMockHttpClient(clientId);
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=" + clientId + "&trust_anchor=" + anchorId + "&entity_type=openid_relying_party");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- final ResolveEntityResponse parsedResponse =
- parseSuccessResponse(result, ResolveEntityResponse.class);
- final SignedJWT response = parsedResponse.getJWT();
- Assert.assertEquals(response.getHeader().getType(), new JOSEObjectType("resolve-response+jwt"));
- Assert.assertEquals(response.getJWTClaimsSet().getSubject(), clientId);
- Assert.assertNotNull(response.getJWTClaimsSet().getClaim("metadata"));
- Assert.assertNull(response.getJWTClaimsSet().getClaim("authority_hints"));
- }
-
- @Test
- public void testRPWithTrustedTrustAnchor_validTrustMark() throws Exception {
- request.setMethod("GET");
- final String clientId = uniqueClientId();
- final String trustMark = FederationJwtSupport.trustMark(JWSAlgorithm.RS256, trustMarkIssuerKey, trustMarkIssuerId,
- clientId, "https://example.org/email-allowing-trust-mark", Instant.now().plusSeconds(300)).serialize();
- final OIDCClientMetadata metadata = new OIDCClientMetadata();
- metadata.setRedirectionURI(new URI(redirectUri));
- metadata.setJWKSet(new JWKSet(rpKey.toPublicJWK()));
- final String rpEntityConfiguration = rpEntityConfiguration(clientId, metadata, List.of(Map.of(
- "trust_mark_type", "https://example.org/email-allowing-trust-mark",
- "trust_mark", trustMark)), leafKey);
- rpConfigureMockHttpClient(clientId, rpEntityConfiguration);
- try {
- mapResponse(entityConfigurationUrl(trustMarkIssuerId),
- mockResponse(trustMarkIssuerConfiguration(trustMarkIssuerId)));
- mapResponse(subordinateStatementUrl(anchorFetchEndpoint, trustMarkIssuerId),
- mockResponse(subordinateStatement(trustMarkIssuerId,
- Map.of("federation_entity", CollectionSupport.emptyMap()), trustMarkIssuerKey)));
- mapResponse(trustMarkStatusEndpoint, mockResponse(200, "application/trust-mark-status-response+jwt",
- trustMarkStatusResponse(trustMarkIssuerId, trustMark, "active", trustMarkIssuerKey)));
- } catch (UnsupportedOperationException | IOException e) {
- Assert.fail("Could not initialize mock HTTP client", e);
- }
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=" + clientId + "&trust_anchor=" + anchorId + "&entity_type=openid_relying_party");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- final ResolveEntityResponse parsedResponse =
- parseSuccessResponse(result, ResolveEntityResponse.class);
- final SignedJWT response = parsedResponse.getJWT();
- Assert.assertEquals(response.getHeader().getType(), new JOSEObjectType("resolve-response+jwt"));
- Assert.assertEquals(response.getJWTClaimsSet().getSubject(), clientId);
- Assert.assertNotNull(response.getJWTClaimsSet().getClaim("metadata"));
- Assert.assertNull(response.getJWTClaimsSet().getClaim("authority_hints"));
- final Map<String,Object> trustMarks = response.getJWTClaimsSet().getJSONObjectClaim("trust_marks");
- Assert.assertNotNull(trustMarks, "Could not find trust marks for client " + clientId);
- Assert.assertEquals(trustMarks.size(), 1);
- Assert.assertEquals(trustMarks.get("https://example.org/email-allowing-trust-mark"), trustMark);
- }
-
- @Test
- public void testRPWithTrustedTrustAnchor_subordinateKeyNotMatchingEntityConfiguration() throws Exception {
- request.setMethod("GET");
- final String clientId = uniqueClientId();
- rpConfigureMockHttpClient(clientId, initializeNewJwk("RSA", 2048, "mockNewLeafKey"));
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=" + clientId + "&trust_anchor=" + anchorId + "&entity_type=openid_relying_party");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "invalid_request");
- assertErrorDescriptionContains(result, "NoTrustChainsResolved");
- }
-
- @Test
- public void testRPWithTrustedTrustAnchorInvalidMetadata() throws Exception {
- request.setMethod("GET");
- final String clientId = uniqueClientId();
- rpConfigureMockHttpClient(clientId, new JSONObject(Map.of("response_types", "invalid")));
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=" + clientId + "&trust_anchor=" + anchorId + "&entity_type=openid_relying_party");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "invalid_metadata");
- }
-
- @Test
- public void testOPWithTrustedTrustAnchor() throws Exception {
- request.setMethod("GET");
- final String entityId = uniqueClientId();
- opConfigureMockHttpClient(entityId);
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=" + entityId + "&trust_anchor=" + anchorId + "&entity_type=openid_provider");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- final ResolveEntityResponse parsedResponse =
- parseSuccessResponse(result, ResolveEntityResponse.class);
- final SignedJWT response = parsedResponse.getJWT();
- Assert.assertEquals(response.getHeader().getType(), new JOSEObjectType("resolve-response+jwt"));
- Assert.assertEquals(response.getJWTClaimsSet().getSubject(), entityId);
- Assert.assertNotNull(response.getJWTClaimsSet().getClaim("metadata"));
- Assert.assertNull(response.getJWTClaimsSet().getClaim("authority_hints"));
- }
-
- @Test
- public void testOPWithTrustedTrustAnchorInvalidMetadata() throws Exception {
- request.setMethod("GET");
- final String entityId = uniqueClientId();
- opConfigureMockHttpClient(entityId, new JSONObject(Map.of("issuer", List.of("unexpected", "values"))));
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=" + entityId + "&trust_anchor=" + anchorId + "&entity_type=openid_provider");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "invalid_metadata");
- }
-
- @Test
- public void testOPWithTrustedTrustAnchor_emptyMetadataPolicyCrit() throws Exception {
- request.setMethod("GET");
- final String entityId = uniqueClientId();
- final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issuer(anchorId).subject(entityId)
- .issueTime(Date.from(Instant.now()))
- .expirationTime(Date.from(Instant.now().plusSeconds(300)))
- .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
- .claim("metadata", Map.of("openid_provider", emptyOpMetadata(entityId).toJSONObject()))
- .build();
- final ObjectMapper objectMapper = payloadObjectMapper;
- assert objectMapper != null;
- final SignedJWT jwt = FederationJwtSupport.entityStatement(JWSAlgorithm.RS256, trustedAnchorKey, claimsSet);
- assert jwt != null;
- final EntityStatement<?> subordinateStatement = SubordinateStatementImpl.parse(jwt, objectMapper);
- try {
- mapResponse(entityConfigurationUrl(entityId), mockResponse(opEntityConfiguration(entityId)));
- mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
- mapResponse(subordinateStatementUrl(anchorFetchEndpoint, entityId),
- mockResponse(subordinateStatement.getJwt().serialize()));
- } catch (UnsupportedOperationException | IOException | URISyntaxException e) {
- Assert.fail("Could not initialize mock HTTP client", e);
- }
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=" + entityId + "&trust_anchor=" + anchorId + "&entity_type=openid_provider");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- final ResolveEntityResponse parsedResponse =
- parseSuccessResponse(result, ResolveEntityResponse.class);
- final SignedJWT response = parsedResponse.getJWT();
- Assert.assertEquals(response.getHeader().getType(), new JOSEObjectType("resolve-response+jwt"));
- Assert.assertEquals(response.getJWTClaimsSet().getSubject(), entityId);
- Assert.assertNotNull(response.getJWTClaimsSet().getClaim("metadata"));
- Assert.assertNull(response.getJWTClaimsSet().getClaim("authority_hints"));
- }
-
- @Test
- public void testRPWithTrustedTrustAnchor_jwtAuth_successWithLeafKey() throws Exception {
- request.setMethod("POST");
- final String requestingClientId = uniqueClientId();
- final String clientId = uniqueClientId();
- final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
- .issuer(requestingClientId)
- .subject(requestingClientId)
- .audience(issuer)
- .expirationTime(Date.from(Instant.now().plusSeconds(600)))
- .jwtID(idGenerator.generateIdentifier())
- .build();
-
- final SignedJWT jwt = createPrivateKeyJWT(claimsSet, leafKey.toRSAKey().toRSAPrivateKey());
- final Map<String, String> requestParams = new HashMap<>();
- requestParams.put("sub", clientId);
- requestParams.put("trust_anchor", anchorId);
- requestParams.put("entity_type", "openid_relying_party");
- populateClientAssertionParams(requestParams, jwt);
- rpConfigureMockHttpClient(requestingClientId);
- rpConfigureMockHttpClient(clientId);
- request.setContentType("application/x-www-form-urlencoded");
- setHttpFormRequest(request, "POST", requestParams);
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- final ResolveEntityResponse parsedResponse =
- parseSuccessResponse(result, ResolveEntityResponse.class);
- final SignedJWT response = parsedResponse.getJWT();
- Assert.assertEquals(response.getHeader().getType(), new JOSEObjectType("resolve-response+jwt"));
- Assert.assertEquals(response.getJWTClaimsSet().getSubject(), clientId);
- Assert.assertNotNull(response.getJWTClaimsSet().getClaim("metadata"));
- Assert.assertNull(response.getJWTClaimsSet().getClaim("authority_hints"));
- }
-
- @Test
- public void testRPWithTrustedTrustAnchor_basicAuth_successForLocalClient() throws Exception {
- request.setMethod("POST");
- final String registeredClientId = "localResolveEntityClient";
- final String secret = "mockClientSecret";
- storeMetadata(storageService, registeredClientId, secret, Scope.parse("openid"), redirectUri);
- final String clientId = uniqueClientId();
- final Map<String, String> requestParams = new HashMap<>();
- requestParams.put("sub", clientId);
- requestParams.put("trust_anchor", anchorId);
- requestParams.put("entity_type", "openid_relying_party");
- setBasicAuth(registeredClientId, secret);
-
- rpConfigureMockHttpClient(clientId);
- request.setContentType("application/x-www-form-urlencoded");
- setHttpFormRequest(request, "POST", requestParams);
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- final ResolveEntityResponse parsedResponse =
- parseSuccessResponse(result, ResolveEntityResponse.class);
- final SignedJWT response = parsedResponse.getJWT();
- Assert.assertEquals(response.getHeader().getType(), new JOSEObjectType("resolve-response+jwt"));
- Assert.assertEquals(response.getJWTClaimsSet().getSubject(), clientId);
- Assert.assertNotNull(response.getJWTClaimsSet().getClaim("metadata"));
- Assert.assertNull(response.getJWTClaimsSet().getClaim("authority_hints"));
- }
-
- @Test
- public void testRPWithTrustedTrustAnchor_basicAuth_failForLocalClientWithDefaultConfig() throws Exception {
- request.setMethod("POST");
- final String registeredClientId = "localDefaultClient";
- final String secret = "mockClientSecret";
- storeMetadata(storageService, registeredClientId, secret, Scope.parse("openid"), redirectUri);
- final String clientId = uniqueClientId();
- final Map<String, String> requestParams = new HashMap<>();
- requestParams.put("sub", clientId);
- requestParams.put("trust_anchor", anchorId);
- requestParams.put("entity_type", "openid_relying_party");
- setBasicAuth(registeredClientId, secret);
-
- rpConfigureMockHttpClient(clientId);
- request.setContentType("application/x-www-form-urlencoded");
- setHttpFormRequest(request, "POST", requestParams);
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "unauthorized_client");
- }
-
- @Test
- public void testRPWithTrustedTrustAnchor_basicAuth_failWithGet() throws Exception {
- request.setMethod("GET");
- final String registeredClientId = "localResolveEntityClient";
- final String secret = "mockClientSecret";
- storeMetadata(storageService, registeredClientId, secret, Scope.parse("openid"), redirectUri);
- final String clientId = uniqueClientId();
- request.setContentType("application/x-www-form-urlencoded");
- request.setQueryString("sub=" + clientId + "&trust_anchor=" + anchorId + "&entity_type=openid_relying_party");
- setBasicAuth(registeredClientId, secret);
-
- rpConfigureMockHttpClient(clientId);
- request.setContentType("application/x-www-form-urlencoded");
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "invalid_request");
- }
-
- @Test
- public void testRPWithTrustedTrustAnchor_jwtAuth_failWithRpfKey() throws Exception {
- request.setMethod("POST");
- final String clientId = uniqueClientId();
- final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder()
- .issuer(clientId)
- .subject(clientId)
- .audience(issuer)
- .expirationTime(Date.from(Instant.now().plusSeconds(600)))
- .jwtID(idGenerator.generateIdentifier())
- .build();
-
- final SignedJWT jwt = createPrivateKeyJWT(claimsSet, rpKey.toRSAKey().toRSAPrivateKey());
- final Map<String, String> requestParams = new HashMap<>();
- requestParams.put("sub", clientId);
- requestParams.put("trust_anchor", anchorId);
- requestParams.put("entity_type", "openid_relying_party");
- populateClientAssertionParams(requestParams, jwt);
- rpConfigureMockHttpClient(clientId);
- request.setContentType("application/x-www-form-urlencoded");
- setHttpFormRequest(request, "POST", requestParams);
- final FlowExecutionResult result = flowExecutor.launchExecution(FLOW_ID, null, externalContext);
- assertErrorCode(result, "invalid_client");
- }
-
- protected JSONErrorResponse parseErrorResponse(final FlowExecutionResult result) {
- final Response response = parseResponse(result);
- Assert.assertTrue(response instanceof JSONErrorResponse);
- return (JSONErrorResponse) response;
- }
-
-}
diff --git a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml
index c3148c8..a91b77f 100644
--- a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml
+++ b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/relying-party.xml
@@ -41,7 +41,6 @@
<ref bean="OIDC.Configuration" />
<bean parent="OIDFED.Configuration" p:cachedSuccessResponseLifetime="PT2S" />
<bean parent="OIDFED.Keyset" p:cachedSuccessResponseLifetime="PT2S" />
- <bean parent="OIDFED.ResolveEntity" />
</list>
</property>
</bean>
@@ -74,15 +73,6 @@
<bean parent="OAUTH2.Token.MDDriven" p:tokenEndpointAuthMethods="client_secret_basic,client_secret_post,client_secret_jwt,private_key_jwt,none"/>
<bean parent="OAUTH2.PAR.MDDriven" p:tokenEndpointAuthMethods="private_key_jwt,none"/>
<ref bean="OIDC.UserInfo.MDDriven" />
- <!-- Enabled for federation-authenticated clients -->
- <bean parent="OIDFED.ResolveEntity" />
- </list>
- </property>
- </bean>
- <bean parent="RelyingPartyByName" c:relyingPartyIds="localResolveEntityClient">
- <property name="profileConfigurations">
- <list>
- <bean parent="OIDFED.ResolveEntity" p:tokenEndpointAuthMethods="client_secret_basic"/>
</list>
</property>
</bean>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list