This is an automated email from the ASF dual-hosted git repository.

adutra pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git


The following commit(s) were added to refs/heads/main by this push:
     new bcc97eca Make JWT brokers injectable (#570)
bcc97eca is described below

commit bcc97ecaa0e3e0ddf0c5f4229fd1f878e3996b14
Author: Dmitri Bourlatchkov <[email protected]>
AuthorDate: Tue Jan 7 08:06:15 2025 -0500

    Make JWT brokers injectable (#570)
---
 .../config/PolarisApplicationConfig.java           | 57 ++++------------
 .../dropwizard/config/TokenBrokerConfig.java       | 75 ++++++++++++++++++++++
 .../main/resources/META-INF/hk2-locator/default    |  5 ++
 .../polaris/service/auth/JWTRSAKeyPairFactory.java | 13 ++--
 .../service/auth/JWTSymmetricKeyFactory.java       | 51 +++++++--------
 ...airFactory.java => NoneTokenBrokerFactory.java} | 45 +++++++++----
 ...rFactory.java => TokenBrokerFactoryConfig.java} | 30 ++++-----
 7 files changed, 165 insertions(+), 111 deletions(-)

diff --git 
a/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/config/PolarisApplicationConfig.java
 
b/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/config/PolarisApplicationConfig.java
index d2afa150..2c351d0f 100644
--- 
a/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/config/PolarisApplicationConfig.java
+++ 
b/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/config/PolarisApplicationConfig.java
@@ -32,7 +32,6 @@ import java.io.IOException;
 import java.util.Date;
 import java.util.List;
 import java.util.Map;
-import java.util.Objects;
 import java.util.Optional;
 import java.util.function.Supplier;
 import org.apache.commons.lang3.StringUtils;
@@ -41,10 +40,8 @@ import 
org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal;
 import org.apache.polaris.core.persistence.MetaStoreManagerFactory;
 import org.apache.polaris.core.storage.PolarisStorageIntegrationProvider;
 import org.apache.polaris.service.auth.Authenticator;
-import org.apache.polaris.service.auth.DecodedToken;
-import org.apache.polaris.service.auth.TokenBroker;
 import org.apache.polaris.service.auth.TokenBrokerFactory;
-import org.apache.polaris.service.auth.TokenResponse;
+import org.apache.polaris.service.auth.TokenBrokerFactoryConfig;
 import org.apache.polaris.service.catalog.api.IcebergRestOAuth2ApiService;
 import org.apache.polaris.service.catalog.io.FileIOFactory;
 import org.apache.polaris.service.config.DefaultConfigurationStore;
@@ -54,7 +51,6 @@ import 
org.apache.polaris.service.context.RealmContextResolver;
 import org.apache.polaris.service.ratelimiter.RateLimiter;
 import org.apache.polaris.service.ratelimiter.TokenBucketFactory;
 import 
org.apache.polaris.service.storage.PolarisStorageIntegrationProviderImpl;
-import org.apache.polaris.service.types.TokenType;
 import org.glassfish.hk2.api.Factory;
 import org.glassfish.hk2.api.ServiceLocator;
 import org.glassfish.hk2.api.TypeLiteral;
@@ -92,7 +88,7 @@ public class PolarisApplicationConfig extends Configuration {
   private FileIOFactory fileIOFactory;
   private RateLimiter rateLimiter;
   private TokenBucketFactory tokenBucketFactory;
-  private TokenBrokerFactory tokenBrokerFactory;
+  private TokenBrokerConfig tokenBroker = new TokenBrokerConfig();
 
   private AccessToken gcpAccessToken;
 
@@ -131,7 +127,13 @@ public class PolarisApplicationConfig extends 
Configuration {
         bindFactory(SupplierFactory.create(serviceLocator, 
config::getPolarisAuthenticator))
             .to(Authenticator.class)
             .ranked(OVERRIDE_BINDING_RANK);
-        bindFactory(SupplierFactory.create(serviceLocator, 
config::getTokenBrokerFactory))
+        bindFactory(SupplierFactory.create(serviceLocator, () -> tokenBroker))
+            .to(TokenBrokerFactoryConfig.class);
+        bindFactory(
+                SupplierFactory.create(
+                    serviceLocator,
+                    () ->
+                        serviceLocator.getService(TokenBrokerFactory.class, 
tokenBroker.getType())))
             .to(TokenBrokerFactory.class)
             .ranked(OVERRIDE_BINDING_RANK);
         bindFactory(SupplierFactory.create(serviceLocator, 
config::getOauth2Service))
@@ -228,45 +230,8 @@ public class PolarisApplicationConfig extends 
Configuration {
   }
 
   @JsonProperty("tokenBroker")
-  @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = 
JsonTypeInfo.As.PROPERTY, property = "type")
-  public void setTokenBrokerFactory(TokenBrokerFactory tokenBrokerFactory) {
-    this.tokenBrokerFactory = tokenBrokerFactory;
-  }
-
-  private TokenBrokerFactory getTokenBrokerFactory() {
-    // return a no-op implementation if none is specified
-    return Objects.requireNonNullElseGet(
-        tokenBrokerFactory,
-        () ->
-            (rc) ->
-                new TokenBroker() {
-                  @Override
-                  public boolean supportsGrantType(String grantType) {
-                    return false;
-                  }
-
-                  @Override
-                  public boolean supportsRequestedTokenType(TokenType 
tokenType) {
-                    return false;
-                  }
-
-                  @Override
-                  public TokenResponse generateFromClientSecrets(
-                      String clientId, String clientSecret, String grantType, 
String scope) {
-                    return null;
-                  }
-
-                  @Override
-                  public TokenResponse generateFromToken(
-                      TokenType tokenType, String subjectToken, String 
grantType, String scope) {
-                    return null;
-                  }
-
-                  @Override
-                  public DecodedToken verify(String token) {
-                    return null;
-                  }
-                });
+  public void setTokenBroker(TokenBrokerConfig tokenBroker) {
+    this.tokenBroker = tokenBroker;
   }
 
   private RealmContextResolver getRealmContextResolver() {
diff --git 
a/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/config/TokenBrokerConfig.java
 
b/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/config/TokenBrokerConfig.java
new file mode 100644
index 00000000..3e349fcf
--- /dev/null
+++ 
b/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/config/TokenBrokerConfig.java
@@ -0,0 +1,75 @@
+/*
+ * 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.
+ */
+package org.apache.polaris.service.dropwizard.config;
+
+import com.fasterxml.jackson.annotation.JsonProperty;
+import jakarta.annotation.Nullable;
+import org.apache.polaris.service.auth.TokenBrokerFactoryConfig;
+
+/**
+ * This object receives the subsection of the server config YAML 
(`polaris-server.yml`) that is
+ * related to JWT brokers.
+ */
+public class TokenBrokerConfig implements TokenBrokerFactoryConfig {
+  private String type = "none";
+  private int maxTokenGenerationInSeconds = 3600;
+  private String file;
+  private String secret;
+
+  @JsonProperty("type")
+  public void setType(String type) {
+    this.type = type;
+  }
+
+  @JsonProperty("file")
+  public void setFile(String file) {
+    this.file = file;
+  }
+
+  @JsonProperty("secret")
+  public void setSecret(String secret) {
+    this.secret = secret;
+  }
+
+  @JsonProperty("maxTokenGenerationInSeconds")
+  public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) {
+    this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds;
+  }
+
+  public String getType() {
+    return type;
+  }
+
+  @Override
+  public int maxTokenGenerationInSeconds() {
+    return maxTokenGenerationInSeconds;
+  }
+
+  @Nullable
+  @Override
+  public String file() {
+    return file;
+  }
+
+  @Nullable
+  @Override
+  public String secret() {
+    return secret;
+  }
+}
diff --git a/dropwizard/service/src/main/resources/META-INF/hk2-locator/default 
b/dropwizard/service/src/main/resources/META-INF/hk2-locator/default
index 1771a6ad..b5eacf9c 100644
--- a/dropwizard/service/src/main/resources/META-INF/hk2-locator/default
+++ b/dropwizard/service/src/main/resources/META-INF/hk2-locator/default
@@ -51,6 +51,11 @@ contract={org.apache.polaris.service.auth.TokenBrokerFactory}
 name=rsa-key-pair
 qualifier={io.smallrye.common.annotation.Identifier}
 
+[org.apache.polaris.service.auth.NoneTokenBrokerFactory]S
+contract={org.apache.polaris.service.auth.TokenBrokerFactory}
+name=none
+qualifier={io.smallrye.common.annotation.Identifier}
+
 [org.apache.polaris.service.context.DefaultRealmContextResolver]S
 contract={org.apache.polaris.service.context.RealmContextResolver}
 name=default
diff --git 
a/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
 
b/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
index 4b11a466..cb49ea2b 100644
--- 
a/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
+++ 
b/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
@@ -25,18 +25,21 @@ import 
org.apache.polaris.core.persistence.MetaStoreManagerFactory;
 
 @Identifier("rsa-key-pair")
 public class JWTRSAKeyPairFactory implements TokenBrokerFactory {
-  private int maxTokenGenerationInSeconds = 3600;
 
-  @Inject private MetaStoreManagerFactory metaStoreManagerFactory;
+  private final TokenBrokerFactoryConfig config;
+  private final MetaStoreManagerFactory metaStoreManagerFactory;
 
-  public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) {
-    this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds;
+  @Inject
+  public JWTRSAKeyPairFactory(
+      TokenBrokerFactoryConfig config, MetaStoreManagerFactory 
metaStoreManagerFactory) {
+    this.config = config;
+    this.metaStoreManagerFactory = metaStoreManagerFactory;
   }
 
   @Override
   public TokenBroker apply(RealmContext realmContext) {
     return new JWTRSAKeyPair(
         metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext),
-        maxTokenGenerationInSeconds);
+        config.maxTokenGenerationInSeconds());
   }
 }
diff --git 
a/service/common/src/main/java/org/apache/polaris/service/auth/JWTSymmetricKeyFactory.java
 
b/service/common/src/main/java/org/apache/polaris/service/auth/JWTSymmetricKeyFactory.java
index bb04b048..061a8a18 100644
--- 
a/service/common/src/main/java/org/apache/polaris/service/auth/JWTSymmetricKeyFactory.java
+++ 
b/service/common/src/main/java/org/apache/polaris/service/auth/JWTSymmetricKeyFactory.java
@@ -18,10 +18,13 @@
  */
 package org.apache.polaris.service.auth;
 
+import static com.google.common.base.Preconditions.checkState;
+
 import io.smallrye.common.annotation.Identifier;
 import jakarta.inject.Inject;
 import java.io.IOException;
 import java.nio.file.Files;
+import java.nio.file.Path;
 import java.nio.file.Paths;
 import java.util.function.Supplier;
 import org.apache.polaris.core.context.RealmContext;
@@ -29,46 +32,38 @@ import 
org.apache.polaris.core.persistence.MetaStoreManagerFactory;
 
 @Identifier("symmetric-key")
 public class JWTSymmetricKeyFactory implements TokenBrokerFactory {
-  @Inject private MetaStoreManagerFactory metaStoreManagerFactory;
-  private int maxTokenGenerationInSeconds = 3600;
-  private String file;
-  private String secret;
+
+  private final MetaStoreManagerFactory metaStoreManagerFactory;
+  private final TokenBrokerFactoryConfig config;
+  private final Supplier<String> secretSupplier;
+
+  @Inject
+  public JWTSymmetricKeyFactory(
+      MetaStoreManagerFactory metaStoreManagerFactory, 
TokenBrokerFactoryConfig config) {
+    this.metaStoreManagerFactory = metaStoreManagerFactory;
+    this.config = config;
+
+    String secret = config.secret();
+    String file = config.file();
+    checkState(secret != null || file != null, "Either file or secret must be 
set");
+    this.secretSupplier = secret != null ? () -> secret : 
readSecretFromDisk(Paths.get(file));
+  }
 
   @Override
   public TokenBroker apply(RealmContext realmContext) {
-    if (file == null && secret == null) {
-      throw new IllegalStateException("Either file or secret must be set");
-    }
-    Supplier<String> secretSupplier = secret != null ? () -> secret : 
readSecretFromDisk();
     return new JWTSymmetricKeyBroker(
         metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext),
-        maxTokenGenerationInSeconds,
+        config.maxTokenGenerationInSeconds(),
         secretSupplier);
   }
 
-  private Supplier<String> readSecretFromDisk() {
+  private Supplier<String> readSecretFromDisk(Path path) {
     return () -> {
       try {
-        return Files.readString(Paths.get(file));
+        return Files.readString(path);
       } catch (IOException e) {
-        throw new RuntimeException("Failed to read secret from file: " + file, 
e);
+        throw new RuntimeException("Failed to read secret from file: " + 
config.file(), e);
       }
     };
   }
-
-  public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) {
-    this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds;
-  }
-
-  public void setFile(String file) {
-    this.file = file;
-  }
-
-  public void setSecret(String secret) {
-    this.secret = secret;
-  }
-
-  public void setMetaStoreManagerFactory(MetaStoreManagerFactory 
metaStoreManagerFactory) {
-    this.metaStoreManagerFactory = metaStoreManagerFactory;
-  }
 }
diff --git 
a/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
 
b/service/common/src/main/java/org/apache/polaris/service/auth/NoneTokenBrokerFactory.java
similarity index 52%
copy from 
service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
copy to 
service/common/src/main/java/org/apache/polaris/service/auth/NoneTokenBrokerFactory.java
index 4b11a466..c39124fe 100644
--- 
a/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
+++ 
b/service/common/src/main/java/org/apache/polaris/service/auth/NoneTokenBrokerFactory.java
@@ -19,24 +19,41 @@
 package org.apache.polaris.service.auth;
 
 import io.smallrye.common.annotation.Identifier;
-import jakarta.inject.Inject;
 import org.apache.polaris.core.context.RealmContext;
-import org.apache.polaris.core.persistence.MetaStoreManagerFactory;
+import org.apache.polaris.service.types.TokenType;
 
-@Identifier("rsa-key-pair")
-public class JWTRSAKeyPairFactory implements TokenBrokerFactory {
-  private int maxTokenGenerationInSeconds = 3600;
+/** Default {@link TokenBrokerFactory} that produces token brokers that do not 
do anything. */
+@Identifier("none")
+public class NoneTokenBrokerFactory implements TokenBrokerFactory {
+  @Override
+  public TokenBroker apply(RealmContext realmContext) {
+    return new TokenBroker() {
+      @Override
+      public boolean supportsGrantType(String grantType) {
+        return false;
+      }
 
-  @Inject private MetaStoreManagerFactory metaStoreManagerFactory;
+      @Override
+      public boolean supportsRequestedTokenType(TokenType tokenType) {
+        return false;
+      }
 
-  public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) {
-    this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds;
-  }
+      @Override
+      public TokenResponse generateFromClientSecrets(
+          String clientId, String clientSecret, String grantType, String 
scope) {
+        return null;
+      }
 
-  @Override
-  public TokenBroker apply(RealmContext realmContext) {
-    return new JWTRSAKeyPair(
-        metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext),
-        maxTokenGenerationInSeconds);
+      @Override
+      public TokenResponse generateFromToken(
+          TokenType tokenType, String subjectToken, String grantType, String 
scope) {
+        return null;
+      }
+
+      @Override
+      public DecodedToken verify(String token) {
+        return null;
+      }
+    };
   }
 }
diff --git 
a/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
 
b/service/common/src/main/java/org/apache/polaris/service/auth/TokenBrokerFactoryConfig.java
similarity index 52%
copy from 
service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
copy to 
service/common/src/main/java/org/apache/polaris/service/auth/TokenBrokerFactoryConfig.java
index 4b11a466..20811e14 100644
--- 
a/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java
+++ 
b/service/common/src/main/java/org/apache/polaris/service/auth/TokenBrokerFactoryConfig.java
@@ -18,25 +18,19 @@
  */
 package org.apache.polaris.service.auth;
 
-import io.smallrye.common.annotation.Identifier;
-import jakarta.inject.Inject;
-import org.apache.polaris.core.context.RealmContext;
-import org.apache.polaris.core.persistence.MetaStoreManagerFactory;
+import jakarta.annotation.Nullable;
 
-@Identifier("rsa-key-pair")
-public class JWTRSAKeyPairFactory implements TokenBrokerFactory {
-  private int maxTokenGenerationInSeconds = 3600;
-
-  @Inject private MetaStoreManagerFactory metaStoreManagerFactory;
+/**
+ * This is a union of configuration settings of all token brokers.
+ *
+ * @see TokenBrokerFactory
+ */
+public interface TokenBrokerFactoryConfig {
+  @Nullable
+  String file();
 
-  public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) {
-    this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds;
-  }
+  @Nullable
+  String secret();
 
-  @Override
-  public TokenBroker apply(RealmContext realmContext) {
-    return new JWTRSAKeyPair(
-        metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext),
-        maxTokenGenerationInSeconds);
-  }
+  int maxTokenGenerationInSeconds();
 }

Reply via email to