[java-idp-plugin-oidc-op-oidfed] branch main updated: Improved entity configuration validation in the metadata cache
Codeberg
noreply at shibboleth.net
Fri Dec 5 12:25:14 UTC 2025
This is an automated email from the git hooks/post-receive script.
codeberg pushed a commit to branch main
in repository java-idp-plugin-oidc-op-oidfed.
View the commit online:
https://codeberg.org/Shibboleth/java-idp-plugin-oidc-op-oidfed/commit/162a88735da4bb5449a1ceda572401eaff087c1c
The following commit(s) were added to refs/heads/main by this push:
new 162a887 Improved entity configuration validation in the metadata cache
162a887 is described below
commit 162a88735da4bb5449a1ceda572401eaff087c1c
Author: Henri Mikkonen <henri.mikkonen at iki.fi>
AuthorDate: Fri Dec 5 14:24:54 2025 +0200
Improved entity configuration validation in the metadata cache
- Content type checks (HTTP response and JWT) by the fetching function (similarly to other federation endpoints)
- New DefaultEntityStatementContentValidationFilterStrategy called after signature validation filter
- Payload claims validation via claims validators (especially syntax validations mostly TODO/missing)
- Header validations (e.g. trust_chain, peer_trust_chain) TODO/missing
- New test class 'EntityConfigurationMetadataCacheTest' to test the default metadata cache
---
...actTrustEngineSignatureValidationComponent.java | 2 +-
...tyStatementContentValidationFilterStrategy.java | 83 ++++++++
.../DefaultEntityStatementFetchingStrategy.java | 18 +-
.../META-INF/net.shibboleth.idp/postconfig.xml | 42 +++-
.../flow/oidfed/AbstractFederationFlowTest.java | 36 ++--
.../EntityConfigurationMetadataCacheTest.java | 234 +++++++++++++++++++++
6 files changed, 393 insertions(+), 22 deletions(-)
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/AbstractTrustEngineSignatureValidationComponent.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/AbstractTrustEngineSignatureValidationComponent.java
index 463dcf9..d47ac17 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/AbstractTrustEngineSignatureValidationComponent.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/AbstractTrustEngineSignatureValidationComponent.java
@@ -96,7 +96,7 @@ public class AbstractTrustEngineSignatureValidationComponent extends AbstractIde
log.warn("Trust Engine validation failed for {}, issued by {}", entityId,
jwt.getJWTClaimsSet().getIssuer());
} catch (final SecurityException | ParseException e) {
- log.debug("Could not validate entity statement for {}", entityId, e);
+ log.warn("Could not validate entity statement for {}", entityId, e);
}
return false;
}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultEntityStatementContentValidationFilterStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultEntityStatementContentValidationFilterStrategy.java
new file mode 100644
index 0000000..2b335bd
--- /dev/null
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultEntityStatementContentValidationFilterStrategy.java
@@ -0,0 +1,83 @@
+/*
+ * 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.function.Function;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+import org.opensaml.profile.context.ProfileRequestContext;
+import org.slf4j.Logger;
+
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.oidc.jwt.claims.ClaimsValidator;
+import net.shibboleth.oidc.jwt.claims.JWTValidationException;
+import net.shibboleth.shared.annotation.constraint.NonnullAfterInit;
+import net.shibboleth.shared.annotation.constraint.ThreadSafeAfterInit;
+import net.shibboleth.shared.component.AbstractIdentifiableInitializableComponent;
+import net.shibboleth.shared.logic.Constraint;
+import net.shibboleth.shared.primitive.LoggerFactory;
+
+/**
+ * Default content validating filter for entity statement. Validates the header contents of the entity statement and
+ * claims via configurable claims validator. Note that the {@link ProfileRequestContext} is not fed to the claims
+ * validator.
+ */
+ at ThreadSafeAfterInit
+public class DefaultEntityStatementContentValidationFilterStrategy
+ extends AbstractIdentifiableInitializableComponent implements Function<EntityStatement, EntityStatement> {
+
+ /** Class logger. */
+ @Nonnull private Logger log =
+ LoggerFactory.getLogger(DefaultEntityStatementContentValidationFilterStrategy.class);
+
+ /** The claims validator to use for validating the entity statement claims. */
+ @NonnullAfterInit private ClaimsValidator claimsValidator;
+
+ /**
+ * Set the claims validator to use for validating the entity statement claims.
+ *
+ * @param validator claims validator
+ */
+ public void setClaimsValidator(@Nonnull final ClaimsValidator validator) {
+ checkSetterPreconditions();
+ claimsValidator = Constraint.isNotNull(validator, "Claims validator cannot be null");
+ }
+
+ /** {@inheritDoc} */
+ @Override @Nullable
+ public EntityStatement apply(@Nullable final EntityStatement entityStatement) {
+ checkComponentActive();
+ if (entityStatement == null) {
+ return null;
+ }
+
+ final String entityId = entityStatement.getEntityID().getValue();
+
+ //TODO: header validations
+
+ try {
+ claimsValidator.validate(entityStatement.getSignedStatement().getJWTClaimsSet(), null);
+ } catch (final JWTValidationException | ParseException e) {
+ log.warn("Claims validation failed for entity configuration {}", entityId, e);
+ return null;
+ }
+ log.debug("Entity configuration {} claims successfully validated", entityId);
+ return entityStatement;
+ }
+}
diff --git a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultEntityStatementFetchingStrategy.java b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultEntityStatementFetchingStrategy.java
index f508d1f..6445b33 100644
--- a/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultEntityStatementFetchingStrategy.java
+++ b/idp-oidfed-op-impl/src/main/java/net/shibboleth/idp/plugin/oidc/op/oidfed/metadata/DefaultEntityStatementFetchingStrategy.java
@@ -17,6 +17,7 @@ package net.shibboleth.idp.plugin.oidc.op.oidfed.metadata;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
+import java.util.Optional;
import java.util.function.Function;
import javax.annotation.Nonnull;
@@ -27,6 +28,7 @@ import org.apache.hc.client5.http.classic.methods.HttpGet;
import org.apache.hc.client5.http.protocol.HttpClientContext;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.HttpEntity;
import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.ParseException;
import org.apache.hc.core5.http.io.entity.EntityUtils;
@@ -139,7 +141,21 @@ public class DefaultEntityStatementFetchingStrategy extends AbstractIdentifiable
assert scheme != null;
HttpClientSecuritySupport.checkTLSCredentialEvaluated(httpContext, scheme);
if (response != null && response.getCode() == HttpStatus.SC_OK) {
- return EntityStatementHelper.deserializeEntityStatement(EntityUtils.toString(response.getEntity()));
+ final HttpEntity responseEntity = response.getEntity();
+ if (!"application/entity-statement+jwt".equals(responseEntity.getContentType())) {
+ log.warn("Unexpected response content type {} from URI {}", responseEntity.getContentType(), uri);
+ return null;
+ }
+ final EntityStatement entityStatement =
+ EntityStatementHelper.deserializeEntityStatement(EntityUtils.toString(response.getEntity()));
+ final String type = Optional.ofNullable(entityStatement)
+ .map(statement -> statement.getSignedStatement().getHeader().getType())
+ .map(header -> header.getType()).orElse(null);
+ if (!"entity-statement+jwt".equals(type)) {
+ log.warn("Ignoring entity statement with unexpected type {}", type);
+ return null;
+ }
+ return entityStatement;
} else {
log.debug("Unable to fetch entity configuration from URI: {} (HTTP status {})", uri,
response == null ? null : response.getCode());
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 58cfd0b..30255cd 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
@@ -103,8 +103,46 @@
</bean>
</property>
<property name="metadataFilterStrategy">
- <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultEntityStatementSignatureValidationFilterStrategy"
- p:trustEngine-ref="shibboleth.oidfed.DefaultEntityConfigurationTrustEngine"/>
+ <bean parent="shibboleth.BiFunctions.Compose">
+ <constructor-arg name="f">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultEntityStatementSignatureValidationFilterStrategy"
+ p:trustEngine-ref="shibboleth.oidfed.DefaultEntityConfigurationTrustEngine"/>
+ </constructor-arg>
+ <constructor-arg name="g">
+ <bean class="net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.DefaultEntityStatementContentValidationFilterStrategy"
+ p:claimsValidator-ref="%{idp.oidfed.entityConfiguration.claimsValidator:shibboleth.oidfed.DefaultEntityConfigurationClaimsValidator}"/>
+ </constructor-arg>
+ </bean>
+ </property>
+ </bean>
+
+ <bean id="shibboleth.oidfed.DefaultEntityConfigurationClaimsValidator"
+ class="net.shibboleth.oidc.security.jwt.claims.impl.ChainingJWTClaimsValidator">
+ <property name="claimValidators">
+ <util:list value-type="net.shibboleth.oidc.jwt.claims.ClaimsValidator">
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.IssuedAtClaimsValidator"
+ p:clockSkew="%{idp.policy.clockSkew:PT1M}"
+ p:messageLifetime="%{idp.policy.messageLifetime:PT1M}"
+ p:requiredRule="true" />
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator"
+ p:requiredClaims="iss" />
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator"
+ p:requiredClaims="sub" />
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.RequiredClaimsValidator"
+ p:requiredClaims="jwks" />
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ExpiryClaimsValidator"
+ p:clockSkew="%{idp.policy.clockSkew:PT1M}" />
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ProhibitedClaimsValidator"
+ p:prohibitedClaims="metadata_policy" />
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ProhibitedClaimsValidator"
+ p:prohibitedClaims="metadata_policy_crit" />
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ProhibitedClaimsValidator"
+ p:prohibitedClaims="constraints" />
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ProhibitedClaimsValidator"
+ p:prohibitedClaims="source_endpoint" />
+ <bean class="net.shibboleth.oidc.security.jwt.claims.impl.ProhibitedClaimsValidator"
+ p:prohibitedClaims="aud" />
+ </util:list>
</property>
</bean>
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 d2526e8..f248ab5 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
@@ -92,24 +92,24 @@ public class AbstractFederationFlowTest extends AbstractOidcFlowTest {
final static AtomicInteger clientIndex = new AtomicInteger();
final static AtomicInteger intermediateIndex = new AtomicInteger();
- final String redirectUri = "https://rp.federation.local/cb";
- final String clientIdPattern = "https://testrp%s.federation.local";
- final String intermediateIdPattern = "https://intermediate-authority%s.federation.local";
- final String anchorId = "https://trust-anchor.federation.local";
- final String anchorFetchEndpoint = anchorId + "/fetch";
- final String anchorResolveEndpoint = anchorId + "/resolve";
- final String trustMarkIssuerId = "https://trust-mark-issuer.federation.local";
- final String trustMarkEndpoint = "https://trust-mark-issuer.federation.local/issue";
- String issuer = "https://op.example.org";
-
- JWK rpKey;
- JWK leafKey;
- JWK anchorKey;
- JWK trustedAnchorKey;
- JWK intermediateKey;
- JWK trustMarkIssuerKey;
-
- String subject = "jdoe";
+ protected final String redirectUri = "https://rp.federation.local/cb";
+ protected final String clientIdPattern = "https://testrp%s.federation.local";
+ protected final String intermediateIdPattern = "https://intermediate-authority%s.federation.local";
+ protected final String anchorId = "https://trust-anchor.federation.local";
+ protected final String anchorFetchEndpoint = anchorId + "/fetch";
+ protected final String anchorResolveEndpoint = anchorId + "/resolve";
+ protected final String trustMarkIssuerId = "https://trust-mark-issuer.federation.local";
+ protected final String trustMarkEndpoint = "https://trust-mark-issuer.federation.local/issue";
+ protected final String issuer = "https://op.example.org";
+
+ protected JWK rpKey;
+ protected JWK leafKey;
+ protected JWK anchorKey;
+ protected JWK trustedAnchorKey;
+ protected JWK intermediateKey;
+ protected JWK trustMarkIssuerKey;
+
+ protected String subject = "jdoe";
@Autowired
@Qualifier("shibboleth.oidfed.HttpClient")
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
new file mode 100644
index 0000000..a55de27
--- /dev/null
+++ b/idp-oidfed-op-impl/src/test/java/net/shibboleth/idp/plugin/oidc/op/profile/flow/oidfed/cache/EntityConfigurationMetadataCacheTest.java
@@ -0,0 +1,234 @@
+/*
+ * 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.cache;
+
+import java.io.IOException;
+import java.net.URISyntaxException;
+import java.time.Instant;
+import java.util.Collections;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.testng.Assert;
+import org.testng.annotations.Test;
+
+import com.nimbusds.jose.JWSAlgorithm;
+import com.nimbusds.jose.jwk.JWKSet;
+import com.nimbusds.jwt.JWTClaimsSet;
+import com.nimbusds.openid.connect.sdk.federation.entities.EntityStatement;
+
+import net.shibboleth.idp.plugin.oidc.op.oidfed.TrustChainTestUtil;
+import net.shibboleth.idp.plugin.oidc.op.oidfed.metadata.SubjectEntityIDCriterion;
+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.oidc.metadata.cache.MetadataCache;
+import net.shibboleth.oidc.metadata.cache.MetadataCacheException;
+import net.shibboleth.shared.resolver.CriteriaSet;
+
+/**
+ * Unit tests for the default entity configuration metadata cache.
+ */
+public class EntityConfigurationMetadataCacheTest extends AbstractFederationFlowTest {
+
+ protected EntityConfigurationMetadataCacheTest() {
+ super(EntityConfigurationFlowTest.FLOW_ID);
+ }
+
+ @Autowired
+ @Qualifier("shibboleth.oidfed.EntityConfigurationMetadataCache")
+ MetadataCache<EntityStatement> entityConfigurationCache;
+
+ @Test
+ public void testValidEntityConfiguration()
+ 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()));
+ final String entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+ try {
+ final List<EntityStatement> result =
+ entityConfigurationCache.get(new CriteriaSet(new SubjectEntityIDCriterion(entityId)));
+ Assert.assertNotNull(result);
+ Assert.assertEquals(result.size(), 1);
+ } catch (MetadataCacheException e) {
+ Assert.fail("Could not resolve entity configuration", e);
+ }
+ }
+
+ @Test
+ public void testSignatureWithNonMathchingKey()
+ 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()));
+ final String entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, anchorKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+ assertNoEntityConfiguration(entityId);
+ }
+
+ @Test
+ public void testNonMatchingSubjectVsIssuer()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+ final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(uniqueClientId()).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 entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+ assertNoEntityConfiguration(entityId);
+ }
+
+ @Test
+ public void testValidClaims_nonMatchingSubject()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+ final String claimsEntityId = uniqueClientId();
+ final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(claimsEntityId).subject(claimsEntityId)
+ .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 entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+ assertNoEntityConfiguration(entityId);
+ }
+
+ @Test
+ public void testValidClaims_invalidTypeHeader()
+ 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()));
+ final String entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, leafKey, "jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+ assertNoEntityConfiguration(entityId);
+ }
+
+ @Test
+ public void testIssuedInFuture()
+ 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().plusSeconds(300)))
+ .expirationTime(Date.from(Instant.now().plusSeconds(300)))
+ .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+ .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 testMissingSub()
+ throws MetadataCacheException, UnsupportedOperationException, IOException, URISyntaxException {
+ final String entityId = uniqueClientId();
+ final JWTClaimsSet.Builder builder = new JWTClaimsSet.Builder().issuer(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 entityConfiguration = TrustChainTestUtil.signedJwt(
+ JWSAlgorithm.RS256, leafKey, "entity-statement+jwt", builder.build()).serialize();
+
+ mapResponse(entityConfigurationUrl(entityId), mockResponse(entityConfiguration));
+ assertNoEntityConfiguration(entityId);
+ }
+
+ @Test
+ public void testExpired()
+ 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().minusSeconds(300)))
+ .claim("jwks", new JWKSet(leafKey).toJSONObject(true))
+ .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 testMissingJwks()
+ 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("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 testInvalidJwksClaim()
+ 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", Map.of("federation_entity", Collections.emptyMap()))
+ .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);
+ }
+
+ protected void assertNoEntityConfiguration(final String entityId) {
+ try {
+ final List<EntityStatement> result =
+ entityConfigurationCache.get(new CriteriaSet(new SubjectEntityIDCriterion(entityId)));
+ Assert.assertNotNull(result);
+ Assert.assertEquals(result.size(), 0);
+ } catch (MetadataCacheException e) {
+ Assert.fail("Could not resolve entity configuration", e);
+ }
+
+ }
+}
--
To stop receiving notification emails like this one, please contact
the administrator of this repository.
More information about the commits
mailing list