diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/StorageCredentialsVendor.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/StorageCredentialsVendor.java deleted file mode 100644 index d634f28704..0000000000 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/StorageCredentialsVendor.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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.core.storage; - -import jakarta.annotation.Nonnull; -import java.util.Optional; -import java.util.Set; -import org.apache.polaris.core.auth.PolarisPrincipal; -import org.apache.polaris.core.config.RealmConfig; -import org.apache.polaris.core.context.CallContext; -import org.apache.polaris.core.context.RealmContext; -import org.apache.polaris.core.entity.PolarisEntity; -import org.apache.polaris.core.persistence.dao.entity.ScopedCredentialsResult; - -public class StorageCredentialsVendor { - - private final PolarisCredentialVendor polarisCredentialVendor; - private final CallContext callContext; - - public StorageCredentialsVendor( - PolarisCredentialVendor polarisCredentialVendor, CallContext callContext) { - this.polarisCredentialVendor = polarisCredentialVendor; - this.callContext = callContext; - } - - public RealmContext getRealmContext() { - return callContext.getRealmContext(); - } - - public RealmConfig getRealmConfig() { - return callContext.getRealmConfig(); - } - - /** - * Get sub-scoped credentials for an entity against the provided allowed read and write locations. - * - * @param entity the entity - * @param allowListOperation whether to allow LIST operation on the allowedReadLocations and - * allowedWriteLocations - * @param allowedReadLocations a set of allowed to read locations - * @param allowedWriteLocations a set of allowed to write locations - * @param refreshCredentialsEndpoint an optional endpoint to use for refreshing credentials. If - * supported by the storage type it will be returned to the client in the appropriate - * properties. The endpoint may be relative to the base URI and the client is responsible for - * handling the relative path - * @return an enum map containing the scoped credentials - */ - @Nonnull - public ScopedCredentialsResult getSubscopedCredsForEntity( - @Nonnull PolarisEntity entity, - boolean allowListOperation, - @Nonnull Set allowedReadLocations, - @Nonnull Set allowedWriteLocations, - @Nonnull PolarisPrincipal polarisPrincipal, - Optional refreshCredentialsEndpoint) { - return polarisCredentialVendor.getSubscopedCredsForEntity( - callContext.getPolarisCallContext(), - entity.getCatalogId(), - entity.getId(), - entity.getType(), - allowListOperation, - allowedReadLocations, - allowedWriteLocations, - polarisPrincipal, - refreshCredentialsEndpoint); - } -} diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/cache/StorageCredentialCache.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/cache/StorageCredentialCache.java index 9f9cf7c407..8638bcaa9d 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/cache/StorageCredentialCache.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/cache/StorageCredentialCache.java @@ -34,12 +34,12 @@ import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.RealmConfig; -import org.apache.polaris.core.context.RealmContext; +import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityType; import org.apache.polaris.core.persistence.dao.entity.ScopedCredentialsResult; +import org.apache.polaris.core.storage.PolarisCredentialVendor; import org.apache.polaris.core.storage.StorageAccessConfig; -import org.apache.polaris.core.storage.StorageCredentialsVendor; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -50,11 +50,15 @@ public class StorageCredentialCache { private final PolarisDiagnostics diagnostics; private final LoadingCache cache; + private final CallContext callContext; /** Initialize the creds cache */ public StorageCredentialCache( - PolarisDiagnostics diagnostics, StorageCredentialCacheConfig cacheConfig) { + PolarisDiagnostics diagnostics, + StorageCredentialCacheConfig cacheConfig, + CallContext callContext) { this.diagnostics = diagnostics; + this.callContext = callContext; cache = Caffeine.newBuilder() .maximumSize(cacheConfig.maxEntries()) @@ -96,7 +100,7 @@ private long maxCacheDurationMs(RealmConfig realmConfig) { /** * Either get from the cache or generate a new entry for a scoped creds * - * @param storageCredentialsVendor the credential vendor used to generate a new scoped creds if + * @param polarisCredentialVendor the credential vendor used to generate a new scoped creds if * needed * @param polarisEntity the polaris entity that is going to scoped creds * @param allowListOperation whether allow list action on the provided read and write locations @@ -105,26 +109,26 @@ private long maxCacheDurationMs(RealmConfig realmConfig) { * @return the a map of string containing the scoped creds information */ public StorageAccessConfig getOrGenerateSubScopeCreds( - @Nonnull StorageCredentialsVendor storageCredentialsVendor, + @Nonnull PolarisCredentialVendor polarisCredentialVendor, @Nonnull PolarisEntity polarisEntity, boolean allowListOperation, @Nonnull Set allowedReadLocations, @Nonnull Set allowedWriteLocations, @Nonnull PolarisPrincipal polarisPrincipal, Optional refreshCredentialsEndpoint) { - RealmContext realmContext = storageCredentialsVendor.getRealmContext(); - RealmConfig realmConfig = storageCredentialsVendor.getRealmConfig(); if (!isTypeSupported(polarisEntity.getType())) { diagnostics.fail( "entity_type_not_suppported_to_scope_creds", "type={}", polarisEntity.getType()); } boolean includePrincipalNameInSubscopedCredential = - realmConfig.getConfig(FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL); + callContext + .getRealmConfig() + .getConfig(FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL); StorageCredentialCacheKey key = StorageCredentialCacheKey.of( - realmContext.getRealmIdentifier(), + callContext.getRealmContext().getRealmIdentifier(), polarisEntity, allowListOperation, allowedReadLocations, @@ -138,15 +142,18 @@ public StorageAccessConfig getOrGenerateSubScopeCreds( k -> { LOGGER.atDebug().log("StorageCredentialCache::load"); ScopedCredentialsResult scopedCredentialsResult = - storageCredentialsVendor.getSubscopedCredsForEntity( - polarisEntity, + polarisCredentialVendor.getSubscopedCredsForEntity( + callContext.getPolarisCallContext(), + polarisEntity.getCatalogId(), + polarisEntity.getId(), + polarisEntity.getType(), allowListOperation, allowedReadLocations, allowedWriteLocations, polarisPrincipal, refreshCredentialsEndpoint); if (scopedCredentialsResult.isSuccess()) { - long maxCacheDurationMs = maxCacheDurationMs(realmConfig); + long maxCacheDurationMs = maxCacheDurationMs(callContext.getRealmConfig()); return new StorageCredentialCacheEntry( scopedCredentialsResult.getStorageAccessConfig(), maxCacheDurationMs); } diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java index b69eb3ceb6..acd2029204 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/cache/StorageCredentialCacheTest.java @@ -26,48 +26,65 @@ import java.util.Optional; import java.util.Set; import org.apache.iceberg.exceptions.UnprocessableEntityException; +import org.apache.polaris.core.PolarisCallContext; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; import org.apache.polaris.core.config.PolarisConfigurationStore; -import org.apache.polaris.core.config.RealmConfig; -import org.apache.polaris.core.config.RealmConfigImpl; +import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityConstants; import org.apache.polaris.core.entity.PolarisEntitySubType; import org.apache.polaris.core.entity.PolarisEntityType; +import org.apache.polaris.core.persistence.BasePersistence; import org.apache.polaris.core.persistence.dao.entity.BaseResult; import org.apache.polaris.core.persistence.dao.entity.ScopedCredentialsResult; +import org.apache.polaris.core.storage.PolarisCredentialVendor; import org.apache.polaris.core.storage.StorageAccessConfig; import org.apache.polaris.core.storage.StorageAccessProperty; -import org.apache.polaris.core.storage.StorageCredentialsVendor; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.RepeatedTest; import org.junit.jupiter.api.Test; import org.mockito.Mockito; +import org.mockito.stubbing.OngoingStubbing; public class StorageCredentialCacheTest { private final PolarisDiagnostics diagServices = new PolarisDefaultDiagServiceImpl(); private final RealmContext realmContext = () -> "testRealm"; - private final RealmConfig realmConfig = - new RealmConfigImpl(new PolarisConfigurationStore() {}, realmContext); - private final StorageCredentialsVendor storageCredentialsVendor; + private final BasePersistence persistence; private StorageCredentialCache storageCredentialCache; + private final PolarisCredentialVendor polarisCredentialVendor; public StorageCredentialCacheTest() { - storageCredentialsVendor = Mockito.mock(StorageCredentialsVendor.class); - Mockito.when(storageCredentialsVendor.getRealmContext()).thenReturn(realmContext); - Mockito.when(storageCredentialsVendor.getRealmConfig()).thenReturn(realmConfig); + persistence = Mockito.mock(BasePersistence.class); + polarisCredentialVendor = Mockito.mock(PolarisCredentialVendor.class); } @BeforeEach void beforeEach() { StorageCredentialCacheConfig storageCredentialCacheConfig = () -> 10_000; - storageCredentialCache = new StorageCredentialCache(diagServices, storageCredentialCacheConfig); + + CallContext callContext = new PolarisCallContext(realmContext, persistence); + storageCredentialCache = + new StorageCredentialCache(diagServices, storageCredentialCacheConfig, callContext); + } + + OngoingStubbing mockAnyCredentialCall() { + return Mockito.when( + polarisCredentialVendor.getSubscopedCredsForEntity( + Mockito.any(), + Mockito.anyLong(), + Mockito.anyLong(), + Mockito.any(), + Mockito.anyBoolean(), + Mockito.anySet(), + Mockito.anySet(), + Mockito.any(), + Mockito.any())); } @Test @@ -75,15 +92,7 @@ public void testBadResult() { ScopedCredentialsResult badResult = new ScopedCredentialsResult( BaseResult.ReturnStatus.SUBSCOPE_CREDS_ERROR, "extra_error_info"); - Mockito.when( - storageCredentialsVendor.getSubscopedCredsForEntity( - Mockito.any(), - Mockito.anyBoolean(), - Mockito.anySet(), - Mockito.anySet(), - Mockito.any(), - Mockito.any())) - .thenReturn(badResult); + mockAnyCredentialCall().thenReturn(badResult); PolarisEntity polarisEntity = new PolarisEntity( new PolarisBaseEntity( @@ -94,7 +103,7 @@ public void testBadResult() { Assertions.assertThatThrownBy( () -> storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, polarisEntity, true, Set.of("s3://bucket1/path"), @@ -109,14 +118,8 @@ public void testBadResult() { public void testCacheHit() { List mockedScopedCreds = getFakeScopedCreds(3, /* expireSoon= */ false); - Mockito.when( - storageCredentialsVendor.getSubscopedCredsForEntity( - Mockito.any(), - Mockito.anyBoolean(), - Mockito.anySet(), - Mockito.anySet(), - Mockito.any(), - Mockito.any())) + + mockAnyCredentialCall() .thenReturn(mockedScopedCreds.get(0)) .thenReturn(mockedScopedCreds.get(1)) .thenReturn(mockedScopedCreds.get(1)); @@ -128,7 +131,7 @@ public void testCacheHit() { // add an item to the cache storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, polarisEntity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -139,7 +142,7 @@ public void testCacheHit() { // subscope for the same entity and same allowed locations, will hit the cache storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, polarisEntity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -151,7 +154,7 @@ public void testCacheHit() { Optional emptyPrincipal = Optional.empty(); storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, polarisEntity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -164,14 +167,7 @@ public void testCacheHit() { private void testCacheForAnotherPrincipal(boolean hitExpected) { List mockedScopedCreds = getFakeScopedCreds(3, /* expireSoon= */ false); - Mockito.when( - storageCredentialsVendor.getSubscopedCredsForEntity( - Mockito.any(), - Mockito.anyBoolean(), - Mockito.anySet(), - Mockito.anySet(), - Mockito.any(), - Mockito.any())) + mockAnyCredentialCall() .thenReturn(mockedScopedCreds.get(0)) .thenReturn(mockedScopedCreds.get(1)) .thenReturn(mockedScopedCreds.get(1)); @@ -185,7 +181,7 @@ private void testCacheForAnotherPrincipal(boolean hitExpected) { // add an item to the cache storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, polarisEntity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -195,7 +191,7 @@ private void testCacheForAnotherPrincipal(boolean hitExpected) { Assertions.assertThat(storageCredentialCache.getEstimatedSize()).isEqualTo(1); storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, polarisEntity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -212,22 +208,24 @@ public void testCacheHitForAnotherPrincipal() { @Test public void testCacheMissForAnotherPrincipal() { - Mockito.when(storageCredentialsVendor.getRealmConfig()) - .thenReturn( - new RealmConfigImpl( - new PolarisConfigurationStore() { - @SuppressWarnings("unchecked") - @Override - public String getConfiguration(@Nonnull RealmContext ctx, String configName) { - if (configName.equals( - FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL - .key())) { - return "true"; - } - return null; - } - }, - () -> "realm")); + PolarisConfigurationStore store = + new PolarisConfigurationStore() { + @SuppressWarnings("unchecked") + @Override + public String getConfiguration(@Nonnull RealmContext ctx, String configName) { + if (configName.equals( + FeatureConfiguration.INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL.key())) { + return "true"; + } + return null; + } + }; + CallContext callContext = new PolarisCallContext(realmContext, persistence, store); + + StorageCredentialCacheConfig storageCredentialCacheConfig = () -> 10_000; + + storageCredentialCache = + new StorageCredentialCache(diagServices, storageCredentialCacheConfig, callContext); testCacheForAnotherPrincipal(false); } @@ -236,14 +234,7 @@ public String getConfiguration(@Nonnull RealmContext ctx, String configName) { public void testCacheEvict() throws Exception { List mockedScopedCreds = getFakeScopedCreds(3, /* expireSoon= */ true); - Mockito.when( - storageCredentialsVendor.getSubscopedCredsForEntity( - Mockito.any(), - Mockito.anyBoolean(), - Mockito.anySet(), - Mockito.anySet(), - Mockito.any(), - Mockito.any())) + mockAnyCredentialCall() .thenReturn(mockedScopedCreds.get(0)) .thenReturn(mockedScopedCreds.get(1)) .thenReturn(mockedScopedCreds.get(2)); @@ -266,7 +257,7 @@ public void testCacheEvict() throws Exception { // the entry will be evicted immediately because the token is expired storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, polarisEntity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -276,7 +267,7 @@ public void testCacheEvict() throws Exception { Assertions.assertThat(storageCredentialCache.getIfPresent(cacheKey)).isNull(); storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, polarisEntity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -286,7 +277,7 @@ public void testCacheEvict() throws Exception { Assertions.assertThat(storageCredentialCache.getIfPresent(cacheKey)).isNull(); storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, polarisEntity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -300,14 +291,7 @@ public void testCacheEvict() throws Exception { public void testCacheGenerateNewEntries() { List mockedScopedCreds = getFakeScopedCreds(3, /* expireSoon= */ false); - Mockito.when( - storageCredentialsVendor.getSubscopedCredsForEntity( - Mockito.any(), - Mockito.anyBoolean(), - Mockito.anySet(), - Mockito.anySet(), - Mockito.any(), - Mockito.any())) + mockAnyCredentialCall() .thenReturn(mockedScopedCreds.get(0)) .thenReturn(mockedScopedCreds.get(1)) .thenReturn(mockedScopedCreds.get(2)); @@ -317,7 +301,7 @@ public void testCacheGenerateNewEntries() { // different catalog will generate new cache entries for (PolarisEntity entity : entityList) { storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, entity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -335,7 +319,7 @@ public void testCacheGenerateNewEntries() { PolarisBaseEntity updateEntity = new PolarisBaseEntity.Builder(entity).internalPropertiesAsMap(internalMap).build(); storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, PolarisEntity.of(updateEntity), /* allowedListAction= */ true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -347,7 +331,7 @@ public void testCacheGenerateNewEntries() { // allowedListAction changed to different value FALSE, will generate new entry for (PolarisEntity entity : entityList) { storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, entity, /* allowedListAction= */ false, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -359,7 +343,7 @@ public void testCacheGenerateNewEntries() { // different allowedWriteLocations, will generate new entry for (PolarisEntity entity : entityList) { storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, entity, /* allowedListAction= */ false, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -376,7 +360,7 @@ public void testCacheGenerateNewEntries() { PolarisBaseEntity updateEntity = new PolarisBaseEntity.Builder(entity).internalPropertiesAsMap(internalMap).build(); storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, PolarisEntity.of(updateEntity), /* allowedListAction= */ false, Set.of("s3://differentbucket/path", "s3://bucket2/path"), @@ -392,14 +376,7 @@ public void testCacheNotAffectedBy() { List mockedScopedCreds = getFakeScopedCreds(3, /* expireSoon= */ false); - Mockito.when( - storageCredentialsVendor.getSubscopedCredsForEntity( - Mockito.any(), - Mockito.anyBoolean(), - Mockito.anySet(), - Mockito.anySet(), - Mockito.any(), - Mockito.any())) + mockAnyCredentialCall() .thenReturn(mockedScopedCreds.get(0)) .thenReturn(mockedScopedCreds.get(1)) .thenReturn(mockedScopedCreds.get(2)); @@ -407,7 +384,7 @@ public void testCacheNotAffectedBy() { PolarisPrincipal polarisPrincipal = PolarisPrincipal.of("principal", Map.of(), Set.of()); for (PolarisEntity entity : entityList) { storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, entity, true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -420,7 +397,7 @@ public void testCacheNotAffectedBy() { // entity ID does not affect the cache for (PolarisEntity entity : entityList) { storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, new PolarisEntity(new PolarisBaseEntity.Builder(entity).id(1234).build()), true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -433,7 +410,7 @@ public void testCacheNotAffectedBy() { // other property changes does not affect the cache for (PolarisEntity entity : entityList) { storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, new PolarisEntity(new PolarisBaseEntity.Builder(entity).entityVersion(5).build()), true, Set.of("s3://bucket1/path", "s3://bucket2/path"), @@ -445,7 +422,7 @@ public void testCacheNotAffectedBy() { // order of the allowedReadLocations does not affect the cache for (PolarisEntity entity : entityList) { storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, new PolarisEntity(new PolarisBaseEntity.Builder(entity).entityVersion(5).build()), true, Set.of("s3://bucket2/path", "s3://bucket1/path"), @@ -458,7 +435,7 @@ public void testCacheNotAffectedBy() { // order of the allowedWriteLocations does not affect the cache for (PolarisEntity entity : entityList) { storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, new PolarisEntity(new PolarisBaseEntity.Builder(entity).entityVersion(5).build()), true, Set.of("s3://bucket2/path", "s3://bucket1/path"), @@ -537,21 +514,13 @@ public void testExtraProperties() { .put(StorageAccessProperty.AWS_ENDPOINT, "test-endpoint1") .put(StorageAccessProperty.AWS_PATH_STYLE_ACCESS, "true") .build()); - Mockito.when( - storageCredentialsVendor.getSubscopedCredsForEntity( - Mockito.any(), - Mockito.anyBoolean(), - Mockito.anySet(), - Mockito.anySet(), - Mockito.any(), - Mockito.any())) - .thenReturn(properties); + mockAnyCredentialCall().thenReturn(properties); List entityList = getPolarisEntities(); PolarisPrincipal polarisPrincipal = PolarisPrincipal.of("principal", Map.of(), Set.of()); StorageAccessConfig config = storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + polarisCredentialVendor, entityList.get(0), true, Set.of("s3://bucket1/path", "s3://bucket2/path"), diff --git a/runtime/service/src/main/java/org/apache/polaris/service/catalog/io/StorageAccessConfigProvider.java b/runtime/service/src/main/java/org/apache/polaris/service/catalog/io/StorageAccessConfigProvider.java index e49bee99a4..b553bc63a2 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/catalog/io/StorageAccessConfigProvider.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/catalog/io/StorageAccessConfigProvider.java @@ -27,11 +27,12 @@ import org.apache.iceberg.catalog.TableIdentifier; import org.apache.polaris.core.auth.PolarisPrincipal; import org.apache.polaris.core.config.FeatureConfiguration; +import org.apache.polaris.core.context.CallContext; import org.apache.polaris.core.entity.PolarisEntity; +import org.apache.polaris.core.persistence.PolarisMetaStoreManager; import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper; import org.apache.polaris.core.storage.PolarisStorageActions; import org.apache.polaris.core.storage.StorageAccessConfig; -import org.apache.polaris.core.storage.StorageCredentialsVendor; import org.apache.polaris.core.storage.cache.StorageCredentialCache; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -49,17 +50,20 @@ public class StorageAccessConfigProvider { private static final Logger LOGGER = LoggerFactory.getLogger(StorageAccessConfigProvider.class); private final StorageCredentialCache storageCredentialCache; - private final StorageCredentialsVendor storageCredentialsVendor; private final PolarisPrincipal polarisPrincipal; + private final PolarisMetaStoreManager metaStoreManager; + private final CallContext callContext; @Inject public StorageAccessConfigProvider( StorageCredentialCache storageCredentialCache, - StorageCredentialsVendor storageCredentialsVendor, - PolarisPrincipal polarisPrincipal) { + PolarisPrincipal polarisPrincipal, + PolarisMetaStoreManager polarisMetaStoreManager, + CallContext callContext) { this.storageCredentialCache = storageCredentialCache; - this.storageCredentialsVendor = storageCredentialsVendor; this.polarisPrincipal = polarisPrincipal; + this.metaStoreManager = polarisMetaStoreManager; + this.callContext = callContext; } /** @@ -96,7 +100,7 @@ public StorageAccessConfig getStorageAccessConfig( PolarisEntity storageInfoEntity = storageInfo.get(); boolean skipCredentialSubscopingIndirection = - storageCredentialsVendor + callContext .getRealmConfig() .getConfig(FeatureConfiguration.SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION); if (skipCredentialSubscopingIndirection) { @@ -118,7 +122,7 @@ public StorageAccessConfig getStorageAccessConfig( : Set.of(); StorageAccessConfig accessConfig = storageCredentialCache.getOrGenerateSubScopeCreds( - storageCredentialsVendor, + metaStoreManager, storageInfoEntity, allowList, tableLocations, diff --git a/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java b/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java index 0042ac84e2..900a9dd52c 100644 --- a/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java +++ b/runtime/service/src/main/java/org/apache/polaris/service/config/ServiceProducers.java @@ -54,7 +54,6 @@ import org.apache.polaris.core.persistence.resolver.ResolverFactory; import org.apache.polaris.core.secrets.UserSecretsManager; import org.apache.polaris.core.secrets.UserSecretsManagerFactory; -import org.apache.polaris.core.storage.StorageCredentialsVendor; import org.apache.polaris.core.storage.cache.StorageCredentialCache; import org.apache.polaris.core.storage.cache.StorageCredentialCacheConfig; import org.apache.polaris.service.auth.AuthenticationConfiguration; @@ -106,8 +105,10 @@ public Clock clock() { @Produces @ApplicationScoped public StorageCredentialCache storageCredentialCache( - PolarisDiagnostics diagnostics, StorageCredentialCacheConfig storageCredentialCacheConfig) { - return new StorageCredentialCache(diagnostics, storageCredentialCacheConfig); + PolarisDiagnostics diagnostics, + StorageCredentialCacheConfig storageCredentialCacheConfig, + CallContext callContext) { + return new StorageCredentialCache(diagnostics, storageCredentialCacheConfig, callContext); } @Produces @@ -222,13 +223,6 @@ public PolarisMetaStoreManager polarisMetaStoreManager( return metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); } - @Produces - @RequestScoped - public StorageCredentialsVendor storageCredentialsVendor( - PolarisMetaStoreManager metaStoreManager, CallContext callContext) { - return new StorageCredentialsVendor(metaStoreManager, callContext); - } - @Produces public UserSecretsManagerFactory userSecretsManagerFactory( SecretsManagerConfiguration config, diff --git a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java index 59af4b5a6c..02efc89bdb 100644 --- a/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java +++ b/runtime/service/src/testFixtures/java/org/apache/polaris/service/TestServices.java @@ -60,7 +60,6 @@ import org.apache.polaris.core.persistence.resolver.ResolverFactory; import org.apache.polaris.core.secrets.UserSecretsManager; import org.apache.polaris.core.secrets.UserSecretsManagerFactory; -import org.apache.polaris.core.storage.StorageCredentialsVendor; import org.apache.polaris.core.storage.cache.StorageCredentialCache; import org.apache.polaris.core.storage.cache.StorageCredentialCacheConfig; import org.apache.polaris.service.admin.PolarisAdminService; @@ -220,10 +219,6 @@ public TestServices build() { new InMemoryPolarisMetaStoreManagerFactory( clock, diagnostics, storageIntegrationProvider); - StorageCredentialCacheConfig storageCredentialCacheConfig = () -> 10_000; - StorageCredentialCache storageCredentialCache = - new StorageCredentialCache(diagnostics, storageCredentialCacheConfig); - UserSecretsManagerFactory userSecretsManagerFactory = new UnsafeInMemorySecretsManagerFactory(); @@ -232,6 +227,10 @@ public TestServices build() { new PolarisCallContext(realmContext, metaStoreSession, configurationStore); RealmConfig realmConfig = callContext.getRealmConfig(); + StorageCredentialCacheConfig storageCredentialCacheConfig = () -> 10_000; + StorageCredentialCache storageCredentialCache = + new StorageCredentialCache(diagnostics, storageCredentialCacheConfig, callContext); + PolarisMetaStoreManager metaStoreManager = metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); @@ -302,11 +301,9 @@ public String getAuthenticationScheme() { PolarisCredentialManager credentialManager = new DefaultPolarisCredentialManager(realmContext, mockCredentialVendors); - StorageCredentialsVendor storageCredentialsVendor = - new StorageCredentialsVendor(metaStoreManager, callContext); StorageAccessConfigProvider storageAccessConfigProvider = new StorageAccessConfigProvider( - storageCredentialCache, storageCredentialsVendor, principal); + storageCredentialCache, principal, metaStoreManager, callContext); FileIOFactory fileIOFactory = fileIOFactorySupplier.get(); TaskExecutor taskExecutor = Mockito.mock(TaskExecutor.class);