Skip to content

Commit bcc97ec

Browse files
authored
Make JWT brokers injectable (#570)
1 parent c5bacbd commit bcc97ec

File tree

7 files changed

+217
-79
lines changed

7 files changed

+217
-79
lines changed

dropwizard/service/src/main/java/org/apache/polaris/service/dropwizard/config/PolarisApplicationConfig.java

+11-46
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
import java.util.Date;
3333
import java.util.List;
3434
import java.util.Map;
35-
import java.util.Objects;
3635
import java.util.Optional;
3736
import java.util.function.Supplier;
3837
import org.apache.commons.lang3.StringUtils;
@@ -41,10 +40,8 @@
4140
import org.apache.polaris.core.persistence.MetaStoreManagerFactory;
4241
import org.apache.polaris.core.storage.PolarisStorageIntegrationProvider;
4342
import org.apache.polaris.service.auth.Authenticator;
44-
import org.apache.polaris.service.auth.DecodedToken;
45-
import org.apache.polaris.service.auth.TokenBroker;
4643
import org.apache.polaris.service.auth.TokenBrokerFactory;
47-
import org.apache.polaris.service.auth.TokenResponse;
44+
import org.apache.polaris.service.auth.TokenBrokerFactoryConfig;
4845
import org.apache.polaris.service.catalog.api.IcebergRestOAuth2ApiService;
4946
import org.apache.polaris.service.catalog.io.FileIOFactory;
5047
import org.apache.polaris.service.config.DefaultConfigurationStore;
@@ -54,7 +51,6 @@
5451
import org.apache.polaris.service.ratelimiter.RateLimiter;
5552
import org.apache.polaris.service.ratelimiter.TokenBucketFactory;
5653
import org.apache.polaris.service.storage.PolarisStorageIntegrationProviderImpl;
57-
import org.apache.polaris.service.types.TokenType;
5854
import org.glassfish.hk2.api.Factory;
5955
import org.glassfish.hk2.api.ServiceLocator;
6056
import org.glassfish.hk2.api.TypeLiteral;
@@ -92,7 +88,7 @@ public class PolarisApplicationConfig extends Configuration {
9288
private FileIOFactory fileIOFactory;
9389
private RateLimiter rateLimiter;
9490
private TokenBucketFactory tokenBucketFactory;
95-
private TokenBrokerFactory tokenBrokerFactory;
91+
private TokenBrokerConfig tokenBroker = new TokenBrokerConfig();
9692

9793
private AccessToken gcpAccessToken;
9894

@@ -131,7 +127,13 @@ protected void configure() {
131127
bindFactory(SupplierFactory.create(serviceLocator, config::getPolarisAuthenticator))
132128
.to(Authenticator.class)
133129
.ranked(OVERRIDE_BINDING_RANK);
134-
bindFactory(SupplierFactory.create(serviceLocator, config::getTokenBrokerFactory))
130+
bindFactory(SupplierFactory.create(serviceLocator, () -> tokenBroker))
131+
.to(TokenBrokerFactoryConfig.class);
132+
bindFactory(
133+
SupplierFactory.create(
134+
serviceLocator,
135+
() ->
136+
serviceLocator.getService(TokenBrokerFactory.class, tokenBroker.getType())))
135137
.to(TokenBrokerFactory.class)
136138
.ranked(OVERRIDE_BINDING_RANK);
137139
bindFactory(SupplierFactory.create(serviceLocator, config::getOauth2Service))
@@ -228,45 +230,8 @@ private Authenticator<String, AuthenticatedPolarisPrincipal> getPolarisAuthentic
228230
}
229231

230232
@JsonProperty("tokenBroker")
231-
@JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.PROPERTY, property = "type")
232-
public void setTokenBrokerFactory(TokenBrokerFactory tokenBrokerFactory) {
233-
this.tokenBrokerFactory = tokenBrokerFactory;
234-
}
235-
236-
private TokenBrokerFactory getTokenBrokerFactory() {
237-
// return a no-op implementation if none is specified
238-
return Objects.requireNonNullElseGet(
239-
tokenBrokerFactory,
240-
() ->
241-
(rc) ->
242-
new TokenBroker() {
243-
@Override
244-
public boolean supportsGrantType(String grantType) {
245-
return false;
246-
}
247-
248-
@Override
249-
public boolean supportsRequestedTokenType(TokenType tokenType) {
250-
return false;
251-
}
252-
253-
@Override
254-
public TokenResponse generateFromClientSecrets(
255-
String clientId, String clientSecret, String grantType, String scope) {
256-
return null;
257-
}
258-
259-
@Override
260-
public TokenResponse generateFromToken(
261-
TokenType tokenType, String subjectToken, String grantType, String scope) {
262-
return null;
263-
}
264-
265-
@Override
266-
public DecodedToken verify(String token) {
267-
return null;
268-
}
269-
});
233+
public void setTokenBroker(TokenBrokerConfig tokenBroker) {
234+
this.tokenBroker = tokenBroker;
270235
}
271236

272237
private RealmContextResolver getRealmContextResolver() {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.service.dropwizard.config;
20+
21+
import com.fasterxml.jackson.annotation.JsonProperty;
22+
import jakarta.annotation.Nullable;
23+
import org.apache.polaris.service.auth.TokenBrokerFactoryConfig;
24+
25+
/**
26+
* This object receives the subsection of the server config YAML (`polaris-server.yml`) that is
27+
* related to JWT brokers.
28+
*/
29+
public class TokenBrokerConfig implements TokenBrokerFactoryConfig {
30+
private String type = "none";
31+
private int maxTokenGenerationInSeconds = 3600;
32+
private String file;
33+
private String secret;
34+
35+
@JsonProperty("type")
36+
public void setType(String type) {
37+
this.type = type;
38+
}
39+
40+
@JsonProperty("file")
41+
public void setFile(String file) {
42+
this.file = file;
43+
}
44+
45+
@JsonProperty("secret")
46+
public void setSecret(String secret) {
47+
this.secret = secret;
48+
}
49+
50+
@JsonProperty("maxTokenGenerationInSeconds")
51+
public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) {
52+
this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds;
53+
}
54+
55+
public String getType() {
56+
return type;
57+
}
58+
59+
@Override
60+
public int maxTokenGenerationInSeconds() {
61+
return maxTokenGenerationInSeconds;
62+
}
63+
64+
@Nullable
65+
@Override
66+
public String file() {
67+
return file;
68+
}
69+
70+
@Nullable
71+
@Override
72+
public String secret() {
73+
return secret;
74+
}
75+
}

dropwizard/service/src/main/resources/META-INF/hk2-locator/default

+5
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,11 @@ contract={org.apache.polaris.service.auth.TokenBrokerFactory}
5151
name=rsa-key-pair
5252
qualifier={io.smallrye.common.annotation.Identifier}
5353

54+
[org.apache.polaris.service.auth.NoneTokenBrokerFactory]S
55+
contract={org.apache.polaris.service.auth.TokenBrokerFactory}
56+
name=none
57+
qualifier={io.smallrye.common.annotation.Identifier}
58+
5459
[org.apache.polaris.service.context.DefaultRealmContextResolver]S
5560
contract={org.apache.polaris.service.context.RealmContextResolver}
5661
name=default

service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,21 @@
2525

2626
@Identifier("rsa-key-pair")
2727
public class JWTRSAKeyPairFactory implements TokenBrokerFactory {
28-
private int maxTokenGenerationInSeconds = 3600;
2928

30-
@Inject private MetaStoreManagerFactory metaStoreManagerFactory;
29+
private final TokenBrokerFactoryConfig config;
30+
private final MetaStoreManagerFactory metaStoreManagerFactory;
3131

32-
public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) {
33-
this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds;
32+
@Inject
33+
public JWTRSAKeyPairFactory(
34+
TokenBrokerFactoryConfig config, MetaStoreManagerFactory metaStoreManagerFactory) {
35+
this.config = config;
36+
this.metaStoreManagerFactory = metaStoreManagerFactory;
3437
}
3538

3639
@Override
3740
public TokenBroker apply(RealmContext realmContext) {
3841
return new JWTRSAKeyPair(
3942
metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext),
40-
maxTokenGenerationInSeconds);
43+
config.maxTokenGenerationInSeconds());
4144
}
4245
}

service/common/src/main/java/org/apache/polaris/service/auth/JWTSymmetricKeyFactory.java

+23-28
Original file line numberDiff line numberDiff line change
@@ -18,57 +18,52 @@
1818
*/
1919
package org.apache.polaris.service.auth;
2020

21+
import static com.google.common.base.Preconditions.checkState;
22+
2123
import io.smallrye.common.annotation.Identifier;
2224
import jakarta.inject.Inject;
2325
import java.io.IOException;
2426
import java.nio.file.Files;
27+
import java.nio.file.Path;
2528
import java.nio.file.Paths;
2629
import java.util.function.Supplier;
2730
import org.apache.polaris.core.context.RealmContext;
2831
import org.apache.polaris.core.persistence.MetaStoreManagerFactory;
2932

3033
@Identifier("symmetric-key")
3134
public class JWTSymmetricKeyFactory implements TokenBrokerFactory {
32-
@Inject private MetaStoreManagerFactory metaStoreManagerFactory;
33-
private int maxTokenGenerationInSeconds = 3600;
34-
private String file;
35-
private String secret;
35+
36+
private final MetaStoreManagerFactory metaStoreManagerFactory;
37+
private final TokenBrokerFactoryConfig config;
38+
private final Supplier<String> secretSupplier;
39+
40+
@Inject
41+
public JWTSymmetricKeyFactory(
42+
MetaStoreManagerFactory metaStoreManagerFactory, TokenBrokerFactoryConfig config) {
43+
this.metaStoreManagerFactory = metaStoreManagerFactory;
44+
this.config = config;
45+
46+
String secret = config.secret();
47+
String file = config.file();
48+
checkState(secret != null || file != null, "Either file or secret must be set");
49+
this.secretSupplier = secret != null ? () -> secret : readSecretFromDisk(Paths.get(file));
50+
}
3651

3752
@Override
3853
public TokenBroker apply(RealmContext realmContext) {
39-
if (file == null && secret == null) {
40-
throw new IllegalStateException("Either file or secret must be set");
41-
}
42-
Supplier<String> secretSupplier = secret != null ? () -> secret : readSecretFromDisk();
4354
return new JWTSymmetricKeyBroker(
4455
metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext),
45-
maxTokenGenerationInSeconds,
56+
config.maxTokenGenerationInSeconds(),
4657
secretSupplier);
4758
}
4859

49-
private Supplier<String> readSecretFromDisk() {
60+
private Supplier<String> readSecretFromDisk(Path path) {
5061
return () -> {
5162
try {
52-
return Files.readString(Paths.get(file));
63+
return Files.readString(path);
5364
} catch (IOException e) {
54-
throw new RuntimeException("Failed to read secret from file: " + file, e);
65+
throw new RuntimeException("Failed to read secret from file: " + config.file(), e);
5566
}
5667
};
5768
}
58-
59-
public void setMaxTokenGenerationInSeconds(int maxTokenGenerationInSeconds) {
60-
this.maxTokenGenerationInSeconds = maxTokenGenerationInSeconds;
61-
}
62-
63-
public void setFile(String file) {
64-
this.file = file;
65-
}
66-
67-
public void setSecret(String secret) {
68-
this.secret = secret;
69-
}
70-
71-
public void setMetaStoreManagerFactory(MetaStoreManagerFactory metaStoreManagerFactory) {
72-
this.metaStoreManagerFactory = metaStoreManagerFactory;
73-
}
7469
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.service.auth;
20+
21+
import io.smallrye.common.annotation.Identifier;
22+
import org.apache.polaris.core.context.RealmContext;
23+
import org.apache.polaris.service.types.TokenType;
24+
25+
/** Default {@link TokenBrokerFactory} that produces token brokers that do not do anything. */
26+
@Identifier("none")
27+
public class NoneTokenBrokerFactory implements TokenBrokerFactory {
28+
@Override
29+
public TokenBroker apply(RealmContext realmContext) {
30+
return new TokenBroker() {
31+
@Override
32+
public boolean supportsGrantType(String grantType) {
33+
return false;
34+
}
35+
36+
@Override
37+
public boolean supportsRequestedTokenType(TokenType tokenType) {
38+
return false;
39+
}
40+
41+
@Override
42+
public TokenResponse generateFromClientSecrets(
43+
String clientId, String clientSecret, String grantType, String scope) {
44+
return null;
45+
}
46+
47+
@Override
48+
public TokenResponse generateFromToken(
49+
TokenType tokenType, String subjectToken, String grantType, String scope) {
50+
return null;
51+
}
52+
53+
@Override
54+
public DecodedToken verify(String token) {
55+
return null;
56+
}
57+
};
58+
}
59+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.service.auth;
20+
21+
import jakarta.annotation.Nullable;
22+
23+
/**
24+
* This is a union of configuration settings of all token brokers.
25+
*
26+
* @see TokenBrokerFactory
27+
*/
28+
public interface TokenBrokerFactoryConfig {
29+
@Nullable
30+
String file();
31+
32+
@Nullable
33+
String secret();
34+
35+
int maxTokenGenerationInSeconds();
36+
}

0 commit comments

Comments
 (0)