[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