This is an automated email from the ASF dual-hosted git repository.
lmccay pushed a commit to branch knox_idf
in repository https://gitbox.apache.org/repos/asf/knox.git
The following commit(s) were added to refs/heads/knox_idf by this push:
new ea39545cf KNOX-3355 - Add TrustedOidcIssuerService schema and
interface (#1311)
ea39545cf is described below
commit ea39545cfa4335a14d414f2e985a764873d1666c
Author: hsheinblatt <[email protected]>
AuthorDate: Tue Jul 21 13:31:28 2026 -0700
KNOX-3355 - Add TrustedOidcIssuerService schema and interface (#1311)
* KNOX-3355 - Add TrustedOidcIssuerService schema and interface
Co-authored-by: Harrison <[email protected]>
---
.../database/AbstractDataSourceFactory.java | 4 +
.../src/main/resources/conf/gateway-site.xml | 49 +++++++++
.../createKnoxIDFTrustedOidcIssuersTable.sql | 23 ++++
.../createKnoxIDFTrustedOidcIssuersTableDerby.sql | 22 ++++
.../createKnoxIDFTrustedOidcIssuersTableOracle.sql | 23 ++++
.../services/AbstractGatewayServicesTest.java | 3 +-
.../trustedoidcissuer/TrustedOidcIssuerTest.java | 71 ++++++++++++
.../TrustedOidcIssuersSchemaTest.java | 121 +++++++++++++++++++++
.../apache/knox/gateway/services/ServiceType.java | 3 +-
.../trustedoidcissuer/TrustedOidcIssuer.java | 63 +++++++++++
.../TrustedOidcIssuerService.java | 96 ++++++++++++++++
11 files changed, 476 insertions(+), 2 deletions(-)
diff --git
a/gateway-server/src/main/java/org/apache/knox/gateway/database/AbstractDataSourceFactory.java
b/gateway-server/src/main/java/org/apache/knox/gateway/database/AbstractDataSourceFactory.java
index a9d544fe8..a76b219a9 100644
---
a/gateway-server/src/main/java/org/apache/knox/gateway/database/AbstractDataSourceFactory.java
+++
b/gateway-server/src/main/java/org/apache/knox/gateway/database/AbstractDataSourceFactory.java
@@ -50,6 +50,10 @@ public abstract class AbstractDataSourceFactory {
public static final String
DERBY_KNOXIDF_FED_IDENTITY_TABLE_CREATE_SQL_FILE_NAME =
"createKnoxIDFFederatedIdentityTableDerby.sql";
public static final String
DERBY_KNOXIDF_FED_IDENTITY_ATTR_TABLE_CREATE_SQL_FILE_NAME =
"createKnoxIDFFederatedIdentityAttributesTableDerby.sql";
+ public static final String KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL =
"createKnoxIDFTrustedOidcIssuersTable.sql";
+ public static final String DERBY_KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL =
"createKnoxIDFTrustedOidcIssuersTableDerby.sql";
+ public static final String ORACLE_KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL =
"createKnoxIDFTrustedOidcIssuersTableOracle.sql";
+
public static final String DATABASE_USER_ALIAS_NAME =
"gateway_database_user";
public static final String DATABASE_PASSWORD_ALIAS_NAME =
"gateway_database_password";
public static final String DATABASE_TRUSTSTORE_PASSWORD_ALIAS_NAME =
"gateway_database_ssl_truststore_password";
diff --git a/gateway-server/src/main/resources/conf/gateway-site.xml
b/gateway-server/src/main/resources/conf/gateway-site.xml
index fda674c17..89215924e 100644
--- a/gateway-server/src/main/resources/conf/gateway-site.xml
+++ b/gateway-server/src/main/resources/conf/gateway-site.xml
@@ -113,4 +113,53 @@ limitations under the License.
<description>Interceptor type.</description>
</property>
+ <!-- KnoxIDF Trusted OIDC Issuer Service Configuration
+ These properties configure the TrustedOidcIssuerService gateway
service,
+ which manages the registry of OIDC issuers trusted for dynamic JWKS
+ discovery. They are gateway-scoped and apply to all topologies that
+ deploy the KNOXIDF or KNOXIDF_ADMIN service roles.
+ -->
+ <!--
+ <property>
+ <name>gateway.trustedoidcissuer.max.issuers</name>
+ <value>10000</value>
+ <description>
+ Maximum number of OIDC issuers that may be registered in the
trusted
+ issuer registry. Increasing this value may delay how quickly
updates
+ take effect.
+ </description>
+ </property>
+
+ <property>
+ <name>gateway.trustedoidcissuer.discovery.cache.ttl.secs</name>
+ <value>600</value>
+ <description>
+ TTL in seconds for the resolved JWKS URI per trusted issuer. After
+ expiry the JWKS URI is re-resolved on the next use. Use the admin
+ API refresh endpoint to force immediate re-resolution without
waiting
+ for TTL expiry.
+ </description>
+ </property>
+
+ <property>
+ <name>gateway.trustedoidcissuer.discovery.connect.timeout.ms</name>
+ <value>3000</value>
+ <description>
+ HTTP connect timeout in milliseconds when fetching an OIDC
discovery
+ document (/.well-known/openid-configuration). A short value fails
fast
+ if the issuer endpoint is unreachable.
+ </description>
+ </property>
+
+ <property>
+ <name>gateway.trustedoidcissuer.discovery.read.timeout.ms</name>
+ <value>10000</value>
+ <description>
+ HTTP read timeout in milliseconds when fetching an OIDC discovery
+ document. Allows for slower issuer endpoints while bounding the
+ latency of a cache-miss resolution.
+ </description>
+ </property>
+ -->
+
</configuration>
\ No newline at end of file
diff --git
a/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTable.sql
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTable.sql
new file mode 100644
index 000000000..a97a4a6cc
--- /dev/null
+++ b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTable.sql
@@ -0,0 +1,23 @@
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with this
+-- work for additional information regarding copyright ownership. The ASF
+-- licenses this file to you 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.
+
+CREATE TABLE IF NOT EXISTS TRUSTED_OIDC_ISSUERS (
+ issuer_url VARCHAR(2048) NOT NULL,
+ dynamic_jwks BOOLEAN DEFAULT false NOT NULL,
+ registered_at TIMESTAMP NOT NULL,
+ registered_by VARCHAR(2048),
+ cluster_name VARCHAR(256),
+ PRIMARY KEY (issuer_url)
+);
diff --git
a/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableDerby.sql
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableDerby.sql
new file mode 100644
index 000000000..a3e77b465
--- /dev/null
+++
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableDerby.sql
@@ -0,0 +1,22 @@
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with this
+-- work for additional information regarding copyright ownership. The ASF
+-- licenses this file to you 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.
+
+CREATE TABLE TRUSTED_OIDC_ISSUERS (
+ issuer_url VARCHAR(2048) PRIMARY KEY NOT NULL,
+ dynamic_jwks BOOLEAN DEFAULT false NOT NULL,
+ registered_at TIMESTAMP NOT NULL,
+ registered_by VARCHAR(2048),
+ cluster_name VARCHAR(256)
+)
diff --git
a/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableOracle.sql
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableOracle.sql
new file mode 100644
index 000000000..2c0de4bd0
--- /dev/null
+++
b/gateway-server/src/main/resources/createKnoxIDFTrustedOidcIssuersTableOracle.sql
@@ -0,0 +1,23 @@
+-- Licensed to the Apache Software Foundation (ASF) under one or more
+-- contributor license agreements. See the NOTICE file distributed with this
+-- work for additional information regarding copyright ownership. The ASF
+-- licenses this file to you 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.
+
+CREATE TABLE TRUSTED_OIDC_ISSUERS (
+ issuer_url VARCHAR2(2048) NOT NULL,
+ dynamic_jwks NUMBER(1) DEFAULT 0 NOT NULL,
+ registered_at TIMESTAMP(6) NOT NULL,
+ registered_by VARCHAR2(2048),
+ cluster_name VARCHAR2(256),
+ PRIMARY KEY (issuer_url)
+)
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/AbstractGatewayServicesTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/AbstractGatewayServicesTest.java
index 57271324c..904111888 100644
---
a/gateway-server/src/test/java/org/apache/knox/gateway/services/AbstractGatewayServicesTest.java
+++
b/gateway-server/src/test/java/org/apache/knox/gateway/services/AbstractGatewayServicesTest.java
@@ -68,7 +68,8 @@ public class AbstractGatewayServicesTest {
ServiceType.GATEWAY_STATUS_SERVICE,
ServiceType.LDAP_SERVICE,
ServiceType.LDAP_ROLES_LOOKUP_SERVICE,
- ServiceType.KNOXIDF_FEDERATED_IDENTITY_SERVICE
+ ServiceType.KNOXIDF_FEDERATED_IDENTITY_SERVICE,
+ ServiceType.TRUSTED_OIDC_ISSUER_SERVICE
};
assertNotEquals(ServiceType.values(), orderedServiceTypes);
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerTest.java
new file mode 100644
index 000000000..a728e99ac
--- /dev/null
+++
b/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerTest.java
@@ -0,0 +1,71 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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 org.apache.knox.gateway.services.knoxidf.trustedoidcissuer;
+
+import org.junit.Test;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.time.Instant;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+public class TrustedOidcIssuerTest {
+
+ @Test
+ public void testGetters() {
+ Instant now = Instant.now();
+ TrustedOidcIssuer issuer = new TrustedOidcIssuer(
+ "https://issuer.example.com", true, "cluster-a", now,
"[email protected]");
+
+ assertEquals("https://issuer.example.com", issuer.getIssuerUrl());
+ assertTrue(issuer.isDynamicJwks());
+ assertEquals("cluster-a", issuer.getClusterName());
+ assertEquals(now, issuer.getRegisteredAt());
+ assertEquals("[email protected]", issuer.getRegisteredBy());
+ }
+
+ @Test
+ public void testNullableOptionalFields() {
+ TrustedOidcIssuer issuer = new TrustedOidcIssuer(
+ "https://issuer.example.com", false, null, Instant.now(), null);
+
+ assertNull("clusterName should be nullable", issuer.getClusterName());
+ assertNull("registeredBy should be nullable", issuer.getRegisteredBy());
+ assertFalse(issuer.isDynamicJwks());
+ }
+
+ @Test
+ public void testAllFieldsAreFinal() {
+ for (Field field : TrustedOidcIssuer.class.getDeclaredFields()) {
+ assertTrue("Field '" + field.getName() + "' must be final for
immutability",
+ Modifier.isFinal(field.getModifiers()));
+ }
+ }
+
+ @Test
+ public void testNoSetterMethods() {
+ for (Method method : TrustedOidcIssuer.class.getDeclaredMethods()) {
+ assertFalse("Setter found in immutable POJO: " + method.getName(),
+ method.getName().startsWith("set"));
+ }
+ }
+}
diff --git
a/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuersSchemaTest.java
b/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuersSchemaTest.java
new file mode 100644
index 000000000..e8cb56015
--- /dev/null
+++
b/gateway-server/src/test/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuersSchemaTest.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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 org.apache.knox.gateway.services.knoxidf.trustedoidcissuer;
+
+import org.apache.commons.io.IOUtils;
+import org.apache.knox.gateway.database.AbstractDataSourceFactory;
+import org.junit.AfterClass;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
+import java.sql.Connection;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+/**
+ * Validates that the TRUSTED_OIDC_ISSUERS DDL scripts parse and execute
+ * correctly against in-memory databases.
+ */
+public class TrustedOidcIssuersSchemaTest {
+
+ private static final String DERBY_DB = "trustedissuers";
+ private static final String DERBY_URL = "jdbc:derby:memory:" + DERBY_DB +
";create=true";
+ private static final String DERBY_SHUTDOWN_URL = "jdbc:derby:memory:" +
DERBY_DB + ";shutdown=true";
+ private static final String HSQL_URL =
"jdbc:hsqldb:mem:trustedissuersschema;ifexists=false";
+ private static final String HSQL_USER = "SA";
+ private static final String HSQL_PASSWORD = "";
+
+ private static Connection derbyConn;
+ private static Connection hsqlConn;
+
+ @BeforeClass
+ public static void setUp() throws SQLException {
+ derbyConn = DriverManager.getConnection(DERBY_URL);
+ hsqlConn = DriverManager.getConnection(HSQL_URL, HSQL_USER, HSQL_PASSWORD);
+ }
+
+ @AfterClass
+ public static void tearDown() throws Exception {
+ // HSQLDB: follow JDBCTokenStateServiceTest pattern — new connection for
SHUTDOWN
+ try (Connection conn = DriverManager.getConnection(HSQL_URL, HSQL_USER,
HSQL_PASSWORD);
+ Statement stmt = conn.createStatement()) {
+ stmt.execute("SHUTDOWN");
+ }
+
+ // Derby: close the shared connection before issuing shutdown
+ if (derbyConn != null && !derbyConn.isClosed()) {
+ derbyConn.close();
+ }
+ try {
+ DriverManager.getConnection(DERBY_SHUTDOWN_URL);
+ } catch (SQLException e) {
+ // Derby signals a successful single-DB shutdown as error code 45000,
state "08006"
+ if (!(e.getErrorCode() == 45000 && "08006".equals(e.getSQLState()))) {
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * The Derby-dialect DDL must execute without error in a Derby in-memory
+ * database and leave the table queryable.
+ */
+ @Test
+ public void testDerbyDdlCreatesTable() throws Exception {
+ try (Statement stmt = derbyConn.createStatement()) {
+
stmt.execute(loadSql(AbstractDataSourceFactory.DERBY_KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL));
+ try (ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM
TRUSTED_OIDC_ISSUERS")) {
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ /**
+ * The standard SQL script uses IF NOT EXISTS. Running the script twice must
+ * not throw, confirming idempotency.
+ */
+ @Test
+ public void testStandardSqlIdempotent() throws Exception {
+ String sql =
loadSql(AbstractDataSourceFactory.KNOXIDF_TRUSTED_OIDC_ISSUERS_TABLE_SQL);
+ try (Statement stmt = hsqlConn.createStatement()) {
+ stmt.execute(sql);
+ // Second execution must succeed due to IF NOT EXISTS
+ stmt.execute(sql);
+ try (ResultSet rs = stmt.executeQuery("SELECT COUNT(*) FROM
TRUSTED_OIDC_ISSUERS")) {
+ assertTrue(rs.next());
+ assertEquals(0, rs.getInt(1));
+ }
+ }
+ }
+
+ private static String loadSql(String fileName) throws IOException {
+ try (InputStream is =
TrustedOidcIssuersSchemaTest.class.getClassLoader().getResourceAsStream(fileName))
{
+ assertNotNull("SQL file not found on classpath: " + fileName, is);
+ return IOUtils.toString(is, StandardCharsets.UTF_8);
+ }
+ }
+}
diff --git
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/ServiceType.java
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/ServiceType.java
index 21794bc21..85afaefae 100644
---
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/ServiceType.java
+++
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/ServiceType.java
@@ -41,7 +41,8 @@ public enum ServiceType {
GATEWAY_STATUS_SERVICE("GatewayStatusService"),
LDAP_SERVICE("LDAPService"),
LDAP_ROLES_LOOKUP_SERVICE("LDAPRoleLookupService"),
- KNOXIDF_FEDERATED_IDENTITY_SERVICE("KnoxIDFFederatedIdentityService");
+ KNOXIDF_FEDERATED_IDENTITY_SERVICE("KnoxIDFFederatedIdentityService"),
+ TRUSTED_OIDC_ISSUER_SERVICE("TrustedOidcIssuerService");
private final String serviceTypeName;
private final String shortName;
diff --git
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuer.java
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuer.java
new file mode 100644
index 000000000..fd4622209
--- /dev/null
+++
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuer.java
@@ -0,0 +1,63 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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 org.apache.knox.gateway.services.knoxidf.trustedoidcissuer;
+
+import java.time.Instant;
+
+public final class TrustedOidcIssuer {
+
+ private final String issuerUrl;
+ private final boolean dynamicJwks;
+ private final String clusterName;
+ private final Instant registeredAt;
+ private final String registeredBy;
+
+ public TrustedOidcIssuer(String issuerUrl, boolean dynamicJwks, String
clusterName,
+ Instant registeredAt, String registeredBy) {
+ this.issuerUrl = issuerUrl;
+ this.dynamicJwks = dynamicJwks;
+ this.clusterName = clusterName;
+ this.registeredAt = registeredAt;
+ this.registeredBy = registeredBy;
+ }
+
+ public String getIssuerUrl() {
+ return issuerUrl;
+ }
+
+ public boolean isDynamicJwks() {
+ return dynamicJwks;
+ }
+
+ /**
+ * @return the cluster name this issuer belongs to, or null if not
cluster-scoped
+ */
+ public String getClusterName() {
+ return clusterName;
+ }
+
+ public Instant getRegisteredAt() {
+ return registeredAt;
+ }
+
+ /**
+ * @return the identity that registered this issuer, or null if not recorded
+ */
+ public String getRegisteredBy() {
+ return registeredBy;
+ }
+}
diff --git
a/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerService.java
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerService.java
new file mode 100644
index 000000000..273dbfa37
--- /dev/null
+++
b/gateway-spi/src/main/java/org/apache/knox/gateway/services/knoxidf/trustedoidcissuer/TrustedOidcIssuerService.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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
+ * <p>
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * <p>
+ * 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 org.apache.knox.gateway.services.knoxidf.trustedoidcissuer;
+
+import org.apache.knox.gateway.services.Service;
+
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * Gateway service managing the registry of OIDC issuers trusted for JWT
+ * verification in Knox. For issuers registered for dynamic JWKS discovery,
+ * resolves JWKS URIs via OpenID Connect Discovery 1.0
+ * (https://openid.net/specs/openid-connect-discovery-1_0.html) rather than
+ * requiring statically configured JWKS endpoints.
+ */
+public interface TrustedOidcIssuerService extends Service {
+
+ /**
+ * Returns {@code true} if the given issuer URL is currently registered as
+ * trusted. This is the primary SSRF gate: callers must verify trust before
+ * requesting any external resource associated with an issuer.
+ */
+ boolean isTrusted(String issuerUrl);
+
+ /**
+ * Returns {@code true} if the given issuer URL is trusted and configured for
+ * OIDC discovery-based JWKS resolution. Returns {@code false} if the issuer
+ * is not trusted, or is trusted but configured for static JWKS only.
+ * <p>
+ * This method combines the trust check with the discovery-mode check.
+ * Callers may use it as a single guard without separately calling
+ * {@link #isTrusted(String)}.
+ */
+ boolean isDynamicJwks(String issuerUrl);
+
+ /**
+ * Resolves the JWKS URI for the given issuer URL using OIDC discovery.
+ * Callers should verify that the issuer is trusted and configured for OIDC
+ * discovery via {@link #isDynamicJwks(String)} before calling this method,
+ * as that check covers both conditions.
+ * <p>
+ * Returns {@link Optional#empty()} in all failure cases — including issuer
not
+ * trusted, dynamic JWKS not configured, discovery document unreachable or
+ * malformed, or any internal error. Failure details are logged internally
for
+ * troubleshooting. Callers should treat an empty result uniformly as
+ * "no trusted JWKS URI available" without branching on the failure cause.
+ */
+ Optional<String> resolveJwksUri(String issuerUrl);
+
+ /**
+ * Forces re-resolution of the JWKS URI for the given issuer URL, discarding
+ * any previously resolved value. Use this when a resolved JWKS URI is
suspected
+ * to be stale (for example, if an issuer has changed its JWKS endpoint).
+ * Has no effect if the issuer is not registered or does not use OIDC
discovery.
+ */
+ void refreshJwksUri(String issuerUrl);
+
+ /**
+ * Registers a new trusted OIDC issuer.
+ *
+ * @throws IllegalStateException if the maximum registered issuer limit is
+ * reached
+ * @throws RuntimeException if registration fails due to a storage error such
+ * as a duplicate issuer URL or a database failure
+ */
+ void register(TrustedOidcIssuer issuer);
+
+ /**
+ * Removes the given issuer URL from the trusted registry and invalidates any
+ * previously resolved JWKS URI for that issuer. Returns silently if the
issuer
+ * is not currently registered.
+ *
+ * @throws RuntimeException if removal fails due to a storage error
+ */
+ void deregister(String issuerUrl);
+
+ /**
+ * Returns all currently registered trusted issuers.
+ */
+ List<TrustedOidcIssuer> list();
+}