[java-idp-plugin-oidc-op-oidfed] 02/02: Initial support for crit claim and custom filter strategies for cache containers
Codeberg
noreply at shibboleth.net
Wed Jan 28 13:46:11 UTC 2026
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch dev/CACHE-REFACTOR
in repository java-idp-plugin-oidc-op-oidfed.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-oidc-op-oidfed/commit/1a5f82b5f34a0c5f80a6d7162ef4d322d0105808
commit 1a5f82b5f34a0c5f80a6d7162ef4d322d0105808
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Wed Jan 28 15:45:34 2026 +0200
Initial support for crit claim and custom filter strategies for cache containers
- CritClaimsValidator verifies that crit claim meets syntax requirements and only contains recognized claims
- Global default may be modified via 'idp.oidfed.cache.default.critClaims'
- For entity configuration via 'idp.oidfed.cache.entityConfiguration.critClaims'
- For subordinate statement via 'idp.oidfed.cache.subordinateStatement.critClaims'
- List of custom cache container filter strategies may be wired via 'idp.oidfed.cache.<cacheType>.customFilterStrategies'
- entityConfiguration, subordinateStatement, trustChain, resolveEntity, trustMarkStatus, trust;ark
- Bean must be a list of matching BiFunction<CONTAINER_TYPE, MetadataFilterContext, CONTAINER_TYPE>
- For instance BiFunction<RemoteEntityConfigurationContainer, MetadataFilterContext, RemoteEntityConfigurationContainer>
---
...faultEntityStatementMetadataFilterStrategy.java | 36 +++++++-
...rustChainSignatureValidationFilterStrategy.java | 36 +++++++-
.../jwt/claims/impl/CritClaimsValidator.java | 97 ++++++++++++++++++++++
.../META-INF/net.shibboleth.idp/postconfig.xml | 22 +++--
.../flow/oidfed/AbstractFederationFlowTest.java | 37 +++++++--
.../AuthorizeFlowAutomaticRegistrationTest.java | 42 +++++++++-
.../EntityConfigurationMetadataCacheTest.java | 73 +++++++++++++++-
.../SubordinateStatementMetadataCacheTest.java | 80 +++++++++++++++++-
.../oidfed/cache/TrustChainMetadataCacheTest.java | 40 +++++++++
.../CustomEntityConfigurationFilterStrategy.java | 58 +++++++++++++
.../CustomSubordinateStatementFilterStrategy.java | 58 +++++++++++++
.../support/CustomTrustChainFilterStrategy.java | 47 +++++++++++
.../net/shibboleth/idp/module/conf/global.xml | 12 +++
.../idp/module/conf/oidfed/oidfed.properties | 8 +-
14 files changed, 628 insertions(+), 18 deletions(-)
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/DefaultEntityStatementMetadataFilterStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/DefaultEntityStatementMetadataFilterStrategy.java
index b111c66..5b8a512 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/DefaultEntityStatementMetadataFilterStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/DefaultEntityStatementMetadataFilterStrategy.java
@@ -25,6 +25,7 @@ import org.slf4j.Logger;
import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
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;
@@ -44,6 +45,17 @@ public class DefaultEntityStatementMetadataFilterStrategy
@NonnullAfterInit
private List<BiPredicate<BaseExpirableStatementContainer<?>, MetadataFilterContext>> validationConditions;
+ /** List of custom metadata filter strategies. */
+ @Nonnull private List<BiFunction<BaseExpirableStatementContainer<?>,
+ MetadataFilterContext, BaseExpirableStatementContainer<?>>> customFilterStrategies;
+
+ /**
+ * Constructor.
+ */
+ public DefaultEntityStatementMetadataFilterStrategy() {
+ customFilterStrategies = CollectionSupport.emptyList();
+ }
+
/**
* Set the list of validation conditions.
*
@@ -55,6 +67,17 @@ public class DefaultEntityStatementMetadataFilterStrategy
validationConditions = Constraint.isNotNull(predicates, "List of validation conditions cannot be null");
}
+ /**
+ * Set the list of custom metadata filter strategies.
+ *
+ * @param strategies metadata filter strategies
+ */
+ public void setCustomFilterStrategies(@Nullable final List<BiFunction<BaseExpirableStatementContainer<?>,
+ MetadataFilterContext, BaseExpirableStatementContainer<?>>> strategies) {
+ checkSetterPreconditions();
+ customFilterStrategies = strategies == null ? CollectionSupport.emptyList() : strategies;
+ }
+
/** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
@@ -80,7 +103,18 @@ public class DefaultEntityStatementMetadataFilterStrategy
}
}
log.debug("Metadata container successfully validated by all predicates for {}", container.getEntityId());
- return container;
+
+ BaseExpirableStatementContainer<?> result = container;
+ for (final BiFunction<BaseExpirableStatementContainer<?>, MetadataFilterContext,
+ BaseExpirableStatementContainer<?>> customFilterStrategy : customFilterStrategies) {
+ log.trace("Applying custom filter strategy {}", customFilterStrategy);
+ result = customFilterStrategy.apply(result, filterContext);
+ if (result == null) {
+ log.debug("Custom filter strategy {} returned null", customFilterStrategy);
+ return null;
+ }
+ }
+ return result;
}
}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultRemoteTrustChainSignatureValidationFilterStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultRemoteTrustChainSignatureValidationFilterStrategy.java
index 10a571c..83c4249 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultRemoteTrustChainSignatureValidationFilterStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/cache/remote/trustchain/DefaultRemoteTrustChainSignatureValidationFilterStrategy.java
@@ -32,6 +32,7 @@ import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteTrus
import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
+import net.shibboleth.shared.collection.CollectionSupport;
import net.shibboleth.shared.component.ComponentInitializationException;
import net.shibboleth.shared.logic.Constraint;
import net.shibboleth.shared.primitive.LoggerFactory;
@@ -62,6 +63,17 @@ public class DefaultRemoteTrustChainSignatureValidationFilterStrategy
@NonnullAfterInit private BiFunction<EntityStatement<?>, MetadataFilterContext, EntityStatement<?>>
entityStatementSignatureValidationFilterStrategy;
+ /** List of custom metadata filter strategies. */
+ @Nonnull private List<BiFunction<RemoteTrustChainsContainer,
+ MetadataFilterContext, RemoteTrustChainsContainer>> customFilterStrategies;
+
+ /**
+ * Constructor.
+ */
+ public DefaultRemoteTrustChainSignatureValidationFilterStrategy() {
+ customFilterStrategies = CollectionSupport.emptyList();
+ }
+
/**
* Set the signature validation filter strategy for trust anchor entity statements.
*
@@ -74,6 +86,17 @@ public class DefaultRemoteTrustChainSignatureValidationFilterStrategy
"EntityStatementSignatureValidationFilterStrategy cannot be null");
}
+ /**
+ * Set the list of custom metadata filter strategies.
+ *
+ * @param strategies metadata filter strategies
+ */
+ public void setCustomFilterStrategies(@Nullable final List<BiFunction<RemoteTrustChainsContainer,
+ MetadataFilterContext, RemoteTrustChainsContainer>> strategies) {
+ checkSetterPreconditions();
+ customFilterStrategies = strategies == null ? CollectionSupport.emptyList() : strategies;
+ }
+
/** {@inheritDoc} */
@Override
protected void doInitialize() throws ComponentInitializationException {
@@ -137,7 +160,18 @@ public class DefaultRemoteTrustChainSignatureValidationFilterStrategy
container.removeTrustChain(trustChain);
}
}
- return container;
+
+ RemoteTrustChainsContainer result = container;
+ for (final BiFunction<RemoteTrustChainsContainer, MetadataFilterContext,
+ RemoteTrustChainsContainer> customFilterStrategy : customFilterStrategies) {
+ log.trace("Applying custom filter strategy {}", customFilterStrategy);
+ result = customFilterStrategy.apply(result, filterContext);
+ if (result == null) {
+ log.debug("Custom filter strategy {} returned null", customFilterStrategy);
+ return null;
+ }
+ }
+ return result;
}
}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/jwt/claims/impl/CritClaimsValidator.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/jwt/claims/impl/CritClaimsValidator.java
new file mode 100644
index 0000000..4f8e692
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/security/jwt/claims/impl/CritClaimsValidator.java
@@ -0,0 +1,97 @@
+/*
+ * 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.security.jwt.claims.impl;
+
+import java.text.ParseException;
+import java.util.Collection;
+import java.util.List;
+import java.util.Optional;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+
+import com.nimbusds.jwt.JWTClaimsSet;
+
+import net.shibboleth.oidc.jwt.claims.AbstractClaimsValidator;
+import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
+import net.shibboleth.oidc.jwt.claims.JWTValidationException;
+import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
+import net.shibboleth.shared.collection.CollectionSupport;
+import net.shibboleth.shared.primitive.NonnullSupplier;
+
+/**
+ * A {@link ClaimsValidator} for validating that crit is not an empty array, does not contain standard claim names and
+ * only contain values that are configured as recognized.
+ */
+ at ThreadSafeAfterInit
+public class CritClaimsValidator extends AbstractClaimsValidator {
+
+ /** The list of standard operators that cannot be included in the metadata_policy_crit array. */
+ @Nonnull public static final List<String> STANDARD_CLAIMS = CollectionSupport.listOf(
+ "iss", "sub", "iat", "exp", "jwks", "metadata", "crit", "authority_hints", "trust_anchor_hints",
+ "trust_marks", "trust_mark_issuers", "trust_mark_owners", "constraints", "metadata_policy",
+ "metadata_policy_crit", "source_endpoint", "aud", "trust_anchor");
+
+ /** The collection of recognized claims. */
+ @Nonnull private Collection<String> recognizedClaims;
+
+ public CritClaimsValidator() {
+ recognizedClaims = CollectionSupport.emptyList();
+ }
+
+ /**
+ * Set the collection of recognized claims
+ *
+ * @param claims recognized claims
+ */
+ public void setRecognizedClaims(@Nullable final Collection<String> claims) {
+ checkSetterPreconditions();
+ final Collection<String> list =
+ Optional.ofNullable(claims).orElseGet(NonnullSupplier.of(CollectionSupport.emptyList()));
+ assert list != null;
+ recognizedClaims = list;
+ }
+
+ /** {@inheritDoc} */
+ protected void doValidate(@Nonnull final JWTClaimsSet claims,
+ @Nonnull final ProfileRequestContext context) throws JWTValidationException {
+ final List<String> values;
+ try {
+ values = claims.getStringListClaim("crit");
+ } catch (final ParseException e) {
+ throw new JWTValidationException("Could not parse crit into a list of strings");
+ }
+ if (values != null) {
+ if (values.isEmpty()) {
+ throw new JWTValidationException("Empty array is not allowed for crit");
+ }
+ for (final String value : values) {
+ if (values.indexOf(value) != values.lastIndexOf(value)) {
+ throw new JWTValidationException(
+ "Claim " + value + " is included more than once in crit");
+ }
+ if (STANDARD_CLAIMS.contains(value)) {
+ throw new JWTValidationException(
+ "Claim " + value + " is standard claim name and is not allowed in crit");
+ }
+ if (!recognizedClaims.contains(value)) {
+ throw new JWTValidationException( "Claim " + value + " is not recognized for crit");
+ }
+ }
+ }
+ }
+}
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 1f74889..12762ff 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
@@ -124,7 +124,8 @@
</bean>
</property>
<property name="metadataFilterStrategy">
- <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+ p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.entityConfiguration.customFilterStrategies:}'.trim())}">
<property name="validationConditions">
<util:list value-type="java.util.function.BiPredicate">
<bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
@@ -168,6 +169,8 @@
p:prohibitedClaims="source_endpoint" />
<bean class="net.shibboleth.oidc.security.jwt.claims.impl.ProhibitedClaimsValidator"
p:prohibitedClaims="aud" />
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.CritClaimsValidator"
+ p:recognizedClaims="%{idp.oidfed.cache.entityConfiguration.critClaims:%{idp.oidfed.cache.default.critClaims:}}" />
</util:list>
</property>
</bean>
@@ -212,7 +215,8 @@
</bean>
</property>
<property name="metadataFilterStrategy">
- <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+ p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.subordinateStatement.customFilterStrategies:}'.trim())}">
<property name="validationConditions">
<util:list value-type="java.util.function.BiPredicate">
<bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
@@ -276,6 +280,8 @@
p:requiredClaims="jwks" />
<bean class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.CritClaimsValidator"
+ p:recognizedClaims="%{idp.oidfed.cache.subordinateStatement.critClaims:%{idp.oidfed.cache.default.critClaims:}}" />
<bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.MetadataPolicyCritClaimsValidator">
<property name="supportedOperatorsLookupStrategy">
<bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.security.jwt.claims.impl.DefaultMetadataPolicyOperatorsLookupStrategy"
@@ -315,7 +321,8 @@
<bean parent="shibboleth.Functions.Expression" c:expression="#input?.getEntityId()"/>
</property>
<property name="metadataFilterStrategy">
- <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustchain.DefaultRemoteTrustChainSignatureValidationFilterStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustchain.DefaultRemoteTrustChainSignatureValidationFilterStrategy"
+ p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.trustChain.customFilterStrategies:}'.trim())}">
<property name="trustEngine">
<bean class="net.shibboleth.oidc.security.impl.ExplicitKeySignedJWTTrustEngine">
<constructor-arg index="0">
@@ -379,7 +386,8 @@
<bean parent="shibboleth.Functions.Expression" c:expression="#input?.getIdentifier()"/>
</property>
<property name="metadataFilterStrategy">
- <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+ p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.resolveEntity.customFilterStrategies:}'.trim())}">
<property name="validationConditions">
<util:list value-type="java.util.function.BiPredicate">
<bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
@@ -506,7 +514,8 @@
<bean parent="shibboleth.Functions.Expression" c:expression="#input?.get(T(net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustmark.TrustMarkStatusIdentifierCriterion))?.getIdentifier()"/>
</property>
<property name="metadataFilterStrategy">
- <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+ p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.trustMarkStatus.customFilterStrategies:}'.trim())}">
<property name="validationConditions">
<util:list value-type="java.util.function.BiPredicate">
<bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
@@ -592,7 +601,8 @@
<bean parent="shibboleth.Functions.Expression" c:expression="#input?.get(T(net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.trustmark.TrustMarkIdentifierCriterion))?.getIdentifier()"/>
</property>
<property name="metadataFilterStrategy">
- <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementMetadataFilterStrategy"
+ p:customFilterStrategies="#{getObject('%{idp.oidfed.cache.trustMark.customFilterStrategies:}'.trim())}">
<property name="validationConditions">
<util:list value-type="java.util.function.BiPredicate">
<bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.DefaultEntityStatementSignatureValidationCondition"
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
index fb41140..bfd6a2b 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AbstractFederationFlowTest.java
@@ -252,6 +252,12 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
protected String rpEntityConfiguration(final String clientId, final OIDCClientMetadata metadata,
final List<Map<String, String>> trustMarks, final JWK leafKey, final String... authorityHints)
throws URISyntaxException {
+ return rpEntityConfiguration(clientId, metadata, trustMarks, null, leafKey, authorityHints);
+ }
+
+ protected String rpEntityConfiguration(final String clientId, final OIDCClientMetadata metadata,
+ final List<Map<String, String>> trustMarks, final List<String> crit, final JWK leafKey,
+ final String... authorityHints) throws URISyntaxException {
final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(clientId).subject(clientId)
.issueTime(Date.from(Instant.now()))
.expirationTime(Date.from(Instant.now().plusSeconds(300)))
@@ -262,6 +268,9 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
if (trustMarks != null) {
builder.claim("trust_marks", trustMarks);
}
+ if (crit != null) {
+ builder.claim("crit", crit);
+ }
final EntityStatement<?> rpConfiguration =
TrustChainTestUtil.entityStatement(JWSAlgorithm.RS256, leafKey, builder.build());
return rpConfiguration.getJwt().serialize();
@@ -391,23 +400,24 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
}
protected String subordinateStatement(final String issuer, final Map<String, Object> metadata,
- final List<String> metadataPolicyCrit) {
- return subordinateStatement(issuer, metadata, metadataPolicyCrit, leafKey);
+ final List<String> metadataPolicyCrit, final List<String> crit) {
+ return subordinateStatement(issuer, metadata, metadataPolicyCrit, crit, leafKey);
}
protected String subordinateStatement(final String issuer, final Map<String, Object> metadata,
final JWK subjectKey) {
- return subordinateStatement(issuer, metadata, null, subjectKey);
+ return subordinateStatement(issuer, metadata, null, null, subjectKey);
}
protected String subordinateStatement(final String issuer, final Map<String, Object> metadata,
- final List<String> metadataPolicyCrit, final JWK subjectKey) {
+ final List<String> metadataPolicyCrit, final List<String> crit, final JWK subjectKey) {
final JWTClaimsSet claimsSet = new JWTClaimsSet.Builder().issuer(anchorId).subject(issuer)
.issueTime(Date.from(Instant.now()))
.expirationTime(Date.from(Instant.now().plusSeconds(300)))
.claim("jwks", new JWKSet(subjectKey).toJSONObject(true))
.claim("metadata", metadata)
.claim("metadata_policy_crit", metadataPolicyCrit)
+ .claim("crit", crit)
.build();
final EntityStatement<?> rpConfiguration =
TrustChainTestUtil.entityStatement(JWSAlgorithm.RS256, trustedAnchorKey, claimsSet);
@@ -561,7 +571,24 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
mapResponse(subordinateStatementUrl(anchorFetchEndpoint, clientId),
mockResponse(subordinateStatement(clientId, Map.of("openid_relying_party",
- new OIDCClientMetadata().toJSONObject()), crit)));
+ new OIDCClientMetadata().toJSONObject()), crit, null)));
+ } catch (UnsupportedOperationException | IOException | URISyntaxException e) {
+ Assert.fail("Could not initialize mock HTTP client", e);
+ }
+ }
+
+ protected void rpConfigureMockHttpClientWithCrit(final String clientId, final List<String> configCrit,
+ final List<String> subordinateCrit) {
+ try {
+ final OIDCClientMetadata metadata = new OIDCClientMetadata();
+ metadata.setRedirectionURI(new URI(redirectUri));
+ metadata.setJWKSet(new JWKSet(rpKey.toPublicJWK()));
+ mapResponse(entityConfigurationUrl(clientId),
+ mockResponse(rpEntityConfiguration(clientId, metadata, null, configCrit, rpKey)));
+ mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+ mapResponse(subordinateStatementUrl(anchorFetchEndpoint, clientId),
+ mockResponse(subordinateStatement(clientId, Map.of("openid_relying_party",
+ new OIDCClientMetadata().toJSONObject()), null, subordinateCrit)));
} catch (UnsupportedOperationException | IOException | URISyntaxException e) {
Assert.fail("Could not initialize mock HTTP client", e);
}
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AuthorizeFlowAutomaticRegistrationTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AuthorizeFlowAutomaticRegistrationTest.java
index f7f3ebd..456e24a 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AuthorizeFlowAutomaticRegistrationTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/AuthorizeFlowAutomaticRegistrationTest.java
@@ -136,6 +136,42 @@ public class AuthorizeFlowAutomaticRegistrationTest extends AbstractFederationFl
Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
}
+ @Test
+ public void testWithValidTrustChain_signedRequestObject_emptyCritInConfiguration()
+ throws IOException, UnsupportedOperationException, URISyntaxException {
+ final String clientId = uniqueClientId();
+ rpConfigureMockHttpClientWithCrit(clientId, CollectionSupport.emptyList(), null);
+ final FlowExecutionResult result =
+ launchAuthenticationRequest(clientId, "openid profile", signedRequestObject(Map.of(
+ "iss", clientId,
+ "client_id", clientId,
+ "aud", issuer,
+ "exp", Instant.now().plus(Duration.ofMinutes(5)).toEpochMilli(),
+ "jti", UUID.randomUUID(),
+ "response_type", "code",
+ "scope", "openid profile",
+ "redirect_uri", redirectUri)));
+ Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
+ }
+
+ @Test
+ public void testWithValidTrustChain_signedRequestObject_emptyCritInSubordinate()
+ throws IOException, UnsupportedOperationException, URISyntaxException {
+ final String clientId = uniqueClientId();
+ rpConfigureMockHttpClientWithCrit(clientId, null, CollectionSupport.emptyList());
+ final FlowExecutionResult result =
+ launchAuthenticationRequest(clientId, "openid profile", signedRequestObject(Map.of(
+ "iss", clientId,
+ "client_id", clientId,
+ "aud", issuer,
+ "exp", Instant.now().plus(Duration.ofMinutes(5)).toEpochMilli(),
+ "jti", UUID.randomUUID(),
+ "response_type", "code",
+ "scope", "openid profile",
+ "redirect_uri", redirectUri)));
+ Assert.assertEquals(result.getOutcome().getId(), "ErrorView");
+ }
+
@Test
public void testWithValidTrustChain_resolveApi_signedRequestObject()
throws IOException, UnsupportedOperationException, URISyntaxException {
@@ -655,8 +691,10 @@ public class AuthorizeFlowAutomaticRegistrationTest extends AbstractFederationFl
final AuthorizeCodeClaimsSet claims;
try {
claims = AuthorizeCodeClaimsSet.parse(code, getDataSealer());
- Assert.assertNotNull(claims.getClaimsSet().getStringListClaim("oidfed_artc"));
- return claims.getClaimsSet().getStringListClaim("oidfed_artc");
+ final JWTClaimsSet claimsSet = claims.getClaimsSet();
+ assert claimsSet != null;
+ Assert.assertNotNull(claimsSet.getStringListClaim("oidfed_artc"));
+ return claimsSet.getStringListClaim("oidfed_artc");
} catch (ParseException | DataSealerException e) {
return null;
}
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/EntityConfigurationMetadataCacheTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/EntityConfigurationMetadataCacheTest.java
index 5eee569..ecb9009 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/EntityConfigurationMetadataCacheTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/EntityConfigurationMetadataCacheTest.java
@@ -36,11 +36,13 @@ import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jwt.JWTClaimsSet;
import net.shibboleth.idp.plugin.oidc.op.oidfed.TrustChainTestUtil;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityConfiguration;
import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.ResponseContainerExpirationCriterion;
import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.SubjectEntityIDCriterion;
import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteEntityConfigurationContainer;
import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.AbstractFederationFlowTest;
import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.EntityConfigurationFlowTest;
+import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomEntityConfigurationFilterStrategy;
import net.shibboleth.oidc.metadata.cache.MetadataCache;
import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
import net.shibboleth.shared.resolver.CriteriaSet;
@@ -78,12 +80,81 @@ public class EntityConfigurationMetadataCacheTest extends AbstractFederationFlow
new ResponseContainerExpirationCriterion(Instant.now().plusSeconds(300))));
Assert.assertNotNull(result);
Assert.assertEquals(result.size(), 1);
- Assert.assertNotNull(result.get(0).getStatement());
+ final EntityConfiguration statement = result.get(0).getStatement();
+ Assert.assertNotNull(statement);
+ assert statement != null;
+ Assert.assertNull(statement.getParsedPayload().getCustomClaims()
+ .get(CustomEntityConfigurationFilterStrategy.CUSTOM_CLAIM_NAME));
} catch (MetadataCacheException e) {
Assert.fail("Could not resolve entity configuration", e);
}
}
+ @Test
+ public void testValidEntityConfiguration_customCriticalClaim()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+ final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(entityId).subject(entityId)
+ .issueTime(Date.from(Instant.now()))
+ .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+ .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+ .claim("crit", List.of("default_crit"))
+ .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+ final String entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+ try {
+ final List<RemoteEntityConfigurationContainer> result =
+ entityConfigurationCache.get(new CriteriaSet(new SubjectEntityIDCriterion(entityId),
+ new ResponseContainerExpirationCriterion(Instant.now().plusSeconds(300))));
+ Assert.assertNotNull(result);
+ Assert.assertEquals(result.size(), 1);
+ final EntityConfiguration statement = result.get(0).getStatement();
+ Assert.assertNotNull(statement);
+ assert statement != null;
+ Assert.assertEquals(statement.getParsedPayload().getCustomClaims()
+ .get(CustomEntityConfigurationFilterStrategy.CUSTOM_CLAIM_NAME),
+ CustomEntityConfigurationFilterStrategy.CUSTOM_CLAIM_VALUE);
+ } catch (MetadataCacheException e) {
+ Assert.fail("Could not resolve entity configuration", e);
+ }
+ }
+
+ @Test
+ public void testWithUnrecognizedCriticalClaim()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+ final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(entityId).subject(entityId)
+ .issueTime(Date.from(Instant.now()))
+ .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+ .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+ .claim("crit", List.of("subordinate_crit"))
+ .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+ final String entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+ assertNoEntityConfiguration(entityId);
+ }
+
+ @Test
+ public void testWithStandardClaimAsCriticalClaim()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+ final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(entityId).subject(entityId)
+ .issueTime(Date.from(Instant.now()))
+ .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+ .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+ .claim("crit", List.of("jwks"))
+ .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+ final String entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+ assertNoEntityConfiguration(entityId);
+ }
+
@Test
public void testSignatureWithNonMathchingKey()
throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/SubordinateStatementMetadataCacheTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/SubordinateStatementMetadataCacheTest.java
index 2afb1d9..7a746c6 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/SubordinateStatementMetadataCacheTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/SubordinateStatementMetadataCacheTest.java
@@ -36,12 +36,14 @@ import com.nimbusds.jose.jwk.JWKSet;
import com.nimbusds.jwt.JWTClaimsSet;
import net.shibboleth.idp.plugin.oidc.op.oidfed.TrustChainTestUtil;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubordinateStatement;
import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.IssuerEntityIDCriterion;
import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.ResponseContainerExpirationCriterion;
import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.SubjectEntityIDCriterion;
import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteSubordinateStatementContainer;
import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.AbstractFederationFlowTest;
import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.EntityConfigurationFlowTest;
+import net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomSubordinateStatementFilterStrategy;
import net.shibboleth.oidc.metadata.cache.MetadataCache;
import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
import net.shibboleth.shared.resolver.CriteriaSet;
@@ -82,12 +84,88 @@ public class SubordinateStatementMetadataCacheTest extends AbstractFederationFlo
new IssuerEntityIDCriterion(anchorId)));
Assert.assertNotNull(result);
Assert.assertEquals(result.size(), 1);
- Assert.assertNotNull(result.get(0).getStatement());
+ final SubordinateStatement statement = result.get(0).getStatement();
+ Assert.assertNotNull(statement);
+ assert statement != null;
+ Assert.assertNull(statement.getParsedPayload().getCustomClaims()
+ .get(CustomSubordinateStatementFilterStrategy.CUSTOM_CLAIM_NAME));
} catch (MetadataCacheException e) {
Assert.fail("Could not resolve entity configuration", e);
}
}
+ @Test
+ public void testValidSubordinateStatement_customCriticalClaim()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+ final JWTClaimsSet.Builder builder = 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("crit", List.of("subordinate_crit"))
+ .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+ final String subordinateStatement = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, trustedAnchorKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+ mapResponse(subordinateStatementUrl(anchorFetchEndpoint, entityId),
+ mockResponse(subordinateStatement));
+ try {
+ final List<RemoteSubordinateStatementContainer> result =
+ subordinateStatementCache.get(new CriteriaSet(new SubjectEntityIDCriterion(entityId),
+ new ResponseContainerExpirationCriterion(Instant.now().plusSeconds(300)),
+ new IssuerEntityIDCriterion(anchorId)));
+ Assert.assertNotNull(result);
+ Assert.assertEquals(result.size(), 1);
+ final SubordinateStatement statement = result.get(0).getStatement();
+ Assert.assertNotNull(statement);
+ assert statement != null;
+ Assert.assertEquals(statement.getParsedPayload().getCustomClaims()
+ .get(CustomSubordinateStatementFilterStrategy.CUSTOM_CLAIM_NAME),
+ CustomSubordinateStatementFilterStrategy.CUSTOM_CLAIM_VALUE);
+ } catch (MetadataCacheException e) {
+ Assert.fail("Could not resolve entity configuration", e);
+ }
+ }
+
+ @Test
+ public void testWithUnrecognizedCriticalClaim()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+ final JWTClaimsSet.Builder builder = 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("crit", List.of("default_crit"))
+ .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+ final String subordinateStatement = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, trustedAnchorKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+ mapResponse(subordinateStatementUrl(anchorFetchEndpoint, entityId),
+ mockResponse(subordinateStatement));
+ assertNoSubordinateStatement(entityId);
+ }
+
+ @Test
+ public void testWithStandardClaimAsCriticalClaim()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+ final JWTClaimsSet.Builder builder = 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("crit", List.of("jwks"))
+ .claim("metadata", Map.of("federation_entity", Collections.emptyMap()));
+ final String subordinateStatement = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, trustedAnchorKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+ mapResponse(subordinateStatementUrl(anchorFetchEndpoint, entityId),
+ mockResponse(subordinateStatement));
+ assertNoSubordinateStatement(entityId);
+ }
+
@Test
public void testSignatureWithNonMathchingKey()
throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/TrustChainMetadataCacheTest.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/TrustChainMetadataCacheTest.java
index 432e0c8..f435d66 100644
--- a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/TrustChainMetadataCacheTest.java
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/TrustChainMetadataCacheTest.java
@@ -231,4 +231,44 @@ public class TrustChainMetadataCacheTest extends AbstractFederationFlowTest {
}
}
+ @Test
+ public void testValidTrustChainResolved_customFilterReturnsNull()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+
+ final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(entityId).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("federation_entity", Collections.emptyMap()))
+ .claim("crit", List.of("default_crit"))
+ .claim("authority_hints", List.of(anchorId));
+ final String entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+
+ final JWTClaimsSet.Builder builder2 = 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("federation_entity", Collections.emptyMap()));
+ final String subordinateStatement = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, trustedAnchorKey, "entity-statement+jwt", builder2.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(anchorId), mockResponse(trustedAnchorConfiguration()));
+ mapResponse(subordinateStatementUrl(anchorFetchEndpoint, entityId),
+ mockResponse(subordinateStatement));
+ try {
+ final List<RemoteTrustChainsContainer> result =
+ trustChainCache.get(new CriteriaSet(new SubjectEntityIDCriterion(entityId),
+ new ResponseContainerExpirationCriterion(Instant.now().plusSeconds(300)),
+ new IssuerEntityIDCriterion(anchorId)));
+ Assert.assertNotNull(result);
+ Assert.assertEquals(result.size(), 0);
+ } catch (MetadataCacheException e) {
+ Assert.fail("Could not resolve entity configuration", e);
+ }
+ }
+
}
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomEntityConfigurationFilterStrategy.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomEntityConfigurationFilterStrategy.java
new file mode 100644
index 0000000..15c1768
--- /dev/null
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomEntityConfigurationFilterStrategy.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support;
+
+import java.time.Instant;
+import java.util.Optional;
+import java.util.function.BiFunction;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityConfiguration;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteEntityConfigurationContainer;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.impl.EntityConfigurationImpl;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.impl.EntityConfigurationPayloadImpl;
+import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
+
+/**
+ * Custom filter strategy for entity configuration container.
+ */
+public class CustomEntityConfigurationFilterStrategy implements
+ BiFunction<RemoteEntityConfigurationContainer, MetadataFilterContext, RemoteEntityConfigurationContainer>{
+
+ public static final String CUSTOM_CLAIM_NAME = "custom_claim";
+ public static final String CUSTOM_CLAIM_VALUE = "custom_value";
+
+ /** {@inheritDoc} */
+ @Override
+ public RemoteEntityConfigurationContainer apply(final RemoteEntityConfigurationContainer container,
+ final MetadataFilterContext context) {
+ final EntityConfiguration statement = container.getStatement();
+ if (statement == null) {
+ return null;
+ }
+ if (Optional.ofNullable(statement.getParsedPayload().getCritical())
+ .map(list -> list.contains("default_crit"))
+ .orElse(false)) {
+ final EntityConfigurationPayloadImpl payload =
+ new EntityConfigurationPayloadImpl(statement.getParsedPayload());
+ payload.setCustomClaims(CUSTOM_CLAIM_NAME, CUSTOM_CLAIM_VALUE);
+ final Instant expiration = Instant.now().plusSeconds(300);
+ assert expiration != null;
+ return new RemoteEntityConfigurationContainer(statement.getSubject(),
+ new EntityConfigurationImpl(statement.getJwt(), payload), expiration, expiration);
+ }
+ return container;
+ }
+
+}
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomSubordinateStatementFilterStrategy.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomSubordinateStatementFilterStrategy.java
new file mode 100644
index 0000000..1f17f57
--- /dev/null
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomSubordinateStatementFilterStrategy.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support;
+
+import java.time.Instant;
+import java.util.Optional;
+import java.util.function.BiFunction;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubordinateStatement;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteSubordinateStatementContainer;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.impl.SubordinateStatementImpl;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.payload.impl.SubordinateStatementPayloadImpl;
+import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
+
+/**
+ * Custom filter strategy for subordinate statement container.
+ */
+public class CustomSubordinateStatementFilterStrategy implements
+ BiFunction<RemoteSubordinateStatementContainer, MetadataFilterContext, RemoteSubordinateStatementContainer>{
+
+ public static final String CUSTOM_CLAIM_NAME = "custom_so_claim";
+ public static final String CUSTOM_CLAIM_VALUE = "custom_so_value";
+
+ /** {@inheritDoc} */
+ @Override
+ public RemoteSubordinateStatementContainer apply(final RemoteSubordinateStatementContainer container,
+ final MetadataFilterContext context) {
+ final SubordinateStatement statement = container.getStatement();
+ if (statement == null) {
+ return null;
+ }
+ if (Optional.ofNullable(statement.getParsedPayload().getCritical())
+ .map(list -> list.contains("subordinate_crit"))
+ .orElse(false)) {
+ final SubordinateStatementPayloadImpl payload =
+ new SubordinateStatementPayloadImpl(statement.getParsedPayload());
+ payload.setCustomClaims(CUSTOM_CLAIM_NAME, CUSTOM_CLAIM_VALUE);
+ final Instant expiration = Instant.now().plusSeconds(300);
+ assert expiration != null;
+ return new RemoteSubordinateStatementContainer(container.getIdentifier(),
+ new SubordinateStatementImpl(statement.getJwt(), payload), expiration, expiration);
+ }
+ return container;
+ }
+
+}
diff --git a/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomTrustChainFilterStrategy.java b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomTrustChainFilterStrategy.java
new file mode 100644
index 0000000..0c8245d
--- /dev/null
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/support/CustomTrustChainFilterStrategy.java
@@ -0,0 +1,47 @@
+/*
+ * 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.support;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.function.BiFunction;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.EntityStatement;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.cache.remote.RemoteTrustChainsContainer;
+import net.shibboleth.oidc.metadata.filter.MetadataFilterContext;
+import net.shibboleth.shared.collection.CollectionSupport;
+
+/**
+ * Custom filter strategy for trust chains container.
+ */
+public class CustomTrustChainFilterStrategy implements
+ BiFunction<RemoteTrustChainsContainer, MetadataFilterContext, RemoteTrustChainsContainer>{
+
+ /** {@inheritDoc} */
+ @Override
+ public RemoteTrustChainsContainer apply(final RemoteTrustChainsContainer container,
+ final MetadataFilterContext context) {
+ final List<List<EntityStatement<?>>> trustChains =
+ Optional.ofNullable(container.getTrustChains()).orElse(CollectionSupport.emptyList());
+ for (final List<EntityStatement<?>> trustChain : trustChains) {
+ final List<String> critical = trustChain.get(0).getParsedPayload().getCritical();
+ if (critical != null && critical.contains("default_crit")) {
+ return null;
+ }
+ }
+ return container;
+ }
+
+}
diff --git a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml
index 1589b62..07602b6 100644
--- a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml
+++ b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/global.xml
@@ -97,4 +97,16 @@
p:customObject-ref="shibboleth.HttpServletRequestSupplier"
c:expression="'true'.equals(#custom.get().getHeader('useCustomFallbackToLocal'))" />
+ <util:list id="CustomEntityConfigurationFilters">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomEntityConfigurationFilterStrategy"/>
+ </util:list>
+
+ <util:list id="CustomSubordinateStatementFilters">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomSubordinateStatementFilterStrategy"/>
+ </util:list>
+
+ <util:list id="CustomTrustChainFilters">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.profile.flow.oidfed.support.CustomTrustChainFilterStrategy"/>
+ </util:list>
+
</beans>
diff --git a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed.properties b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed.properties
index d682aae..d18dc17 100644
--- a/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed.properties
+++ b/idp-oidfed-op-impl/src/test/resources/net/shibboleth/idp/module/conf/oidfed/oidfed.properties
@@ -1,4 +1,10 @@
idp.oidfed.entity.authorityHints = https://anchor1.example.org, https://anchor2.example.org
idp.oidfed.cache.entityConfiguration.invalidContainerLifetime = PT0S
-idp.oidfed.cache.entityConfiguration.minRefreshDelay = PT0S
\ No newline at end of file
+idp.oidfed.cache.entityConfiguration.minRefreshDelay = PT0S
+
+idp.oidfed.cache.default.critClaims = default_crit
+idp.oidfed.cache.entityConfiguration.customFilterStrategies = CustomEntityConfigurationFilters
+idp.oidfed.cache.subordinateStatement.customFilterStrategies = CustomSubordinateStatementFilters
+idp.oidfed.cache.subordinateStatement.critClaims = subordinate_crit
+idp.oidfed.cache.trustChain.customFilterStrategies = CustomTrustChainFilters
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list