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 34623ee71..3eb47a812 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.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,9 @@ 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.JWTRSAKeyPairFactory; +import org.apache.polaris.service.auth.JWTSymmetricKeyFactory; import org.apache.polaris.service.auth.TokenBrokerFactory; -import org.apache.polaris.service.auth.TokenResponse; import org.apache.polaris.service.catalog.api.IcebergRestOAuth2ApiService; import org.apache.polaris.service.catalog.io.FileIOFactory; import org.apache.polaris.service.config.DefaultConfigurationStore; @@ -53,7 +51,6 @@ import org.apache.polaris.service.context.RealmContextResolver; import org.apache.polaris.service.ratelimiter.RateLimiter; 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; @@ -90,7 +87,7 @@ public class PolarisApplicationConfig extends Configuration { private String awsSecretKey; private FileIOFactory fileIOFactory; private RateLimiter rateLimiter; - private TokenBrokerFactory tokenBrokerFactory; + private TokenBrokerConfig tokenBroker = new TokenBrokerConfig(); private AccessToken gcpAccessToken; @@ -129,7 +126,14 @@ protected void configure() { 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(JWTRSAKeyPairFactory.Config.class) + .to(JWTSymmetricKeyFactory.Config.class); + bindFactory( + SupplierFactory.create( + serviceLocator, + () -> + serviceLocator.getService(TokenBrokerFactory.class, tokenBroker.getType()))) .to(TokenBrokerFactory.class) .ranked(OVERRIDE_BINDING_RANK); bindFactory(SupplierFactory.create(serviceLocator, config::getOauth2Service)) @@ -223,45 +227,8 @@ private Authenticator getPolarisAuthentic } @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 000000000..156a03957 --- /dev/null +++ b/dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/config/TokenBrokerConfig.java @@ -0,0 +1,68 @@ +/* + * 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 jakarta.annotation.Nullable; +import org.apache.polaris.service.auth.JWTRSAKeyPairFactory; +import org.apache.polaris.service.auth.JWTSymmetricKeyFactory; + +public class TokenBrokerConfig + implements JWTSymmetricKeyFactory.Config, JWTRSAKeyPairFactory.Config { + private String type = "none"; + private int maxTokenGenerationInSeconds = 3600; + private String file; + private String secret; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) { + this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds; + } + + @Override + public int maxTokenGenerationInSeconds() { + return maxTokenGenerationInSeconds; + } + + public void setFile(String file) { + this.file = file; + } + + @Nullable + @Override + public String file() { + return file; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + @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 5c9359851..9c2bea690 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 4b11a4666..2fc9fff7b 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,24 @@ @Identifier("rsa-key-pair") public class JWTRSAKeyPairFactory implements TokenBrokerFactory { - private int maxTokenGenerationInSeconds = 3600; - @Inject private MetaStoreManagerFactory metaStoreManagerFactory; + private final Config config; + private final MetaStoreManagerFactory metaStoreManagerFactory; - public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) { - this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds; + @Inject + public JWTRSAKeyPairFactory(Config config, MetaStoreManagerFactory metaStoreManagerFactory) { + this.config = config; + this.metaStoreManagerFactory = metaStoreManagerFactory; } @Override public TokenBroker apply(RealmContext realmContext) { return new JWTRSAKeyPair( metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext), - maxTokenGenerationInSeconds); + config.maxTokenGenerationInSeconds()); + } + + public interface Config { + int 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 bb04b0482..6a17704bf 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 @@ -19,56 +19,58 @@ package org.apache.polaris.service.auth; import io.smallrye.common.annotation.Identifier; +import jakarta.annotation.Nullable; import jakarta.inject.Inject; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; +import java.util.Objects; import java.util.function.Supplier; import org.apache.polaris.core.context.RealmContext; 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 Config config; + + @Inject + public JWTSymmetricKeyFactory(MetaStoreManagerFactory metaStoreManagerFactory, Config config) { + this.metaStoreManagerFactory = metaStoreManagerFactory; + this.config = config; + } @Override public TokenBroker apply(RealmContext realmContext) { - if (file == null && secret == null) { + String secret = config.secret(); + if (config.file() == null && secret == null) { throw new IllegalStateException("Either file or secret must be set"); } Supplier secretSupplier = secret != null ? () -> secret : readSecretFromDisk(); return new JWTSymmetricKeyBroker( metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext), - maxTokenGenerationInSeconds, + config.maxTokenGenerationInSeconds(), secretSupplier); } private Supplier readSecretFromDisk() { return () -> { try { - return Files.readString(Paths.get(file)); + return Files.readString(Paths.get(Objects.requireNonNull(config.file()))); } 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 interface Config { + int maxTokenGenerationInSeconds(); - public void setFile(String file) { - this.file = file; - } - - public void setSecret(String secret) { - this.secret = secret; - } + @Nullable + String file(); - public void setMetaStoreManagerFactory(MetaStoreManagerFactory metaStoreManagerFactory) { - this.metaStoreManagerFactory = metaStoreManagerFactory; + @Nullable + String secret(); } } diff --git a/service/common/src/main/java/org/apache/polaris/service/auth/NoneTokenBrokerFactory.java b/service/common/src/main/java/org/apache/polaris/service/auth/NoneTokenBrokerFactory.java new file mode 100644 index 000000000..c39124feb --- /dev/null +++ b/service/common/src/main/java/org/apache/polaris/service/auth/NoneTokenBrokerFactory.java @@ -0,0 +1,59 @@ +/* + * 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.auth; + +import io.smallrye.common.annotation.Identifier; +import org.apache.polaris.core.context.RealmContext; +import org.apache.polaris.service.types.TokenType; + +/** 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; + } + + @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; + } + }; + } +}