[java-idp-oidc] 04/31: JOIDC-222 - Support for OpenID Federation
Henri Mikkonen
henri.mikkonen at iki.fi
Tue Jun 24 08:52:40 UTC 2025
This is an automated email from the git hooks/post-receive script.
hjmikkon pushed a commit to branch dev/JOIDC-222
in repository java-idp-oidc.
View the commit online:
http://git.shibboleth.net/view/?p=java-idp-oidc.git;a=commit;h=bee9bb40e6639cf2f4599b6c97ec4e9c6dada79c
commit bee9bb40e6639cf2f4599b6c97ec4e9c6dada79c
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Wed Jan 15 17:44:35 2025 +0200
JOIDC-222 - Support for OpenID Federation
https://shibboleth.atlassian.net/browse/JOIDC-222
Current state of work (WIP), containing initial implementations for the following new features:
- OIDFED.AutomaticRegistration profile
- Provides means to do trust anchor specific configurations (trust marks, local policy, etc)
- Relying party context is stored as a child under RelyingPartyTrustChainContext
- Trust marks are resolved after the trust chain has been selected
- Trust mark validation exploits trust chain cache + trust engine
- TODO: trust mark subject validation and trust mark delegation
- RelyingPartyByTrustAnchor bean for relying party overrides
- provides trust anchor specific overrides via 'c:trustAnchorIds="TRUST_ANCHOR_ID"' attribute
- Metadata (client information) resolution now exploits immediate superior as mandated by spec draft
---
...derationAutomaticRegistrationConfiguration.java | 172 ++++++++++
...derationAutomaticRegistrationConfiguration.java | 82 +++++
.../config/RelyingPartyConfigurationSupport.java | 59 ++++
.../oidfed/config/TrustAnchorIdLookupFunction.java | 74 +++++
.../op/oidfed/config/TrustAnchorIdPredicate.java | 96 ++++++
...ClientMetadataFromTrustChainLookupStrategy.java | 73 +++++
...DefaultTrustChainTrustMarksParsingStrategy.java | 104 ++++++
...ChainTrustedTrustMarkIssuersLookupStrategy.java | 114 +++++++
.../impl/RelyingPartyTrustChainContext.java | 29 +-
.../op/oidfed/profile/impl/ResolveTrustChains.java | 23 +-
.../op/oidfed/profile/impl/ResolveTrustMarks.java | 360 +++++++++++++++++++++
.../op/oidfed/profile/impl/SelectTrustChain.java | 40 ++-
...eAutomaticRegistrationProfileConfiguration.java | 229 +++++++++++++
.../DefaultLocalMetadataPolicyMergingStrategy.java | 112 +++++++
.../LocalMetadataPolicyLookupFunction.java | 49 +++
.../MandatoryTrustMarksLookupFunction.java | 48 +++
.../MaximumTrustMarkLifetimeLookupFunction.java | 52 +++
.../idp/flows/oidc/authorize/authorize-beans.xml | 46 +++
.../idp/flows/oidc/authorize/authorize-flow.xml | 4 +
.../idp/service/relying-party/postconfig.xml | 8 +
20 files changed, 1756 insertions(+), 18 deletions(-)
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java
new file mode 100644
index 00000000..42c52a7b
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/DefaultOIDFederationAutomaticRegistrationConfiguration.java
@@ -0,0 +1,172 @@
+/*
+ * 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.config;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
+import net.shibboleth.profile.config.AbstractConditionalProfileConfiguration;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotEmpty;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Positive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.FunctionSupport;
+
+/**
+ * Implementation of a profile configuration for the OpenID Federation Automatic Registration.
+ */
+public class DefaultOIDFederationAutomaticRegistrationConfiguration extends AbstractConditionalProfileConfiguration
+ implements OIDFederationAutomaticRegistrationConfiguration {
+
+ /** OIDC provider information profile counter name. */
+ @Nonnull @NotEmpty
+ public static final String PROFILE_COUNTER = "net.shibboleth.idp.profiles.oidfed.automaticregistration";
+
+ /** Lookup function to local metadata policy to be merged into the federation policy. */
+ @Nonnull private Function<ProfileRequestContext,Map<String, MetadataPolicy>> localMetadataPolicyLookupStrategy;
+
+ /** Lookup function to mandatory trust marks. */
+ @Nonnull private Function<ProfileRequestContext,List<String>> mandatoryTrustMarksLookupStrategy;
+
+ /** Lookup function to supply maximum trust mark lifetime. */
+ @Nonnull private Function<ProfileRequestContext,Duration> maximumTrustMarkLifetimeLookupStrategy;
+
+ /**
+ * Constructor.
+ */
+ public DefaultOIDFederationAutomaticRegistrationConfiguration() {
+ this(PROFILE_ID);
+ }
+
+ /**
+ * Creates a new configuration instance.
+ *
+ * @param profileId Unique profile identifier.
+ */
+ public DefaultOIDFederationAutomaticRegistrationConfiguration(@Nonnull @NotEmpty final String profileId) {
+ super(profileId);
+ localMetadataPolicyLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyMap());
+ mandatoryTrustMarksLookupStrategy = FunctionSupport.constant(CollectionSupport.emptyList());
+ maximumTrustMarkLifetimeLookupStrategy = FunctionSupport.constant(Duration.ofDays(365));
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull @NonnullElements @NotLive @Unmodifiable
+ public Map<String, MetadataPolicy> getLocalMetadataPolicy(
+ @Nullable final ProfileRequestContext profileRequestContext) {
+ final Map<String, MetadataPolicy> policy = localMetadataPolicyLookupStrategy.apply(profileRequestContext);
+ if (policy != null) {
+ return CollectionSupport.copyToMap(policy);
+ }
+ return CollectionSupport.emptyMap();
+ }
+
+ /**
+ * Set local metadata policy to be merged into the federation policy.
+ *
+ * @param policy metadata policy
+ */
+ public void setLocalMetadataPolicy(
+ @Nonnull @NonnullElements @NotLive @Unmodifiable final Map<String, MetadataPolicy> policy) {
+ localMetadataPolicyLookupStrategy = FunctionSupport.constant(policy);
+ }
+
+ /**
+ * Sets lookup strategy for local metadata policy to be merged into the federation policy.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setLocalMetadataPolicyLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,Map<String, MetadataPolicy>> strategy) {
+ localMetadataPolicyLookupStrategy =
+ Constraint.isNotNull(strategy, "Local metadata policy lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nonnull @NonnullElements @NotLive @Unmodifiable
+ public List<String> getMandatoryTrustMarks(@Nullable final ProfileRequestContext profileRequestContext) {
+ final List<String> trustMarks = mandatoryTrustMarksLookupStrategy.apply(profileRequestContext);
+ if (trustMarks != null) {
+ return CollectionSupport.copyToList(trustMarks);
+ }
+ return CollectionSupport.emptyList();
+ }
+
+ /**
+ * Set mandatory trust marks.
+ *
+ * @param marks trust marks
+ */
+ public void setMandatoryTrustMarks(@Nonnull @NonnullElements @NotLive @Unmodifiable final List<String> marks) {
+ mandatoryTrustMarksLookupStrategy = FunctionSupport.constant(marks);
+ }
+
+ /**
+ * Sets lookup strategy for mandatory trust marks value.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setMandatoryTrustMarksLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext,List<String>> strategy) {
+ mandatoryTrustMarksLookupStrategy =
+ Constraint.isNotNull(strategy, "Mandatory trust marks lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ @Positive @Nonnull
+ public Duration getMaximumTrustMarkLifetime(@Nullable final ProfileRequestContext profileRequestContext) {
+ final Duration lifetime = maximumTrustMarkLifetimeLookupStrategy.apply(profileRequestContext);
+
+ Constraint.isTrue(lifetime != null && !lifetime.isZero() && !lifetime.isNegative(),
+ "Maximum trust mark lifetime must be greater than 0");
+ assert lifetime != null;
+ return lifetime;
+ }
+
+ /**
+ * Set the maximum lifetime of a trust mark.
+ *
+ * @param lifetime lifetime of a trust mark
+ */
+ public void setMaximumTrustMarkLifetime(@Positive @Nonnull final Duration lifetime) {
+ final Duration trustMarkLifetime = Constraint.isNotNull(lifetime, "Maximum trust mark lifetime cannot be null");
+ Constraint.isTrue(!trustMarkLifetime.isZero() && !trustMarkLifetime.isNegative(),
+ "Maximum trust mark lifetime must be greater than 0");
+
+ maximumTrustMarkLifetimeLookupStrategy = FunctionSupport.constant(trustMarkLifetime);
+ }
+
+ /**
+ * Set a lookup strategy for the maximum trust mark lifetime.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setMaximumTrustMarkLifetimeLookupStrategy(
+ @Nullable final Function<ProfileRequestContext,Duration> strategy) {
+ maximumTrustMarkLifetimeLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java
new file mode 100644
index 00000000..d396684b
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/OIDFederationAutomaticRegistrationConfiguration.java
@@ -0,0 +1,82 @@
+/*
+ * 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.config;
+
+import java.time.Duration;
+import java.util.List;
+import java.util.Map;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
+import net.shibboleth.oidc.profile.config.OIDCProfileConfiguration;
+import net.shibboleth.shared.annotation.ConfigurationSetting;
+import net.shibboleth.shared.annotation.constraint.NonnullElements;
+import net.shibboleth.shared.annotation.constraint.NotLive;
+import net.shibboleth.shared.annotation.constraint.Positive;
+import net.shibboleth.shared.annotation.constraint.Unmodifiable;
+
+/**
+ * Profile configuration for an OpenID Federation Automatic Registration.
+ */
+public interface OIDFederationAutomaticRegistrationConfiguration extends OIDCProfileConfiguration {
+
+ /** OIDC base protocol URI. Sections 4 and 11 are the most relevant. */
+ public static final String PROTOCOL_URI = "https://openid.net/specs/openid-federation-1_0.html";
+
+ /** ID for this profile configuration. */
+ public static final String PROFILE_ID = "http://shibboleth.net/ns/profiles/oidfed/automaticregistration";
+
+ /**
+ * Get local metadata policy to be merged into the federation policy.
+ *
+ * <p>Defaults to empty map.</p>
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return local metadata policy
+ */
+ @ConfigurationSetting(name="localMetadataPolicy")
+ @Nonnull @NonnullElements @NotLive @Unmodifiable
+ Map<String, MetadataPolicy> getLocalMetadataPolicy(@Nullable final ProfileRequestContext profileRequestContext);
+
+ /**
+ * Get the mandatory trust mark identifiers required by this profile configuration.
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return mandatory trust mark identifiers
+ */
+ @ConfigurationSetting(name="mandatoryTrustMarks")
+ @Nonnull @NonnullElements @NotLive @Unmodifiable List<String> getMandatoryTrustMarks(
+ @Nullable final ProfileRequestContext profileRequestContext);
+
+ /**
+ * Get maximum lifetime for trust marks.
+ *
+ * <p>Defaults to one year.</p>
+ *
+ * @param profileRequestContext profile request context
+ *
+ * @return maximum lifetime
+ */
+ @ConfigurationSetting(name="maximumTrustMarkLifetime")
+ @Positive @Nonnull
+ Duration getMaximumTrustMarkLifetime(@Nullable final ProfileRequestContext profileRequestContext);
+
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/RelyingPartyConfigurationSupport.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/RelyingPartyConfigurationSupport.java
new file mode 100644
index 00000000..2655d226
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/RelyingPartyConfigurationSupport.java
@@ -0,0 +1,59 @@
+/*
+ * 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.config;
+
+import java.util.Collection;
+
+import javax.annotation.Nonnull;
+
+import net.shibboleth.profile.relyingparty.BasicRelyingPartyConfiguration;
+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-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/TrustAnchorIdLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/TrustAnchorIdLookupFunction.java
new file mode 100644
index 00000000..d6ebfdb2
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/TrustAnchorIdLookupFunction.java
@@ -0,0 +1,74 @@
+/*
+ * 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.config;
+
+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.context.ProfileRequestContext;
+import org.opensaml.profile.context.navigate.InboundMessageContextLookup;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext;
+import net.shibboleth.shared.logic.Constraint;
+
+/**
+ * A function that returns selected trust anchor ID from a {@link RelyingPartyTrustChainContext} obtained via a lookup
+ * function.
+ *
+ * <p>If a specific setting is unavailable, a null value is returned.</p>
+ */
+public class TrustAnchorIdLookupFunction implements Function<ProfileRequestContext, String> {
+
+ /** Strategy used to lookup the trust chain context. */
+ @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
+
+ /**
+ * Constructor.
+ */
+ public TrustAnchorIdLookupFunction() {
+ final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
+ new ChildContextLookup<>(RelyingPartyTrustChainContext.class).compose(
+ new InboundMessageContextLookup());
+ assert tcls != null;
+ trustChainContextLookupStrategy = tcls;
+ }
+
+ /**
+ * Constructor.
+ *
+ * @param strategy strategy used to lookup the trust chain context
+ */
+ public TrustAnchorIdLookupFunction(
+ @Nonnull final Function<ProfileRequestContext, RelyingPartyTrustChainContext> strategy) {
+ trustChainContextLookupStrategy = Constraint.isNotNull(strategy, "Lookup strategy cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Nullable public String apply(@Nullable final ProfileRequestContext profileRequestContext) {
+ return Optional.ofNullable(profileRequestContext)
+ .map(profileCtx -> trustChainContextLookupStrategy.apply(profileCtx))
+ .map(trustChainCtx -> trustChainCtx.getSelectedTrustChain())
+ .filter(pair -> pair != null && pair.getFirst() != null)
+ .map(pair -> pair.getFirst())
+ .filter(list -> !list.isEmpty())
+ .map(chain -> chain.get(chain.size() - 1))
+ .map(statement -> statement.getEntityID().getValue())
+ .orElse(null);
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/TrustAnchorIdPredicate.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/TrustAnchorIdPredicate.java
new file mode 100644
index 00000000..0b381ed3
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/config/TrustAnchorIdPredicate.java
@@ -0,0 +1,96 @@
+/*
+ * 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.config;
+
+import java.util.Collection;
+import java.util.function.Predicate;
+
+import javax.annotation.Nonnull;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+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-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultClientMetadataFromTrustChainLookupStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultClientMetadataFromTrustChainLookupStrategy.java
new file mode 100644
index 00000000..55c4cef6
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultClientMetadataFromTrustChainLookupStrategy.java
@@ -0,0 +1,73 @@
+/*
+ * 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.metadata;
+
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
+
+import net.minidev.json.JSONObject;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Default strategy to combine {@link OIDCClientMetadata} from the trust chain by exploiting both entity configuration
+ * and subordinate statement issued by the immediate superior.
+ */
+public class DefaultClientMetadataFromTrustChainLookupStrategy
+ implements Function<List<EntityStatement>,OIDCClientMetadata> {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(DefaultClientMetadataFromTrustChainLookupStrategy.class);
+
+ /** {@inheritDoc} */
+ @Override @Nullable
+ public OIDCClientMetadata apply(@Nullable final List<EntityStatement> chain) {
+ if (chain == null || chain.size() < 3) {
+ log.warn("Unexpected trust chain input: {}", chain == null ? null : "size = " + chain.size());
+ return null;
+ }
+ final OIDCClientMetadata configurationMetadata = chain.get(0).getClaimsSet().getRPMetadata();
+ final OIDCClientMetadata subordinateMetadata = chain.get(1).getClaimsSet().getRPMetadata();
+ if (subordinateMetadata == null || subordinateMetadata.toJSONObject().isEmpty()) {
+ return configurationMetadata;
+ }
+ if (configurationMetadata == null || configurationMetadata.toJSONObject().isEmpty()) {
+ return subordinateMetadata;
+ }
+ final Map<String, Object> configurationClaims = configurationMetadata.toJSONObject();
+ final Map<String, Object> subordinateClaims = subordinateMetadata.toJSONObject();
+ for (final String configurationClaim : configurationClaims.keySet()) {
+ if (!subordinateClaims.containsKey(configurationClaim)) {
+ subordinateClaims.put(configurationClaim, configurationClaims.get(configurationClaim));
+ }
+ }
+ try {
+ return OIDCClientMetadata.parse(new JSONObject(subordinateClaims));
+ } catch (final ParseException e) {
+ log.error("Could not parse from combined metadata map into a metadata object", e);
+ }
+ return null;
+ }
+
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainTrustMarksParsingStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainTrustMarksParsingStrategy.java
new file mode 100644
index 00000000..f0add6d7
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainTrustMarksParsingStrategy.java
@@ -0,0 +1,104 @@
+/*
+ * 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.metadata;
+
+import java.text.ParseException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import javax.annotation.concurrent.ThreadSafe;
+
+import org.slf4j.Logger;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.oauth2.sdk.id.Identifier;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.StringSupport;
+
+/**
+ * Default strategy for parsing map of trust marks for the given trust chain. The keys in the map refer to the entity
+ * ID for which the trust mark has been issued to.
+ *
+ * TODO: iat / subject validation (switch into using claims validators)
+ */
+ at ThreadSafe
+public class DefaultTrustChainTrustMarksParsingStrategy
+ implements Function<List<EntityStatement>,Map<String,List<SignedJWT>>> {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(DefaultTrustChainTrustMarksParsingStrategy.class);
+
+ /** {@inheritDoc} */
+ @Override @Nullable
+ public Map<String, List<SignedJWT>> apply(@Nullable final List<EntityStatement> trustChain) {
+ if (trustChain == null || trustChain.size() < 3) {
+ log.error("Unexpected length in the trust chain: {}", trustChain == null ? "null" : trustChain.size());
+ return null;
+ }
+
+ final Map<String, List<SignedJWT>> result = new HashMap<>();
+ for (final EntityStatement statement : trustChain) {
+ assert statement != null;
+ final SignedJWT statementJwt = statement.getSignedStatement();
+ log.trace("Inspecting entity statement {} with trust marks {}",
+ statementJwt.serialize(), statement.getClaimsSet().getTrustMarks());
+
+ if (statement.getClaimsSet().getTrustMarks() != null) {
+ final List<SignedJWT> trustMarks = statement.getClaimsSet().getTrustMarks()
+ .stream()
+ .filter(entry -> verifyTrustMark(entry.getTrustMark(), entry.getID()))
+ .map(entry -> entry.getTrustMark())
+ .toList();
+ result.put(statement.getEntityID().getValue(), trustMarks);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Verifies the trust mark id and issuer claims.
+ *
+ * @param trustMark trust mark to be verified
+ * @param id the id to be verified from the JWT claims set
+ * @return true if valid, false otherwise
+ */
+ private boolean verifyTrustMark(@Nullable final SignedJWT trustMark, @Nullable final Identifier id) {
+ if (trustMark == null || id == null) {
+ return false;
+ }
+ try {
+ final JWTClaimsSet trustMarkClaims = trustMark.getJWTClaimsSet();
+ if (StringSupport.trimOrNull(trustMarkClaims.getIssuer()) == null) {
+ log.error("Trust Mark {} is missing mandatory issuer", trustMarkClaims.getStringClaim("id"));
+ return false;
+ }
+ if (id.getValue().equals(trustMarkClaims.getStringClaim("id"))) {
+ return true;
+ }
+ log.error("The id {} is not matching with the id-claim {}", id, trustMarkClaims.getStringClaim("id"));
+ } catch (final ParseException e) {
+ log.error("Could not parse id-claim from the trust mark", e);
+ }
+ return false;
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainTrustedTrustMarkIssuersLookupStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainTrustedTrustMarkIssuersLookupStrategy.java
new file mode 100644
index 00000000..64d15be7
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultTrustChainTrustedTrustMarkIssuersLookupStrategy.java
@@ -0,0 +1,114 @@
+/*
+ * 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.metadata;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import com.fasterxml.jackson.databind.JavaType;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.type.ArrayType;
+import com.fasterxml.jackson.databind.type.MapType;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Default function for fetching trusted trust mark issuers from a trust chain: they are read from the trust anchor's
+ * entity configuration.
+ */
+public class DefaultTrustChainTrustedTrustMarkIssuersLookupStrategy extends AbstractIdentifiableInitializableComponent
+ implements Function<List<EntityStatement>, Map<String, List<String>>> {
+
+ /** Class logger. */
+ @Nonnull private final Logger log =
+ LoggerFactory.getLogger(DefaultTrustChainTrustedTrustMarkIssuersLookupStrategy.class);
+
+ /** JSON object mapper used for decoding JSON into Map. */
+ @NonnullAfterInit private ObjectMapper objectMapper;
+
+ /**
+ * Set the JSON {@link ObjectMapper} used for decoding JSON into Map.
+ *
+ * @param mapper object mapper
+ */
+ public void setObjectMapper(@Nonnull final ObjectMapper mapper) {
+ checkSetterPreconditions();
+
+ objectMapper = Constraint.isNotNull(mapper, "Object mapper cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+ if (objectMapper == null) {
+ throw new ComponentInitializationException("Object mapper cannot be null");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Nullable @Override
+ public Map<String, List<String>> apply(@Nullable final List<EntityStatement> trustChain) {
+ checkComponentActive();
+ if (trustChain == null || trustChain.size() < 3) {
+ return null;
+ }
+ return parseTrustedIssuers(trustChain)
+ .entrySet().stream()
+ .filter(entry -> entry.getKey() != null && entry.getValue() != null)
+ .map(entry -> Map.entry(entry.getKey(), Arrays.stream(entry.getValue()).toList()))
+ .collect(Collectors.toUnmodifiableMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ /**
+ * Parse the map of trusted issuers from the given trust chain, keyed with trust mark identifiers.
+ *
+ * @param trustChain trust chain to be parsed
+ * @return map of trusted issuers
+ */
+ @Nonnull protected Map<String, String[]> parseTrustedIssuers(@Nonnull final List<EntityStatement> trustChain) {
+ final Object issuersClaim =
+ trustChain.get(trustChain.size() - 1).getClaimsSet().getClaim("trust_mark_issuers");
+ if (issuersClaim != null) {
+ final ArrayType arrayType = objectMapper.getTypeFactory().constructArrayType(String.class);
+ final JavaType stringType = objectMapper.constructType(String.class);
+ final MapType mapType =
+ objectMapper.getTypeFactory().constructMapType(Map.class, stringType, arrayType);
+ try {
+ final Map<String, String[]> result = objectMapper.readValue(issuersClaim.toString(), mapType);
+ if (result != null) {
+ return result;
+ }
+ } catch (final JsonProcessingException e) {
+ log.warn("Could not parse trust mark issuers from the trust chain", e);
+ }
+ }
+ return CollectionSupport.emptyMap();
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/RelyingPartyTrustChainContext.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/RelyingPartyTrustChainContext.java
index 8948c452..94442062 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/RelyingPartyTrustChainContext.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/RelyingPartyTrustChainContext.java
@@ -16,6 +16,7 @@ package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl;
import java.time.Instant;
import java.util.List;
+import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -46,6 +47,9 @@ public final class RelyingPartyTrustChainContext extends BaseContext {
/** Expiration instant for the selected metadata. */
@Nullable private Instant selectedMetadataExpiration;
+ /** Verified trust mark IDs for the selected trust chain. */
+ @Nullable private Map<String, List<String>> verifiedTrustMarkIds;
+
/**
* Get the resolved trust chains for the relying party.
*
@@ -113,7 +117,7 @@ public final class RelyingPartyTrustChainContext extends BaseContext {
}
/**
- * Get the expiration instant for the selected metadata
+ * Get the expiration instant for the selected metadata.
*
* @return the expiration instant
*/
@@ -132,4 +136,27 @@ public final class RelyingPartyTrustChainContext extends BaseContext {
selectedMetadataExpiration = expiration;
return this;
}
+
+ /**
+ * Get the verified trust mark IDs for the selected trust chain.
+ *
+ * @return verified trust mark IDs
+ */
+ @Nullable public Map<String, List<String>> getVerifiedTrustMarkIds() {
+ return verifiedTrustMarkIds;
+ }
+
+ /**
+ * Set the verified trust mark IDs for the selected trust chain.
+ *
+ * @param ids verified trust mark IDs
+ *
+ * @return this context
+ */
+ @Nonnull public RelyingPartyTrustChainContext setVerifiedTrustMarkIds(
+ @Nullable final Map<String, List<String>> ids) {
+ verifiedTrustMarkIds = ids;
+ return this;
+ }
+
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustChains.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustChains.java
index d01db3d9..e55c5f81 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustChains.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustChains.java
@@ -38,6 +38,7 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
import com.nimbusds.openid.connect.sdk.rp.OIDCClientMetadata;
import net.minidev.json.JSONObject;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultClientMetadataFromTrustChainLookupStrategy;
import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubjectEntityIDCriterion;
import net.shibboleth.idp.profile.AbstractProfileAction;
import net.shibboleth.oidc.metadata.cache.MetadataCache;
@@ -78,6 +79,9 @@ public class ResolveTrustChains extends AbstractProfileAction {
/** Strategy used to create the trust chain context. */
@Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextCreationStrategy;
+ /** Strategy used to get combined OIDC client metadata from trust chain. */
+ @Nonnull private Function<List<EntityStatement>,OIDCClientMetadata> metadataLookupStrategy;
+
/** Strategy used to merge metadata policies in trust chain for specific entity type. */
@NonnullAfterInit private BiFunction<List<EntityStatement>,String,Map<String, MetadataPolicy>>
metadataPolicyMergingStrategy;
@@ -97,6 +101,7 @@ public class ResolveTrustChains extends AbstractProfileAction {
new InboundMessageContextLookup());
assert tccs != null;
trustChainContextCreationStrategy = tccs;
+ metadataLookupStrategy = new DefaultClientMetadataFromTrustChainLookupStrategy();
}
/**
@@ -117,7 +122,18 @@ public class ResolveTrustChains extends AbstractProfileAction {
public void setClientIDLookupStrategy(@Nonnull final Function<MessageContext, ClientID> strategy) {
checkSetterPreconditions();
clientIDLookupStrategy =
- Constraint.isNotNull(strategy, "ClientIDLookupStrategy lookup strategy cannot be null");
+ Constraint.isNotNull(strategy, "ClientIDLookupStrategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to get combined OIDC client metadata from trust chain.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setMetadataLookupStrategy(@Nonnull final Function<List<EntityStatement>,OIDCClientMetadata> strategy) {
+ checkSetterPreconditions();
+ metadataLookupStrategy =
+ Constraint.isNotNull(strategy, "MetadataLookupStrategy cannot be null");
}
/**
@@ -129,7 +145,7 @@ public class ResolveTrustChains extends AbstractProfileAction {
BiFunction<List<EntityStatement>,String,Map<String, MetadataPolicy>> strategy) {
checkSetterPreconditions();
metadataPolicyMergingStrategy =
- Constraint.isNotNull(strategy, "MetadataPolicyMergingStrategy lookup strategy cannot be null");
+ Constraint.isNotNull(strategy, "MetadataPolicyMergingStrategy cannot be null");
}
/**
@@ -208,7 +224,8 @@ public class ResolveTrustChains extends AbstractProfileAction {
final Map<String, MetadataPolicy> mergedPolicies =
metadataPolicyMergingStrategy.apply(chain, EntityType.OPENID_RELYING_PARTY.getValue());
log.debug("{} Merged policy for chain {}", getLogPrefix(), mergedPolicies);
- final OIDCClientMetadata metadata = chain.get(0).getClaimsSet().getRPMetadata();
+ assert chain != null;
+ final OIDCClientMetadata metadata = metadataLookupStrategy.apply(chain);
if (metadata != null) {
final OIDCClientInformation clientInformation = new OIDCClientInformation(
new ClientID(chain.get(0).getEntityID().getValue()), metadata);
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustMarks.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustMarks.java
new file mode 100644
index 00000000..ac8b9712
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ResolveTrustMarks.java
@@ -0,0 +1,360 @@
+/*
+ * 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.text.ParseException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.function.Predicate;
+
+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.opensaml.security.SecurityException;
+import org.opensaml.security.trust.TrustEngine;
+import org.slf4j.Logger;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.jwt.SignedJWT;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustChainTrustMarksParsingStrategy;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubjectEntityIDCriterion;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubjectEntityStatementCriterion;
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.oidc.metadata.cache.MetadataCache;
+import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.logic.PredicateSupport;
+import net.shibboleth.shared.primitive.LoggerFactory;
+import net.shibboleth.shared.primitive.NonnullSupplier;
+import net.shibboleth.shared.primitive.StringSupport;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+/**
+ * Resolves the trust marks for the selected trust chain and stores the data into {@link RelyingPartyTrustChainContext}.
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_MSG_CTX}
+ *
+ * @since 4.3.0
+ */
+public class ResolveTrustMarks extends AbstractProfileAction {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(ResolveTrustMarks.class);
+
+ /** Strategy used to lookup the trust chain context. */
+ @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
+
+ /** Strategy used to parse trust marks from the selected trust chain. */
+ @Nonnull private Function<List<EntityStatement>,Map<String,List<SignedJWT>>> trustChainTrustMarksParsingStrategy;
+
+ /** Strategy used to lookup trusted trust mark issuers for the trust chain. */
+ @NonnullAfterInit
+ private Function<List<EntityStatement>, Map<String, List<String>>> trustedTrustMarkIssuersLookupStrategy;
+
+ /** Condition to solely take trusted trust mark issuers into account. */
+ @Nonnull private Predicate<ProfileRequestContext> trustedTrustMarkIssuersOnlyCondition;
+
+ /** Metadata cache for trust chains (for trust mark issuers). */
+ @NonnullAfterInit private MetadataCache<List<List<EntityStatement>>> trustChainCache;
+
+ /** Trust engine used to validate a trust mark signature. */
+ @NonnullAfterInit private TrustEngine<SignedJWT> trustEngine;
+
+ /** Trust chain context to operate on. */
+ @NonnullBeforeExec private RelyingPartyTrustChainContext trustChainContext;
+
+ /** The selected trust chain to resolve trust marks from. */
+ @NonnullBeforeExec private List<EntityStatement> selectedTrustChain;
+
+ /** Flag indicating that only trusted trust mark issuers are taken into account. */
+ private boolean onlyTrustedIssuers;
+
+ /** Map of trusted trust mark issuers. */
+ @NonnullBeforeExec private Map<String, List<String>> trustedIssuers;
+
+ /**
+ * Constructor.
+ */
+ public ResolveTrustMarks() {
+ final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
+ new ChildContextLookup<>(RelyingPartyTrustChainContext.class).compose(
+ new InboundMessageContextLookup());
+ assert tcls != null;
+ trustChainContextLookupStrategy = tcls;
+ trustChainTrustMarksParsingStrategy = new DefaultTrustChainTrustMarksParsingStrategy();
+ trustedTrustMarkIssuersOnlyCondition = PredicateSupport.alwaysTrue();
+ }
+
+ /**
+ * 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 trusted trust mark issuers for the trust chain.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setTrustedTrustMarkIssuersLookupStrategy(
+ @Nonnull final Function<List<EntityStatement>, Map<String, List<String>>> strategy) {
+ checkSetterPreconditions();
+ trustedTrustMarkIssuersLookupStrategy =
+ Constraint.isNotNull(strategy, "trustedTrustMarkIssuersLookupStrategy cannot be null");
+ }
+
+ /**
+ * Set the condition to solely take trusted trust mark issuers into account.
+ *
+ * @param condition
+ */
+ public void setTrustedTrustMarkIssuersOnlyCondition(@Nonnull final Predicate<ProfileRequestContext> condition) {
+ checkSetterPreconditions();
+ trustedTrustMarkIssuersOnlyCondition =
+ Constraint.isNotNull(condition, "TrustedTrustMarkIssuersOnlyCondition cannot be null");
+ }
+
+ /**
+ * Set the metadata cache for trust chains.
+ *
+ * @param cache metadata cache
+ */
+ public void setTrustChainCache(@Nonnull final MetadataCache<List<List<EntityStatement>>> cache) {
+ checkSetterPreconditions();
+ trustChainCache = Constraint.isNotNull(cache, "TrustChainCache cannot be null");
+ }
+
+ /**
+ * Set trust engine used to validate a signature.
+ *
+ * @param engine trust engine
+ */
+ public void setTrustEngine(@Nonnull final TrustEngine<SignedJWT> engine) {
+ checkSetterPreconditions();
+ trustEngine = Constraint.isNotNull(engine, "Trust Engine cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (trustChainCache == null) {
+ throw new ComponentInitializationException("TrustChainCache cannot be null");
+ }
+ if (trustEngine == null) {
+ throw new ComponentInitializationException("Trust Engine cannot be null");
+ }
+ if (trustedTrustMarkIssuersLookupStrategy == null) {
+ throw new ComponentInitializationException("Trusted trust mark issuers lookup strategy 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 || trustChainContext.getPolicyCompliantTrustChains() == null) {
+ log.error("{} Unable to locate policy-compliant trust chains", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return false;
+ }
+
+ final Pair<List<EntityStatement>, OIDCClientInformation> selectedChain =
+ trustChainContext.getSelectedTrustChain();
+
+ if (selectedChain == null || selectedChain.getFirst() == null) {
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ log.error("{} No selected trust chain could be resolved", getLogPrefix());
+ return false;
+ }
+
+ selectedTrustChain = selectedChain.getFirst();
+ assert selectedTrustChain != null;
+ if (selectedTrustChain.size() < 3) {
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ log.error("{} Unexpected length in the selected trust chain: {}", getLogPrefix(),
+ selectedTrustChain.size());
+ return false;
+ }
+
+ onlyTrustedIssuers = trustedTrustMarkIssuersOnlyCondition.test(profileRequestContext);
+ trustedIssuers = Optional.ofNullable(trustedTrustMarkIssuersLookupStrategy.apply(selectedTrustChain))
+ .orElseGet(NonnullSupplier.of(CollectionSupport.emptyMap()));
+
+ if (trustedIssuers.isEmpty()) {
+ if (onlyTrustedIssuers) {
+ log.debug("{} No trusted issuers set in the selected trust anchor, trust marks won't be resolved",
+ getLogPrefix());
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ log.debug("{} Trusted trust mark issuers {}", getLogPrefix(), trustedIssuers);
+ final Map<String, List<SignedJWT>> chainTrustMarks =
+ trustChainTrustMarksParsingStrategy.apply(selectedTrustChain);
+ if (chainTrustMarks == null || chainTrustMarks.isEmpty()) {
+ log.debug("{} No trust marks found from the selected trust chain", getLogPrefix());
+ return;
+ }
+
+ final Map<String, List<String>> verifiedTrustMarks = new HashMap<>();
+ for (final EntityStatement statement : selectedTrustChain) {
+ final List<SignedJWT> trustMarks = chainTrustMarks.get(statement.getEntityID().getValue());
+ if (trustMarks == null || trustMarks.isEmpty()) {
+ break;
+ }
+ verifiedTrustMarks.put(
+ statement.getEntityID().getValue(),
+ trustMarks.stream()
+ .filter(entry -> checkTrustedIssuer(entry))
+ .filter(entry -> verifyTrustMark(entry))
+ .map(entry -> getTrustMarkId(entry))
+ .filter(Objects::nonNull)
+ .toList());
+ }
+ log.debug("{} The following trust marks are validated: {}", getLogPrefix(), verifiedTrustMarks);
+ trustChainContext.setVerifiedTrustMarkIds(verifiedTrustMarks);
+ }
+
+ /**
+ * Verifies the given trust mark meets configuration for trusted trust mark issuers.
+ *
+ * @param jwt the trust mark to be verified
+ * @return true if the trust mark meets configuration, false otherwise
+ */
+ protected boolean checkTrustedIssuer(@Nullable final SignedJWT jwt) {
+ if (jwt == null) {
+ return false;
+ }
+ if (onlyTrustedIssuers) {
+ try {
+ final JWTClaimsSet claimsSet = jwt.getJWTClaimsSet();
+ final String id = StringSupport.trimOrNull(getTrustMarkId(jwt));
+ if (id == null) {
+ return false;
+ }
+ if (trustedIssuers.containsKey(id)) {
+ final String issuer = claimsSet.getIssuer();
+ assert issuer != null;
+ final List<String> validIssuers = trustedIssuers.get(id);
+ if (onlyTrustedIssuers && (validIssuers == null || !validIssuers.contains(issuer)) ) {
+ log.debug("{} Issuer {} is not valid trust mark issuer", getLogPrefix(), issuer);
+ return false;
+ }
+ }
+ } catch (final ParseException e) {
+ log.error("Could not parse TrustMark JWT contents", e);
+ return false;
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Verifies the given trust mark by exploiting (1) the trust chain cache for fetching the trust chain for the issuer
+ * entity configuration and (2) the trust engine for validating the trust mark signature.
+ *
+ * @param jwt the trust mark to be verified
+ * @return true if trust mark verification was successful, false otherwise
+ */
+ protected boolean verifyTrustMark(@Nullable final SignedJWT jwt) {
+ if (jwt == null) {
+ return false;
+ }
+ final JWTClaimsSet trustMarkClaims;
+ try {
+ trustMarkClaims = jwt.getJWTClaimsSet();
+ } catch (final ParseException e) {
+ log.error("{} Could not parse the TrustMark JWT contents", getLogPrefix(), e);
+ return false;
+ }
+ final String issuer = trustMarkClaims.getIssuer();
+ assert issuer != null;
+ log.debug("Resolving trust chain for {}", issuer);
+ final List<List<List<EntityStatement>>> cacheResult;
+ try {
+ cacheResult = trustChainCache.get(new CriteriaSet(new SubjectEntityIDCriterion(issuer)));
+ } catch (final MetadataCacheException e) {
+ log.warn("{} Exception while fetching trust chains for {}", getLogPrefix(), issuer, e);
+ return false;
+ }
+ if (cacheResult.isEmpty() || cacheResult.get(0).isEmpty()) {
+ log.warn("{} No trust chains resolved for {}", getLogPrefix(), issuer);
+ return false;
+ }
+ final List<EntityStatement> trustMarkChain = cacheResult.get(0).get(0);
+ final EntityStatement trustMarkIssuer = trustMarkChain.get(0);
+ assert trustMarkIssuer != null;
+ final CriteriaSet criteria = new CriteriaSet(new SubjectEntityStatementCriterion(trustMarkIssuer));
+ try {
+ if (trustEngine.validate(jwt, criteria)) {
+ log.debug("Successfully validated trust mark issued by {}", issuer);
+ return true;
+ }
+ } catch (final SecurityException e) {
+ log.debug("Security exception while validating trust mark signature for {}", issuer, e);
+ }
+ return false;
+ }
+
+ /**
+ * Parses the trust mark ID for the given trust mark.
+ *
+ * @param trustMark the trust mark
+ * @return the ID, or null if it could not be parsed
+ */
+ @Nullable private String getTrustMarkId(@Nullable final SignedJWT trustMark) {
+ try {
+ return trustMark == null ? null : trustMark.getJWTClaimsSet().getStringClaim("id");
+ } catch (final ParseException e) {
+ log.error("{} Could not parse the TrustMark JWT contents", getLogPrefix(), e);
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/SelectTrustChain.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/SelectTrustChain.java
index 048638ad..897dfc69 100644
--- a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/SelectTrustChain.java
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/SelectTrustChain.java
@@ -14,7 +14,6 @@
package net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl;
-import java.time.Instant;
import java.util.List;
import java.util.function.Function;
@@ -32,7 +31,7 @@ import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultTrustChainSelectionStrategy;
import net.shibboleth.idp.profile.AbstractProfileAction;
-import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
+import net.shibboleth.profile.context.RelyingPartyContext;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.NonnullBeforeExec;
import net.shibboleth.shared.collection.Pair;
@@ -57,6 +56,9 @@ public class SelectTrustChain extends AbstractProfileAction {
/** Strategy used to lookup the trust chain context. */
@Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
+ /** Strategy used to create the relying party context where to signal the selected trust anchor. */
+ @Nonnull private Function<ProfileRequestContext, RelyingPartyContext> relyingPartyContextCreationStrategy;
+
/** Strategy used to fetch the selected trust chain and metadata. */
@NonnullAfterInit private Function<ProfileRequestContext,Pair<List<EntityStatement>, OIDCClientInformation>>
selectedTrustChainLookupStrategy;
@@ -73,9 +75,26 @@ public class SelectTrustChain extends AbstractProfileAction {
new InboundMessageContextLookup());
assert tcls != null;
trustChainContextLookupStrategy = tcls;
+ final Function<ProfileRequestContext, RelyingPartyContext> rpccs =
+ new ChildContextLookup<>(RelyingPartyContext.class, true).compose(tcls);
+ assert rpccs != null;
+ relyingPartyContextCreationStrategy = rpccs;
selectedTrustChainLookupStrategy = new DefaultTrustChainSelectionStrategy();
}
+ /**
+ * Set the strategy used to return or create the {@link RelyingPartyContext}
+ *
+ * @param strategy
+ * creation strategy
+ */
+ public void setRelyingPartyContextCreationStrategy(
+ @Nonnull final Function<ProfileRequestContext, RelyingPartyContext> strategy) {
+ checkSetterPreconditions();
+ relyingPartyContextCreationStrategy = Constraint.isNotNull(strategy,
+ "RelyingPartyContext creation strategy cannot be null");
+ }
+
/**
* Set the strategy used to lookup the trust chain context.
*
@@ -140,17 +159,10 @@ public class SelectTrustChain extends AbstractProfileAction {
trustChainContext.setSelectedTrustChains(selectedChain);
final List<EntityStatement> selectedTrustChain = selectedChain.getFirst();
assert selectedTrustChain != null;
- Instant metadataExpiration = null;
- for (final EntityStatement statement : selectedTrustChain) {
- final Instant statementExpiration = statement.getClaimsSet().getExpirationTime().toInstant();
- metadataExpiration = metadataExpiration == null ? statementExpiration :
- statementExpiration.isBefore(metadataExpiration) ? statementExpiration : metadataExpiration;
- }
- trustChainContext.setSelectedMetadataExpiration(metadataExpiration);
-
- final OIDCMetadataContext oidcCtx = new OIDCMetadataContext();
- oidcCtx.setClientInformation(selectedChain.getSecond());
- profileRequestContext.ensureInboundMessageContext().addSubcontext(oidcCtx);
- log.debug("{} Client information attached to the OIDCMetadataContext", getLogPrefix());
+ final RelyingPartyContext relyingPartyContext =
+ relyingPartyContextCreationStrategy.apply(profileRequestContext);
+ relyingPartyContext.setRelyingPartyId(
+ selectedTrustChain.get(selectedTrustChain.size() - 1).getEntityID().getValue());
+ relyingPartyContext.setVerified(true);
}
}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateAutomaticRegistrationProfileConfiguration.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateAutomaticRegistrationProfileConfiguration.java
new file mode 100644
index 00000000..24b0f7de
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/impl/ValidateAutomaticRegistrationProfileConfiguration.java
@@ -0,0 +1,229 @@
+/*
+ * 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.List;
+import java.util.Map;
+import java.util.function.BiFunction;
+import java.util.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+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.openid.connect.sdk.federation.entities.EntityStatement;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.MandatoryTrustMarksLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.LocalMetadataPolicyLookupFunction;
+import net.shibboleth.idp.profile.AbstractProfileAction;
+import net.shibboleth.oidc.metadata.context.OIDCMetadataContext;
+import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+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;
+
+import org.opensaml.messaging.context.navigate.ChildContextLookup;
+import org.opensaml.profile.action.ActionSupport;
+
+/**
+ * Resolves metadata policy-compliant trust chains from the configurable trust chain cache, metadata policy merging
+ * strategy and enforcer. The data is populated to the {@link RelyingPartyTrustChainContext}.
+ *
+ * @event {@link EventIds#PROCEED_EVENT_ID}
+ * @event {@link EventIds#INVALID_MSG_CTX}
+ *
+ * @since 4.3.0
+ */
+public class ValidateAutomaticRegistrationProfileConfiguration extends AbstractProfileAction {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(ValidateAutomaticRegistrationProfileConfiguration.class);
+
+ /** Strategy used to lookup the trust chain context. */
+ @Nonnull private Function<ProfileRequestContext, RelyingPartyTrustChainContext> trustChainContextLookupStrategy;
+
+ /** Strategy used to lookup mandatory trust marks. */
+ @Nonnull private Function<ProfileRequestContext, List<String>> mandatoryTrustMarksLookupStrategy;
+
+ /** Strategy used to lookup local metadata policy to be merged to the client metadata. */
+ @Nonnull private Function<ProfileRequestContext, Map<String, MetadataPolicy>> localMetadataPolicyLookupStrategy;
+
+ /** Strategy used to merge local metadata policy into the client metadata. */
+ @NonnullAfterInit private BiFunction<OIDCClientInformation,Map<String, MetadataPolicy>,OIDCClientInformation>
+ localMetadataPolicyMergingStrategy;
+
+ /** Trust chain context to operate on. */
+ @NonnullBeforeExec private RelyingPartyTrustChainContext trustChainContext;
+
+ /** Selected trust chain to operate on. */
+ @NonnullBeforeExec private Pair<List<EntityStatement>, OIDCClientInformation> selectedTrustChain;
+
+ /** Client ID of the client. */
+ @NonnullBeforeExec private String clientId;
+
+ /**
+ * Constructor.
+ */
+ public ValidateAutomaticRegistrationProfileConfiguration() {
+ final Function<ProfileRequestContext, RelyingPartyTrustChainContext> tcls =
+ new ChildContextLookup<>(RelyingPartyTrustChainContext.class).compose(
+ new InboundMessageContextLookup());
+ assert tcls != null;
+ trustChainContextLookupStrategy = tcls;
+ mandatoryTrustMarksLookupStrategy = new MandatoryTrustMarksLookupFunction();
+ localMetadataPolicyLookupStrategy = new LocalMetadataPolicyLookupFunction();
+ }
+
+ /**
+ * 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 mandatory trust marks.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setMandatoryTrustMarksLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, List<String>> strategy) {
+ checkSetterPreconditions();
+ mandatoryTrustMarksLookupStrategy = Constraint.isNotNull(strategy,
+ "Mandatory trust marks lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to lookup local metadata policy to be merged to the client metadata.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setLocalMetadataPolicyLookupStrategy(
+ @Nonnull final Function<ProfileRequestContext, Map<String, MetadataPolicy>> strategy) {
+ checkSetterPreconditions();
+ localMetadataPolicyLookupStrategy = Constraint.isNotNull(strategy,
+ "Local metadata policy lookup strategy cannot be null");
+ }
+
+ /**
+ * Set the strategy used to merge local metadata policy into the client metadata.
+ *
+ * @param strategy lookup strategy
+ */
+ public void setLocalMetadataPolicyMergingStrategy(@Nonnull
+ final BiFunction<OIDCClientInformation,Map<String, MetadataPolicy>,OIDCClientInformation> strategy) {
+ checkSetterPreconditions();
+ localMetadataPolicyMergingStrategy =
+ Constraint.isNotNull(strategy, "Local metadata policy merging strategy 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 || trustChainContext.getSelectedTrustChain() == null) {
+ log.error("{} Unable to locate selected trust chain", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return false;
+ }
+
+ selectedTrustChain = trustChainContext.getSelectedTrustChain();
+ assert selectedTrustChain != null;
+ if (selectedTrustChain.getFirst() == null || selectedTrustChain.getSecond() == null) {
+ log.error("{} Selected trust chain contents is not populated", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return false;
+ }
+
+ final OIDCClientInformation clientInformation = selectedTrustChain.getSecond();
+ assert clientInformation != null;
+ clientId = clientInformation.getID().getValue();
+
+ return true;
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doExecute(@Nonnull final ProfileRequestContext profileRequestContext) {
+ final Map<String, MetadataPolicy> localMetadataPolicy =
+ localMetadataPolicyLookupStrategy.apply(profileRequestContext);
+ if (localMetadataPolicy != null && !localMetadataPolicy.isEmpty()) {
+ log.debug("{} Applying local metadata policy into the client metadata", getLogPrefix());
+ final OIDCClientInformation enforcedMetadata =
+ localMetadataPolicyMergingStrategy.apply(selectedTrustChain.getSecond(), localMetadataPolicy);
+ if (enforcedMetadata == null) {
+ log.error("{} Could not apply the local metadata policy into the client metadata", getLogPrefix());
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return;
+ }
+ selectedTrustChain.setSecond(enforcedMetadata);
+ }
+
+ final List<String> mandatoryTrustMarks = mandatoryTrustMarksLookupStrategy.apply(profileRequestContext);
+ if (mandatoryTrustMarks != null && !mandatoryTrustMarks.isEmpty()) {
+ log.debug("{} Verifying the mandatory trust marks {}", getLogPrefix(), mandatoryTrustMarks);
+ final Map<String,List<String>> verifiedTrustMarks = trustChainContext.getVerifiedTrustMarkIds();
+ if (verifiedTrustMarks == null || verifiedTrustMarks.get(clientId) == null
+ || !verifiedTrustMarks.get(clientId).containsAll(mandatoryTrustMarks)) {
+ log.info("{} Rejecting registration as some of the following mandatory trust marks are missing: {}",
+ getLogPrefix(), mandatoryTrustMarks);
+ ActionSupport.buildEvent(profileRequestContext, EventIds.INVALID_MSG_CTX);
+ return;
+ }
+ }
+
+ final List<EntityStatement> trustChain = selectedTrustChain.getFirst();
+ assert trustChain != null;
+ trustChainContext.setSelectedMetadataExpiration(resolveTrustChainExpiration(trustChain));
+
+ final OIDCMetadataContext oidcCtx = new OIDCMetadataContext();
+ oidcCtx.setClientInformation(selectedTrustChain.getSecond());
+ profileRequestContext.ensureInboundMessageContext().addSubcontext(oidcCtx);
+ log.debug("{} Client information attached to the OIDCMetadataContext", getLogPrefix());
+ }
+
+ /**
+ * 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.getClaimsSet().getExpirationTime().toInstant();
+ metadataExpiration = metadataExpiration == null ? statementExpiration :
+ statementExpiration.isBefore(metadataExpiration) ? statementExpiration : metadataExpiration;
+ }
+ return metadataExpiration;
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultLocalMetadataPolicyMergingStrategy.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultLocalMetadataPolicyMergingStrategy.java
new file mode 100644
index 00000000..684aa0de
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/DefaultLocalMetadataPolicyMergingStrategy.java
@@ -0,0 +1,112 @@
+/*
+ * 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.Map;
+import java.util.function.BiFunction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.slf4j.Logger;
+
+import com.nimbusds.oauth2.sdk.ParseException;
+import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
+
+import net.minidev.json.JSONObject;
+import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.collection.Pair;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.component.ComponentInitializationException;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Default strategy to merge the metadata policies for the given entity type from the given trust chain. Finally,
+ * a configurable local metadata policy is merged to the resulting map of metadata policies.
+ */
+public class DefaultLocalMetadataPolicyMergingStrategy extends AbstractIdentifiableInitializableComponent
+ implements BiFunction<OIDCClientInformation,Map<String, MetadataPolicy>,OIDCClientInformation> {
+
+ /** Class logger. */
+ @Nonnull private Logger log = LoggerFactory.getLogger(DefaultLocalMetadataPolicyMergingStrategy.class);
+
+ /** Enforcer function for applying metadata policy for an item. */
+ @NonnullAfterInit private BiFunction<Object, MetadataPolicy, Pair<Object, Boolean>> metadataPolicyEnforcer;
+
+ /**
+ * Set the enforcer function for applying metadata policy for an item.
+ *
+ * @param enforcer policy enforcer
+ */
+ public void setMetadataPolicyEnforcer(
+ @Nonnull final BiFunction<Object, MetadataPolicy, Pair<Object, Boolean>> enforcer) {
+ checkSetterPreconditions();
+ metadataPolicyEnforcer = Constraint.isNotNull(enforcer, "Metadata policy enforcer cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ protected void doInitialize() throws ComponentInitializationException {
+ super.doInitialize();
+
+ if (metadataPolicyEnforcer == null) {
+ throw new ComponentInitializationException("MetadataPolicyEnforcer cannot be null");
+ }
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable
+ public OIDCClientInformation apply(@Nullable final OIDCClientInformation inputMetadata,
+ @Nullable final Map<String, MetadataPolicy> localPolicy) {
+ checkComponentActive();
+ if (inputMetadata == null) {
+ return null;
+ }
+ if (localPolicy == null || localPolicy.isEmpty()) {
+ return inputMetadata;
+ }
+ final JSONObject result = inputMetadata.getOIDCMetadata().toJSONObject();
+ boolean compliant = true;
+ for (final String claim : localPolicy.keySet()) {
+ final MetadataPolicy policy = localPolicy.get(claim);
+ final Object value = inputMetadata.getOIDCMetadata().toJSONObject().get(claim);
+ log.debug("Claim {} set in policy included in the input: {}", claim, value == null);
+ final Pair<Object,Boolean> mergeResult = metadataPolicyEnforcer.apply(value, policy);
+ final Boolean enforcerResult = mergeResult != null ? mergeResult.getSecond() : null;
+ if (enforcerResult == null || !enforcerResult.booleanValue()) {
+ log.warn("Metadata claim {} is not compliant with the policy", claim);
+ compliant = false;
+ } else {
+ log.trace("Validation result is OK for claim {}", claim);
+ final Object enforcedValue = mergeResult != null ? mergeResult.getFirst() : null;
+ result.put(claim, enforcedValue);
+ }
+ }
+
+ if (!compliant) {
+ log.warn("The requested metadata is not compliant with the policy");
+ } else {
+ log.debug("The requested metadata is compliant with the policy");
+ try {
+ return OIDCClientInformation.parse(result);
+ } catch (final ParseException e) {
+ log.error("Could not parse the metadata object", e);
+ }
+ }
+ return null;
+ }
+}
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/LocalMetadataPolicyLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/LocalMetadataPolicyLookupFunction.java
new file mode 100644
index 00000000..54acc5dd
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/LocalMetadataPolicyLookupFunction.java
@@ -0,0 +1,49 @@
+/*
+ * 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.Map;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationAutomaticRegistrationConfiguration;
+import net.shibboleth.oidc.metadata.policy.MetadataPolicy;
+
+/**
+ * A function that obtains
+ * {@link OIDFederationAutomaticRegistrationConfiguration#getLocalMetadataPolicy(ProfileRequestContext)}.
+ *
+ * <p>If a specific setting is unavailable, a null value is returned.</p>
+ */
+public class LocalMetadataPolicyLookupFunction extends AbstractRelyingPartyLookupFunction<Map<String, MetadataPolicy>> {
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public Map<String, MetadataPolicy> apply(@Nullable final ProfileRequestContext input) {
+ final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+ if (rpc != null) {
+ final ProfileConfiguration pc = rpc.getProfileConfig();
+ if (pc instanceof OIDFederationAutomaticRegistrationConfiguration ofarc) {
+ return ofarc.getLocalMetadataPolicy(input);
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MandatoryTrustMarksLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MandatoryTrustMarksLookupFunction.java
new file mode 100644
index 00000000..3a4b8f39
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MandatoryTrustMarksLookupFunction.java
@@ -0,0 +1,48 @@
+/*
+ * 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 javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationAutomaticRegistrationConfiguration;
+
+/**
+ * A function that obtains
+ * {@link OIDFederationAutomaticRegistrationConfiguration#getMandatoryTrustMarks(ProfileRequestContext)}.
+ *
+ * <p>If a specific setting is unavailable, a null value is returned.</p>
+ */
+public class MandatoryTrustMarksLookupFunction extends AbstractRelyingPartyLookupFunction<List<String>> {
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public List<String> apply(@Nullable final ProfileRequestContext input) {
+ final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+ if (rpc != null) {
+ final ProfileConfiguration pc = rpc.getProfileConfig();
+ if (pc instanceof OIDFederationAutomaticRegistrationConfiguration ofarc) {
+ return ofarc.getMandatoryTrustMarks(input);
+ }
+ }
+ return null;
+ }
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MaximumTrustMarkLifetimeLookupFunction.java b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MaximumTrustMarkLifetimeLookupFunction.java
new file mode 100644
index 00000000..c39ed5d7
--- /dev/null
+++ b/idp-oidc-extension-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/profile/navigate/MaximumTrustMarkLifetimeLookupFunction.java
@@ -0,0 +1,52 @@
+/*
+ * 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.time.Duration;
+
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import net.shibboleth.profile.config.ProfileConfiguration;
+import net.shibboleth.profile.context.RelyingPartyContext;
+import net.shibboleth.profile.context.navigate.AbstractRelyingPartyLookupFunction;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationAutomaticRegistrationConfiguration;
+
+/**
+ * A function that returns
+ * {@link OIDFederationAutomaticRegistrationConfiguration#getMaximumTrustMarkLifetime(ProfileRequestContext)} if such a
+ * profile is available from a {@link RelyingPartyContext} obtained via a lookup function, by default a child of the
+ * {@link ProfileRequestContext}.
+ *
+ * <p>If a specific setting is unavailable, a null value is returned.</p>
+ */
+public class MaximumTrustMarkLifetimeLookupFunction extends AbstractRelyingPartyLookupFunction<Duration> {
+
+ /** {@inheritDoc} */
+ @Override
+ @Nullable public Duration apply(@Nullable final ProfileRequestContext input) {
+ final RelyingPartyContext rpc = getRelyingPartyContextLookupStrategy().apply(input);
+ if (rpc != null) {
+ final ProfileConfiguration pc = rpc.getProfileConfig();
+ if (pc instanceof OIDFederationAutomaticRegistrationConfiguration ofarc) {
+ return ofarc.getMaximumTrustMarkLifetime(input);
+ }
+ }
+
+ return null;
+ }
+
+}
\ No newline at end of file
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
index 4ebc79fa..4b966da8 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-beans.xml
@@ -1176,6 +1176,23 @@
</constructor-arg>
</bean>
+ <bean id="AutomaticRegistrationRelyingPartyCreationStrategy" parent="shibboleth.Functions.Compose"
+ c:g-ref="shibboleth.ChildLookupOrCreate.RelyingPartyContext"
+ c:f-ref="RelyingPartyTrustChainContextLookupStrategy" />
+
+ <bean id="RelyingPartyTrustChainContextLookupStrategy" parent="shibboleth.Functions.Expression"
+ c:expression="#input.ensureInboundMessageContext().getSubcontext(T(net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.RelyingPartyTrustChainContext))" />
+
+ <bean id="SelectAutomaticRegistrationRelyingPartyConfiguration"
+ class="net.shibboleth.idp.profile.impl.SelectRelyingPartyConfiguration" scope="prototype"
+ p:relyingPartyContextLookupStrategy-ref="AutomaticRegistrationRelyingPartyCreationStrategy"
+ p:relyingPartyConfigurationResolver-ref="shibboleth.RelyingPartyResolverService" />
+
+ <bean id="SelectAutomaticRegistrationProfileConfiguration"
+ class="net.shibboleth.idp.profile.impl.SelectProfileConfiguration" scope="prototype"
+ p:relyingPartyContextLookupStrategy-ref="AutomaticRegistrationRelyingPartyCreationStrategy"
+ p:profileId="#{T(net.shibboleth.idp.plugin.oidc.op.oidfed.config.OIDFederationAutomaticRegistrationConfiguration).PROFILE_ID}" />
+
<bean id="SelectTrustChain" class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.SelectTrustChain"
scope="prototype">
<property name="activationCondition">
@@ -1184,6 +1201,35 @@
</property>
</bean>
+ <bean id="ResolveTrustMarks" class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.ResolveTrustMarks"
+ scope="prototype"
+ p:trustChainCache-ref="#{'%{idp.oidfed.authorize.TrustChainMetadataCache:shibboleth.oidfed.TrustChainMetadataCache}'.trim()}">
+ <property name="trustEngine">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.TokenPayloadAsymmetricKeyTrustEngine">
+ <constructor-arg>
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultEntityStatementCredentialResolver" />
+ </constructor-arg>
+ </bean>
+ </property>
+ <property name="trustedTrustMarkIssuersLookupStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultTrustChainTrustedTrustMarkIssuersLookupStrategy"
+ p:objectMapper-ref="shibboleth.oidc.JSONObjectMapper" />
+ </property>
+ </bean>
+
+ <bean id="ValidateAutomaticRegistrationProfileConfiguration"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.impl.ValidateAutomaticRegistrationProfileConfiguration"
+ scope="prototype">
+ <property name="localMetadataPolicyMergingStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.DefaultLocalMetadataPolicyMergingStrategy"
+ p:metadataPolicyEnforcer-ref="#{'%{idp.oidfed.authorize.MetadataPolicyEnforcer:DefaultMetadataPolicyEnforcer}'.trim()}"/>
+ </property>
+ <property name="mandatoryTrustMarksLookupStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.profile.navigate.MandatoryTrustMarksLookupFunction"
+ p:relyingPartyContextLookupStrategy-ref="AutomaticRegistrationRelyingPartyCreationStrategy"/>
+ </property>
+ </bean>
+
<bean id="InitializeRelyingPartyContext"
class="net.shibboleth.idp.plugin.oidc.op.profile.impl.InitializeRelyingPartyContext" scope="prototype"
p:clientIDLookupStrategy-ref="shibboleth.ClientIDLookupStrategy">
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
index 413675da..9846f0f2 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/flows/oidc/authorize/authorize-flow.xml
@@ -47,6 +47,10 @@
<action-state id="DoAutomaticRegistration">
<evaluate expression="ResolveTrustChains" />
<evaluate expression="SelectTrustChain" />
+ <evaluate expression="ResolveTrustMarks" />
+ <evaluate expression="SelectAutomaticRegistrationRelyingPartyConfiguration" />
+ <evaluate expression="SelectAutomaticRegistrationProfileConfiguration" />
+ <evaluate expression="ValidateAutomaticRegistrationProfileConfiguration" />
<evaluate expression="InitializeRelyingPartyContext" />
<evaluate expression="'proceed'" />
<transition on="proceed" to="DoSelectConfiguration">
diff --git a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
index b3a3d105..6cdccafd 100644
--- a/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
+++ b/idp-oidc-extension-impl/src/main/resources/META-INF/net/shibboleth/idp/service/relying-party/postconfig.xml
@@ -116,6 +116,11 @@
<bean id="DefaultLogoutHintMatchingPredicate"
class="net.shibboleth.idp.plugin.oidc.op.profile.logic.DefaultLogoutHintMatchingPredicate"/>
+ <bean id="OIDFED.AutomaticRegistration" parent="AbstractOIDCProfile" lazy-init="true"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.config.DefaultOIDFederationAutomaticRegistrationConfiguration"
+ p:mandatoryTrustMarks="%{idp.oidfed.automaticRegistration.mandatoryTrustMarks:}"
+ p:securityConfiguration-ref="shibboleth.oidc.federation.DefaultSecurityConfiguration" />
+
<!-- Metadata-driven variants. -->
<bean id="AbstractMDDrivenOAuthTokenValidatingProfile" parent="AbstractMDDrivenOAuthClientAuthenticatableProfile" abstract="true">
@@ -936,4 +941,7 @@
</property>
</bean>
+ <bean id="RelyingPartyByTrustAnchor" abstract="true" parent="RelyingParty"
+ class="net.shibboleth.idp.plugin.oidc.op.oidfed.config.RelyingPartyConfigurationSupport" factory-method="byTrustAnchor" />
+
</beans>
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list