Skip to content

Commit 0bdc3f4

Browse files
authored
Refactor: Use per-request STS credentials (#1629)
* Refactor: Use per-request STS credentials No functional changes. This is mostly to allow more storage integration flexibility in downstream build. This might also be useful for non-AWS storage.
1 parent 463682f commit 0bdc3f4

File tree

6 files changed

+73
-27
lines changed

6 files changed

+73
-27
lines changed

polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
import org.apache.polaris.core.storage.InMemoryStorageIntegration;
3434
import org.apache.polaris.core.storage.StorageAccessProperty;
3535
import org.apache.polaris.core.storage.StorageUtil;
36+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
3637
import software.amazon.awssdk.policybuilder.iam.IamConditionOperator;
3738
import software.amazon.awssdk.policybuilder.iam.IamEffect;
3839
import software.amazon.awssdk.policybuilder.iam.IamPolicy;
@@ -46,10 +47,17 @@
4647
public class AwsCredentialsStorageIntegration
4748
extends InMemoryStorageIntegration<AwsStorageConfigurationInfo> {
4849
private final StsClient stsClient;
50+
private final Optional<AwsCredentialsProvider> credentialsProvider;
4951

5052
public AwsCredentialsStorageIntegration(StsClient stsClient) {
53+
this(stsClient, Optional.empty());
54+
}
55+
56+
public AwsCredentialsStorageIntegration(
57+
StsClient stsClient, Optional<AwsCredentialsProvider> credentialsProvider) {
5158
super(AwsCredentialsStorageIntegration.class.getName());
5259
this.stsClient = stsClient;
60+
this.credentialsProvider = credentialsProvider;
5361
}
5462

5563
/** {@inheritDoc} */
@@ -60,21 +68,22 @@ public EnumMap<StorageAccessProperty, String> getSubscopedCreds(
6068
boolean allowListOperation,
6169
@Nonnull Set<String> allowedReadLocations,
6270
@Nonnull Set<String> allowedWriteLocations) {
63-
AssumeRoleResponse response =
64-
stsClient.assumeRole(
65-
AssumeRoleRequest.builder()
66-
.externalId(storageConfig.getExternalId())
67-
.roleArn(storageConfig.getRoleARN())
68-
.roleSessionName("PolarisAwsCredentialsStorageIntegration")
69-
.policy(
70-
policyString(
71-
storageConfig.getRoleARN(),
72-
allowListOperation,
73-
allowedReadLocations,
74-
allowedWriteLocations)
75-
.toJson())
76-
.durationSeconds(loadConfig(STORAGE_CREDENTIAL_DURATION_SECONDS))
77-
.build());
71+
AssumeRoleRequest.Builder request =
72+
AssumeRoleRequest.builder()
73+
.externalId(storageConfig.getExternalId())
74+
.roleArn(storageConfig.getRoleARN())
75+
.roleSessionName("PolarisAwsCredentialsStorageIntegration")
76+
.policy(
77+
policyString(
78+
storageConfig.getRoleARN(),
79+
allowListOperation,
80+
allowedReadLocations,
81+
allowedWriteLocations)
82+
.toJson())
83+
.durationSeconds(loadConfig(STORAGE_CREDENTIAL_DURATION_SECONDS));
84+
credentialsProvider.ifPresent(
85+
cp -> request.overrideConfiguration(b -> b.credentialsProvider(cp)));
86+
AssumeRoleResponse response = stsClient.assumeRole(request.build());
7887
EnumMap<StorageAccessProperty, String> credentialMap =
7988
new EnumMap<>(StorageAccessProperty.class);
8089
credentialMap.put(StorageAccessProperty.AWS_KEY_ID, response.credentials().accessKeyId());

quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAuthzTestBase.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import java.util.Date;
3636
import java.util.List;
3737
import java.util.Map;
38+
import java.util.Optional;
3839
import java.util.Set;
3940
import java.util.function.Function;
4041
import java.util.stream.Collectors;
@@ -219,7 +220,9 @@ public Map<String, String> getConfigOverrides() {
219220
public static void setUpMocks() {
220221
PolarisStorageIntegrationProviderImpl mock =
221222
new PolarisStorageIntegrationProviderImpl(
222-
Mockito::mock, () -> GoogleCredentials.create(new AccessToken("abc", new Date())));
223+
Mockito::mock,
224+
Optional.empty(),
225+
() -> GoogleCredentials.create(new AccessToken("abc", new Date())));
223226
QuarkusMock.installMockForType(mock, PolarisStorageIntegrationProviderImpl.class);
224227
}
225228

service/common/src/main/java/org/apache/polaris/service/storage/PolarisStorageIntegrationProviderImpl.java

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import jakarta.inject.Inject;
2929
import java.util.EnumMap;
3030
import java.util.Map;
31+
import java.util.Optional;
3132
import java.util.Set;
3233
import java.util.function.Supplier;
3334
import org.apache.polaris.core.PolarisDiagnostics;
@@ -39,22 +40,30 @@
3940
import org.apache.polaris.core.storage.aws.AwsCredentialsStorageIntegration;
4041
import org.apache.polaris.core.storage.azure.AzureCredentialsStorageIntegration;
4142
import org.apache.polaris.core.storage.gcp.GcpCredentialsStorageIntegration;
43+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
4244
import software.amazon.awssdk.services.sts.StsClient;
4345

4446
@ApplicationScoped
4547
public class PolarisStorageIntegrationProviderImpl implements PolarisStorageIntegrationProvider {
4648

4749
private final Supplier<StsClient> stsClientSupplier;
50+
private final Optional<AwsCredentialsProvider> stsCredentials;
4851
private final Supplier<GoogleCredentials> gcpCredsProvider;
4952

5053
@Inject
5154
public PolarisStorageIntegrationProviderImpl(StorageConfiguration storageConfiguration) {
52-
this(storageConfiguration.stsClientSupplier(), storageConfiguration.gcpCredentialsSupplier());
55+
this(
56+
storageConfiguration.stsClientSupplier(false),
57+
Optional.ofNullable(storageConfiguration.stsCredentials()),
58+
storageConfiguration.gcpCredentialsSupplier());
5359
}
5460

5561
public PolarisStorageIntegrationProviderImpl(
56-
Supplier<StsClient> stsClientSupplier, Supplier<GoogleCredentials> gcpCredsProvider) {
62+
Supplier<StsClient> stsClientSupplier,
63+
Optional<AwsCredentialsProvider> stsCredentials,
64+
Supplier<GoogleCredentials> gcpCredsProvider) {
5765
this.stsClientSupplier = stsClientSupplier;
66+
this.stsCredentials = stsCredentials;
5867
this.gcpCredsProvider = gcpCredsProvider;
5968
}
6069

@@ -71,7 +80,7 @@ public PolarisStorageIntegrationProviderImpl(
7180
case S3:
7281
storageIntegration =
7382
(PolarisStorageIntegration<T>)
74-
new AwsCredentialsStorageIntegration(stsClientSupplier.get());
83+
new AwsCredentialsStorageIntegration(stsClientSupplier.get(), stsCredentials);
7584
break;
7685
case GCS:
7786
storageIntegration =

service/common/src/main/java/org/apache/polaris/service/storage/StorageConfiguration.java

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
import java.util.function.Supplier;
2929
import org.slf4j.LoggerFactory;
3030
import software.amazon.awssdk.auth.credentials.AwsBasicCredentials;
31+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
32+
import software.amazon.awssdk.auth.credentials.DefaultCredentialsProvider;
3133
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
3234
import software.amazon.awssdk.services.sts.StsClient;
3335
import software.amazon.awssdk.services.sts.StsClientBuilder;
@@ -61,21 +63,31 @@ public interface StorageConfiguration {
6163
Optional<Duration> gcpAccessTokenLifespan();
6264

6365
default Supplier<StsClient> stsClientSupplier() {
66+
return stsClientSupplier(true);
67+
}
68+
69+
default Supplier<StsClient> stsClientSupplier(boolean withCredentials) {
6470
return Suppliers.memoize(
6571
() -> {
6672
StsClientBuilder stsClientBuilder = StsClient.builder();
67-
if (awsAccessKey().isPresent() && awsSecretKey().isPresent()) {
68-
LoggerFactory.getLogger(StorageConfiguration.class)
69-
.warn("Using hard-coded AWS credentials - this is not recommended for production");
70-
StaticCredentialsProvider awsCredentialsProvider =
71-
StaticCredentialsProvider.create(
72-
AwsBasicCredentials.create(awsAccessKey().get(), awsSecretKey().get()));
73-
stsClientBuilder.credentialsProvider(awsCredentialsProvider);
73+
if (withCredentials) {
74+
stsClientBuilder.credentialsProvider(stsCredentials());
7475
}
7576
return stsClientBuilder.build();
7677
});
7778
}
7879

80+
default AwsCredentialsProvider stsCredentials() {
81+
if (awsAccessKey().isPresent() && awsSecretKey().isPresent()) {
82+
LoggerFactory.getLogger(StorageConfiguration.class)
83+
.warn("Using hard-coded AWS credentials - this is not recommended for production");
84+
return StaticCredentialsProvider.create(
85+
AwsBasicCredentials.create(awsAccessKey().get(), awsSecretKey().get()));
86+
} else {
87+
return DefaultCredentialsProvider.create();
88+
}
89+
}
90+
7991
default Supplier<GoogleCredentials> gcpCredentialsSupplier() {
8092
return Suppliers.memoize(
8193
() -> {

service/common/src/test/java/org/apache/polaris/service/storage/StorageConfigurationTest.java

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.mockito.ArgumentCaptor;
3232
import org.mockito.MockedStatic;
3333
import org.mockito.Mockito;
34+
import software.amazon.awssdk.auth.credentials.AwsCredentialsProvider;
3435
import software.amazon.awssdk.auth.credentials.StaticCredentialsProvider;
3536
import software.amazon.awssdk.services.sts.StsClient;
3637
import software.amazon.awssdk.services.sts.StsClientBuilder;
@@ -105,7 +106,7 @@ public void testSingletonStsClientWithStaticCredentials() {
105106
staticMock.when(StsClient::builder).thenReturn(mockBuilder);
106107

107108
StorageConfiguration config = configWithAwsCredentialsAndGcpToken();
108-
Supplier<StsClient> supplier = config.stsClientSupplier();
109+
Supplier<StsClient> supplier = config.stsClientSupplier(true);
109110
StsClient client1 = supplier.get();
110111
StsClient client2 = supplier.get();
111112

@@ -119,6 +120,16 @@ public void testSingletonStsClientWithStaticCredentials() {
119120
}
120121
}
121122

123+
@Test
124+
public void testStaticStsCredentials() {
125+
StorageConfiguration config = configWithAwsCredentialsAndGcpToken();
126+
AwsCredentialsProvider credentialsProvider = config.stsCredentials();
127+
assertThat(credentialsProvider).isInstanceOf(StaticCredentialsProvider.class);
128+
assertThat(credentialsProvider.resolveCredentials().accessKeyId()).isEqualTo(TEST_ACCESS_KEY);
129+
assertThat(credentialsProvider.resolveCredentials().secretAccessKey())
130+
.isEqualTo(TEST_SECRET_KEY);
131+
}
132+
122133
@Test
123134
public void testCreateGcpCredentialsFromStaticToken() {
124135
Supplier<GoogleCredentials> supplier =

service/common/src/testFixtures/java/org/apache/polaris/service/TestServices.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
import java.util.Date;
3030
import java.util.HashMap;
3131
import java.util.Map;
32+
import java.util.Optional;
3233
import java.util.Set;
3334
import org.apache.polaris.core.PolarisCallContext;
3435
import org.apache.polaris.core.PolarisDiagnostics;
@@ -150,6 +151,7 @@ public TestServices build() {
150151
PolarisStorageIntegrationProviderImpl storageIntegrationProvider =
151152
new PolarisStorageIntegrationProviderImpl(
152153
() -> stsClient,
154+
Optional.empty(),
153155
() -> GoogleCredentials.create(new AccessToken(GCP_ACCESS_TOKEN, new Date())));
154156
InMemoryPolarisMetaStoreManagerFactory metaStoreManagerFactory =
155157
new InMemoryPolarisMetaStoreManagerFactory(

0 commit comments

Comments
 (0)