From 8fff253ff2dff57bde2f5d26e9a55e5815a9d119 Mon Sep 17 00:00:00 2001 From: Yufei Gu Date: Thu, 30 Jan 2025 21:54:51 -0800 Subject: [PATCH 1/3] Revert "Copy RealmId when passing it to TaskExecutorImpl (#879)" This reverts commit febe4e83c53c6864fa42114d25aff56786b2057a. --- .../main/java/org/apache/polaris/core/context/RealmId.java | 4 ---- .../org/apache/polaris/service/task/TaskExecutorImpl.java | 5 +---- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/polaris-core/src/main/java/org/apache/polaris/core/context/RealmId.java b/polaris-core/src/main/java/org/apache/polaris/core/context/RealmId.java index 56bedbd3e..98e58bb5e 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/context/RealmId.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/context/RealmId.java @@ -37,10 +37,6 @@ static RealmId newRealmId(String id) { return ImmutableRealmId.of(id); } - static RealmId copyOf(RealmId realmId) { - return ImmutableRealmId.copyOf(realmId); - } - @Value.Parameter @JsonValue String id(); diff --git a/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutorImpl.java b/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutorImpl.java index 68be1b458..04472decd 100644 --- a/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutorImpl.java +++ b/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutorImpl.java @@ -97,10 +97,7 @@ public void addTaskHandler(TaskHandler taskHandler) { */ @Override public void addTaskHandlerContext(long taskEntityId, RealmId realmId) { - // Realm id is a request-scoped component, so we need to copy it to ensure it is available when - // the task is executed, even if the original realm id is no longer available because the - // request has completed. - tryHandleTask(taskEntityId, RealmId.copyOf(realmId), null, 1); + tryHandleTask(taskEntityId, realmId, null, 1); } private @Nonnull CompletableFuture tryHandleTask( From b5c8e631031137b400ed11118bbfd6fa981d4e2a Mon Sep 17 00:00:00 2001 From: Yufei Gu Date: Thu, 30 Jan 2025 22:50:29 -0800 Subject: [PATCH 2/3] Revert 741 --- ...pseLinkPolarisMetaStoreManagerFactory.java | 8 +- ...olarisEclipseLinkMetaStoreSessionImpl.java | 19 +- .../PolarisEclipseLinkPersistenceUnit.java | 27 ++- ...olarisEclipseLinkMetaStoreManagerTest.java | 10 +- polaris-core/build.gradle.kts | 3 - .../core/PolarisConfigurationStore.java | 24 +-- .../polaris/core/auth/PolarisAuthorizer.java | 6 +- .../core/auth/PolarisAuthorizerImpl.java | 11 +- .../{RealmId.java => RealmContext.java} | 25 +-- .../LocalPolarisMetaStoreManagerFactory.java | 94 ++++---- .../persistence/MetaStoreManagerFactory.java | 10 +- .../PolarisMetaStoreManagerImpl.java | 14 +- .../storage/InMemoryStorageIntegration.java | 14 +- .../PolarisStorageConfigurationInfo.java | 8 +- .../storage/PolarisStorageIntegration.java | 12 +- .../aws/AwsCredentialsStorageIntegration.java | 6 +- .../AzureCredentialsStorageIntegration.java | 6 +- .../gcp/GcpCredentialsStorageIntegration.java | 4 +- .../core/persistence/EntityCacheTest.java | 6 +- .../PolarisTreeMapMetaStoreManagerTest.java | 3 +- .../core/persistence/ResolverTest.java | 6 +- .../InMemoryStorageIntegrationTest.java | 16 +- .../PolarisConfigurationStoreTest.java | 12 +- .../AwsCredentialsStorageIntegrationTest.java | 4 +- ...AzureCredentialStorageIntegrationTest.java | 29 ++- .../GcpCredentialsStorageIntegrationTest.java | 16 +- .../quarkus/config/QuarkusProducers.java | 38 ++-- .../QuarkusRealmContextConfiguration.java | 5 +- .../logging/QuarkusLoggingMDCFilter.java | 8 +- .../QuarkusValueExpressionResolver.java | 6 +- .../metrics/RealmIdTagContributor.java | 14 +- .../quarkus/task/QuarkusTaskExecutorImpl.java | 9 +- .../TimedApplicationEventListenerTest.java | 2 +- .../quarkus/admin/ManagementServiceTest.java | 11 +- .../admin/PolarisAdminServiceAuthzTest.java | 2 +- .../quarkus/admin/PolarisAuthzTestBase.java | 16 +- .../admin/PolarisOverlappingCatalogTest.java | 2 +- .../admin/PolarisOverlappingTableTest.java | 9 +- .../service/quarkus/auth/TokenUtils.java | 2 +- .../catalog/BasePolarisCatalogTest.java | 45 ++-- .../catalog/BasePolarisCatalogViewTest.java | 15 +- ...PolarisCatalogHandlerWrapperAuthzTest.java | 2 +- .../service/quarkus/catalog/TestUtil.java | 201 ++++++++++++++++++ .../catalog/io/FileIOExceptionsTest.java | 6 +- .../config/DefaultConfigurationStoreTest.java | 36 ++-- .../service/quarkus/ratelimiter/TestUtil.java | 2 +- .../ManifestFileCleanupTaskHandlerTest.java | 18 +- .../task/TableCleanupTaskHandlerTest.java | 34 +-- .../test/PolarisIntegrationTestFixture.java | 10 +- server-templates/api.mustache | 6 +- server-templates/apiService.mustache | 4 +- server-templates/apiServiceImpl.mustache | 4 +- .../service/admin/PolarisAdminService.java | 33 +-- .../service/admin/PolarisServiceImpl.java | 147 ++++++------- .../auth/BasePolarisAuthenticator.java | 8 +- .../auth/DefaultActiveRolesProvider.java | 8 +- .../service/auth/DefaultOAuth2ApiService.java | 6 +- .../service/auth/JWTRSAKeyPairFactory.java | 8 +- .../service/auth/JWTSymmetricKeyFactory.java | 8 +- .../service/auth/NoneTokenBrokerFactory.java | 4 +- .../service/auth/TestOAuth2ApiService.java | 6 +- .../service/auth/TokenBrokerFactory.java | 4 +- .../service/catalog/BasePolarisCatalog.java | 40 ++-- .../catalog/IcebergCatalogAdapter.java | 63 +++--- .../catalog/PolarisCatalogHandlerWrapper.java | 29 +-- .../catalog/io/DefaultFileIOFactory.java | 12 +- .../service/catalog/io/FileIOFactory.java | 6 +- .../service/catalog/io/FileIOUtil.java | 6 +- .../io/WasbTranslatingFileIOFactory.java | 6 +- .../config/DefaultConfigurationStore.java | 10 +- .../config/RealmEntityManagerFactory.java | 8 +- ....java => DefaultRealmContextResolver.java} | 10 +- ...esolver.java => RealmContextResolver.java} | 6 +- ...ver.java => TestRealmContextResolver.java} | 15 +- ...nMemoryPolarisMetaStoreManagerFactory.java | 28 ++- .../DefaultTokenBucketFactory.java | 7 +- .../RealmTokenBucketRateLimiter.java | 11 +- .../ratelimiter/TokenBucketFactory.java | 4 +- ...PolarisStorageIntegrationProviderImpl.java | 6 +- .../task/ManifestFileCleanupTaskHandler.java | 10 +- .../service/task/TableCleanupTaskHandler.java | 22 +- .../polaris/service/task/TaskExecutor.java | 4 +- .../service/task/TaskExecutorImpl.java | 27 +-- .../service/task/TaskFileIOSupplier.java | 8 +- .../polaris/service/task/TaskHandler.java | 4 +- .../service/catalog/io/FileIOFactoryTest.java | 33 +-- .../apache/polaris/service/TestServices.java | 23 +- .../catalog/io/MeasuredFileIOFactory.java | 6 +- .../configuring-polaris-for-production.md | 192 ++++++----------- 89 files changed, 951 insertions(+), 777 deletions(-) rename polaris-core/src/main/java/org/apache/polaris/core/context/{RealmId.java => RealmContext.java} (56%) create mode 100644 quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/TestUtil.java rename service/common/src/main/java/org/apache/polaris/service/context/{DefaultRealmIdResolver.java => DefaultRealmContextResolver.java} (85%) rename service/common/src/main/java/org/apache/polaris/service/context/{RealmIdResolver.java => RealmContextResolver.java} (88%) rename service/common/src/main/java/org/apache/polaris/service/context/{TestRealmIdResolver.java => TestRealmContextResolver.java} (89%) diff --git a/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/EclipseLinkPolarisMetaStoreManagerFactory.java b/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/EclipseLinkPolarisMetaStoreManagerFactory.java index 851320eb6..5e27dddc2 100644 --- a/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/EclipseLinkPolarisMetaStoreManagerFactory.java +++ b/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/EclipseLinkPolarisMetaStoreManagerFactory.java @@ -27,7 +27,7 @@ import java.time.Clock; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.LocalPolarisMetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisCredentialsBootstrap; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; @@ -71,16 +71,16 @@ protected PolarisEclipseLinkStore createBackingStore(@Nonnull PolarisDiagnostics @Override protected PolarisMetaStoreSession createMetaStoreSession( @Nonnull PolarisEclipseLinkStore store, - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nullable PolarisCredentialsBootstrap credentialsBootstrap, @Nonnull PolarisDiagnostics diagnostics) { return new PolarisEclipseLinkMetaStoreSessionImpl( store, storageIntegrationProvider, - realmId, + realmContext, configurationFile(), persistenceUnitName(), - secretsGenerator(realmId, credentialsBootstrap), + secretsGenerator(realmContext, credentialsBootstrap), diagnostics); } diff --git a/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java b/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java index 20ad64692..6a5670137 100644 --- a/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java +++ b/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreSessionImpl.java @@ -38,7 +38,7 @@ import java.util.function.Supplier; import java.util.stream.Collectors; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisChangeTrackingVersions; import org.apache.polaris.core.entity.PolarisEntitiesActiveKey; @@ -88,21 +88,22 @@ public class PolarisEclipseLinkMetaStoreSessionImpl implements PolarisMetaStoreS * * @param store Backing store of EclipseLink implementation * @param storageIntegrationProvider Storage integration provider - * @param realmId Realm context used to communicate with different database. + * @param realmContext Realm context used to communicate with different database. * @param confFile Optional EclipseLink configuration file. Default to 'META-INF/persistence.xml'. * @param persistenceUnitName Optional persistence-unit name in confFile. Default to 'polaris'. */ public PolarisEclipseLinkMetaStoreSessionImpl( @Nonnull PolarisEclipseLinkStore store, @Nonnull PolarisStorageIntegrationProvider storageIntegrationProvider, - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nullable String confFile, @Nullable String persistenceUnitName, @Nonnull PrincipalSecretsGenerator secretsGenerator, @Nonnull PolarisDiagnostics diagnostics) { this.diagnostics = diagnostics; - LOGGER.debug("Creating EclipseLink Meta Store Session for realm {}", realmId.id()); - emf = createEntityManagerFactory(realmId, confFile, persistenceUnitName); + LOGGER.debug( + "Creating EclipseLink Meta Store Session for realm {}", realmContext.getRealmIdentifier()); + emf = createEntityManagerFactory(realmContext, confFile, persistenceUnitName); // init store this.store = store; @@ -120,8 +121,10 @@ public PolarisEclipseLinkMetaStoreSessionImpl( * realm. */ private EntityManagerFactory createEntityManagerFactory( - @Nonnull RealmId realmId, @Nullable String confFile, @Nullable String persistenceUnitName) { - String realm = realmId.id(); + @Nonnull RealmContext realmContext, + @Nullable String confFile, + @Nullable String persistenceUnitName) { + String realm = realmContext.getRealmIdentifier(); return realmFactories.computeIfAbsent( realm, key -> { @@ -129,7 +132,7 @@ private EntityManagerFactory createEntityManagerFactory( PolarisEclipseLinkPersistenceUnit persistenceUnit = PolarisEclipseLinkPersistenceUnit.locatePersistenceUnit( confFile, persistenceUnitName); - return persistenceUnit.createEntityManagerFactory(realmId); + return persistenceUnit.createEntityManagerFactory(realmContext); } catch (IOException e) { throw new UncheckedIOException(e); } diff --git a/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkPersistenceUnit.java b/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkPersistenceUnit.java index 20ac6795f..7ea8749fd 100644 --- a/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkPersistenceUnit.java +++ b/extension/persistence/eclipselink/src/main/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkPersistenceUnit.java @@ -43,7 +43,7 @@ import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpressionException; import javax.xml.xpath.XPathFactory; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.extension.persistence.impl.eclipselink.PolarisEclipseLinkPersistenceUnit.ClasspathResourcePolarisEclipseLinkPersistenceUnit; import org.apache.polaris.extension.persistence.impl.eclipselink.PolarisEclipseLinkPersistenceUnit.FileSystemPolarisEclipseLinkPersistenceUnit; import org.apache.polaris.extension.persistence.impl.eclipselink.PolarisEclipseLinkPersistenceUnit.JarFilePolarisEclipseLinkPersistenceUnit; @@ -57,15 +57,16 @@ sealed interface PolarisEclipseLinkPersistenceUnit FileSystemPolarisEclipseLinkPersistenceUnit, JarFilePolarisEclipseLinkPersistenceUnit { - EntityManagerFactory createEntityManagerFactory(RealmId realmId) throws IOException; + EntityManagerFactory createEntityManagerFactory(RealmContext realmContext) throws IOException; record ClasspathResourcePolarisEclipseLinkPersistenceUnit( URL resource, String resourceName, String persistenceUnitName) implements PolarisEclipseLinkPersistenceUnit { @Override - public EntityManagerFactory createEntityManagerFactory(RealmId realmId) throws IOException { - Map properties = loadProperties(resource, persistenceUnitName, realmId); + public EntityManagerFactory createEntityManagerFactory(RealmContext realmContext) + throws IOException { + Map properties = loadProperties(resource, persistenceUnitName, realmContext); properties.put(ECLIPSELINK_PERSISTENCE_XML, resourceName); return Persistence.createEntityManagerFactory(persistenceUnitName, properties); } @@ -75,9 +76,10 @@ record FileSystemPolarisEclipseLinkPersistenceUnit(Path path, String persistence implements PolarisEclipseLinkPersistenceUnit { @Override - public EntityManagerFactory createEntityManagerFactory(RealmId realmId) throws IOException { + public EntityManagerFactory createEntityManagerFactory(RealmContext realmContext) + throws IOException { Map properties = - loadProperties(path.toUri().toURL(), persistenceUnitName, realmId); + loadProperties(path.toUri().toURL(), persistenceUnitName, realmContext); Path archiveDirectory = path.getParent(); String descriptorPath = archiveDirectory.getParent().relativize(path).toString(); properties.put(ECLIPSELINK_PERSISTENCE_XML, descriptorPath); @@ -99,8 +101,9 @@ record JarFilePolarisEclipseLinkPersistenceUnit( implements PolarisEclipseLinkPersistenceUnit { @Override - public EntityManagerFactory createEntityManagerFactory(RealmId realmId) throws IOException { - Map properties = loadProperties(confUrl, persistenceUnitName, realmId); + public EntityManagerFactory createEntityManagerFactory(RealmContext realmContext) + throws IOException { + Map properties = loadProperties(confUrl, persistenceUnitName, realmContext); properties.put(ECLIPSELINK_PERSISTENCE_XML, descriptorPath); ClassLoader prevClassLoader = Thread.currentThread().getContextClassLoader(); try (URLClassLoader currentClassLoader = @@ -180,7 +183,9 @@ private static URL classpathResource(String resourceName) throws IOException { /** Load the persistence unit properties from a given configuration file */ private static Map loadProperties( - @Nonnull URL confFile, @Nonnull String persistenceUnitName, @Nonnull RealmId realmId) + @Nonnull URL confFile, + @Nonnull String persistenceUnitName, + @Nonnull RealmContext realmContext) throws IOException { try (InputStream input = confFile.openStream()) { DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); @@ -200,7 +205,9 @@ private static Map loadProperties( } // Replace database name in JDBC URL with realm if (properties.containsKey(JDBC_URL)) { - properties.put(JDBC_URL, properties.get(JDBC_URL).replace("{realm}", realmId.id())); + properties.put( + JDBC_URL, + properties.get(JDBC_URL).replace("{realm}", realmContext.getRealmIdentifier())); } return properties; } catch (XPathExpressionException diff --git a/extension/persistence/eclipselink/src/test/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreManagerTest.java b/extension/persistence/eclipselink/src/test/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreManagerTest.java index 3458bdf7a..147f76e22 100644 --- a/extension/persistence/eclipselink/src/test/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreManagerTest.java +++ b/extension/persistence/eclipselink/src/test/java/org/apache/polaris/extension/persistence/impl/eclipselink/PolarisEclipseLinkMetaStoreManagerTest.java @@ -38,7 +38,7 @@ import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisPrincipalSecrets; import org.apache.polaris.core.persistence.BasePolarisMetaStoreManagerTest; import org.apache.polaris.core.persistence.PolarisMetaStoreManagerImpl; @@ -101,13 +101,13 @@ static void deleteConfFiles() throws IOException { protected PolarisTestMetaStoreManager createPolarisTestMetaStoreManager() { PolarisDiagnostics diagServices = new PolarisDefaultDiagServiceImpl(); PolarisEclipseLinkStore store = new PolarisEclipseLinkStore(diagServices); - RealmId realmId = RealmId.newRealmId("realm"); + RealmContext realmContext = () -> "realm"; PolarisMetaStoreSession session = new PolarisEclipseLinkMetaStoreSessionImpl( - store, Mockito.mock(), realmId, null, "polaris", RANDOM_SECRETS, diagServices); + store, Mockito.mock(), realmContext, null, "polaris", RANDOM_SECRETS, diagServices); return new PolarisTestMetaStoreManager( new PolarisMetaStoreManagerImpl( - realmId, + realmContext, diagServices, new PolarisConfigurationStore() {}, timeSource.withZone(ZoneId.systemDefault())), @@ -128,7 +128,7 @@ void testCreateStoreSession(String confFile, boolean success) { new PolarisEclipseLinkMetaStoreSessionImpl( store, Mockito.mock(), - RealmId.newRealmId("realm"), + () -> "realm", confFile, "polaris", RANDOM_SECRETS, diff --git a/polaris-core/build.gradle.kts b/polaris-core/build.gradle.kts index b611509f8..a346a74ef 100644 --- a/polaris-core/build.gradle.kts +++ b/polaris-core/build.gradle.kts @@ -44,9 +44,6 @@ dependencies { compileOnly(libs.jetbrains.annotations) compileOnly(libs.spotbugs.annotations) - compileOnly(project(":polaris-immutables")) - annotationProcessor(project(":polaris-immutables", configuration = "processor")) - constraints { implementation("org.xerial.snappy:snappy-java:1.1.10.7") { because("Vulnerability detected in 1.1.8.2") diff --git a/polaris-core/src/main/java/org/apache/polaris/core/PolarisConfigurationStore.java b/polaris-core/src/main/java/org/apache/polaris/core/PolarisConfigurationStore.java index 9e7f43c2e..369821c08 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/PolarisConfigurationStore.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/PolarisConfigurationStore.java @@ -23,7 +23,7 @@ import jakarta.annotation.Nullable; import java.util.ArrayList; import java.util.List; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; /** @@ -35,11 +35,11 @@ public interface PolarisConfigurationStore { * Retrieve the current value for a configuration key. May be null if not set. * * @param the type of the configuration value - * @param realmId the realm context to check for overrides; may be null. + * @param realmContext the realm context to check for overrides; may be null. * @param configName the name of the configuration key to check * @return the current value set for the configuration key or null if not set */ - default @Nullable T getConfiguration(@Nullable RealmId realmId, String configName) { + default @Nullable T getConfiguration(@Nullable RealmContext realmContext, String configName) { return null; } @@ -48,15 +48,15 @@ public interface PolarisConfigurationStore { * value. * * @param the type of the configuration value - * @param realmId the realm context to check for overrides; may be null. + * @param realmContext the realm context to check for overrides; may be null. * @param configName the name of the configuration key to check * @param defaultValue the default value if the configuration key has no value * @return the current value or the supplied default value */ default @Nonnull T getConfiguration( - @Nullable RealmId realmId, String configName, @Nonnull T defaultValue) { + @Nullable RealmContext realmContext, String configName, @Nonnull T defaultValue) { Preconditions.checkNotNull(defaultValue, "Cannot pass null as a default value"); - T configValue = getConfiguration(realmId, configName); + T configValue = getConfiguration(realmContext, configName); return configValue != null ? configValue : defaultValue; } @@ -88,13 +88,13 @@ public interface PolarisConfigurationStore { * Retrieve the current value for a configuration. * * @param the type of the configuration value - * @param realmId the realm context to check for overrides; may be null. + * @param realmContext the realm context to check for overrides; may be null. * @param config the configuration to load * @return the current value set for the configuration key or null if not set */ default @Nonnull T getConfiguration( - @Nullable RealmId realmId, PolarisConfiguration config) { - T result = getConfiguration(realmId, config.key, config.defaultValue); + @Nullable RealmContext realmContext, PolarisConfiguration config) { + T result = getConfiguration(realmContext, config.key, config.defaultValue); return tryCast(config, result); } @@ -103,20 +103,20 @@ public interface PolarisConfigurationStore { * present. * * @param the type of the configuration value - * @param realmId the realm context to check for overrides; may be null. + * @param realmContext the realm context to check for overrides; may be null. * @param catalogEntity the catalog to check for an override * @param config the configuration to load * @return the current value set for the configuration key or null if not set */ default @Nonnull T getConfiguration( - @Nullable RealmId realmId, + @Nullable RealmContext realmContext, @Nonnull CatalogEntity catalogEntity, PolarisConfiguration config) { if (config.hasCatalogConfig() && catalogEntity.getPropertiesAsMap().containsKey(config.catalogConfig())) { return tryCast(config, catalogEntity.getPropertiesAsMap().get(config.catalogConfig())); } else { - return getConfiguration(realmId, config); + return getConfiguration(realmContext, config); } } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizer.java b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizer.java index 245f07b62..adb5cf7b8 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizer.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizer.java @@ -22,7 +22,7 @@ import jakarta.annotation.Nullable; import java.util.List; import java.util.Set; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper; @@ -30,7 +30,7 @@ public interface PolarisAuthorizer { void authorizeOrThrow( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull AuthenticatedPolarisPrincipal authenticatedPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, @@ -38,7 +38,7 @@ void authorizeOrThrow( @Nullable PolarisResolvedPathWrapper secondary); void authorizeOrThrow( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull AuthenticatedPolarisPrincipal authenticatedPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, diff --git a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizerImpl.java b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizerImpl.java index 199b73d60..a8410584f 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizerImpl.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizerImpl.java @@ -100,7 +100,7 @@ import org.apache.iceberg.exceptions.ForbiddenException; import org.apache.polaris.core.PolarisConfiguration; import org.apache.polaris.core.PolarisConfigurationStore; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityConstants; import org.apache.polaris.core.entity.PolarisEntityCore; @@ -487,14 +487,14 @@ public boolean matchesOrIsSubsumedBy( @Override public void authorizeOrThrow( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull AuthenticatedPolarisPrincipal authenticatedPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, @Nullable PolarisResolvedPathWrapper target, @Nullable PolarisResolvedPathWrapper secondary) { authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, activatedEntities, authzOp, @@ -504,7 +504,7 @@ public void authorizeOrThrow( @Override public void authorizeOrThrow( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull AuthenticatedPolarisPrincipal authenticatedPrincipal, @Nonnull Set activatedEntities, @Nonnull PolarisAuthorizableOperation authzOp, @@ -512,7 +512,8 @@ public void authorizeOrThrow( @Nullable List secondaries) { boolean enforceCredentialRotationRequiredState = featureConfig.getConfiguration( - realmId, PolarisConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING); + realmContext, + PolarisConfiguration.ENFORCE_PRINCIPAL_CREDENTIAL_ROTATION_REQUIRED_CHECKING); if (enforceCredentialRotationRequiredState && authenticatedPrincipal .getPrincipalEntity() diff --git a/polaris-core/src/main/java/org/apache/polaris/core/context/RealmId.java b/polaris-core/src/main/java/org/apache/polaris/core/context/RealmContext.java similarity index 56% rename from polaris-core/src/main/java/org/apache/polaris/core/context/RealmId.java rename to polaris-core/src/main/java/org/apache/polaris/core/context/RealmContext.java index 98e58bb5e..cce27972d 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/context/RealmId.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/context/RealmContext.java @@ -18,26 +18,17 @@ */ package org.apache.polaris.core.context; -import com.fasterxml.jackson.annotation.JsonValue; -import com.fasterxml.jackson.databind.annotation.JsonDeserialize; -import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import org.apache.polaris.immutables.PolarisImmutable; -import org.immutables.value.Value; - /** - * Represents the ID of the realm used in a REST request associated with routing to independent and - * isolated "universes". + * Represents the elements of a REST request associated with routing to independent and isolated + * "universes". This may include properties such as region, deployment environment (e.g. dev, qa, + * prod), and/or account. */ -@PolarisImmutable -@JsonSerialize(as = ImmutableRealmId.class) -@JsonDeserialize(as = ImmutableRealmId.class) -public interface RealmId { +public interface RealmContext { - static RealmId newRealmId(String id) { - return ImmutableRealmId.of(id); + static RealmContext copyOf(RealmContext original) { + String realmIdentifier = original.getRealmIdentifier(); + return () -> realmIdentifier; } - @Value.Parameter - @JsonValue - String id(); + String getRealmIdentifier(); } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/persistence/LocalPolarisMetaStoreManagerFactory.java b/polaris-core/src/main/java/org/apache/polaris/core/persistence/LocalPolarisMetaStoreManagerFactory.java index 2d64e45b0..59d326dd0 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/persistence/LocalPolarisMetaStoreManagerFactory.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/persistence/LocalPolarisMetaStoreManagerFactory.java @@ -28,7 +28,7 @@ import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.PolarisSecretsManager.PrincipalSecretsResult; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityConstants; import org.apache.polaris.core.entity.PolarisEntitySubType; @@ -70,29 +70,31 @@ protected LocalPolarisMetaStoreManagerFactory( protected abstract PolarisMetaStoreSession createMetaStoreSession( @Nonnull StoreType store, - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nullable PolarisCredentialsBootstrap credentialsBootstrap, @Nonnull PolarisDiagnostics diagnostics); protected PrincipalSecretsGenerator secretsGenerator( - RealmId realmId, @Nullable PolarisCredentialsBootstrap credentialsBootstrap) { + RealmContext realmContext, @Nullable PolarisCredentialsBootstrap credentialsBootstrap) { if (credentialsBootstrap != null) { - return PrincipalSecretsGenerator.bootstrap(realmId.id(), credentialsBootstrap); + return PrincipalSecretsGenerator.bootstrap( + realmContext.getRealmIdentifier(), credentialsBootstrap); } else { return PrincipalSecretsGenerator.RANDOM_SECRETS; } } private void initializeForRealm( - RealmId realmId, PolarisCredentialsBootstrap credentialsBootstrap) { + RealmContext realmContext, PolarisCredentialsBootstrap credentialsBootstrap) { final StoreType backingStore = createBackingStore(diagnostics); sessionSupplierMap.put( - realmId.id(), - () -> createMetaStoreSession(backingStore, realmId, credentialsBootstrap, diagnostics)); + realmContext.getRealmIdentifier(), + () -> + createMetaStoreSession(backingStore, realmContext, credentialsBootstrap, diagnostics)); PolarisMetaStoreManager metaStoreManager = - new PolarisMetaStoreManagerImpl(realmId, diagnostics, configurationStore, clock); - metaStoreManagerMap.put(realmId.id(), metaStoreManager); + new PolarisMetaStoreManagerImpl(realmContext, diagnostics, configurationStore, clock); + metaStoreManagerMap.put(realmContext.getRealmIdentifier(), metaStoreManager); } @Override @@ -101,13 +103,13 @@ public synchronized Map bootstrapRealms( Map results = new HashMap<>(); for (String realm : realms) { - RealmId realmId = RealmId.newRealmId(realm); - if (!metaStoreManagerMap.containsKey(realmId.id())) { - initializeForRealm(realmId, credentialsBootstrap); + RealmContext realmContext = () -> realm; + if (!metaStoreManagerMap.containsKey(realmContext.getRealmIdentifier())) { + initializeForRealm(realmContext, credentialsBootstrap); PrincipalSecretsResult secretsResult = bootstrapServiceAndCreatePolarisPrincipalForRealm( - realmId, metaStoreManagerMap.get(realmId.id())); - results.put(realmId.id(), secretsResult); + realmContext, metaStoreManagerMap.get(realmContext.getRealmIdentifier())); + results.put(realmContext.getRealmIdentifier(), secretsResult); } } @@ -117,9 +119,8 @@ public synchronized Map bootstrapRealms( @Override public void purgeRealms(List realms) { for (String realm : realms) { - PolarisMetaStoreManager metaStoreManager = - getOrCreateMetaStoreManager(RealmId.newRealmId(realm)); - PolarisMetaStoreSession session = getOrCreateSessionSupplier(RealmId.newRealmId(realm)).get(); + PolarisMetaStoreManager metaStoreManager = getOrCreateMetaStoreManager(() -> realm); + PolarisMetaStoreSession session = getOrCreateSessionSupplier(() -> realm).get(); metaStoreManager.purge(session); @@ -130,44 +131,51 @@ public void purgeRealms(List realms) { } @Override - public synchronized PolarisMetaStoreManager getOrCreateMetaStoreManager(RealmId realmId) { - if (!metaStoreManagerMap.containsKey(realmId.id())) { - initializeForRealm(realmId, null); - checkPolarisServiceBootstrappedForRealm(realmId, metaStoreManagerMap.get(realmId.id())); + public synchronized PolarisMetaStoreManager getOrCreateMetaStoreManager( + RealmContext realmContext) { + if (!metaStoreManagerMap.containsKey(realmContext.getRealmIdentifier())) { + initializeForRealm(realmContext, null); + checkPolarisServiceBootstrappedForRealm( + realmContext, metaStoreManagerMap.get(realmContext.getRealmIdentifier())); } - return metaStoreManagerMap.get(realmId.id()); + return metaStoreManagerMap.get(realmContext.getRealmIdentifier()); } @Override public synchronized Supplier getOrCreateSessionSupplier( - RealmId realmId) { - if (!sessionSupplierMap.containsKey(realmId.id())) { - initializeForRealm(realmId, null); - checkPolarisServiceBootstrappedForRealm(realmId, metaStoreManagerMap.get(realmId.id())); + RealmContext realmContext) { + if (!sessionSupplierMap.containsKey(realmContext.getRealmIdentifier())) { + initializeForRealm(realmContext, null); + checkPolarisServiceBootstrappedForRealm( + realmContext, metaStoreManagerMap.get(realmContext.getRealmIdentifier())); } else { - checkPolarisServiceBootstrappedForRealm(realmId, metaStoreManagerMap.get(realmId.id())); + checkPolarisServiceBootstrappedForRealm( + realmContext, metaStoreManagerMap.get(realmContext.getRealmIdentifier())); } - return sessionSupplierMap.get(realmId.id()); + return sessionSupplierMap.get(realmContext.getRealmIdentifier()); } @Override - public synchronized StorageCredentialCache getOrCreateStorageCredentialCache(RealmId realmId) { - if (!storageCredentialCacheMap.containsKey(realmId.id())) { + public synchronized StorageCredentialCache getOrCreateStorageCredentialCache( + RealmContext realmContext) { + if (!storageCredentialCacheMap.containsKey(realmContext.getRealmIdentifier())) { storageCredentialCacheMap.put( - realmId.id(), new StorageCredentialCache(diagnostics, configurationStore)); + realmContext.getRealmIdentifier(), + new StorageCredentialCache(diagnostics, configurationStore)); } - return storageCredentialCacheMap.get(realmId.id()); + return storageCredentialCacheMap.get(realmContext.getRealmIdentifier()); } @Override - public synchronized EntityCache getOrCreateEntityCache(RealmId realmId) { - if (!entityCacheMap.containsKey(realmId.id())) { - PolarisMetaStoreManager metaStoreManager = getOrCreateMetaStoreManager(realmId); - entityCacheMap.put(realmId.id(), new EntityCache(metaStoreManager, diagnostics)); + public synchronized EntityCache getOrCreateEntityCache(RealmContext realmContext) { + if (!entityCacheMap.containsKey(realmContext.getRealmIdentifier())) { + PolarisMetaStoreManager metaStoreManager = getOrCreateMetaStoreManager(realmContext); + entityCacheMap.put( + realmContext.getRealmIdentifier(), new EntityCache(metaStoreManager, diagnostics)); } - return entityCacheMap.get(realmId.id()); + return entityCacheMap.get(realmContext.getRealmIdentifier()); } /** @@ -176,8 +184,9 @@ public synchronized EntityCache getOrCreateEntityCache(RealmId realmId) { * credentials and print them to stdout */ private PrincipalSecretsResult bootstrapServiceAndCreatePolarisPrincipalForRealm( - RealmId realmId, PolarisMetaStoreManager metaStoreManager) { - PolarisMetaStoreSession metaStoreSession = sessionSupplierMap.get(realmId.id()).get(); + RealmContext realmContext, PolarisMetaStoreManager metaStoreManager) { + PolarisMetaStoreSession metaStoreSession = + sessionSupplierMap.get(realmContext.getRealmIdentifier()).get(); PolarisMetaStoreManager.EntityResult preliminaryRootPrincipalLookup = metaStoreManager.readEntityByName( @@ -229,9 +238,10 @@ private PrincipalSecretsResult bootstrapServiceAndCreatePolarisPrincipalForRealm * entities */ private void checkPolarisServiceBootstrappedForRealm( - RealmId realmId, PolarisMetaStoreManager metaStoreManager) { + RealmContext realmContext, PolarisMetaStoreManager metaStoreManager) { - PolarisMetaStoreSession metaStoreSession = sessionSupplierMap.get(realmId.id()).get(); + PolarisMetaStoreSession metaStoreSession = + sessionSupplierMap.get(realmContext.getRealmIdentifier()).get(); PolarisMetaStoreManager.EntityResult rootPrincipalLookup = metaStoreManager.readEntityByName( @@ -244,7 +254,7 @@ private void checkPolarisServiceBootstrappedForRealm( if (!rootPrincipalLookup.isSuccess()) { LOGGER.error( "\n\n Realm {} is not bootstrapped, could not load root principal. Please run Bootstrap command. \n\n", - realmId.id()); + realmContext.getRealmIdentifier()); throw new IllegalStateException( "Realm is not bootstrapped, please run server in bootstrap mode."); } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/persistence/MetaStoreManagerFactory.java b/polaris-core/src/main/java/org/apache/polaris/core/persistence/MetaStoreManagerFactory.java index bcf87bd6e..4e37f034c 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/persistence/MetaStoreManagerFactory.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/persistence/MetaStoreManagerFactory.java @@ -22,20 +22,20 @@ import java.util.Map; import java.util.function.Supplier; import org.apache.polaris.core.auth.PolarisSecretsManager.PrincipalSecretsResult; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.cache.EntityCache; import org.apache.polaris.core.storage.cache.StorageCredentialCache; /** Configuration interface for configuring the {@link PolarisMetaStoreManager}. */ public interface MetaStoreManagerFactory { - PolarisMetaStoreManager getOrCreateMetaStoreManager(RealmId realmId); + PolarisMetaStoreManager getOrCreateMetaStoreManager(RealmContext realmContext); - Supplier getOrCreateSessionSupplier(RealmId realmId); + Supplier getOrCreateSessionSupplier(RealmContext realmContext); - StorageCredentialCache getOrCreateStorageCredentialCache(RealmId realmId); + StorageCredentialCache getOrCreateStorageCredentialCache(RealmContext realmContext); - EntityCache getOrCreateEntityCache(RealmId realmId); + EntityCache getOrCreateEntityCache(RealmContext realmContext); Map bootstrapRealms( List realms, PolarisCredentialsBootstrap credentialsBootstrap); diff --git a/polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisMetaStoreManagerImpl.java b/polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisMetaStoreManagerImpl.java index 83979d4ae..3566fc5db 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisMetaStoreManagerImpl.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisMetaStoreManagerImpl.java @@ -38,7 +38,7 @@ import java.util.stream.Collectors; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.AsyncTaskType; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisChangeTrackingVersions; @@ -76,17 +76,17 @@ public class PolarisMetaStoreManagerImpl implements PolarisMetaStoreManager { /** use synchronous drop for entities */ private static final boolean USE_SYNCHRONOUS_DROP = true; - private final RealmId realmId; + private final RealmContext realmContext; private final PolarisDiagnostics diagnostics; private final PolarisConfigurationStore configurationStore; private final Clock clock; public PolarisMetaStoreManagerImpl( - RealmId realmId, + RealmContext realmContext, PolarisDiagnostics diagnostics, PolarisConfigurationStore configurationStore, Clock clock) { - this.realmId = realmId; + this.realmContext = realmContext; this.diagnostics = diagnostics; this.configurationStore = configurationStore; this.clock = clock; @@ -1815,7 +1815,7 @@ private PolarisEntityResolver resolveSecurableToRoleGrant( PolarisObjectMapperUtil.parseTaskState(entity); long taskAgeTimeout = configurationStore.getConfiguration( - realmId, + realmContext, PolarisTaskConstants.TASK_TIMEOUT_MILLIS_CONFIG, PolarisTaskConstants.TASK_TIMEOUT_MILLIS); return taskState == null @@ -1888,7 +1888,7 @@ private PolarisEntityResolver resolveSecurableToRoleGrant( try { EnumMap creds = storageIntegration.getSubscopedCreds( - realmId, + realmContext, diagnostics, storageConfigurationInfo, allowListOperation, @@ -1935,7 +1935,7 @@ private PolarisEntityResolver resolveSecurableToRoleGrant( readStorageConfiguration(diagnostics, reloadedEntity.getEntity()); Map validateLocationAccess = storageIntegration - .validateAccessToLocations(realmId, storageConfigurationInfo, actions, locations) + .validateAccessToLocations(realmContext, storageConfigurationInfo, actions, locations) .entrySet() .stream() .collect( diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/InMemoryStorageIntegration.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/InMemoryStorageIntegration.java index 5570d85a8..307650279 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/InMemoryStorageIntegration.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/InMemoryStorageIntegration.java @@ -26,11 +26,11 @@ import java.util.function.Function; import java.util.stream.Collectors; import org.apache.polaris.core.PolarisConfigurationStore; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; /** * Base class for in-memory implementations of {@link PolarisStorageIntegration}. A basic - * implementation of {@link PolarisStorageIntegration#validateAccessToLocations(RealmId, + * implementation of {@link PolarisStorageIntegration#validateAccessToLocations(RealmContext, * PolarisStorageConfigurationInfo, Set, Set)} is provided that checks to see that the list of * locations being accessed is among the list of {@link * PolarisStorageConfigurationInfo#getAllowedLocations()}. Locations being accessed must be equal to @@ -53,7 +53,7 @@ public InMemoryStorageIntegration( * Check that the locations being accessed are all equal to or subdirectories of at least one of * the {@link PolarisStorageConfigurationInfo#getAllowedLocations}. * - * @param realmId + * @param realmContext * @param configurationStore * @param actions a set of operation actions to validate, like LIST/READ/DELETE/WRITE/ALL * @param locations a set of locations to get access to @@ -63,7 +63,7 @@ public InMemoryStorageIntegration( */ public static Map> validateSubpathsOfAllowedLocations( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull PolarisConfigurationStore configurationStore, @Nonnull PolarisStorageConfigurationInfo storageConfig, @Nonnull Set actions, @@ -86,7 +86,7 @@ public InMemoryStorageIntegration( allowedLocationStrings.stream().map(StorageLocation::of).collect(Collectors.toList()); boolean allowWildcardLocation = - configurationStore.getConfiguration(realmId, "ALLOW_WILDCARD_LOCATION", false); + configurationStore.getConfiguration(realmContext, "ALLOW_WILDCARD_LOCATION", false); if (allowWildcardLocation && allowedLocationStrings.contains("*")) { return locations.stream() @@ -129,11 +129,11 @@ public InMemoryStorageIntegration( @Override @Nonnull public Map> validateAccessToLocations( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull T storageConfig, @Nonnull Set actions, @Nonnull Set locations) { return validateSubpathsOfAllowedLocations( - realmId, configurationStore, storageConfig, actions, locations); + realmContext, configurationStore, storageConfig, actions, locations); } } diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageConfigurationInfo.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageConfigurationInfo.java index f56677dfd..a65e1e8eb 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageConfigurationInfo.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageConfigurationInfo.java @@ -40,7 +40,7 @@ import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.admin.model.Catalog; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityConstants; @@ -137,7 +137,7 @@ public static PolarisStorageConfigurationInfo deserialize( } public static Optional forEntityPath( - RealmId realmId, + RealmContext realmContext, PolarisConfigurationStore configurationStore, PolarisDiagnostics diagnostics, List entityPath) { @@ -167,7 +167,9 @@ public static Optional forEntityPath( CatalogEntity catalog = CatalogEntity.of(entityPath.get(0)); boolean allowEscape = configurationStore.getConfiguration( - realmId, catalog, PolarisConfiguration.ALLOW_UNSTRUCTURED_TABLE_LOCATION); + realmContext, + catalog, + PolarisConfiguration.ALLOW_UNSTRUCTURED_TABLE_LOCATION); if (!allowEscape && catalog.getCatalogType() != Catalog.TypeEnum.EXTERNAL && baseLocation != null) { diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageIntegration.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageIntegration.java index 0ea08d78e..9c50c6305 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageIntegration.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/PolarisStorageIntegration.java @@ -24,7 +24,7 @@ import java.util.Objects; import java.util.Set; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; /** * Abstract of Polaris Storage Integration. It holds the reference to an object that having the @@ -47,7 +47,7 @@ public String getStorageIdentifierOrId() { /** * Subscope the creds against the allowed read and write locations. * - * @param realmId the realm context + * @param realmContext the realm context * @param diagnostics the diagnostics service * @param storageConfig storage configuration * @param allowListOperation whether to allow LIST on all the provided allowed read/write @@ -57,7 +57,7 @@ public String getStorageIdentifierOrId() { * @return An enum map including the scoped credentials */ public abstract EnumMap getSubscopedCreds( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull PolarisDiagnostics diagnostics, @Nonnull T storageConfig, boolean allowListOperation, @@ -67,7 +67,7 @@ public abstract EnumMap getSubscopedCreds( /** * Validate access for the provided operation actions and locations. * - * @param realmId + * @param realmContext * @param actions a set of operation actions to validate, like LIST/READ/DELETE/WRITE/ALL * @param locations a set of locations to get access to * @return A Map of string, representing the result of validation, the key value is {@code @@ -99,13 +99,13 @@ public abstract EnumMap getSubscopedCreds( @Nonnull public abstract Map> validateAccessToLocations( - RealmId realmId, + RealmContext realmContext, @Nonnull T storageConfig, @Nonnull Set actions, @Nonnull Set locations); /** - * Result of calling {@link PolarisStorageIntegration#validateAccessToLocations(RealmId, + * Result of calling {@link PolarisStorageIntegration#validateAccessToLocations(RealmContext, * PolarisStorageConfigurationInfo, Set, Set)} */ public static final class ValidationResult { diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java index 45b406ba5..0d751ec75 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/aws/AwsCredentialsStorageIntegration.java @@ -30,7 +30,7 @@ import java.util.stream.Stream; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.storage.InMemoryStorageIntegration; import org.apache.polaris.core.storage.PolarisCredentialProperty; import org.apache.polaris.core.storage.StorageUtil; @@ -60,7 +60,7 @@ public AwsCredentialsStorageIntegration( /** {@inheritDoc} */ @Override public EnumMap getSubscopedCreds( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull PolarisDiagnostics diagnostics, @Nonnull AwsStorageConfigurationInfo storageConfig, boolean allowListOperation, @@ -81,7 +81,7 @@ public EnumMap getSubscopedCreds( .toJson()) .durationSeconds( configurationStore.getConfiguration( - realmId, STORAGE_CREDENTIAL_DURATION_SECONDS)) + realmContext, STORAGE_CREDENTIAL_DURATION_SECONDS)) .build()); EnumMap credentialMap = new EnumMap<>(PolarisCredentialProperty.class); diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/azure/AzureCredentialsStorageIntegration.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/azure/AzureCredentialsStorageIntegration.java index 4e82a792e..5da34596d 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/azure/AzureCredentialsStorageIntegration.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/azure/AzureCredentialsStorageIntegration.java @@ -48,7 +48,7 @@ import org.apache.polaris.core.PolarisConfiguration; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.storage.InMemoryStorageIntegration; import org.apache.polaris.core.storage.PolarisCredentialProperty; import org.slf4j.Logger; @@ -75,7 +75,7 @@ public AzureCredentialsStorageIntegration(PolarisConfigurationStore configuratio @Override public EnumMap getSubscopedCreds( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull PolarisDiagnostics diagnostics, @Nonnull AzureStorageConfigurationInfo storageConfig, boolean allowListOperation, @@ -132,7 +132,7 @@ public EnumMap getSubscopedCreds( OffsetDateTime startTime = start.truncatedTo(ChronoUnit.SECONDS).atOffset(ZoneOffset.UTC); int intendedDurationSeconds = configurationStore.getConfiguration( - realmId, PolarisConfiguration.STORAGE_CREDENTIAL_DURATION_SECONDS); + realmContext, PolarisConfiguration.STORAGE_CREDENTIAL_DURATION_SECONDS); OffsetDateTime intendedEndTime = start.plusSeconds(intendedDurationSeconds).atOffset(ZoneOffset.UTC); OffsetDateTime maxAllowedEndTime = diff --git a/polaris-core/src/main/java/org/apache/polaris/core/storage/gcp/GcpCredentialsStorageIntegration.java b/polaris-core/src/main/java/org/apache/polaris/core/storage/gcp/GcpCredentialsStorageIntegration.java index 224b850d2..fb88e4bec 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/storage/gcp/GcpCredentialsStorageIntegration.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/storage/gcp/GcpCredentialsStorageIntegration.java @@ -40,7 +40,7 @@ import java.util.stream.Stream; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.storage.InMemoryStorageIntegration; import org.apache.polaris.core.storage.PolarisCredentialProperty; import org.apache.polaris.core.storage.PolarisStorageIntegration; @@ -74,7 +74,7 @@ public GcpCredentialsStorageIntegration( @Override public EnumMap getSubscopedCreds( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull PolarisDiagnostics diagnostics, @Nonnull GcpStorageConfigurationInfo storageConfig, boolean allowListOperation, diff --git a/polaris-core/src/test/java/org/apache/polaris/core/persistence/EntityCacheTest.java b/polaris-core/src/test/java/org/apache/polaris/core/persistence/EntityCacheTest.java index c87eb5e7f..4e918a42d 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/persistence/EntityCacheTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/persistence/EntityCacheTest.java @@ -26,7 +26,6 @@ import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntitySubType; import org.apache.polaris.core.entity.PolarisEntityType; @@ -85,10 +84,7 @@ public EntityCacheTest() { new PolarisTreeMapMetaStoreSessionImpl(store, Mockito.mock(), RANDOM_SECRETS, diagServices); metaStoreManager = new PolarisMetaStoreManagerImpl( - RealmId.newRealmId("test"), - diagServices, - new PolarisConfigurationStore() {}, - Clock.systemUTC()); + () -> "test", diagServices, new PolarisConfigurationStore() {}, Clock.systemUTC()); // bootstrap the mata store with our test schema tm = new PolarisTestMetaStoreManager(metaStoreManager, metaStore, diagServices); diff --git a/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisTreeMapMetaStoreManagerTest.java b/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisTreeMapMetaStoreManagerTest.java index ac84bb3f4..2a0a96aea 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisTreeMapMetaStoreManagerTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisTreeMapMetaStoreManagerTest.java @@ -24,7 +24,6 @@ import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; import org.mockito.Mockito; public class PolarisTreeMapMetaStoreManagerTest extends BasePolarisMetaStoreManagerTest { @@ -36,7 +35,7 @@ public PolarisTestMetaStoreManager createPolarisTestMetaStoreManager() { new PolarisTreeMapMetaStoreSessionImpl(store, Mockito.mock(), RANDOM_SECRETS, diagServices); return new PolarisTestMetaStoreManager( new PolarisMetaStoreManagerImpl( - RealmId.newRealmId("test"), + () -> "test", diagServices, new PolarisConfigurationStore() {}, timeSource.withZone(ZoneId.systemDefault())), diff --git a/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java b/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java index 6eef9dc9a..d30559719 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java @@ -37,7 +37,6 @@ import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; -import org.apache.polaris.core.context.RealmId; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityCore; import org.apache.polaris.core.entity.PolarisEntitySubType; @@ -106,10 +105,7 @@ public ResolverTest() { new PolarisTreeMapMetaStoreSessionImpl(store, Mockito.mock(), RANDOM_SECRETS, diagServices); metaStoreManager = new PolarisMetaStoreManagerImpl( - RealmId.newRealmId("test"), - diagServices, - new PolarisConfigurationStore() {}, - Clock.systemUTC()); + () -> "test", diagServices, new PolarisConfigurationStore() {}, Clock.systemUTC()); // bootstrap the mata store with our test schema tm = new PolarisTestMetaStoreManager(metaStoreManager, metaStore, diagServices); diff --git a/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java index c0c686bbc..413cad5ee 100644 --- a/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/core/storage/InMemoryStorageIntegrationTest.java @@ -26,14 +26,14 @@ import java.util.Set; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.storage.aws.AwsStorageConfigurationInfo; import org.assertj.core.api.Assertions; import org.junit.jupiter.api.Test; class InMemoryStorageIntegrationTest { - private final RealmId realmId = RealmId.newRealmId("test"); + private RealmContext realmContext = () -> "test"; @Test public void testValidateAccessToLocations() { @@ -41,7 +41,7 @@ public void testValidateAccessToLocations() { new MockInMemoryStorageIntegration(new PolarisConfigurationStore() {}); Map> result = storage.validateAccessToLocations( - realmId, + realmContext, new AwsStorageConfigurationInfo( PolarisStorageConfigurationInfo.StorageType.S3, List.of( @@ -96,14 +96,14 @@ public void testValidateAccessToLocationsWithWildcard() { new PolarisConfigurationStore() { @SuppressWarnings("unchecked") @Override - public @Nullable T getConfiguration(RealmId realmId, String configName) { + public @Nullable T getConfiguration(RealmContext realmContext, String configName) { return (T) config.get(configName); } }; MockInMemoryStorageIntegration storage = new MockInMemoryStorageIntegration(configurationStore); Map> result = storage.validateAccessToLocations( - realmId, + realmContext, new FileStorageConfigurationInfo(List.of("file://", "*")), Set.of(PolarisStorageActions.READ), Set.of( @@ -144,7 +144,7 @@ public void testValidateAccessToLocationsNoAllowedLocations() { new MockInMemoryStorageIntegration(new PolarisConfigurationStore() {}); Map> result = storage.validateAccessToLocations( - realmId, + realmContext, new AwsStorageConfigurationInfo( PolarisStorageConfigurationInfo.StorageType.S3, List.of(), @@ -180,7 +180,7 @@ public void testValidateAccessToLocationsWithPrefixOfAllowedLocation() { new MockInMemoryStorageIntegration(new PolarisConfigurationStore() {}); Map> result = storage.validateAccessToLocations( - realmId, + realmContext, new AwsStorageConfigurationInfo( PolarisStorageConfigurationInfo.StorageType.S3, List.of("s3://bucket/path/to/warehouse"), @@ -206,7 +206,7 @@ public MockInMemoryStorageIntegration(PolarisConfigurationStore configurationSto @Override public EnumMap getSubscopedCreds( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull PolarisDiagnostics diagnostics, @Nonnull PolarisStorageConfigurationInfo storageConfig, boolean allowListOperation, diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisConfigurationStoreTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisConfigurationStoreTest.java index a438e6058..6e9379775 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisConfigurationStoreTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/PolarisConfigurationStoreTest.java @@ -22,14 +22,14 @@ import java.util.List; import org.apache.polaris.core.PolarisConfiguration; import org.apache.polaris.core.PolarisConfigurationStore; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; /** Unit test for the default behaviors of the PolarisConfigurationStore interface. */ public class PolarisConfigurationStoreTest { - private final RealmId realmId = RealmId.newRealmId("test"); + private RealmContext realmContext = () -> "test"; @Test public void testConfigsCanBeCastedFromString() { @@ -48,7 +48,7 @@ public void testConfigsCanBeCastedFromString() { */ @SuppressWarnings("unchecked") @Override - public @Nullable T getConfiguration(RealmId realmId, String configName) { + public @Nullable T getConfiguration(RealmContext realmContext, String configName) { for (PolarisConfiguration c : configs) { if (c.key.equals(configName)) { return (T) String.valueOf(c.defaultValue); @@ -65,7 +65,7 @@ public void testConfigsCanBeCastedFromString() { // Ensure that we can fetch all the configs and that the value is what we expect, which // is the config's default value based on how we've implemented PolarisConfigurationStore above. for (PolarisConfiguration c : configs) { - Assertions.assertEquals(c.defaultValue, store.getConfiguration(realmId, c)); + Assertions.assertEquals(c.defaultValue, store.getConfiguration(realmContext, c)); } } @@ -79,14 +79,14 @@ public void testInvalidCastThrowsException() { new PolarisConfigurationStore() { @SuppressWarnings("unchecked") @Override - public T getConfiguration(RealmId realmId, String configName) { + public T getConfiguration(RealmContext realmContext, String configName) { return (T) "abc123"; } }; for (PolarisConfiguration c : configs) { Assertions.assertThrows( - NumberFormatException.class, () -> store.getConfiguration(realmId, c)); + NumberFormatException.class, () -> store.getConfiguration(realmContext, c)); } } diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java index 27e6602b0..bf563da45 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/aws/AwsCredentialsStorageIntegrationTest.java @@ -26,7 +26,7 @@ import java.util.Set; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.storage.PolarisCredentialProperty; import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; import org.apache.polaris.core.storage.aws.AwsCredentialsStorageIntegration; @@ -61,7 +61,7 @@ class AwsCredentialsStorageIntegrationTest { .build(); public static final String AWS_PARTITION = "aws"; - private static final RealmId REALM_CONTEXT = RealmId.newRealmId("realm"); + private static final RealmContext REALM_CONTEXT = () -> "realm"; @Test public void testGetSubscopedCreds() { diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/azure/AzureCredentialStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/azure/AzureCredentialStorageIntegrationTest.java index f0b296e4b..8c366ee6a 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/azure/AzureCredentialStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/azure/AzureCredentialStorageIntegrationTest.java @@ -49,7 +49,7 @@ import java.util.stream.Stream; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.storage.PolarisCredentialProperty; import org.apache.polaris.core.storage.azure.AzureCredentialsStorageIntegration; import org.apache.polaris.core.storage.azure.AzureStorageConfigurationInfo; @@ -68,7 +68,7 @@ public class AzureCredentialStorageIntegrationTest { private final String clientId = System.getenv("AZURE_CLIENT_ID"); private final String clientSecret = System.getenv("AZURE_CLIENT_SECRET"); private final String tenantId = System.getenv("AZURE_TENANT_ID"); - private final RealmId realmId = RealmId.newRealmId("realm"); + private final RealmContext realmContext = () -> "realm"; private void assumeEnvVariablesNotNull() { Assumptions.assumeThat( @@ -88,7 +88,10 @@ public void testNegativeCases() { Assertions.assertThatThrownBy( () -> subscopedCredsForOperations( - realmId, differentEndpointList, /* allowedWriteLoc= */ new ArrayList<>(), true)) + realmContext, + differentEndpointList, + /* allowedWriteLoc= */ new ArrayList<>(), + true)) .isInstanceOf(RuntimeException.class); List differentStorageAccts = @@ -98,7 +101,10 @@ public void testNegativeCases() { Assertions.assertThatThrownBy( () -> subscopedCredsForOperations( - realmId, differentStorageAccts, /* allowedWriteLoc= */ new ArrayList<>(), true)) + realmContext, + differentStorageAccts, + /* allowedWriteLoc= */ new ArrayList<>(), + true)) .isInstanceOf(RuntimeException.class); List differentContainers = Arrays.asList( @@ -108,7 +114,10 @@ public void testNegativeCases() { Assertions.assertThatThrownBy( () -> subscopedCredsForOperations( - realmId, differentContainers, /* allowedWriteLoc= */ new ArrayList<>(), true)) + realmContext, + differentContainers, + /* allowedWriteLoc= */ new ArrayList<>(), + true)) .isInstanceOf(RuntimeException.class); } @@ -125,7 +134,7 @@ public void testGetSubscopedTokenList(boolean allowListAction, String service) { service)); Map credsMap = subscopedCredsForOperations( - /* allowedReadLoc= */ realmId, + /* allowedReadLoc= */ realmContext, allowedLoc, /* allowedWriteLoc= */ new ArrayList<>(), allowListAction); @@ -197,7 +206,7 @@ public void testGetSubscopedTokenRead( service, allowedPrefix)); Map credsMap = subscopedCredsForOperations( - /* allowedReadLoc= */ realmId, + /* allowedReadLoc= */ realmContext, allowedLoc, /* allowedWriteLoc= */ new ArrayList<>(), /* allowListAction= */ false); @@ -268,7 +277,7 @@ public void testGetSubscopedTokenWrite( service, allowedPrefix)); Map credsMap = subscopedCredsForOperations( - /* allowedReadLoc= */ realmId, + /* allowedReadLoc= */ realmContext, new ArrayList<>(), /* allowedWriteLoc= */ allowedLoc, /* allowListAction= */ false); @@ -345,7 +354,7 @@ public void testGetSubscopedTokenWrite( } private Map subscopedCredsForOperations( - RealmId realmId, + RealmContext realmContext, List allowedReadLoc, List allowedWriteLoc, boolean allowListAction) { @@ -358,7 +367,7 @@ private Map subscopedCredsForOperations( new AzureCredentialsStorageIntegration(new PolarisConfigurationStore() {}); EnumMap credsMap = azureCredsIntegration.getSubscopedCreds( - realmId, + realmContext, new PolarisDefaultDiagServiceImpl(), azureConfig, allowListAction, diff --git a/polaris-core/src/test/java/org/apache/polaris/service/storage/gcp/GcpCredentialsStorageIntegrationTest.java b/polaris-core/src/test/java/org/apache/polaris/service/storage/gcp/GcpCredentialsStorageIntegrationTest.java index 44f316531..17bf389f1 100644 --- a/polaris-core/src/test/java/org/apache/polaris/service/storage/gcp/GcpCredentialsStorageIntegrationTest.java +++ b/polaris-core/src/test/java/org/apache/polaris/service/storage/gcp/GcpCredentialsStorageIntegrationTest.java @@ -50,7 +50,7 @@ import java.util.Set; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDefaultDiagServiceImpl; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.storage.PolarisCredentialProperty; import org.apache.polaris.core.storage.gcp.GcpCredentialsStorageIntegration; import org.apache.polaris.core.storage.gcp.GcpStorageConfigurationInfo; @@ -73,7 +73,7 @@ public void testSubscope(boolean allowedListAction) throws Exception { .describedAs("Environment variable GOOGLE_APPLICATION_CREDENTIALS not exits") .isNotNull() .isNotEmpty(); - RealmId realmId = RealmId.newRealmId("realm"); + RealmContext realmContext = () -> "realm"; List allowedRead = Arrays.asList( "gs://sfc-dev1-regtest/polaris-test/subscoped-test/read1/", @@ -83,7 +83,7 @@ public void testSubscope(boolean allowedListAction) throws Exception { "gs://sfc-dev1-regtest/polaris-test/subscoped-test/write1/", "gs://sfc-dev1-regtest/polaris-test/subscoped-test/write2/"); Storage storageClient = - setupStorageClient(realmId, allowedRead, allowedWrite, allowedListAction); + setupStorageClient(realmContext, allowedRead, allowedWrite, allowedListAction); BlobInfo blobInfoGoodWrite = createStorageBlob("sfc-dev1-regtest", "polaris-test/subscoped-test/write1/", "file.txt"); BlobInfo blobInfoBad = @@ -126,7 +126,7 @@ public void testSubscope(boolean allowedListAction) throws Exception { "gs://sfc-dev1-regtest/polaris-test/subscoped-test/write2/", "gs://sfc-dev1-regtest/polaris-test/subscoped-test/write3/"); Storage clientForDelete = - setupStorageClient(realmId, List.of(), allowedWrite2, allowedListAction); + setupStorageClient(realmContext, List.of(), allowedWrite2, allowedListAction); // can not delete because it is not in allowed write path for this client Assertions.assertThatThrownBy(() -> clientForDelete.delete(blobInfoGoodWrite.getBlobId())) @@ -138,13 +138,13 @@ public void testSubscope(boolean allowedListAction) throws Exception { } private Storage setupStorageClient( - RealmId realmId, + RealmContext realmContext, List allowedReadLoc, List allowedWriteLoc, boolean allowListAction) throws IOException { Map credsMap = - subscopedCredsForOperations(realmId, allowedReadLoc, allowedWriteLoc, allowListAction); + subscopedCredsForOperations(realmContext, allowedReadLoc, allowedWriteLoc, allowListAction); return createStorageClient(credsMap); } @@ -167,7 +167,7 @@ private Storage createStorageClient(Map creds } private Map subscopedCredsForOperations( - RealmId realmId, + RealmContext realmContext, List allowedReadLoc, List allowedWriteLoc, boolean allowListAction) @@ -183,7 +183,7 @@ private Map subscopedCredsForOperations( ServiceOptions.getFromServiceLoader(HttpTransportFactory.class, NetHttpTransport::new)); EnumMap credsMap = gcpCredsIntegration.getSubscopedCreds( - realmId, + realmContext, new PolarisDefaultDiagServiceImpl(), gcpConfig, allowListAction, diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/config/QuarkusProducers.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/config/QuarkusProducers.java index 446d73716..d06af60ac 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/config/QuarkusProducers.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/config/QuarkusProducers.java @@ -39,7 +39,7 @@ import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisEntityManager; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; @@ -53,7 +53,7 @@ import org.apache.polaris.service.catalog.io.FileIOFactory; import org.apache.polaris.service.config.RealmEntityManagerFactory; import org.apache.polaris.service.context.RealmContextConfiguration; -import org.apache.polaris.service.context.RealmIdResolver; +import org.apache.polaris.service.context.RealmContextResolver; import org.apache.polaris.service.persistence.InMemoryPolarisMetaStoreManagerFactory; import org.apache.polaris.service.quarkus.auth.QuarkusAuthenticationConfiguration; import org.apache.polaris.service.quarkus.catalog.io.QuarkusFileIOConfiguration; @@ -100,8 +100,9 @@ public PolarisDiagnostics polarisDiagnostics() { @Produces @RequestScoped - public RealmId realmId(@Context HttpServerRequest request, RealmIdResolver realmIdResolver) { - return realmIdResolver.resolveRealmContext( + public RealmContext realmContext( + @Context HttpServerRequest request, RealmContextResolver realmContextResolver) { + return realmContextResolver.resolveRealmContext( request.absoluteURI(), request.method().name(), request.path(), @@ -112,50 +113,51 @@ public RealmId realmId(@Context HttpServerRequest request, RealmIdResolver realm @Produces @RequestScoped public PolarisMetaStoreSession metaStoreSession( - MetaStoreManagerFactory metaStoreManagerFactory, RealmId realmId) { - return metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + MetaStoreManagerFactory metaStoreManagerFactory, RealmContext realmContext) { + return metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); } @Produces @RequestScoped // TODO break into separate beans public PolarisMetaStoreManager polarisMetaStoreManager( - MetaStoreManagerFactory metaStoreManagerFactory, RealmId realmId) { - return metaStoreManagerFactory.getOrCreateMetaStoreManager(realmId); + MetaStoreManagerFactory metaStoreManagerFactory, RealmContext realmContext) { + return metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); } @Produces @RequestScoped public StorageCredentialCache storageCredentialCache( - MetaStoreManagerFactory metaStoreManagerFactory, RealmId realmId) { - return metaStoreManagerFactory.getOrCreateStorageCredentialCache(realmId); + MetaStoreManagerFactory metaStoreManagerFactory, RealmContext realmContext) { + return metaStoreManagerFactory.getOrCreateStorageCredentialCache(realmContext); } @Produces @RequestScoped - public EntityCache entityCache(MetaStoreManagerFactory metaStoreManagerFactory, RealmId realmId) { - return metaStoreManagerFactory.getOrCreateEntityCache(realmId); + public EntityCache entityCache( + MetaStoreManagerFactory metaStoreManagerFactory, RealmContext realmContext) { + return metaStoreManagerFactory.getOrCreateEntityCache(realmContext); } @Produces @RequestScoped public PolarisEntityManager polarisEntityManager( - RealmEntityManagerFactory realmEntityManagerFactory, RealmId realmId) { - return realmEntityManagerFactory.getOrCreateEntityManager(realmId); + RealmEntityManagerFactory realmEntityManagerFactory, RealmContext realmContext) { + return realmEntityManagerFactory.getOrCreateEntityManager(realmContext); } @Produces @RequestScoped - public TokenBroker tokenBroker(TokenBrokerFactory tokenBrokerFactory, RealmId realmId) { - return tokenBrokerFactory.apply(realmId); + public TokenBroker tokenBroker(TokenBrokerFactory tokenBrokerFactory, RealmContext realmContext) { + return tokenBrokerFactory.apply(realmContext); } // Polaris service beans - selected from @Identifier-annotated beans @Produces - public RealmIdResolver realmContextResolver( + public RealmContextResolver realmContextResolver( QuarkusRealmContextConfiguration config, - @Any Instance realmContextResolvers) { + @Any Instance realmContextResolvers) { return realmContextResolvers.select(Identifier.Literal.of(config.type())).get(); } diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/context/QuarkusRealmContextConfiguration.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/context/QuarkusRealmContextConfiguration.java index a05cf57a8..dce989404 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/context/QuarkusRealmContextConfiguration.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/context/QuarkusRealmContextConfiguration.java @@ -21,15 +21,14 @@ import io.quarkus.runtime.annotations.StaticInitSafe; import io.smallrye.config.ConfigMapping; import org.apache.polaris.service.context.RealmContextConfiguration; -import org.apache.polaris.service.context.RealmIdResolver; @StaticInitSafe @ConfigMapping(prefix = "polaris.realm-context") public interface QuarkusRealmContextConfiguration extends RealmContextConfiguration { /** - * The type of the realm context resolver. Must be a registered {@link RealmIdResolver} - * identifier. + * The type of the realm context resolver. Must be a registered {@link + * org.apache.polaris.service.context.RealmContextResolver} identifier. */ String type(); } diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/logging/QuarkusLoggingMDCFilter.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/logging/QuarkusLoggingMDCFilter.java index 79664c705..98fb2888d 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/logging/QuarkusLoggingMDCFilter.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/logging/QuarkusLoggingMDCFilter.java @@ -22,7 +22,7 @@ import io.vertx.ext.web.RoutingContext; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.slf4j.MDC; @ApplicationScoped @@ -33,7 +33,7 @@ public class QuarkusLoggingMDCFilter { private static final String REQUEST_ID_KEY = "requestId"; private static final String REALM_ID_KEY = "realmId"; - @Inject RealmId realmId; + @Inject RealmContext realmContext; @Inject QuarkusLoggingConfiguration loggingConfiguration; @@ -57,8 +57,8 @@ public void applyMDCContext(RoutingContext rc) { MDC.put(REQUEST_ID_KEY, requestId); rc.put(REQUEST_ID_KEY, requestId); } - MDC.put(REALM_ID_KEY, realmId.id()); - rc.put(REALM_ID_KEY, realmId.id()); + MDC.put(REALM_ID_KEY, realmContext.getRealmIdentifier()); + rc.put(REALM_ID_KEY, realmContext.getRealmIdentifier()); // Do not explicitly remove the MDC values from the request context with an end handler, // as this could remove MDC context still in use in TaskExecutor threads // rc.addEndHandler( diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusValueExpressionResolver.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusValueExpressionResolver.java index bbbeb7cbc..98783de60 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusValueExpressionResolver.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/QuarkusValueExpressionResolver.java @@ -22,7 +22,7 @@ import io.micrometer.common.lang.Nullable; import jakarta.annotation.Nonnull; import jakarta.enterprise.context.ApplicationScoped; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; @ApplicationScoped public class QuarkusValueExpressionResolver implements ValueExpressionResolver { @@ -30,8 +30,8 @@ public class QuarkusValueExpressionResolver implements ValueExpressionResolver { @Override public String resolve(@Nonnull String expression, @Nullable Object parameter) { // TODO maybe replace with CEL of some expression engine and make this more generic - if (parameter instanceof RealmId realmId && expression.equals("realm.id")) { - return realmId.id(); + if (parameter instanceof RealmContext realmContext && expression.equals("realmIdentifier")) { + return realmContext.getRealmIdentifier(); } return null; } diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagContributor.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagContributor.java index df7b1f137..147ebd7fd 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagContributor.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/metrics/RealmIdTagContributor.java @@ -24,26 +24,26 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.util.HashMap; -import org.apache.polaris.core.context.RealmId; -import org.apache.polaris.service.context.RealmIdResolver; +import org.apache.polaris.core.context.RealmContext; +import org.apache.polaris.service.context.RealmContextResolver; @ApplicationScoped public class RealmIdTagContributor implements HttpServerMetricsTagsContributor { public static final String TAG_REALM = "realm_id"; - @Inject RealmIdResolver realmIdResolver; + @Inject RealmContextResolver realmContextResolver; @Override public Tags contribute(Context context) { // FIXME request scope does not work here, so we have to resolve the realm context manually HttpServerRequest request = context.request(); - RealmId realmId = resolveRealmContext(request); - return Tags.of(TAG_REALM, realmId.id()); + RealmContext realmContext = resolveRealmContext(request); + return Tags.of(TAG_REALM, realmContext.getRealmIdentifier()); } - private RealmId resolveRealmContext(HttpServerRequest request) { - return realmIdResolver.resolveRealmContext( + private RealmContext resolveRealmContext(HttpServerRequest request) { + return realmContextResolver.resolveRealmContext( request.absoluteURI(), request.method().name(), request.path(), diff --git a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/task/QuarkusTaskExecutorImpl.java b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/task/QuarkusTaskExecutorImpl.java index a9c5c1efd..d14a9c2da 100644 --- a/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/task/QuarkusTaskExecutorImpl.java +++ b/quarkus/service/src/main/java/org/apache/polaris/service/quarkus/task/QuarkusTaskExecutorImpl.java @@ -30,7 +30,7 @@ import java.util.concurrent.ExecutorService; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.service.quarkus.tracing.QuarkusTracingFilter; import org.apache.polaris.service.task.TaskExecutorImpl; @@ -71,17 +71,18 @@ public void init() { } @Override - protected void handleTask(long taskEntityId, RealmId realmId, int attempt) { + protected void handleTask(long taskEntityId, RealmContext realmContext, int attempt) { Span span = tracer .spanBuilder("polaris.task") .setParent(Context.current()) - .setAttribute(QuarkusTracingFilter.REALM_ID_ATTRIBUTE, realmId.id()) + .setAttribute( + QuarkusTracingFilter.REALM_ID_ATTRIBUTE, realmContext.getRealmIdentifier()) .setAttribute("polaris.task.entity.id", taskEntityId) .setAttribute("polaris.task.attempt", attempt) .startSpan(); try (Scope ignored = span.makeCurrent()) { - super.handleTask(taskEntityId, realmId, attempt); + super.handleTask(taskEntityId, realmContext, attempt); } finally { span.end(); } diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/TimedApplicationEventListenerTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/TimedApplicationEventListenerTest.java index 36e84ac17..66a8aa388 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/TimedApplicationEventListenerTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/TimedApplicationEventListenerTest.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.quarkus; -import static org.apache.polaris.service.context.TestRealmIdResolver.REALM_PROPERTY_KEY; +import static org.apache.polaris.service.context.TestRealmContextResolver.REALM_PROPERTY_KEY; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.InstanceOfAssertFactories.type; diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/ManagementServiceTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/ManagementServiceTest.java index c4e10852f..351424a5a 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/ManagementServiceTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/ManagementServiceTest.java @@ -61,7 +61,7 @@ public void testCreateCatalogWithDisallowedStorageConfig() { .catalogsApi() .createCatalog( new CreateCatalogRequest(catalog), - services.realmId(), + services.realmContext(), services.securityContext())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Unsupported storage type: FILE"); @@ -90,7 +90,7 @@ public void testUpdateCatalogWithDisallowedStorageConfig() { .catalogsApi() .createCatalog( new CreateCatalogRequest(catalog), - services.realmId(), + services.realmContext(), services.securityContext())) { assertThat(response).returns(Response.Status.CREATED.getStatusCode(), Response::getStatus); } @@ -100,7 +100,7 @@ public void testUpdateCatalogWithDisallowedStorageConfig() { try (Response response = services .catalogsApi() - .getCatalog(catalogName, services.realmId(), services.securityContext())) { + .getCatalog(catalogName, services.realmContext(), services.securityContext())) { assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus); fetchedCatalog = (Catalog) response.getEntity(); @@ -126,7 +126,10 @@ public void testUpdateCatalogWithDisallowedStorageConfig() { services .catalogsApi() .updateCatalog( - catalogName, updateRequest, services.realmId(), services.securityContext())) + catalogName, + updateRequest, + services.realmContext(), + services.securityContext())) .isInstanceOf(IllegalArgumentException.class) .hasMessage("Unsupported storage type: FILE"); } diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAdminServiceAuthzTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAdminServiceAuthzTest.java index d92ce3c8e..303746d5d 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAdminServiceAuthzTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAdminServiceAuthzTest.java @@ -49,7 +49,7 @@ private PolarisAdminService newTestAdminService(Set activatedPrincipalRo final AuthenticatedPolarisPrincipal authenticatedPrincipal = new AuthenticatedPolarisPrincipal(principalEntity, activatedPrincipalRoles); return new PolarisAdminService( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAuthzTestBase.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAuthzTestBase.java index 629951df7..730572af9 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAuthzTestBase.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisAuthzTestBase.java @@ -51,7 +51,7 @@ import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.CatalogRoleEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; @@ -164,7 +164,7 @@ public Map getConfigOverrides() { protected FileIOFactory fileIOFactory; protected PolarisBaseEntity catalogEntity; protected PrincipalEntity principalEntity; - protected RealmId realmId; + protected RealmContext realmContext; protected AuthenticatedPolarisPrincipal authenticatedRoot; @BeforeAll @@ -179,10 +179,10 @@ public static void setUpMocks() { @BeforeEach public void before(TestInfo testInfo) { - realmId = testInfo::getDisplayName; - metaStoreManager = managerFactory.getOrCreateMetaStoreManager(realmId); - metaStoreSession = managerFactory.getOrCreateSessionSupplier(realmId).get(); - entityManager = realmEntityManagerFactory.getOrCreateEntityManager(realmId); + realmContext = testInfo::getDisplayName; + metaStoreManager = managerFactory.getOrCreateMetaStoreManager(realmContext); + metaStoreSession = managerFactory.getOrCreateSessionSupplier(realmContext).get(); + entityManager = realmEntityManagerFactory.getOrCreateEntityManager(realmContext); PrincipalEntity rootEntity = new PrincipalEntity( @@ -200,7 +200,7 @@ public void before(TestInfo testInfo) { this.adminService = new PolarisAdminService( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, @@ -391,7 +391,7 @@ private void initBaseCatalog() { new DefaultFileIOFactory(realmEntityManagerFactory, managerFactory, configurationStore); this.baseCatalog = new BasePolarisCatalog( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisOverlappingCatalogTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisOverlappingCatalogTest.java index 9e60049ce..a1d308b00 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisOverlappingCatalogTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisOverlappingCatalogTest.java @@ -78,7 +78,7 @@ private Response createCatalog( return services .catalogsApi() .createCatalog( - new CreateCatalogRequest(catalog), services.realmId(), services.securityContext()); + new CreateCatalogRequest(catalog), services.realmContext(), services.securityContext()); } @ParameterizedTest diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisOverlappingTableTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisOverlappingTableTest.java index 03a5b7237..878c35e82 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisOverlappingTableTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/admin/PolarisOverlappingTableTest.java @@ -63,7 +63,7 @@ private int createTable(TestServices services, String location) { namespace, createTableRequest, null, - services.realmId(), + services.realmContext(), services.securityContext())) { return response.getStatus(); } catch (ForbiddenException e) { @@ -130,7 +130,7 @@ void testTableLocationRestrictions( .catalogsApi() .createCatalog( new CreateCatalogRequest(catalogObject), - services.realmId(), + services.realmContext(), services.securityContext())) { assertThat(response.getStatus()).isEqualTo(Response.Status.CREATED.getStatusCode()); } @@ -141,7 +141,10 @@ void testTableLocationRestrictions( services .restApi() .createNamespace( - catalog, createNamespaceRequest, services.realmId(), services.securityContext())) { + catalog, + createNamespaceRequest, + services.realmContext(), + services.securityContext())) { assertThat(response.getStatus()).isEqualTo(Response.Status.OK.getStatusCode()); } diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/auth/TokenUtils.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/auth/TokenUtils.java index bd860af11..094323084 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/auth/TokenUtils.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/auth/TokenUtils.java @@ -19,7 +19,7 @@ package org.apache.polaris.service.quarkus.auth; import static org.apache.polaris.service.auth.BasePolarisAuthenticator.PRINCIPAL_ROLE_ALL; -import static org.apache.polaris.service.context.TestRealmIdResolver.REALM_PROPERTY_KEY; +import static org.apache.polaris.service.context.TestRealmContextResolver.REALM_PROPERTY_KEY; import static org.assertj.core.api.Assertions.assertThat; import jakarta.ws.rs.client.Client; diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/BasePolarisCatalogTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/BasePolarisCatalogTest.java index cab70907f..373df2517 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/BasePolarisCatalogTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/BasePolarisCatalogTest.java @@ -73,7 +73,7 @@ import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; import org.apache.polaris.core.auth.PolarisSecretsManager.PrincipalSecretsResult; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntity; @@ -158,7 +158,7 @@ public Map getConfigOverrides() { @Inject Clock clock; private BasePolarisCatalog catalog; - private RealmId realmId; + private RealmContext realmContext; private PolarisMetaStoreManager metaStoreManager; private PolarisMetaStoreSession metaStoreSession; private PolarisAdminService adminService; @@ -183,10 +183,10 @@ public void before(TestInfo testInfo) { "realm_%s_%s" .formatted( testInfo.getTestMethod().map(Method::getName).orElse("test"), System.nanoTime()); - realmId = RealmId.newRealmId(realmName); - metaStoreManager = managerFactory.getOrCreateMetaStoreManager(realmId); - metaStoreSession = managerFactory.getOrCreateSessionSupplier(realmId).get(); - entityManager = entityManagerFactory.getOrCreateEntityManager(realmId); + realmContext = () -> realmName; + metaStoreManager = managerFactory.getOrCreateMetaStoreManager(realmContext); + metaStoreSession = managerFactory.getOrCreateSessionSupplier(realmContext).get(); + entityManager = entityManagerFactory.getOrCreateEntityManager(realmContext); PrincipalEntity rootEntity = new PrincipalEntity( @@ -207,7 +207,7 @@ public void before(TestInfo testInfo) { when(securityContext.isUserInRole(isA(String.class))).thenReturn(true); adminService = new PolarisAdminService( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, @@ -264,7 +264,7 @@ public void before(TestInfo testInfo) { this.catalog = new BasePolarisCatalog( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, @@ -313,22 +313,23 @@ protected boolean supportsNotifications() { private MetaStoreManagerFactory createMockMetaStoreManagerFactory() { return new MetaStoreManagerFactory() { @Override - public PolarisMetaStoreManager getOrCreateMetaStoreManager(RealmId realmId) { + public PolarisMetaStoreManager getOrCreateMetaStoreManager(RealmContext realmContext) { return metaStoreManager; } @Override - public Supplier getOrCreateSessionSupplier(RealmId realmId) { + public Supplier getOrCreateSessionSupplier( + RealmContext realmContext) { return () -> metaStoreSession; } @Override - public StorageCredentialCache getOrCreateStorageCredentialCache(RealmId realmId) { + public StorageCredentialCache getOrCreateStorageCredentialCache(RealmContext realmContext) { return new StorageCredentialCache(diagServices, configurationStore); } @Override - public EntityCache getOrCreateEntityCache(RealmId realmId) { + public EntityCache getOrCreateEntityCache(RealmContext realmContext) { return new EntityCache(metaStoreManager, diagServices); } @@ -518,7 +519,7 @@ public void testValidateNotificationFailToCreateFileIO() throws IOException { spy(new DefaultFileIOFactory(entityManagerFactory, managerFactory, configurationStore)); BasePolarisCatalog catalog = new BasePolarisCatalog( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, @@ -846,7 +847,7 @@ public void testUpdateNotificationCreateTableWithLocalFilePrefix() { TaskExecutor taskExecutor = Mockito.mock(); BasePolarisCatalog catalog = new BasePolarisCatalog( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, @@ -880,7 +881,7 @@ public void testUpdateNotificationCreateTableWithLocalFilePrefix() { TableMetadataParser.toJson(createSampleTableMetadata(metadataLocation)).getBytes(UTF_8)); if (!configurationStore - .getConfiguration(realmId, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES) + .getConfiguration(realmContext, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES) .contains("FILE")) { Assertions.assertThatThrownBy(() -> catalog.sendNotification(table, request)) .isInstanceOf(ForbiddenException.class) @@ -911,7 +912,7 @@ public void testUpdateNotificationCreateTableWithHttpPrefix() { TaskExecutor taskExecutor = Mockito.mock(); BasePolarisCatalog catalog = new BasePolarisCatalog( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, @@ -947,7 +948,7 @@ public void testUpdateNotificationCreateTableWithHttpPrefix() { TableMetadataParser.toJson(createSampleTableMetadata(metadataLocation)).getBytes(UTF_8)); if (!configurationStore - .getConfiguration(realmId, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES) + .getConfiguration(realmContext, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES) .contains("FILE")) { Assertions.assertThatThrownBy(() -> catalog.sendNotification(table, request)) .isInstanceOf(ForbiddenException.class) @@ -967,7 +968,7 @@ public void testUpdateNotificationCreateTableWithHttpPrefix() { TableMetadataParser.toJson(createSampleTableMetadata(metadataLocation)).getBytes(UTF_8)); if (!configurationStore - .getConfiguration(realmId, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES) + .getConfiguration(realmContext, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES) .contains("FILE")) { Assertions.assertThatThrownBy(() -> catalog.sendNotification(table, newRequest)) .isInstanceOf(ForbiddenException.class) @@ -1412,7 +1413,7 @@ public void testDropTableWithPurge() { .containsEntry(PolarisCredentialProperty.AWS_KEY_ID, TEST_ACCESS_KEY) .containsEntry(PolarisCredentialProperty.AWS_SECRET_KEY, SECRET_ACCESS_KEY) .containsEntry(PolarisCredentialProperty.AWS_TOKEN, SESSION_TOKEN); - FileIO fileIO = new TaskFileIOSupplier(fileIOFactory).apply(taskEntity, realmId); + FileIO fileIO = new TaskFileIOSupplier(fileIOFactory).apply(taskEntity, realmContext); Assertions.assertThat(fileIO).isNotNull().isInstanceOf(InMemoryFileIO.class); } @@ -1444,7 +1445,7 @@ public void testDropTableWithPurgeDisabled() { entityManager, metaStoreSession, securityContext, noPurgeCatalogName); BasePolarisCatalog noPurgeCatalog = new BasePolarisCatalog( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, @@ -1529,7 +1530,7 @@ public void testFileIOWrapper() { new MeasuredFileIOFactory(entityManagerFactory, managerFactory, configurationStore); BasePolarisCatalog catalog = new BasePolarisCatalog( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, @@ -1579,7 +1580,7 @@ public void testFileIOWrapper() { diagServices, new TaskFileIOSupplier(measured), clock); - handler.handleTask(taskEntity, realmId); + handler.handleTask(taskEntity, realmContext); Assertions.assertThat(measured.getNumDeletedFiles()).as("A table was deleted").isGreaterThan(0); } } diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/BasePolarisCatalogViewTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/BasePolarisCatalogViewTest.java index f5cdbcafe..c86cf9926 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/BasePolarisCatalogViewTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/BasePolarisCatalogViewTest.java @@ -44,7 +44,7 @@ import org.apache.polaris.core.admin.model.StorageConfigInfo; import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizerImpl; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntitySubType; @@ -123,10 +123,10 @@ public void before(TestInfo testInfo) { "realm_%s_%s" .formatted( testInfo.getTestMethod().map(Method::getName).orElse("test"), System.nanoTime()); - RealmId realmId = RealmId.newRealmId(realmName); + RealmContext realmContext = () -> realmName; - metaStoreManager = managerFactory.getOrCreateMetaStoreManager(realmId); - metaStoreSession = managerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManager = managerFactory.getOrCreateMetaStoreManager(realmContext); + metaStoreSession = managerFactory.getOrCreateSessionSupplier(realmContext).get(); PrincipalEntity rootEntity = new PrincipalEntity( @@ -142,14 +142,15 @@ public void before(TestInfo testInfo) { AuthenticatedPolarisPrincipal authenticatedRoot = new AuthenticatedPolarisPrincipal(rootEntity, Set.of()); - PolarisEntityManager entityManager = entityManagerFactory.getOrCreateEntityManager(realmId); + PolarisEntityManager entityManager = + entityManagerFactory.getOrCreateEntityManager(realmContext); SecurityContext securityContext = Mockito.mock(SecurityContext.class); when(securityContext.getUserPrincipal()).thenReturn(authenticatedRoot); when(securityContext.isUserInRole(Mockito.anyString())).thenReturn(true); PolarisAdminService adminService = new PolarisAdminService( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, @@ -177,7 +178,7 @@ public void before(TestInfo testInfo) { new DefaultFileIOFactory(entityManagerFactory, managerFactory, configurationStore); this.catalog = new BasePolarisCatalog( - realmId, + realmContext, entityManager, metaStoreManager, metaStoreSession, diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisCatalogHandlerWrapperAuthzTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisCatalogHandlerWrapperAuthzTest.java index ef3b7fa5c..507ac1afd 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisCatalogHandlerWrapperAuthzTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolarisCatalogHandlerWrapperAuthzTest.java @@ -106,7 +106,7 @@ private PolarisCatalogHandlerWrapper newWrapper(SecurityContext securityContext) private PolarisCatalogHandlerWrapper newWrapper( SecurityContext securityContext, String catalogName) { return new PolarisCatalogHandlerWrapper( - realmId, + realmContext, metaStoreSession, configurationStore, diagServices, diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/TestUtil.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/TestUtil.java new file mode 100644 index 000000000..5d4ee4466 --- /dev/null +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/TestUtil.java @@ -0,0 +1,201 @@ +/* + * 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.quarkus.catalog; + +import static org.apache.polaris.service.context.TestRealmContextResolver.REALM_PROPERTY_KEY; +import static org.assertj.core.api.Assertions.assertThat; + +import com.google.common.collect.ImmutableMap; +import jakarta.ws.rs.client.Entity; +import jakarta.ws.rs.core.Response; +import java.net.URI; +import java.util.Map; +import org.apache.iceberg.CatalogProperties; +import org.apache.iceberg.catalog.SessionCatalog; +import org.apache.iceberg.rest.HTTPClient; +import org.apache.iceberg.rest.RESTCatalog; +import org.apache.iceberg.rest.auth.OAuth2Properties; +import org.apache.polaris.core.admin.model.Catalog; +import org.apache.polaris.core.admin.model.CatalogGrant; +import org.apache.polaris.core.admin.model.CatalogPrivilege; +import org.apache.polaris.core.admin.model.CatalogRole; +import org.apache.polaris.core.admin.model.GrantResource; +import org.apache.polaris.service.auth.BasePolarisAuthenticator; +import org.apache.polaris.service.quarkus.test.PolarisIntegrationTestFixture; +import org.apache.polaris.service.quarkus.test.TestEnvironment; + +/** Test utilities for catalog tests */ +public class TestUtil { + + /** + * Creates a catalog and grants the snowman principal permission to manage it. + * + * @return A client to interact with the catalog. + */ + public static RESTCatalog createSnowmanManagedCatalog( + TestEnvironment testEnv, + PolarisIntegrationTestFixture fixture, + Catalog catalog, + Map extraProperties) { + return createSnowmanManagedCatalog( + testEnv, fixture, testEnv.baseUri(), fixture.realm, catalog, extraProperties); + } + + /** + * Creates a catalog and grants the snowman principal permission to manage it. + * + * @return A client to interact with the catalog. + */ + public static RESTCatalog createSnowmanManagedCatalog( + TestEnvironment testEnv, + PolarisIntegrationTestFixture fixture, + URI baseUri, + String realm, + Catalog catalog, + Map extraProperties) { + String currentCatalogName = catalog.getName(); + try (Response response = + fixture + .client + .target(String.format("%s/api/management/v1/catalogs", baseUri)) + .request("application/json") + .header("Authorization", "Bearer " + fixture.adminToken) + .header(REALM_PROPERTY_KEY, realm) + .post(Entity.json(catalog))) { + assertStatusCode(response, Response.Status.CREATED.getStatusCode()); + } + + // Create a new CatalogRole that has CATALOG_MANAGE_CONTENT and CATALOG_MANAGE_ACCESS + CatalogRole newRole = new CatalogRole("custom-admin"); + try (Response response = + fixture + .client + .target( + String.format( + "%s/api/management/v1/catalogs/%s/catalog-roles", baseUri, currentCatalogName)) + .request("application/json") + .header("Authorization", "Bearer " + fixture.adminToken) + .header(REALM_PROPERTY_KEY, realm) + .post(Entity.json(newRole))) { + assertStatusCode(response, Response.Status.CREATED.getStatusCode()); + } + CatalogGrant grantResource = + new CatalogGrant(CatalogPrivilege.CATALOG_MANAGE_CONTENT, GrantResource.TypeEnum.CATALOG); + try (Response response = + fixture + .client + .target( + String.format( + "%s/api/management/v1/catalogs/%s/catalog-roles/custom-admin/grants", + baseUri, currentCatalogName)) + .request("application/json") + .header("Authorization", "Bearer " + fixture.adminToken) + .header(REALM_PROPERTY_KEY, realm) + .put(Entity.json(grantResource))) { + assertStatusCode(response, Response.Status.CREATED.getStatusCode()); + } + CatalogGrant grantAccessResource = + new CatalogGrant(CatalogPrivilege.CATALOG_MANAGE_ACCESS, GrantResource.TypeEnum.CATALOG); + try (Response response = + fixture + .client + .target( + String.format( + "%s/api/management/v1/catalogs/%s/catalog-roles/custom-admin/grants", + baseUri, currentCatalogName)) + .request("application/json") + .header("Authorization", "Bearer " + fixture.adminToken) + .header(REALM_PROPERTY_KEY, realm) + .put(Entity.json(grantAccessResource))) { + assertStatusCode(response, Response.Status.CREATED.getStatusCode()); + } + + // Assign this new CatalogRole to the service_admin PrincipalRole + try (Response response = + fixture + .client + .target( + String.format( + "%s/api/management/v1/catalogs/%s/catalog-roles/custom-admin", + baseUri, currentCatalogName)) + .request("application/json") + .header("Authorization", "Bearer " + fixture.adminToken) + .header(REALM_PROPERTY_KEY, realm) + .get()) { + assertStatusCode(response, Response.Status.OK.getStatusCode()); + + CatalogRole catalogRole = response.readEntity(CatalogRole.class); + try (Response assignResponse = + fixture + .client + .target( + String.format( + "%s/api/management/v1/principal-roles/%s/catalog-roles/%s", + baseUri, + fixture.snowmanCredentials.identifier().principalRoleName(), + currentCatalogName)) + .request("application/json") + .header("Authorization", "Bearer " + fixture.adminToken) + .header(REALM_PROPERTY_KEY, realm) + .put(Entity.json(catalogRole))) { + assertStatusCode(assignResponse, Response.Status.CREATED.getStatusCode()); + } + } + + SessionCatalog.SessionContext context = SessionCatalog.SessionContext.createEmpty(); + RESTCatalog restCatalog = + new RESTCatalog( + context, + (config) -> HTTPClient.builder(config).uri(config.get(CatalogProperties.URI)).build()); + + ImmutableMap.Builder propertiesBuilder = + ImmutableMap.builder() + .put(CatalogProperties.URI, baseUri + "/api/catalog") + .put( + OAuth2Properties.CREDENTIAL, + fixture.snowmanCredentials.clientId() + + ":" + + fixture.snowmanCredentials.clientSecret()) + .put(OAuth2Properties.SCOPE, BasePolarisAuthenticator.PRINCIPAL_ROLE_ALL) + .put(CatalogProperties.FILE_IO_IMPL, "org.apache.iceberg.inmemory.InMemoryFileIO") + .put("warehouse", currentCatalogName) + .put("header." + REALM_PROPERTY_KEY, realm) + .putAll(extraProperties); + restCatalog.initialize("polaris", propertiesBuilder.buildKeepingLast()); + return restCatalog; + } + + /** + * Asserts that the response has the expected status code, with a fail message if the assertion + * fails. The response entity is buffered so it can be read multiple times. + * + * @param response The response to check + * @param expectedStatusCode The expected status code + */ + private static void assertStatusCode(Response response, int expectedStatusCode) { + // Buffer the entity so we can read it multiple times + response.bufferEntity(); + + assertThat(response) + .withFailMessage( + "Expected status code %s but got %s with message: %s", + expectedStatusCode, response.getStatus(), response.readEntity(String.class)) + .returns(expectedStatusCode, Response::getStatus); + } +} diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/io/FileIOExceptionsTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/io/FileIOExceptionsTest.java index e78d9a688..9604ffff5 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/io/FileIOExceptionsTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/io/FileIOExceptionsTest.java @@ -82,7 +82,7 @@ public static void beforeAll() { .catalogsApi() .createCatalog( new CreateCatalogRequest(catalog), - services.realmId(), + services.realmContext(), services.securityContext())) { assertThat(res.getStatus()).isEqualTo(201); } @@ -93,7 +93,7 @@ public static void beforeAll() { .createNamespace( FileIOExceptionsTest.catalog, CreateNamespaceRequest.builder().withNamespace(Namespace.of("ns1")).build(), - services.realmId(), + services.realmContext(), services.securityContext())) { assertThat(res.getStatus()).isEqualTo(200); } @@ -113,7 +113,7 @@ private static void requestCreateTable() { services .restApi() .createTable( - catalog, "ns1", request, null, services.realmId(), services.securityContext()); + catalog, "ns1", request, null, services.realmContext(), services.securityContext()); res.close(); } diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/config/DefaultConfigurationStoreTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/config/DefaultConfigurationStoreTest.java index ac2f72132..937dd3429 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/config/DefaultConfigurationStoreTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/config/DefaultConfigurationStoreTest.java @@ -21,7 +21,7 @@ import static org.assertj.core.api.Assertions.assertThat; import java.util.Map; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.service.config.DefaultConfigurationStore; import org.junit.jupiter.api.Test; @@ -31,16 +31,17 @@ public class DefaultConfigurationStoreTest { public void testGetConfiguration() { DefaultConfigurationStore defaultConfigurationStore = new DefaultConfigurationStore(Map.of("key1", 1, "key2", "value")); - RealmId realmId = RealmId.newRealmId("test"); - Object value = defaultConfigurationStore.getConfiguration(realmId, "missingKeyWithoutDefault"); + RealmContext realmContext = () -> "test"; + Object value = + defaultConfigurationStore.getConfiguration(realmContext, "missingKeyWithoutDefault"); assertThat(value).isNull(); Object defaultValue = defaultConfigurationStore.getConfiguration( - realmId, "missingKeyWithDefault", "defaultValue"); + realmContext, "missingKeyWithDefault", "defaultValue"); assertThat(defaultValue).isEqualTo("defaultValue"); - Integer keyOne = defaultConfigurationStore.getConfiguration(realmId, "key1"); + Integer keyOne = defaultConfigurationStore.getConfiguration(realmContext, "key1"); assertThat(keyOne).isEqualTo(1); - String keyTwo = defaultConfigurationStore.getConfiguration(realmId, "key2"); + String keyTwo = defaultConfigurationStore.getConfiguration(realmContext, "key2"); assertThat(keyTwo).isEqualTo("value"); } @@ -62,30 +63,31 @@ public void testGetRealmConfiguration() { Map.of("key1", realm2KeyOneValue, "key2", realm2KeyTwoValue))); // check realm1 values - RealmId realmId = RealmId.newRealmId("realm1"); - Object value = defaultConfigurationStore.getConfiguration(realmId, "missingKeyWithoutDefault"); + RealmContext realmContext = () -> "realm1"; + Object value = + defaultConfigurationStore.getConfiguration(realmContext, "missingKeyWithoutDefault"); assertThat(value).isNull(); Object defaultValue = defaultConfigurationStore.getConfiguration( - realmId, "missingKeyWithDefault", "defaultValue"); + realmContext, "missingKeyWithDefault", "defaultValue"); assertThat(defaultValue).isEqualTo("defaultValue"); - Integer keyOneRealm1 = defaultConfigurationStore.getConfiguration(realmId, "key1"); + Integer keyOneRealm1 = defaultConfigurationStore.getConfiguration(realmContext, "key1"); assertThat(keyOneRealm1).isEqualTo(realm1KeyOneValue); - String keyTwoRealm1 = defaultConfigurationStore.getConfiguration(realmId, "key2"); + String keyTwoRealm1 = defaultConfigurationStore.getConfiguration(realmContext, "key2"); assertThat(keyTwoRealm1).isEqualTo(defaultKeyTwoValue); // check realm2 values - realmId = RealmId.newRealmId("realm2"); - Integer keyOneRealm2 = defaultConfigurationStore.getConfiguration(realmId, "key1"); + realmContext = () -> "realm2"; + Integer keyOneRealm2 = defaultConfigurationStore.getConfiguration(realmContext, "key1"); assertThat(keyOneRealm2).isEqualTo(realm2KeyOneValue); - String keyTwoRealm2 = defaultConfigurationStore.getConfiguration(realmId, "key2"); + String keyTwoRealm2 = defaultConfigurationStore.getConfiguration(realmContext, "key2"); assertThat(keyTwoRealm2).isEqualTo(realm2KeyTwoValue); // realm3 has no realm-overrides, so just returns default values - realmId = RealmId.newRealmId("realm3"); - Integer keyOneRealm3 = defaultConfigurationStore.getConfiguration(realmId, "key1"); + realmContext = () -> "realm3"; + Integer keyOneRealm3 = defaultConfigurationStore.getConfiguration(realmContext, "key1"); assertThat(keyOneRealm3).isEqualTo(defaultKeyOneValue); - String keyTwoRealm3 = defaultConfigurationStore.getConfiguration(realmId, "key2"); + String keyTwoRealm3 = defaultConfigurationStore.getConfiguration(realmContext, "key2"); assertThat(keyTwoRealm3).isEqualTo(defaultKeyTwoValue); } } diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/ratelimiter/TestUtil.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/ratelimiter/TestUtil.java index 35c90e600..80c328804 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/ratelimiter/TestUtil.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/ratelimiter/TestUtil.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.quarkus.ratelimiter; -import static org.apache.polaris.service.context.TestRealmIdResolver.REALM_PROPERTY_KEY; +import static org.apache.polaris.service.context.TestRealmContextResolver.REALM_PROPERTY_KEY; import static org.assertj.core.api.Assertions.assertThat; import jakarta.ws.rs.core.Response; diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/task/ManifestFileCleanupTaskHandlerTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/task/ManifestFileCleanupTaskHandlerTest.java index 1c16b8864..e0932252b 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/task/ManifestFileCleanupTaskHandlerTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/task/ManifestFileCleanupTaskHandlerTest.java @@ -46,7 +46,7 @@ import org.apache.iceberg.io.OutputFile; import org.apache.iceberg.io.PositionOutputStream; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.AsyncTaskType; import org.apache.polaris.core.entity.TaskEntity; import org.apache.polaris.service.task.ManifestFileCleanupTaskHandler; @@ -58,7 +58,7 @@ class ManifestFileCleanupTaskHandlerTest { @Inject PolarisDiagnostics diagnostics; - private final RealmId realmId = RealmId.newRealmId("realmName"); + private final RealmContext realmContext = () -> "realmName"; @Test public void testCleanupFileNotExists() throws IOException { @@ -81,7 +81,7 @@ public void testCleanupFileNotExists() throws IOException { .setName(UUID.randomUUID().toString()) .build(); assertThat(handler.canHandleTask(task)).isTrue(); - assertThat(handler.handleTask(task, realmId)).isTrue(); + assertThat(handler.handleTask(task, realmContext)).isTrue(); } @Test @@ -104,7 +104,7 @@ public void testCleanupFileManifestExistsDataFilesDontExist() throws IOException .setName(UUID.randomUUID().toString()) .build(); assertThat(handler.canHandleTask(task)).isTrue(); - assertThat(handler.handleTask(task, realmId)).isTrue(); + assertThat(handler.handleTask(task, realmContext)).isTrue(); } @Test @@ -142,7 +142,7 @@ public void close() { .setName(UUID.randomUUID().toString()) .build(); assertThat(handler.canHandleTask(task)).isTrue(); - assertThat(handler.handleTask(task, realmId)).isTrue(); + assertThat(handler.handleTask(task, realmContext)).isTrue(); assertThat(TaskUtils.exists(dataFile1Path, fileIO)).isFalse(); assertThat(TaskUtils.exists(dataFile2Path, fileIO)).isFalse(); } @@ -196,7 +196,7 @@ public void deleteFile(String location) { .setName(UUID.randomUUID().toString()) .build(); assertThat(handler.canHandleTask(task)).isTrue(); - assertThat(handler.handleTask(task, realmId)).isTrue(); + assertThat(handler.handleTask(task, realmContext)).isTrue(); assertThat(TaskUtils.exists(dataFile1Path, fileIO)).isFalse(); assertThat(TaskUtils.exists(dataFile2Path, fileIO)).isFalse(); } @@ -288,7 +288,7 @@ public void close() { .build(); assertThat(handler.canHandleTask(task)).isTrue(); - assertThat(handler.handleTask(task, realmId)).isTrue(); + assertThat(handler.handleTask(task, realmContext)).isTrue(); assertThat(TaskUtils.exists(firstMetadataFile, fileIO)).isFalse(); assertThat(TaskUtils.exists(statisticsFile1.path(), fileIO)).isFalse(); @@ -330,7 +330,7 @@ public void testMetadataFileCleanupIfFileNotExist() throws IOException { .setName(UUID.randomUUID().toString()) .build(); assertThat(handler.canHandleTask(task)).isTrue(); - assertThat(handler.handleTask(task, realmId)).isTrue(); + assertThat(handler.handleTask(task, realmContext)).isTrue(); } @Test @@ -390,7 +390,7 @@ public void deleteFile(String location) { CompletableFuture.runAsync( () -> { assertThat(handler.canHandleTask(task)).isTrue(); - handler.handleTask(task, realmId); // this will schedule the batch deletion + handler.handleTask(task, realmContext); // this will schedule the batch deletion }, executor); // Wait for all async tasks to finish diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/task/TableCleanupTaskHandlerTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/task/TableCleanupTaskHandlerTest.java index 5b9032918..fecb7ca8e 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/task/TableCleanupTaskHandlerTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/task/TableCleanupTaskHandlerTest.java @@ -38,7 +38,7 @@ import org.apache.iceberg.io.FileIO; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.AsyncTaskType; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntityType; @@ -60,12 +60,12 @@ class TableCleanupTaskHandlerTest { @Inject PolarisConfigurationStore configurationStore; @Inject PolarisDiagnostics diagnostics; - private final RealmId realmId = RealmId.newRealmId("realmName"); + private final RealmContext realmContext = () -> "realmName"; @Test public void testTableCleanup() throws IOException { PolarisMetaStoreSession metaStoreSession = - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); FileIO fileIO = new InMemoryFileIO(); TableIdentifier tableIdentifier = TableIdentifier.of(Namespace.of("db1", "schema1"), "table1"); TableCleanupTaskHandler handler = @@ -105,11 +105,11 @@ public void testTableCleanup() throws IOException { .build(); Assertions.assertThatPredicate(handler::canHandleTask).accepts(task); - handler.handleTask(task, realmId); + handler.handleTask(task, realmContext); assertThat( metaStoreManagerFactory - .getOrCreateMetaStoreManager(realmId) + .getOrCreateMetaStoreManager(realmContext) .loadTasks(metaStoreSession, "test", 2) .getEntities()) .hasSize(2) @@ -148,7 +148,7 @@ public void testTableCleanup() throws IOException { @Test public void testTableCleanupHandlesAlreadyDeletedMetadata() throws IOException { PolarisMetaStoreSession metaStoreSession = - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); FileIO fileIO = new InMemoryFileIO() { @Override @@ -191,13 +191,13 @@ public void close() { // handle the same task twice // the first one should successfully delete the metadata List results = - List.of(handler.handleTask(task, realmId), handler.handleTask(task, realmId)); + List.of(handler.handleTask(task, realmContext), handler.handleTask(task, realmContext)); assertThat(results).containsExactly(true, true); // both tasks successfully executed, but only one should queue subtasks assertThat( metaStoreManagerFactory - .getOrCreateMetaStoreManager(realmId) + .getOrCreateMetaStoreManager(realmContext) .loadTasks(metaStoreSession, "test", 5) .getEntities()) .hasSize(1); @@ -206,7 +206,7 @@ public void close() { @Test public void testTableCleanupDuplicatesTasksIfFileStillExists() throws IOException { PolarisMetaStoreSession metaStoreSession = - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); FileIO fileIO = new InMemoryFileIO() { @Override @@ -257,13 +257,13 @@ public void close() { // handle the same task twice // the first one should successfully delete the metadata List results = - List.of(handler.handleTask(task, realmId), handler.handleTask(task, realmId)); + List.of(handler.handleTask(task, realmContext), handler.handleTask(task, realmContext)); assertThat(results).containsExactly(true, true); // both tasks successfully executed, but only one should queue subtasks assertThat( metaStoreManagerFactory - .getOrCreateMetaStoreManager(realmId) + .getOrCreateMetaStoreManager(realmContext) .loadTasks(metaStoreSession, "test", 5) .getEntities()) .hasSize(2) @@ -303,7 +303,7 @@ public void close() { @Test public void testTableCleanupMultipleSnapshots() throws IOException { PolarisMetaStoreSession metaStoreSession = - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); FileIO fileIO = new InMemoryFileIO(); TableIdentifier tableIdentifier = TableIdentifier.of(Namespace.of("db1", "schema1"), "table1"); TableCleanupTaskHandler handler = @@ -365,11 +365,11 @@ public void testTableCleanupMultipleSnapshots() throws IOException { .build(); Assertions.assertThatPredicate(handler::canHandleTask).accepts(task); - handler.handleTask(task, realmId); + handler.handleTask(task, realmContext); List entities = metaStoreManagerFactory - .getOrCreateMetaStoreManager(realmId) + .getOrCreateMetaStoreManager(realmContext) .loadTasks(metaStoreSession, "test", 5) .getEntities(); @@ -452,7 +452,7 @@ public void testTableCleanupMultipleSnapshots() throws IOException { @Test public void testTableCleanupMultipleMetadata() throws IOException { PolarisMetaStoreSession metaStoreSession = - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); FileIO fileIO = new InMemoryFileIO(); TableIdentifier tableIdentifier = TableIdentifier.of(Namespace.of("db1", "schema1"), "table1"); TableCleanupTaskHandler handler = @@ -528,11 +528,11 @@ public void testTableCleanupMultipleMetadata() throws IOException { Assertions.assertThatPredicate(handler::canHandleTask).accepts(task); - handler.handleTask(task, realmId); + handler.handleTask(task, realmContext); List entities = metaStoreManagerFactory - .getOrCreateMetaStoreManager(realmId) + .getOrCreateMetaStoreManager(realmContext) .loadTasks(metaStoreSession, "test", 6) .getEntities(); diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/test/PolarisIntegrationTestFixture.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/test/PolarisIntegrationTestFixture.java index ea064ade2..b7e38dd43 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/test/PolarisIntegrationTestFixture.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/test/PolarisIntegrationTestFixture.java @@ -18,7 +18,7 @@ */ package org.apache.polaris.service.quarkus.test; -import static org.apache.polaris.service.context.TestRealmIdResolver.REALM_PROPERTY_KEY; +import static org.apache.polaris.service.context.TestRealmContextResolver.REALM_PROPERTY_KEY; import static org.assertj.core.api.Assertions.assertThat; import com.fasterxml.jackson.core.JsonProcessingException; @@ -35,7 +35,7 @@ import org.apache.polaris.core.admin.model.Principal; import org.apache.polaris.core.admin.model.PrincipalRole; import org.apache.polaris.core.admin.model.PrincipalWithCredentials; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntityConstants; import org.apache.polaris.core.entity.PolarisEntitySubType; import org.apache.polaris.core.entity.PolarisEntityType; @@ -100,12 +100,12 @@ private PolarisPrincipalSecrets fetchAdminSecrets() { List.of(realm), PolarisCredentialsBootstrap.fromEnvironment()); } - RealmId realmId = RealmId.newRealmId(realm); + RealmContext realmContext = () -> realm; PolarisMetaStoreSession metaStoreSession = - helper.metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + helper.metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); PolarisMetaStoreManager metaStoreManager = - helper.metaStoreManagerFactory.getOrCreateMetaStoreManager(realmId); + helper.metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); PolarisMetaStoreManager.EntityResult principal = metaStoreManager.readEntityByName( metaStoreSession, diff --git a/server-templates/api.mustache b/server-templates/api.mustache index bf694284a..c0d2c06cc 100644 --- a/server-templates/api.mustache +++ b/server-templates/api.mustache @@ -55,7 +55,7 @@ import {{javaxPackage}}.ws.rs.core.SecurityContext; import {{javaxPackage}}.inject.Inject; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -107,7 +107,7 @@ public class {{classname}} { @Consumes({ {{#consumes}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/consumes}} }){{/hasConsumes}}{{#hasProduces}} @Produces({ {{#produces}}"{{{mediaType}}}"{{^-last}}, {{/-last}}{{/produces}} }){{/hasProduces}} @Timed("{{metricsPrefix}}.{{baseName}}.{{nickname}}") - public Response {{nickname}}({{#isMultipart}}MultipartFormDataInput input,{{/isMultipart}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{^isMultipart}}{{>formParams}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}@Context @MeterTag(key="realm_id",expression="realm.id") RealmId realmId,@Context SecurityContext securityContext) { + public Response {{nickname}}({{#isMultipart}}MultipartFormDataInput input,{{/isMultipart}}{{#allParams}}{{>queryParams}}{{>pathParams}}{{>headerParams}}{{>bodyParams}}{{^isMultipart}}{{>formParams}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}@Context @MeterTag(key="realm_id",expression="realmIdentifier") RealmContext realmContext,@Context SecurityContext securityContext) { {{! Don't log form or header params in case there are secrets, e.g., OAuth tokens }} LOGGER.atDebug().setMessage("Invoking {{baseName}} with params") .addKeyValue("operation", "{{nickname}}"){{#allParams}}{{^isHeaderParam}}{{^isFormParam}} @@ -115,7 +115,7 @@ public class {{classname}} { .log(); Response ret = - service.{{nickname}}({{#isMultipart}}input,{{/isMultipart}}{{#allParams}}{{^isMultipart}}{{paramName}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}}{{paramName}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}realmId,securityContext); + service.{{nickname}}({{#isMultipart}}input,{{/isMultipart}}{{#allParams}}{{^isMultipart}}{{paramName}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}}{{paramName}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}realmContext,securityContext); LOGGER.debug("Completed execution of {{nickname}} API with status code {}", ret.getStatus()); return ret; } diff --git a/server-templates/apiService.mustache b/server-templates/apiService.mustache index 6489b9536..19e9e8d6c 100644 --- a/server-templates/apiService.mustache +++ b/server-templates/apiService.mustache @@ -35,7 +35,7 @@ import {{javaxPackage}}.validation.Valid; import {{javaxPackage}}.ws.rs.core.Response; import {{javaxPackage}}.ws.rs.core.SecurityContext; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; {{! Note that this template is copied from https://github.com/OpenAPITools/openapi-generator/blob/783e68c7acbbdcbb2282d167d1644b069f12d486/modules/openapi-generator/src/main/resources/JavaJaxRS/resteasy/apiService.mustache @@ -54,7 +54,7 @@ It is here to remove some unsupported imports and to update the default implemen {{#operations}} public interface {{classname}}Service { {{#operation}} - default Response {{nickname}}({{#isMultipart}}MultipartFormDataInput input,{{/isMultipart}}{{#allParams}}{{>serviceQueryParams}}{{>servicePathParams}}{{>serviceHeaderParams}}{{>serviceBodyParams}}{{^isMultipart}}{{>serviceFormParams}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}RealmId realmId,SecurityContext securityContext) { + default Response {{nickname}}({{#isMultipart}}MultipartFormDataInput input,{{/isMultipart}}{{#allParams}}{{>serviceQueryParams}}{{>servicePathParams}}{{>serviceHeaderParams}}{{>serviceBodyParams}}{{^isMultipart}}{{>serviceFormParams}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}RealmContext realmContext,SecurityContext securityContext) { return Response.status(501).build(); // not implemented } {{/operation}} diff --git a/server-templates/apiServiceImpl.mustache b/server-templates/apiServiceImpl.mustache index ce42416e6..4eb50646a 100644 --- a/server-templates/apiServiceImpl.mustache +++ b/server-templates/apiServiceImpl.mustache @@ -36,7 +36,7 @@ import {{javaxPackage}}.validation.Valid; import {{javaxPackage}}.ws.rs.core.Response; import {{javaxPackage}}.ws.rs.core.SecurityContext; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; {{! Note that this template is copied from https://github.com/OpenAPITools/openapi-generator/blob/783e68c7acbbdcbb2282d167d1644b069f12d486/modules/openapi-generator/src/main/resources/JavaJaxRS/resteasy/apiServiceImpl.mustache @@ -55,7 +55,7 @@ It is here to remove some unsupported imports (ApiResponseMessage, openapi.tools {{#operations}} public class {{classname}}ServiceImpl implements {{classname}}Service { {{#operation}} - public Response {{nickname}}({{#isMultipart}}MultipartFormDataInput input,{{/isMultipart}}{{#allParams}}{{>serviceQueryParams}}{{>servicePathParams}}{{>serviceHeaderParams}}{{>serviceBodyParams}}{{^isMultipart}}{{>serviceFormParams}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}RealmId realmId,SecurityContext securityContext) { + public Response {{nickname}}({{#isMultipart}}MultipartFormDataInput input,{{/isMultipart}}{{#allParams}}{{>serviceQueryParams}}{{>servicePathParams}}{{>serviceHeaderParams}}{{>serviceBodyParams}}{{^isMultipart}}{{>serviceFormParams}},{{/isMultipart}}{{#isMultipart}}{{^isFormParam}},{{/isFormParam}}{{/isMultipart}}{{/allParams}}RealmContext realmContext,SecurityContext securityContext) { return Response.status(501).build(); // not implemented } {{/operation}} diff --git a/service/common/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java b/service/common/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java index 4a3f278bd..9d183033b 100644 --- a/service/common/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java +++ b/service/common/src/main/java/org/apache/polaris/service/admin/PolarisAdminService.java @@ -66,7 +66,7 @@ import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.auth.PolarisGrantManager.LoadGrantsResult; import org.apache.polaris.core.catalog.PolarisCatalogHelpers; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.CatalogRoleEntity; import org.apache.polaris.core.entity.NamespaceEntity; @@ -107,7 +107,7 @@ public class PolarisAdminService { private static final Logger LOGGER = LoggerFactory.getLogger(PolarisAdminService.class); - private final RealmId realmId; + private final RealmContext realmContext; private final PolarisMetaStoreSession metaStoreSession; private final PolarisConfigurationStore configurationStore; private final PolarisEntityManager entityManager; @@ -120,7 +120,7 @@ public class PolarisAdminService { private PolarisResolutionManifest resolutionManifest = null; public PolarisAdminService( - RealmId realmId, + RealmContext realmContext, PolarisEntityManager entityManager, PolarisMetaStoreManager metaStoreManager, PolarisMetaStoreSession metaStoreSession, @@ -128,7 +128,7 @@ public PolarisAdminService( PolarisDiagnostics diagServices, SecurityContext securityContext, PolarisAuthorizer authorizer) { - this.realmId = realmId; + this.realmContext = realmContext; this.metaStoreSession = metaStoreSession; this.configurationStore = configurationStore; this.entityManager = entityManager; @@ -176,7 +176,7 @@ private void authorizeBasicRootOperationOrThrow(PolarisAuthorizableOperation op) PolarisResolvedPathWrapper rootContainerWrapper = resolutionManifest.getResolvedRootContainerEntityAsPath(); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedPrincipalRoleEntities(), op, @@ -222,7 +222,7 @@ private void authorizeBasicTopLevelEntityOperationOrThrow( return; } authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -243,7 +243,7 @@ private void authorizeBasicCatalogRoleOperationOrThrow( throw new NotFoundException("CatalogRole does not exist: %s", catalogRoleName); } authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -274,7 +274,7 @@ private void authorizeGrantOnRootContainerToPrincipalRoleOperationOrThrow( principalRoleName, PolarisEntityType.PRINCIPAL_ROLE); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -311,7 +311,7 @@ private void authorizeGrantOnTopLevelEntityToPrincipalRoleOperationOrThrow( principalRoleName, PolarisEntityType.PRINCIPAL_ROLE); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -342,7 +342,7 @@ private void authorizeGrantOnPrincipalRoleToPrincipalOperationOrThrow( resolutionManifest.getResolvedTopLevelEntity(principalName, PolarisEntityType.PRINCIPAL); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -381,7 +381,7 @@ private void authorizeGrantOnCatalogRoleToPrincipalRoleOperationOrThrow( resolutionManifest.getResolvedPath(catalogRoleName, true); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -411,7 +411,7 @@ private void authorizeGrantOnCatalogOperationOrThrow( PolarisResolvedPathWrapper catalogRoleWrapper = resolutionManifest.getResolvedPath(catalogRoleName, true); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -451,7 +451,7 @@ private void authorizeGrantOnNamespaceOperationOrThrow( resolutionManifest.getResolvedPath(catalogRoleName, true); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -496,7 +496,7 @@ private void authorizeGrantOnTableLikeOperationOrThrow( resolutionManifest.getResolvedPath(catalogRoleName, true); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -535,7 +535,7 @@ private String terminateWithSlash(String path) { private boolean catalogOverlapsWithExistingCatalog(CatalogEntity catalogEntity) { boolean allowOverlappingCatalogUrls = configurationStore.getConfiguration( - realmId, PolarisConfiguration.ALLOW_OVERLAPPING_CATALOG_URLS); + realmContext, PolarisConfiguration.ALLOW_OVERLAPPING_CATALOG_URLS); if (allowOverlappingCatalogUrls) { return false; @@ -602,7 +602,8 @@ public void deleteCatalog(String name) { .orElseThrow(() -> new NotFoundException("Catalog %s not found", name)); // TODO: Handle return value in case of concurrent modification boolean cleanup = - configurationStore.getConfiguration(realmId, PolarisConfiguration.CLEANUP_ON_CATALOG_DROP); + configurationStore.getConfiguration( + realmContext, PolarisConfiguration.CLEANUP_ON_CATALOG_DROP); PolarisMetaStoreManager.DropEntityResult dropEntityResult = metaStoreManager.dropEntityIfExists(metaStoreSession, null, entity, Map.of(), cleanup); diff --git a/service/common/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java b/service/common/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java index fb195e95d..4f0a8dc7c 100644 --- a/service/common/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java +++ b/service/common/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java @@ -59,7 +59,7 @@ import org.apache.polaris.core.admin.model.ViewGrant; import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.CatalogRoleEntity; import org.apache.polaris.core.entity.PolarisPrivilege; @@ -105,7 +105,8 @@ public PolarisServiceImpl( this.diagnostics = diagnostics; } - private PolarisAdminService newAdminService(RealmId realmId, SecurityContext securityContext) { + private PolarisAdminService newAdminService( + RealmContext realmContext, SecurityContext securityContext) { AuthenticatedPolarisPrincipal authenticatedPrincipal = (AuthenticatedPolarisPrincipal) securityContext.getUserPrincipal(); if (authenticatedPrincipal == null) { @@ -113,7 +114,7 @@ private PolarisAdminService newAdminService(RealmId realmId, SecurityContext sec } return new PolarisAdminService( - realmId, + realmContext, entityManager, metaStoreManager, session, @@ -126,10 +127,10 @@ private PolarisAdminService newAdminService(RealmId realmId, SecurityContext sec /** From PolarisCatalogsApiService */ @Override public Response createCatalog( - CreateCatalogRequest request, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + CreateCatalogRequest request, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); Catalog catalog = request.getCatalog(); - validateStorageConfig(catalog.getStorageConfigInfo(), realmId); + validateStorageConfig(catalog.getStorageConfigInfo(), realmContext); Catalog newCatalog = new CatalogEntity(adminService.createCatalog(CatalogEntity.fromCatalog(catalog))) .asCatalog(); @@ -137,10 +138,11 @@ public Response createCatalog( return Response.status(Response.Status.CREATED).build(); } - private void validateStorageConfig(StorageConfigInfo storageConfigInfo, RealmId realmId) { + private void validateStorageConfig( + StorageConfigInfo storageConfigInfo, RealmContext realmContext) { List allowedStorageTypes = configurationStore.getConfiguration( - realmId, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES); + realmContext, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES); if (!allowedStorageTypes.contains(storageConfigInfo.getStorageType().name())) { LOGGER .atWarn() @@ -154,16 +156,17 @@ private void validateStorageConfig(StorageConfigInfo storageConfigInfo, RealmId /** From PolarisCatalogsApiService */ @Override public Response deleteCatalog( - String catalogName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + String catalogName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); adminService.deleteCatalog(catalogName); return Response.status(Response.Status.NO_CONTENT).build(); } /** From PolarisCatalogsApiService */ @Override - public Response getCatalog(String catalogName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + public Response getCatalog( + String catalogName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); return Response.ok(adminService.getCatalog(catalogName).asCatalog()).build(); } @@ -172,19 +175,19 @@ public Response getCatalog(String catalogName, RealmId realmId, SecurityContext public Response updateCatalog( String catalogName, UpdateCatalogRequest updateRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); if (updateRequest.getStorageConfigInfo() != null) { - validateStorageConfig(updateRequest.getStorageConfigInfo(), realmId); + validateStorageConfig(updateRequest.getStorageConfigInfo(), realmContext); } return Response.ok(adminService.updateCatalog(catalogName, updateRequest).asCatalog()).build(); } /** From PolarisCatalogsApiService */ @Override - public Response listCatalogs(RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + public Response listCatalogs(RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); List catalogList = adminService.listCatalogs().stream() .map(CatalogEntity::new) @@ -198,8 +201,8 @@ public Response listCatalogs(RealmId realmId, SecurityContext securityContext) { /** From PolarisPrincipalsApiService */ @Override public Response createPrincipal( - CreatePrincipalRequest request, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + CreatePrincipalRequest request, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); PrincipalEntity principal = PrincipalEntity.fromPrincipal(request.getPrincipal()); if (Boolean.TRUE.equals(request.getCredentialRotationRequired())) { principal = @@ -213,8 +216,8 @@ public Response createPrincipal( /** From PolarisPrincipalsApiService */ @Override public Response deletePrincipal( - String principalName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + String principalName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); adminService.deletePrincipal(principalName); return Response.status(Response.Status.NO_CONTENT).build(); } @@ -222,8 +225,8 @@ public Response deletePrincipal( /** From PolarisPrincipalsApiService */ @Override public Response getPrincipal( - String principalName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + String principalName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); return Response.ok(adminService.getPrincipal(principalName).asPrincipal()).build(); } @@ -232,9 +235,9 @@ public Response getPrincipal( public Response updatePrincipal( String principalName, UpdatePrincipalRequest updateRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); return Response.ok(adminService.updatePrincipal(principalName, updateRequest).asPrincipal()) .build(); } @@ -242,15 +245,15 @@ public Response updatePrincipal( /** From PolarisPrincipalsApiService */ @Override public Response rotateCredentials( - String principalName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + String principalName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); return Response.ok(adminService.rotateCredentials(principalName)).build(); } /** From PolarisPrincipalsApiService */ @Override - public Response listPrincipals(RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + public Response listPrincipals(RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); List principalList = adminService.listPrincipals().stream() .map(PrincipalEntity::new) @@ -264,8 +267,10 @@ public Response listPrincipals(RealmId realmId, SecurityContext securityContext) /** From PolarisPrincipalRolesApiService */ @Override public Response createPrincipalRole( - CreatePrincipalRoleRequest request, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + CreatePrincipalRoleRequest request, + RealmContext realmContext, + SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); PrincipalRole newPrincipalRole = new PrincipalRoleEntity( adminService.createPrincipalRole( @@ -278,8 +283,8 @@ public Response createPrincipalRole( /** From PolarisPrincipalRolesApiService */ @Override public Response deletePrincipalRole( - String principalRoleName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + String principalRoleName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); adminService.deletePrincipalRole(principalRoleName); return Response.status(Response.Status.NO_CONTENT).build(); } @@ -287,8 +292,8 @@ public Response deletePrincipalRole( /** From PolarisPrincipalRolesApiService */ @Override public Response getPrincipalRole( - String principalRoleName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + String principalRoleName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); return Response.ok(adminService.getPrincipalRole(principalRoleName).asPrincipalRole()).build(); } @@ -297,9 +302,9 @@ public Response getPrincipalRole( public Response updatePrincipalRole( String principalRoleName, UpdatePrincipalRoleRequest updateRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); return Response.ok( adminService.updatePrincipalRole(principalRoleName, updateRequest).asPrincipalRole()) .build(); @@ -307,8 +312,8 @@ public Response updatePrincipalRole( /** From PolarisPrincipalRolesApiService */ @Override - public Response listPrincipalRoles(RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + public Response listPrincipalRoles(RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); List principalRoleList = adminService.listPrincipalRoles().stream() .map(PrincipalRoleEntity::new) @@ -324,9 +329,9 @@ public Response listPrincipalRoles(RealmId realmId, SecurityContext securityCont public Response createCatalogRole( String catalogName, CreateCatalogRoleRequest request, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); CatalogRole newCatalogRole = new CatalogRoleEntity( adminService.createCatalogRole( @@ -341,9 +346,9 @@ public Response createCatalogRole( public Response deleteCatalogRole( String catalogName, String catalogRoleName, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); adminService.deleteCatalogRole(catalogName, catalogRoleName); return Response.status(Response.Status.NO_CONTENT).build(); } @@ -353,9 +358,9 @@ public Response deleteCatalogRole( public Response getCatalogRole( String catalogName, String catalogRoleName, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); return Response.ok(adminService.getCatalogRole(catalogName, catalogRoleName).asCatalogRole()) .build(); } @@ -366,9 +371,9 @@ public Response updateCatalogRole( String catalogName, String catalogRoleName, UpdateCatalogRoleRequest updateRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); return Response.ok( adminService .updateCatalogRole(catalogName, catalogRoleName, updateRequest) @@ -379,8 +384,8 @@ public Response updateCatalogRole( /** From PolarisCatalogsApiService */ @Override public Response listCatalogRoles( - String catalogName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + String catalogName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); List catalogRoleList = adminService.listCatalogRoles(catalogName).stream() .map(CatalogRoleEntity::new) @@ -396,13 +401,13 @@ public Response listCatalogRoles( public Response assignPrincipalRole( String principalName, GrantPrincipalRoleRequest request, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { LOGGER.info( "Assigning principalRole {} to principal {}", request.getPrincipalRole().getName(), principalName); - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); adminService.assignPrincipalRole(principalName, request.getPrincipalRole().getName()); return Response.status(Response.Status.CREATED).build(); } @@ -412,10 +417,10 @@ public Response assignPrincipalRole( public Response revokePrincipalRole( String principalName, String principalRoleName, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { LOGGER.info("Revoking principalRole {} from principal {}", principalRoleName, principalName); - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); adminService.revokePrincipalRole(principalName, principalRoleName); return Response.status(Response.Status.NO_CONTENT).build(); } @@ -423,8 +428,8 @@ public Response revokePrincipalRole( /** From PolarisPrincipalsApiService */ @Override public Response listPrincipalRolesAssigned( - String principalName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + String principalName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); List principalRoleList = adminService.listPrincipalRolesAssigned(principalName).stream() .map(PrincipalRoleEntity::new) @@ -441,14 +446,14 @@ public Response assignCatalogRoleToPrincipalRole( String principalRoleName, String catalogName, GrantCatalogRoleRequest request, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { LOGGER.info( "Assigning catalogRole {} in catalog {} to principalRole {}", request.getCatalogRole().getName(), catalogName, principalRoleName); - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); adminService.assignCatalogRoleToPrincipalRole( principalRoleName, catalogName, request.getCatalogRole().getName()); return Response.status(Response.Status.CREATED).build(); @@ -460,14 +465,14 @@ public Response revokeCatalogRoleFromPrincipalRole( String principalRoleName, String catalogName, String catalogRoleName, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { LOGGER.info( "Revoking catalogRole {} in catalog {} from principalRole {}", catalogRoleName, catalogName, principalRoleName); - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); adminService.revokeCatalogRoleFromPrincipalRole( principalRoleName, catalogName, catalogRoleName); return Response.status(Response.Status.NO_CONTENT).build(); @@ -476,8 +481,8 @@ public Response revokeCatalogRoleFromPrincipalRole( /** From PolarisPrincipalRolesApiService */ @Override public Response listAssigneePrincipalsForPrincipalRole( - String principalRoleName, RealmId realmId, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + String principalRoleName, RealmContext realmContext, SecurityContext securityContext) { + PolarisAdminService adminService = newAdminService(realmContext, securityContext); List principalList = adminService.listAssigneePrincipalsForPrincipalRole(principalRoleName).stream() .map(PrincipalEntity::new) @@ -493,9 +498,9 @@ public Response listAssigneePrincipalsForPrincipalRole( public Response listCatalogRolesForPrincipalRole( String principalRoleName, String catalogName, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); List catalogRoleList = adminService.listCatalogRolesForPrincipalRole(principalRoleName, catalogName).stream() .map(CatalogRoleEntity::new) @@ -512,14 +517,14 @@ public Response addGrantToCatalogRole( String catalogName, String catalogRoleName, AddGrantRequest grantRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { LOGGER.info( "Adding grant {} to catalogRole {} in catalog {}", grantRequest, catalogRoleName, catalogName); - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); switch (grantRequest.getGrant()) { // The per-securable-type Privilege enums must be exact String match for a subset of all // PolarisPrivilege values. @@ -583,7 +588,7 @@ public Response revokeGrantFromCatalogRole( String catalogRoleName, Boolean cascade, RevokeGrantRequest grantRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { LOGGER.info( "Revoking grant {} from catalogRole {} in catalog {}", @@ -595,7 +600,7 @@ public Response revokeGrantFromCatalogRole( return Response.status(501).build(); // not implemented } - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); switch (grantRequest.getGrant()) { // The per-securable-type Privilege enums must be exact String match for a subset of all // PolarisPrivilege values. @@ -657,9 +662,9 @@ public Response revokeGrantFromCatalogRole( public Response listAssigneePrincipalRolesForCatalogRole( String catalogName, String catalogRoleName, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); List principalRoleList = adminService.listAssigneePrincipalRolesForCatalogRole(catalogName, catalogRoleName).stream() .map(PrincipalRoleEntity::new) @@ -675,9 +680,9 @@ public Response listAssigneePrincipalRolesForCatalogRole( public Response listGrantsForCatalogRole( String catalogName, String catalogRoleName, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - PolarisAdminService adminService = newAdminService(realmId, securityContext); + PolarisAdminService adminService = newAdminService(realmContext, securityContext); List grantList = adminService.listGrantsForCatalogRole(catalogName, catalogRoleName); GrantResources grantResources = new GrantResources(grantList); diff --git a/service/common/src/main/java/org/apache/polaris/service/auth/BasePolarisAuthenticator.java b/service/common/src/main/java/org/apache/polaris/service/auth/BasePolarisAuthenticator.java index 7d188f2f8..2ad132c34 100644 --- a/service/common/src/main/java/org/apache/polaris/service/auth/BasePolarisAuthenticator.java +++ b/service/common/src/main/java/org/apache/polaris/service/auth/BasePolarisAuthenticator.java @@ -26,7 +26,7 @@ import org.apache.iceberg.exceptions.NotAuthorizedException; import org.apache.iceberg.exceptions.ServiceFailureException; import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntitySubType; import org.apache.polaris.core.entity.PolarisEntityType; @@ -39,9 +39,9 @@ /** * Base implementation of {@link Authenticator} constructs a {@link AuthenticatedPolarisPrincipal} * from the token parsed by subclasses. The {@link AuthenticatedPolarisPrincipal} is read from the - * {@link PolarisMetaStoreManager} for the current {@link RealmId}. If the token defines a non-empty - * set of scopes, only the principal roles specified in the scopes will be active for the current - * principal. Only the grants assigned to these roles will be active in the current request. + * {@link PolarisMetaStoreManager} for the current {@link RealmContext}. If the token defines a + * non-empty set of scopes, only the principal roles specified in the scopes will be active for the + * current principal. Only the grants assigned to these roles will be active in the current request. */ public abstract class BasePolarisAuthenticator implements Authenticator { diff --git a/service/common/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java b/service/common/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java index 4c282affe..71f289831 100644 --- a/service/common/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java +++ b/service/common/src/main/java/org/apache/polaris/service/auth/DefaultActiveRolesProvider.java @@ -28,7 +28,7 @@ import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisGrantManager; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PrincipalRoleEntity; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; @@ -47,7 +47,7 @@ public class DefaultActiveRolesProvider implements ActiveRolesProvider { private static final Logger LOGGER = LoggerFactory.getLogger(DefaultActiveRolesProvider.class); - @Inject RealmId realmId; + @Inject RealmContext realmContext; @Inject MetaStoreManagerFactory metaStoreManagerFactory; @Inject PolarisDiagnostics diagnostics; @@ -57,8 +57,8 @@ public Set getActiveRoles(AuthenticatedPolarisPrincipal principal) { loadActivePrincipalRoles( principal.getActivatedPrincipalRoleNames(), principal.getPrincipalEntity(), - metaStoreManagerFactory.getOrCreateMetaStoreManager(realmId), - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get()); + metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext), + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get()); return activeRoles.stream().map(PrincipalRoleEntity::getName).collect(Collectors.toSet()); } diff --git a/service/common/src/main/java/org/apache/polaris/service/auth/DefaultOAuth2ApiService.java b/service/common/src/main/java/org/apache/polaris/service/auth/DefaultOAuth2ApiService.java index edfe2fe01..f35dd370f 100644 --- a/service/common/src/main/java/org/apache/polaris/service/auth/DefaultOAuth2ApiService.java +++ b/service/common/src/main/java/org/apache/polaris/service/auth/DefaultOAuth2ApiService.java @@ -27,7 +27,7 @@ import jakarta.ws.rs.core.SecurityContext; import org.apache.commons.codec.binary.Base64; import org.apache.iceberg.rest.responses.OAuthTokenResponse; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.service.catalog.api.IcebergRestOAuth2ApiService; import org.apache.polaris.service.types.TokenType; import org.slf4j.Logger; @@ -65,10 +65,10 @@ public Response getToken( TokenType subjectTokenType, String actorToken, TokenType actorTokenType, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { - TokenBroker tokenBroker = tokenBrokerFactory.apply(realmId); + TokenBroker tokenBroker = tokenBrokerFactory.apply(realmContext); if (!tokenBroker.supportsGrantType(grantType)) { return OAuthUtils.getResponseFromError(OAuthTokenErrorResponse.Error.unsupported_grant_type); } 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 9f959a219..89e43caba 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,7 +25,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.security.NoSuchAlgorithmException; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.service.auth.AuthenticationConfiguration.TokenBrokerConfiguration; import org.apache.polaris.service.auth.AuthenticationConfiguration.TokenBrokerConfiguration.RSAKeyPairConfiguration; @@ -53,10 +53,10 @@ public JWTRSAKeyPairFactory( } @Override - public TokenBroker apply(RealmId realmId) { + public TokenBroker apply(RealmContext realmContext) { return new JWTRSAKeyPair( - metaStoreManagerFactory.getOrCreateMetaStoreManager(realmId), - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(), + metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext), + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(), (int) tokenBrokerConfiguration.maxTokenGeneration().toSeconds(), keyPairConfiguration.publicKeyFile(), keyPairConfiguration.privateKeyFile()); 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 21f991190..38cdb3663 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 @@ -28,7 +28,7 @@ import java.nio.file.Path; import java.time.Duration; import java.util.function.Supplier; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.service.auth.AuthenticationConfiguration.TokenBrokerConfiguration.SymmetricKeyConfiguration; @@ -58,10 +58,10 @@ public JWTSymmetricKeyFactory( } @Override - public TokenBroker apply(RealmId realmId) { + public TokenBroker apply(RealmContext realmContext) { return new JWTSymmetricKeyBroker( - metaStoreManagerFactory.getOrCreateMetaStoreManager(realmId), - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(), + metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext), + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(), (int) maxTokenGeneration.toSeconds(), secretSupplier); } 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 index e8f16f32c..9f642a2b8 100644 --- 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 @@ -20,7 +20,7 @@ import io.smallrye.common.annotation.Identifier; import jakarta.enterprise.context.ApplicationScoped; -import org.apache.polaris.core.context.RealmId; +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. */ @@ -59,7 +59,7 @@ public DecodedToken verify(String token) { }; @Override - public TokenBroker apply(RealmId realmId) { + public TokenBroker apply(RealmContext realmContext) { return NONE_TOKEN_BROKER; } } diff --git a/service/common/src/main/java/org/apache/polaris/service/auth/TestOAuth2ApiService.java b/service/common/src/main/java/org/apache/polaris/service/auth/TestOAuth2ApiService.java index d04632311..2f53c20e5 100644 --- a/service/common/src/main/java/org/apache/polaris/service/auth/TestOAuth2ApiService.java +++ b/service/common/src/main/java/org/apache/polaris/service/auth/TestOAuth2ApiService.java @@ -28,7 +28,7 @@ import java.util.Objects; import org.apache.iceberg.exceptions.NotAuthorizedException; import org.apache.polaris.core.auth.PolarisSecretsManager.PrincipalSecretsResult; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntitySubType; import org.apache.polaris.core.entity.PolarisEntityType; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; @@ -58,7 +58,7 @@ public Response getToken( TokenType subjectTokenType, String actorToken, TokenType actorTokenType, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Map response = new HashMap<>(); String principalName = getPrincipalName(clientId); @@ -69,7 +69,7 @@ public Response getToken( + ";password:" + clientSecret + ";realm:" - + realmId.id() + + realmContext.getRealmIdentifier() + ";role:" + scope.replaceAll(BasePolarisAuthenticator.PRINCIPAL_ROLE_PREFIX, "")); response.put("token_type", "bearer"); diff --git a/service/common/src/main/java/org/apache/polaris/service/auth/TokenBrokerFactory.java b/service/common/src/main/java/org/apache/polaris/service/auth/TokenBrokerFactory.java index 60624e70a..131f3ed64 100644 --- a/service/common/src/main/java/org/apache/polaris/service/auth/TokenBrokerFactory.java +++ b/service/common/src/main/java/org/apache/polaris/service/auth/TokenBrokerFactory.java @@ -19,10 +19,10 @@ package org.apache.polaris.service.auth; import java.util.function.Function; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; /** * Factory that creates a {@link TokenBroker} for generating and parsing. The {@link TokenBroker} is * created based on the realm context. */ -public interface TokenBrokerFactory extends Function {} +public interface TokenBrokerFactory extends Function {} diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/BasePolarisCatalog.java b/service/common/src/main/java/org/apache/polaris/service/catalog/BasePolarisCatalog.java index 91c18b488..743749bcb 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/BasePolarisCatalog.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/BasePolarisCatalog.java @@ -79,7 +79,7 @@ import org.apache.polaris.core.admin.model.StorageConfigInfo; import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.catalog.PolarisCatalogHelpers; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.NamespaceEntity; import org.apache.polaris.core.entity.PolarisEntity; @@ -156,7 +156,7 @@ public class BasePolarisCatalog extends BaseMetastoreViewCatalog private final PolarisMetaStoreSession metaStoreSession; private final PolarisConfigurationStore configurationStore; private final PolarisDiagnostics diagnostics; - private final RealmId realmId; + private final RealmContext realmContext; private final PolarisResolutionManifestCatalogView resolvedEntityView; private final CatalogEntity catalogEntity; private final TaskExecutor taskExecutor; @@ -174,7 +174,7 @@ public class BasePolarisCatalog extends BaseMetastoreViewCatalog private Map tableDefaultProperties; /** - * @param realmId the current RealmId + * @param realmContext the current RealmContext * @param entityManager provides handle to underlying PolarisMetaStoreManager with which to * perform mutations on entities. * @param resolvedEntityView accessor to resolved entity paths that have been pre-vetted to ensure @@ -182,7 +182,7 @@ public class BasePolarisCatalog extends BaseMetastoreViewCatalog * @param taskExecutor Executor we use to register cleanup task handlers */ public BasePolarisCatalog( - RealmId realmId, + RealmContext realmContext, PolarisEntityManager entityManager, PolarisMetaStoreManager metaStoreManager, PolarisMetaStoreSession metaStoreSession, @@ -192,7 +192,7 @@ public BasePolarisCatalog( SecurityContext securityContext, TaskExecutor taskExecutor, FileIOFactory fileIOFactory) { - this.realmId = realmId; + this.realmContext = realmContext; this.entityManager = entityManager; this.metaStoreManager = metaStoreManager; this.metaStoreSession = metaStoreSession; @@ -452,7 +452,7 @@ public boolean dropTable(TableIdentifier tableIdentifier, boolean purge) { "Scheduled cleanup task {} for table {}", dropEntityResult.getCleanupTaskId(), tableIdentifier); - taskExecutor.addTaskHandlerContext(dropEntityResult.getCleanupTaskId(), realmId); + taskExecutor.addTaskHandlerContext(dropEntityResult.getCleanupTaskId(), realmContext); } return true; @@ -516,7 +516,7 @@ private void createNamespaceInternal( .setBaseLocation(baseLocation) .build(); if (!configurationStore.getConfiguration( - realmId, PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP)) { + realmContext, PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP)) { LOGGER.debug("Validating no overlap for {} with sibling tables or namespaces", namespace); validateNoLocationOverlap( entity.getBaseLocation(), resolvedParent.getRawFullPath(), entity.getName()); @@ -641,7 +641,7 @@ public boolean dropNamespace(Namespace namespace) throws NamespaceNotEmptyExcept leafEntity, Map.of(), configurationStore.getConfiguration( - realmId, PolarisConfiguration.CLEANUP_ON_NAMESPACE_DROP)); + realmContext, PolarisConfiguration.CLEANUP_ON_NAMESPACE_DROP)); if (!dropEntityResult.isSuccess() && dropEntityResult.failedBecauseNotEmpty()) { throw new NamespaceNotEmptyException("Namespace %s is not empty", namespace); @@ -667,7 +667,7 @@ public boolean setProperties(Namespace namespace, Map properties new PolarisEntity.Builder(entity).setProperties(newProperties).build(); if (!configurationStore.getConfiguration( - realmId, PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP)) { + realmContext, PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP)) { LOGGER.debug("Validating no overlap with sibling tables or namespaces"); validateNoLocationOverlap( NamespaceEntity.of(updatedEntity).getBaseLocation(), @@ -821,7 +821,7 @@ public Map getCredentialConfig( return Map.of(); } return FileIOUtil.refreshCredentials( - realmId, + realmContext, entityManager, getCredentialVendor(), metaStoreSession, @@ -905,14 +905,14 @@ private void validateLocationsForTableLike( PolarisResolvedPathWrapper resolvedStorageEntity) { Optional optStorageConfiguration = PolarisStorageConfigurationInfo.forEntityPath( - realmId, configurationStore, diagnostics, resolvedStorageEntity.getRawFullPath()); + realmContext, configurationStore, diagnostics, resolvedStorageEntity.getRawFullPath()); optStorageConfiguration.ifPresentOrElse( storageConfigInfo -> { Map> validationResults = InMemoryStorageIntegration.validateSubpathsOfAllowedLocations( - realmId, + realmContext, configurationStore, storageConfigInfo, Set.of(PolarisStorageActions.ALL), @@ -959,7 +959,7 @@ private void validateLocationsForTableLike( () -> { List allowedStorageTypes = configurationStore.getConfiguration( - realmId, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES); + realmContext, PolarisConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES); if (!allowedStorageTypes.contains(StorageConfigInfo.StorageTypeEnum.FILE.name())) { List invalidLocations = locations.stream() @@ -984,7 +984,7 @@ private void validateNoLocationOverlap( List resolvedNamespace, String location) { if (configurationStore.getConfiguration( - realmId, catalog, PolarisConfiguration.ALLOW_TABLE_LOCATION_OVERLAP)) { + realmContext, catalog, PolarisConfiguration.ALLOW_TABLE_LOCATION_OVERLAP)) { LOGGER.debug("Skipping location overlap validation for identifier '{}'", identifier); } else { // if (entity.getSubType().equals(PolarisEntitySubType.TABLE)) { // TODO - is this necessary for views? overlapping views do not expose subdirectories via the @@ -1351,10 +1351,10 @@ private void validateMetadataFileInTableDir( TableIdentifier identifier, TableMetadata metadata, CatalogEntity catalog) { boolean allowEscape = configurationStore.getConfiguration( - realmId, PolarisConfiguration.ALLOW_EXTERNAL_TABLE_LOCATION); + realmContext, PolarisConfiguration.ALLOW_EXTERNAL_TABLE_LOCATION); if (!allowEscape && !configurationStore.getConfiguration( - realmId, PolarisConfiguration.ALLOW_EXTERNAL_METADATA_FILE_LOCATION)) { + realmContext, PolarisConfiguration.ALLOW_EXTERNAL_METADATA_FILE_LOCATION)) { LOGGER.debug( "Validating base location {} for table {} in metadata file {}", metadata.location(), @@ -1535,7 +1535,7 @@ private FileIO loadFileIOForTableLike( // Reload fileIO based on table specific context FileIO fileIO = fileIOFactory.loadFileIO( - realmId, + realmContext, ioImplClassName, tableProperties, identifier, @@ -1771,7 +1771,7 @@ private void updateTableLike(TableIdentifier identifier, PolarisEntity entity) { if (catalogPath != null && !catalogPath.isEmpty() && purge) { boolean dropWithPurgeEnabled = configurationStore.getConfiguration( - realmId, catalogEntity, PolarisConfiguration.DROP_WITH_PURGE_ENABLED); + realmContext, catalogEntity, PolarisConfiguration.DROP_WITH_PURGE_ENABLED); if (!dropWithPurgeEnabled) { throw new ForbiddenException( String.format( @@ -1983,7 +1983,7 @@ private FileIO loadFileIO(String ioImpl, Map properties) { new PolarisResolvedPathWrapper(List.of(resolvedCatalogEntity)); Set storageActions = Set.of(PolarisStorageActions.ALL); return fileIOFactory.loadFileIO( - realmId, ioImpl, properties, identifier, locations, storageActions, resolvedPath); + realmContext, ioImpl, properties, identifier, locations, storageActions, resolvedPath); } private void blockedUserSpecifiedWriteLocation(Map properties) { @@ -1998,7 +1998,7 @@ private void blockedUserSpecifiedWriteLocation(Map properties) { /** Helper to retrieve dynamic context-based configuration that has a boolean value. */ private Boolean getBooleanContextConfiguration(String configKey, boolean defaultValue) { - return configurationStore.getConfiguration(realmId, configKey, defaultValue); + return configurationStore.getConfiguration(realmContext, configKey, defaultValue); } /** diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/IcebergCatalogAdapter.java b/service/common/src/main/java/org/apache/polaris/service/catalog/IcebergCatalogAdapter.java index 10727a7cf..a46b2af0c 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/IcebergCatalogAdapter.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/IcebergCatalogAdapter.java @@ -58,7 +58,7 @@ import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.persistence.PolarisEntityManager; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; @@ -79,7 +79,7 @@ /** * {@link IcebergRestCatalogApiService} implementation that delegates operations to {@link * org.apache.iceberg.rest.CatalogHandlers} after finding the appropriate {@link Catalog} for the - * current {@link RealmId}. + * current {@link RealmContext}. */ @RequestScoped public class IcebergCatalogAdapter @@ -119,7 +119,7 @@ public class IcebergCatalogAdapter .add(Endpoint.create("POST", ResourcePaths.V1_TRANSACTIONS_COMMIT)) .build(); - private final RealmId realmId; + private final RealmContext realmContext; private final PolarisMetaStoreManager metaStoreManager; private final PolarisEntityManager entityManager; private final PolarisMetaStoreSession session; @@ -131,7 +131,7 @@ public class IcebergCatalogAdapter @Inject public IcebergCatalogAdapter( - RealmId realmId, + RealmContext realmContext, PolarisEntityManager entityManager, PolarisMetaStoreManager metaStoreManager, PolarisMetaStoreSession session, @@ -140,7 +140,7 @@ public IcebergCatalogAdapter( PolarisAuthorizer polarisAuthorizer, TaskExecutor taskExecutor, FileIOFactory fileIOFactory) { - this.realmId = realmId; + this.realmContext = realmContext; this.entityManager = entityManager; this.metaStoreManager = metaStoreManager; this.session = session; @@ -179,7 +179,7 @@ private PolarisCatalogHandlerWrapper newHandlerWrapper( } return new PolarisCatalogHandlerWrapper( - realmId, + realmContext, session, configurationStore, diagnostics, @@ -196,7 +196,7 @@ private PolarisCatalogHandlerWrapper newHandlerWrapper( public Response createNamespace( String prefix, CreateNamespaceRequest createNamespaceRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { return withCatalog( securityContext, @@ -210,7 +210,7 @@ public Response listNamespaces( String pageToken, Integer pageSize, String parent, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Optional namespaceOptional = Optional.ofNullable(parent).map(IcebergCatalogAdapter::decodeNamespace); @@ -223,7 +223,7 @@ public Response listNamespaces( @Override public Response loadNamespaceMetadata( - String prefix, String namespace, RealmId realmId, SecurityContext securityContext) { + String prefix, String namespace, RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); return withCatalog( securityContext, prefix, catalog -> Response.ok(catalog.loadNamespaceMetadata(ns)).build()); @@ -235,7 +235,7 @@ private static Namespace decodeNamespace(String namespace) { @Override public Response namespaceExists( - String prefix, String namespace, RealmId realmId, SecurityContext securityContext) { + String prefix, String namespace, RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); return withCatalog( securityContext, @@ -248,7 +248,7 @@ public Response namespaceExists( @Override public Response dropNamespace( - String prefix, String namespace, RealmId realmId, SecurityContext securityContext) { + String prefix, String namespace, RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); return withCatalog( securityContext, @@ -264,7 +264,7 @@ public Response updateProperties( String prefix, String namespace, UpdateNamespacePropertiesRequest updateNamespacePropertiesRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); return withCatalog( @@ -291,7 +291,7 @@ public Response createTable( String namespace, CreateTableRequest createTableRequest, String accessDelegationMode, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { EnumSet delegationModes = parseAccessDelegationModes(accessDelegationMode); @@ -323,7 +323,7 @@ public Response listTables( String namespace, String pageToken, Integer pageSize, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); return withCatalog( @@ -337,7 +337,7 @@ public Response loadTable( String table, String accessDelegationMode, String snapshots, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { EnumSet delegationModes = parseAccessDelegationModes(accessDelegationMode); @@ -361,7 +361,7 @@ public Response tableExists( String prefix, String namespace, String table, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(table)); @@ -380,7 +380,7 @@ public Response dropTable( String namespace, String table, Boolean purgeRequested, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(table)); @@ -402,7 +402,7 @@ public Response registerTable( String prefix, String namespace, RegisterTableRequest registerTableRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); return withCatalog( @@ -415,7 +415,7 @@ public Response registerTable( public Response renameTable( String prefix, RenameTableRequest renameTableRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { return withCatalog( securityContext, @@ -432,7 +432,7 @@ public Response updateTable( String namespace, String table, CommitTableRequest commitTableRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(table)); @@ -455,7 +455,7 @@ public Response createView( String prefix, String namespace, CreateViewRequest createViewRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); return withCatalog( @@ -470,7 +470,7 @@ public Response listViews( String namespace, String pageToken, Integer pageSize, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); return withCatalog( @@ -482,7 +482,7 @@ public Response loadView( String prefix, String namespace, String view, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(view)); @@ -495,7 +495,7 @@ public Response viewExists( String prefix, String namespace, String view, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(view)); @@ -513,7 +513,7 @@ public Response dropView( String prefix, String namespace, String view, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(view)); @@ -530,7 +530,7 @@ public Response dropView( public Response renameView( String prefix, RenameTableRequest renameTableRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { return withCatalog( securityContext, @@ -547,7 +547,7 @@ public Response replaceView( String namespace, String view, CommitViewRequest commitViewRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(view)); @@ -561,7 +561,7 @@ public Response replaceView( public Response commitTransaction( String prefix, CommitTransactionRequest commitTransactionRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { return withCatalog( securityContext, @@ -578,7 +578,7 @@ public Response reportMetrics( String namespace, String table, ReportMetricsRequest reportMetricsRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { return Response.status(Response.Status.NO_CONTENT).build(); } @@ -589,7 +589,7 @@ public Response sendNotification( String namespace, String table, NotificationRequest notificationRequest, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext) { Namespace ns = decodeNamespace(namespace); TableIdentifier tableIdentifier = TableIdentifier.of(ns, RESTUtil.decodeString(table)); @@ -604,7 +604,8 @@ public Response sendNotification( /** From IcebergRestConfigurationApiService. */ @Override - public Response getConfig(String warehouse, RealmId realmId, SecurityContext securityContext) { + public Response getConfig( + String warehouse, RealmContext realmContext, SecurityContext securityContext) { // 'warehouse' as an input here is catalogName. // 'warehouse' as an output will be treated by the client as a default catalog // storage diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/PolarisCatalogHandlerWrapper.java b/service/common/src/main/java/org/apache/polaris/service/catalog/PolarisCatalogHandlerWrapper.java index ea014f2cc..26befcedc 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/PolarisCatalogHandlerWrapper.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/PolarisCatalogHandlerWrapper.java @@ -82,7 +82,7 @@ import org.apache.polaris.core.auth.PolarisAuthorizableOperation; import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.catalog.PolarisCatalogHelpers; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.CatalogEntity; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntitySubType; @@ -120,7 +120,7 @@ public class PolarisCatalogHandlerWrapper implements AutoCloseable { private static final Logger LOGGER = LoggerFactory.getLogger(PolarisCatalogHandlerWrapper.class); - private final RealmId realmId; + private final RealmContext realmContext; private final PolarisMetaStoreSession session; private final PolarisConfigurationStore configurationStore; private final PolarisDiagnostics diagnostics; @@ -143,7 +143,7 @@ public class PolarisCatalogHandlerWrapper implements AutoCloseable { private ViewCatalog viewCatalog = null; public PolarisCatalogHandlerWrapper( - RealmId realmId, + RealmContext realmContext, PolarisMetaStoreSession session, PolarisConfigurationStore configurationStore, PolarisDiagnostics diagnostics, @@ -154,7 +154,7 @@ public PolarisCatalogHandlerWrapper( PolarisAuthorizer authorizer, TaskExecutor taskExecutor, FileIOFactory fileIOFactory) { - this.realmId = realmId; + this.realmContext = realmContext; this.session = session; this.entityManager = entityManager; this.metaStoreManager = metaStoreManager; @@ -208,7 +208,7 @@ private void initializeCatalog() { resolutionManifest.getResolvedReferenceCatalogEntity().getRawLeafEntity(); CatalogEntity catalog = CatalogEntity.of(baseCatalogEntity); - String realm = realmId.id(); + String realm = realmContext.getRealmIdentifier(); String catalogKey = realm + "/" + catalogName; LOGGER.info("Initializing new BasePolarisCatalog for key: {}", catalogKey); @@ -235,7 +235,7 @@ protected Catalog createBasePolarisCatalog(Map catalogProperties BasePolarisCatalog catalogInstance = new BasePolarisCatalog( - realmId, + realmContext, entityManager, metaStoreManager, session, @@ -291,7 +291,7 @@ private void authorizeBasicNamespaceOperationOrThrow( throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace); } authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -325,7 +325,7 @@ private void authorizeCreateNamespaceUnderNamespaceOperationOrThrow( throw new NoSuchNamespaceException("Namespace does not exist: %s", parentNamespace); } authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -363,7 +363,7 @@ private void authorizeCreateTableLikeUnderNamespaceOperationOrThrow( throw new NoSuchNamespaceException("Namespace does not exist: %s", namespace); } authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -396,7 +396,7 @@ private void authorizeBasicTableLikeOperationOrThrow( } } authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -450,7 +450,7 @@ private void authorizeCollectionOfTableLikeOperationOrThrow( "View does not exist: %s", identifier))) .toList(); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -511,7 +511,7 @@ private void authorizeRenameTableLikeOperationOrThrow( PolarisResolvedPathWrapper secondary = resolutionManifest.getResolvedPath(dst.namespace(), true); authorizer.authorizeOrThrow( - realmId, + realmContext, authenticatedPrincipal, resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), op, @@ -872,7 +872,7 @@ public LoadTableResponse loadTableWithAccessDelegation( .getCatalogType() .equals(org.apache.polaris.core.admin.model.Catalog.TypeEnum.EXTERNAL) && !configurationStore.getConfiguration( - realmId, + realmContext, catalogEntity, PolarisConfiguration.ALLOW_EXTERNAL_CATALOG_CREDENTIAL_VENDING)) { throw new ForbiddenException( @@ -1083,7 +1083,8 @@ public void commitTransaction(CommitTransactionRequest commitTransactionRequest) .location() .equals(((MetadataUpdate.SetLocation) singleUpdate).location()) && !configurationStore.getConfiguration( - realmId, PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP)) { + realmContext, + PolarisConfiguration.ALLOW_NAMESPACE_LOCATION_OVERLAP)) { throw new BadRequestException( "Unsupported operation: commitTransaction containing SetLocation" + " for table '%s' and new location '%s'", diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/io/DefaultFileIOFactory.java b/service/common/src/main/java/org/apache/polaris/service/catalog/io/DefaultFileIOFactory.java index a92d4c358..0d8a65569 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/io/DefaultFileIOFactory.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/io/DefaultFileIOFactory.java @@ -32,7 +32,7 @@ import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.io.FileIO; import org.apache.polaris.core.PolarisConfigurationStore; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisEntityManager; @@ -70,7 +70,7 @@ public DefaultFileIOFactory( @Override public FileIO loadFileIO( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull String ioImplClassName, @Nonnull Map properties, @Nonnull TableIdentifier identifier, @@ -78,11 +78,11 @@ public FileIO loadFileIO( @Nonnull Set storageActions, @Nonnull PolarisResolvedPathWrapper resolvedEntityPath) { PolarisEntityManager entityManager = - realmEntityManagerFactory.getOrCreateEntityManager(realmId); + realmEntityManagerFactory.getOrCreateEntityManager(realmContext); PolarisCredentialVendor credentialVendor = - metaStoreManagerFactory.getOrCreateMetaStoreManager(realmId); + metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); PolarisMetaStoreSession metaStoreSession = - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); // Get subcoped creds properties = new HashMap<>(properties); @@ -93,7 +93,7 @@ public FileIO loadFileIO( .map( storageInfo -> FileIOUtil.refreshCredentials( - realmId, + realmContext, entityManager, credentialVendor, metaStoreSession, diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/io/FileIOFactory.java b/service/common/src/main/java/org/apache/polaris/service/catalog/io/FileIOFactory.java index 905441d79..451aaf716 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/io/FileIOFactory.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/io/FileIOFactory.java @@ -24,7 +24,7 @@ import java.util.Set; import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.io.FileIO; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper; import org.apache.polaris.core.storage.PolarisStorageActions; @@ -41,7 +41,7 @@ public interface FileIOFactory { *

This method may obtain subscoped credentials to restrict the FileIO's permissions, ensuring * secure and limited access to the table's data and locations. * - * @param realmId the realm for which the FileIO is being loaded. + * @param realmContext the realm for which the FileIO is being loaded. * @param ioImplClassName the class name of the FileIO implementation to load. * @param properties configuration properties for the FileIO. * @param identifier the table identifier. @@ -51,7 +51,7 @@ public interface FileIOFactory { * @return a configured FileIO instance. */ FileIO loadFileIO( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull String ioImplClassName, @Nonnull Map properties, @Nonnull TableIdentifier identifier, diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/io/FileIOUtil.java b/service/common/src/main/java/org/apache/polaris/service/catalog/io/FileIOUtil.java index 9775ebd84..d21256e45 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/io/FileIOUtil.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/io/FileIOUtil.java @@ -24,7 +24,7 @@ import org.apache.iceberg.catalog.TableIdentifier; import org.apache.polaris.core.PolarisConfiguration; import org.apache.polaris.core.PolarisConfigurationStore; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PolarisEntityConstants; import org.apache.polaris.core.persistence.PolarisEntityManager; @@ -77,7 +77,7 @@ public static Optional findStorageInfoFromHierarchy( * */ public static Map refreshCredentials( - RealmId realmId, + RealmContext realmContext, PolarisEntityManager entityManager, PolarisCredentialVendor credentialVendor, PolarisMetaStoreSession metaStoreSession, @@ -88,7 +88,7 @@ public static Map refreshCredentials( PolarisEntity entity) { boolean skipCredentialSubscopingIndirection = configurationStore.getConfiguration( - realmId, + realmContext, PolarisConfiguration.SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION.key, PolarisConfiguration.SKIP_CREDENTIAL_SUBSCOPING_INDIRECTION.defaultValue); if (skipCredentialSubscopingIndirection) { diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIOFactory.java b/service/common/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIOFactory.java index fe2cd0a75..c98dd4e27 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIOFactory.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/io/WasbTranslatingFileIOFactory.java @@ -27,7 +27,7 @@ import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.io.FileIO; import org.apache.polaris.core.PolarisConfigurationStore; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper; import org.apache.polaris.core.storage.PolarisStorageActions; @@ -52,7 +52,7 @@ public WasbTranslatingFileIOFactory( @Override public FileIO loadFileIO( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull String ioImplClassName, @Nonnull Map properties, @Nonnull TableIdentifier identifier, @@ -61,7 +61,7 @@ public FileIO loadFileIO( @Nonnull PolarisResolvedPathWrapper resolvedEntityPath) { return new WasbTranslatingFileIO( defaultFileIOFactory.loadFileIO( - realmId, + realmContext, ioImplClassName, properties, identifier, diff --git a/service/common/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java b/service/common/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java index def080261..17a0472a4 100644 --- a/service/common/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java +++ b/service/common/src/main/java/org/apache/polaris/service/config/DefaultConfigurationStore.java @@ -24,7 +24,7 @@ import jakarta.inject.Inject; import java.util.Map; import org.apache.polaris.core.PolarisConfigurationStore; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; @ApplicationScoped public class DefaultConfigurationStore implements PolarisConfigurationStore { @@ -53,11 +53,13 @@ public DefaultConfigurationStore( } @Override - public @Nullable T getConfiguration(@Nullable RealmId realmId, String configName) { + public @Nullable T getConfiguration(@Nullable RealmContext realmContext, String configName) { Object rawValue = defaults.get(configName); - if (realmId != null) { + if (realmContext != null) { rawValue = - realmOverrides.getOrDefault(realmId.id(), Map.of()).getOrDefault(configName, rawValue); + realmOverrides + .getOrDefault(realmContext.getRealmIdentifier(), Map.of()) + .getOrDefault(configName, rawValue); } @SuppressWarnings("unchecked") T value = (T) rawValue; diff --git a/service/common/src/main/java/org/apache/polaris/service/config/RealmEntityManagerFactory.java b/service/common/src/main/java/org/apache/polaris/service/config/RealmEntityManagerFactory.java index c38504f32..4936dcf34 100644 --- a/service/common/src/main/java/org/apache/polaris/service/config/RealmEntityManagerFactory.java +++ b/service/common/src/main/java/org/apache/polaris/service/config/RealmEntityManagerFactory.java @@ -23,13 +23,13 @@ import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisEntityManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -/** Gets or creates PolarisEntityManager instances based on config values and RealmId. */ +/** Gets or creates PolarisEntityManager instances based on config values and RealmContext. */ @ApplicationScoped public class RealmEntityManagerFactory { @@ -48,8 +48,8 @@ public RealmEntityManagerFactory( this.diagnostics = diagnostics; } - public PolarisEntityManager getOrCreateEntityManager(RealmId context) { - String realm = context.id(); + public PolarisEntityManager getOrCreateEntityManager(RealmContext context) { + String realm = context.getRealmIdentifier(); LOGGER.debug("Looking up PolarisEntityManager for realm {}", realm); diff --git a/service/common/src/main/java/org/apache/polaris/service/context/DefaultRealmIdResolver.java b/service/common/src/main/java/org/apache/polaris/service/context/DefaultRealmContextResolver.java similarity index 85% rename from service/common/src/main/java/org/apache/polaris/service/context/DefaultRealmIdResolver.java rename to service/common/src/main/java/org/apache/polaris/service/context/DefaultRealmContextResolver.java index 7e6a3b424..c12cdd42b 100644 --- a/service/common/src/main/java/org/apache/polaris/service/context/DefaultRealmIdResolver.java +++ b/service/common/src/main/java/org/apache/polaris/service/context/DefaultRealmContextResolver.java @@ -22,21 +22,21 @@ import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import java.util.Map; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; @ApplicationScoped @Identifier("default") -public class DefaultRealmIdResolver implements RealmIdResolver { +public class DefaultRealmContextResolver implements RealmContextResolver { private final RealmContextConfiguration configuration; @Inject - public DefaultRealmIdResolver(RealmContextConfiguration configuration) { + public DefaultRealmContextResolver(RealmContextConfiguration configuration) { this.configuration = configuration; } @Override - public RealmId resolveRealmContext( + public RealmContext resolveRealmContext( String requestURL, String method, String path, Map headers) { String realm; @@ -50,6 +50,6 @@ public RealmId resolveRealmContext( realm = configuration.defaultRealm(); } - return RealmId.newRealmId(realm); + return () -> realm; } } diff --git a/service/common/src/main/java/org/apache/polaris/service/context/RealmIdResolver.java b/service/common/src/main/java/org/apache/polaris/service/context/RealmContextResolver.java similarity index 88% rename from service/common/src/main/java/org/apache/polaris/service/context/RealmIdResolver.java rename to service/common/src/main/java/org/apache/polaris/service/context/RealmContextResolver.java index 7ee2d8050..dae00f377 100644 --- a/service/common/src/main/java/org/apache/polaris/service/context/RealmIdResolver.java +++ b/service/common/src/main/java/org/apache/polaris/service/context/RealmContextResolver.java @@ -19,10 +19,10 @@ package org.apache.polaris.service.context; import java.util.Map; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; -public interface RealmIdResolver { +public interface RealmContextResolver { - RealmId resolveRealmContext( + RealmContext resolveRealmContext( String requestURL, String method, String path, Map headers); } diff --git a/service/common/src/main/java/org/apache/polaris/service/context/TestRealmIdResolver.java b/service/common/src/main/java/org/apache/polaris/service/context/TestRealmContextResolver.java similarity index 89% rename from service/common/src/main/java/org/apache/polaris/service/context/TestRealmIdResolver.java rename to service/common/src/main/java/org/apache/polaris/service/context/TestRealmContextResolver.java index 6962ae38e..122a5436a 100644 --- a/service/common/src/main/java/org/apache/polaris/service/context/TestRealmIdResolver.java +++ b/service/common/src/main/java/org/apache/polaris/service/context/TestRealmContextResolver.java @@ -24,7 +24,7 @@ import jakarta.inject.Inject; import java.util.HashMap; import java.util.Map; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -36,25 +36,26 @@ */ @ApplicationScoped @Identifier("test") -public class TestRealmIdResolver implements RealmIdResolver { - private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRealmIdResolver.class); +public class TestRealmContextResolver implements RealmContextResolver { + private static final Logger LOGGER = LoggerFactory.getLogger(DefaultRealmContextResolver.class); public static final String REALM_PROPERTY_KEY = "realm"; private final RealmContextConfiguration configuration; @Inject - public TestRealmIdResolver(RealmContextConfiguration configuration) { + public TestRealmContextResolver(RealmContextConfiguration configuration) { this.configuration = configuration; } @Override - public RealmId resolveRealmContext( + public RealmContext resolveRealmContext( String requestURL, String method, String path, Map headers) { // Since this default resolver is strictly for use in test/dev environments, we'll consider // it safe to log all contents. Any "real" resolver used in a prod environment should make // sure to only log non-sensitive contents. - LOGGER.debug("Resolving RealmId for method: {}, path: {}, headers: {}", method, path, headers); + LOGGER.debug( + "Resolving RealmContext for method: {}, path: {}, headers: {}", method, path, headers); Map parsedProperties = parseBearerTokenAsKvPairs(headers); if (!parsedProperties.containsKey(REALM_PROPERTY_KEY) @@ -70,7 +71,7 @@ public RealmId resolveRealmContext( parsedProperties.put(REALM_PROPERTY_KEY, configuration.defaultRealm()); } String realmId = parsedProperties.get(REALM_PROPERTY_KEY); - return RealmId.newRealmId(realmId); + return () -> realmId; } /** diff --git a/service/common/src/main/java/org/apache/polaris/service/persistence/InMemoryPolarisMetaStoreManagerFactory.java b/service/common/src/main/java/org/apache/polaris/service/persistence/InMemoryPolarisMetaStoreManagerFactory.java index c5ce47593..c9bd21f12 100644 --- a/service/common/src/main/java/org/apache/polaris/service/persistence/InMemoryPolarisMetaStoreManagerFactory.java +++ b/service/common/src/main/java/org/apache/polaris/service/persistence/InMemoryPolarisMetaStoreManagerFactory.java @@ -32,7 +32,7 @@ import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.PolarisSecretsManager.PrincipalSecretsResult; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.LocalPolarisMetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisCredentialsBootstrap; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; @@ -76,28 +76,34 @@ protected PolarisTreeMapStore createBackingStore(@Nonnull PolarisDiagnostics dia @Override protected PolarisMetaStoreSession createMetaStoreSession( @Nonnull PolarisTreeMapStore store, - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nullable PolarisCredentialsBootstrap credentialsBootstrap, @Nonnull PolarisDiagnostics diagnostics) { return new PolarisTreeMapMetaStoreSessionImpl( - store, storageIntegration, secretsGenerator(realmId, credentialsBootstrap), diagnostics); + store, + storageIntegration, + secretsGenerator(realmContext, credentialsBootstrap), + diagnostics); } @Override - public synchronized PolarisMetaStoreManager getOrCreateMetaStoreManager(RealmId realmId) { - if (!bootstrappedRealms.contains(realmId.id())) { - bootstrapRealmsAndPrintCredentials(List.of(realmId.id())); + public synchronized PolarisMetaStoreManager getOrCreateMetaStoreManager( + RealmContext realmContext) { + String realmId = realmContext.getRealmIdentifier(); + if (!bootstrappedRealms.contains(realmId)) { + bootstrapRealmsAndPrintCredentials(List.of(realmId)); } - return super.getOrCreateMetaStoreManager(realmId); + return super.getOrCreateMetaStoreManager(realmContext); } @Override public synchronized Supplier getOrCreateSessionSupplier( - RealmId realmId) { - if (!bootstrappedRealms.contains(realmId.id())) { - bootstrapRealmsAndPrintCredentials(List.of(realmId.id())); + RealmContext realmContext) { + String realmId = realmContext.getRealmIdentifier(); + if (!bootstrappedRealms.contains(realmId)) { + bootstrapRealmsAndPrintCredentials(List.of(realmId)); } - return super.getOrCreateSessionSupplier(realmId); + return super.getOrCreateSessionSupplier(realmContext); } private void bootstrapRealmsAndPrintCredentials(List realms) { diff --git a/service/common/src/main/java/org/apache/polaris/service/ratelimiter/DefaultTokenBucketFactory.java b/service/common/src/main/java/org/apache/polaris/service/ratelimiter/DefaultTokenBucketFactory.java index 6b93cf4b4..04d652ad7 100644 --- a/service/common/src/main/java/org/apache/polaris/service/ratelimiter/DefaultTokenBucketFactory.java +++ b/service/common/src/main/java/org/apache/polaris/service/ratelimiter/DefaultTokenBucketFactory.java @@ -25,7 +25,7 @@ import java.time.Duration; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; @ApplicationScoped @Identifier("default") @@ -48,9 +48,10 @@ public DefaultTokenBucketFactory(long requestsPerSecond, Duration window, Clock } @Override - public TokenBucket getOrCreateTokenBucket(RealmId realmId) { + public TokenBucket getOrCreateTokenBucket(RealmContext realmContext) { + String realmId = realmContext.getRealmIdentifier(); return perRealmBuckets.computeIfAbsent( - realmId.id(), + realmId, k -> new TokenBucket( requestsPerSecond, diff --git a/service/common/src/main/java/org/apache/polaris/service/ratelimiter/RealmTokenBucketRateLimiter.java b/service/common/src/main/java/org/apache/polaris/service/ratelimiter/RealmTokenBucketRateLimiter.java index 657a4e557..ee451f8cd 100644 --- a/service/common/src/main/java/org/apache/polaris/service/ratelimiter/RealmTokenBucketRateLimiter.java +++ b/service/common/src/main/java/org/apache/polaris/service/ratelimiter/RealmTokenBucketRateLimiter.java @@ -21,7 +21,7 @@ import io.smallrye.common.annotation.Identifier; import jakarta.enterprise.context.RequestScoped; import jakarta.inject.Inject; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; /** * Rate limiter that maps the request's realm identifier to its own TokenBucket, with its own @@ -32,12 +32,13 @@ public class RealmTokenBucketRateLimiter implements RateLimiter { private final TokenBucketFactory tokenBucketFactory; - private final RealmId realmId; + private final RealmContext realmContext; @Inject - public RealmTokenBucketRateLimiter(TokenBucketFactory tokenBucketFactory, RealmId realmId) { + public RealmTokenBucketRateLimiter( + TokenBucketFactory tokenBucketFactory, RealmContext realmContext) { this.tokenBucketFactory = tokenBucketFactory; - this.realmId = realmId; + this.realmContext = realmContext; } /** @@ -48,6 +49,6 @@ public RealmTokenBucketRateLimiter(TokenBucketFactory tokenBucketFactory, RealmI */ @Override public boolean canProceed() { - return tokenBucketFactory.getOrCreateTokenBucket(realmId).tryAcquire(); + return tokenBucketFactory.getOrCreateTokenBucket(realmContext).tryAcquire(); } } diff --git a/service/common/src/main/java/org/apache/polaris/service/ratelimiter/TokenBucketFactory.java b/service/common/src/main/java/org/apache/polaris/service/ratelimiter/TokenBucketFactory.java index 6b62cc71f..4ea382ba2 100644 --- a/service/common/src/main/java/org/apache/polaris/service/ratelimiter/TokenBucketFactory.java +++ b/service/common/src/main/java/org/apache/polaris/service/ratelimiter/TokenBucketFactory.java @@ -18,10 +18,10 @@ */ package org.apache.polaris.service.ratelimiter; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; /** Factory for creating token buckets per realm. */ public interface TokenBucketFactory { - TokenBucket getOrCreateTokenBucket(RealmId realmId); + TokenBucket getOrCreateTokenBucket(RealmContext realmContext); } diff --git a/service/common/src/main/java/org/apache/polaris/service/storage/PolarisStorageIntegrationProviderImpl.java b/service/common/src/main/java/org/apache/polaris/service/storage/PolarisStorageIntegrationProviderImpl.java index 8b967df7f..cd7759819 100644 --- a/service/common/src/main/java/org/apache/polaris/service/storage/PolarisStorageIntegrationProviderImpl.java +++ b/service/common/src/main/java/org/apache/polaris/service/storage/PolarisStorageIntegrationProviderImpl.java @@ -32,7 +32,7 @@ import java.util.function.Supplier; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.storage.PolarisCredentialProperty; import org.apache.polaris.core.storage.PolarisStorageActions; import org.apache.polaris.core.storage.PolarisStorageConfigurationInfo; @@ -102,7 +102,7 @@ public PolarisStorageIntegrationProviderImpl( new PolarisStorageIntegration<>("file") { @Override public EnumMap getSubscopedCreds( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull PolarisDiagnostics diagnostics, @Nonnull T storageConfig, boolean allowListOperation, @@ -114,7 +114,7 @@ public EnumMap getSubscopedCreds( @Override public @Nonnull Map> validateAccessToLocations( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull T storageConfig, @Nonnull Set actions, @Nonnull Set locations) { diff --git a/service/common/src/main/java/org/apache/polaris/service/task/ManifestFileCleanupTaskHandler.java b/service/common/src/main/java/org/apache/polaris/service/task/ManifestFileCleanupTaskHandler.java index 2cd5c6e40..f37400e6c 100644 --- a/service/common/src/main/java/org/apache/polaris/service/task/ManifestFileCleanupTaskHandler.java +++ b/service/common/src/main/java/org/apache/polaris/service/task/ManifestFileCleanupTaskHandler.java @@ -37,7 +37,7 @@ import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.io.FileIO; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.AsyncTaskType; import org.apache.polaris.core.entity.TaskEntity; import org.slf4j.Logger; @@ -56,12 +56,12 @@ public class ManifestFileCleanupTaskHandler implements TaskHandler { public static final int FILE_DELETION_RETRY_MILLIS = 100; private static final Logger LOGGER = LoggerFactory.getLogger(ManifestFileCleanupTaskHandler.class); - private final BiFunction fileIOSupplier; + private final BiFunction fileIOSupplier; private final ExecutorService executorService; private final PolarisDiagnostics diagnostics; public ManifestFileCleanupTaskHandler( - BiFunction fileIOSupplier, + BiFunction fileIOSupplier, ExecutorService executorService, PolarisDiagnostics diagnostics) { this.fileIOSupplier = fileIOSupplier; @@ -76,10 +76,10 @@ public boolean canHandleTask(TaskEntity task) { } @Override - public boolean handleTask(TaskEntity task, RealmId realmId) { + public boolean handleTask(TaskEntity task, RealmContext realmContext) { ManifestCleanupTask cleanupTask = task.readData(diagnostics, ManifestCleanupTask.class); TableIdentifier tableId = cleanupTask.getTableId(); - try (FileIO authorizedFileIO = fileIOSupplier.apply(task, realmId)) { + try (FileIO authorizedFileIO = fileIOSupplier.apply(task, realmContext)) { if (task.getTaskType(diagnostics) == AsyncTaskType.MANIFEST_FILE_CLEANUP) { ManifestFile manifestFile = decodeManifestData(cleanupTask.getManifestFileData()); return cleanUpManifestFile(manifestFile, authorizedFileIO, tableId); diff --git a/service/common/src/main/java/org/apache/polaris/service/task/TableCleanupTaskHandler.java b/service/common/src/main/java/org/apache/polaris/service/task/TableCleanupTaskHandler.java index 94e8d3946..e675121e9 100644 --- a/service/common/src/main/java/org/apache/polaris/service/task/TableCleanupTaskHandler.java +++ b/service/common/src/main/java/org/apache/polaris/service/task/TableCleanupTaskHandler.java @@ -33,7 +33,7 @@ import org.apache.iceberg.io.FileIO; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.AsyncTaskType; import org.apache.polaris.core.entity.PolarisBaseEntity; import org.apache.polaris.core.entity.PolarisEntity; @@ -59,7 +59,7 @@ public class TableCleanupTaskHandler implements TaskHandler { private final MetaStoreManagerFactory metaStoreManagerFactory; private final PolarisConfigurationStore configurationStore; private final PolarisDiagnostics diagnostics; - private final BiFunction fileIOSupplier; + private final BiFunction fileIOSupplier; private final Clock clock; public TableCleanupTaskHandler( @@ -67,7 +67,7 @@ public TableCleanupTaskHandler( MetaStoreManagerFactory metaStoreManagerFactory, PolarisConfigurationStore configurationStore, PolarisDiagnostics diagnostics, - BiFunction fileIOSupplier, + BiFunction fileIOSupplier, Clock clock) { this.taskExecutor = taskExecutor; this.metaStoreManagerFactory = metaStoreManagerFactory; @@ -89,12 +89,12 @@ private boolean taskEntityIsTable(TaskEntity task) { } @Override - public boolean handleTask(TaskEntity cleanupTask, RealmId realmId) { + public boolean handleTask(TaskEntity cleanupTask, RealmContext realmContext) { PolarisBaseEntity entity = cleanupTask.readData(diagnostics, PolarisBaseEntity.class); PolarisMetaStoreManager metaStoreManager = - metaStoreManagerFactory.getOrCreateMetaStoreManager(realmId); + metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); PolarisMetaStoreSession metaStoreSession = - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); TableLikeEntity tableEntity = TableLikeEntity.of(entity); LOGGER @@ -106,7 +106,7 @@ public boolean handleTask(TaskEntity cleanupTask, RealmId realmId) { // It's likely the cleanupTask has already been completed, but wasn't dropped successfully. // Log a // warning and move on - try (FileIO fileIO = fileIOSupplier.apply(cleanupTask, realmId)) { + try (FileIO fileIO = fileIOSupplier.apply(cleanupTask, realmContext)) { if (!TaskUtils.exists(tableEntity.getMetadataLocation(), fileIO)) { LOGGER .atWarn() @@ -132,7 +132,7 @@ public boolean handleTask(TaskEntity cleanupTask, RealmId realmId) { // TODO: handle partition statistics files Stream metadataFileCleanupTasks = getMetadataTaskStream( - realmId, + realmContext, cleanupTask, tableMetadata, tableEntity, @@ -157,7 +157,7 @@ public boolean handleTask(TaskEntity cleanupTask, RealmId realmId) { .log( "Successfully queued tasks to delete manifests, previous metadata, and statistics files - deleting table metadata file"); for (PolarisBaseEntity createdTask : createdTasks) { - taskExecutor.addTaskHandlerContext(createdTask.getId(), realmId); + taskExecutor.addTaskHandlerContext(createdTask.getId(), realmContext); } fileIO.deleteFile(tableEntity.getMetadataLocation()); @@ -222,7 +222,7 @@ private Stream getManifestTaskStream( } private Stream getMetadataTaskStream( - RealmId realmId, + RealmContext realmContext, TaskEntity cleanupTask, TableMetadata tableMetadata, TableLikeEntity tableEntity, @@ -230,7 +230,7 @@ private Stream getMetadataTaskStream( PolarisMetaStoreSession metaStoreSession, PolarisConfigurationStore configurationStore, Clock clock) { - int batchSize = configurationStore.getConfiguration(realmId, BATCH_SIZE_CONFIG_KEY, 10); + int batchSize = configurationStore.getConfiguration(realmContext, BATCH_SIZE_CONFIG_KEY, 10); return getMetadataFileBatches(tableMetadata, batchSize).stream() .map( metadataBatch -> { diff --git a/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutor.java b/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutor.java index d3e10b815..bcf2f6169 100644 --- a/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutor.java +++ b/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutor.java @@ -18,12 +18,12 @@ */ package org.apache.polaris.service.task; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; /** * Execute a task asynchronously with a provided context. The context must be cloned so that callers * can close their own context and closables */ public interface TaskExecutor { - void addTaskHandlerContext(long taskEntityId, RealmId realmId); + void addTaskHandlerContext(long taskEntityId, RealmContext realmContext); } diff --git a/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutorImpl.java b/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutorImpl.java index 04472decd..ff245dcf4 100644 --- a/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutorImpl.java +++ b/service/common/src/main/java/org/apache/polaris/service/task/TaskExecutorImpl.java @@ -30,7 +30,7 @@ import java.util.concurrent.TimeUnit; import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.PolarisDiagnostics; -import org.apache.polaris.core.context.RealmId; +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.PolarisEntityType; @@ -43,7 +43,7 @@ /** * Given a list of registered {@link TaskHandler}s, execute tasks asynchronously with the provided - * {@link RealmId}. + * {@link RealmContext}. */ public class TaskExecutorImpl implements TaskExecutor { private static final Logger LOGGER = LoggerFactory.getLogger(TaskExecutorImpl.class); @@ -91,36 +91,37 @@ public void addTaskHandler(TaskHandler taskHandler) { } /** - * Register a {@link RealmId} for a specific task id. That task will be loaded and executed - * asynchronously with a copy of the provided {@link RealmId} (because the realm context is a + * Register a {@link RealmContext} for a specific task id. That task will be loaded and executed + * asynchronously with a copy of the provided {@link RealmContext} (because the realm context is a * request-scoped component). */ @Override - public void addTaskHandlerContext(long taskEntityId, RealmId realmId) { - tryHandleTask(taskEntityId, realmId, null, 1); + public void addTaskHandlerContext(long taskEntityId, RealmContext realmContext) { + tryHandleTask(taskEntityId, RealmContext.copyOf(realmContext), null, 1); } private @Nonnull CompletableFuture tryHandleTask( - long taskEntityId, RealmId realmId, Throwable e, int attempt) { + long taskEntityId, RealmContext realmContext, Throwable e, int attempt) { if (attempt > 3) { return CompletableFuture.failedFuture(e); } - return CompletableFuture.runAsync(() -> handleTask(taskEntityId, realmId, attempt), executor) + return CompletableFuture.runAsync( + () -> handleTask(taskEntityId, realmContext, attempt), executor) .exceptionallyComposeAsync( (t) -> { LOGGER.warn("Failed to handle task entity id {}", taskEntityId, t); - return tryHandleTask(taskEntityId, realmId, t, attempt + 1); + return tryHandleTask(taskEntityId, realmContext, t, attempt + 1); }, CompletableFuture.delayedExecutor( TASK_RETRY_DELAY * (long) attempt, TimeUnit.MILLISECONDS, executor)); } - protected void handleTask(long taskEntityId, RealmId realmId, int attempt) { + protected void handleTask(long taskEntityId, RealmContext realmContext, int attempt) { LOGGER.info("Handling task entity id {}", taskEntityId); PolarisMetaStoreManager metaStoreManager = - metaStoreManagerFactory.getOrCreateMetaStoreManager(realmId); + metaStoreManagerFactory.getOrCreateMetaStoreManager(realmContext); PolarisMetaStoreSession metaStoreSession = - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManagerFactory.getOrCreateSessionSupplier(realmContext).get(); PolarisBaseEntity taskEntity = metaStoreManager.loadEntity(metaStoreSession, 0L, taskEntityId).getEntity(); if (!PolarisEntityType.TASK.equals(taskEntity.getType())) { @@ -138,7 +139,7 @@ protected void handleTask(long taskEntityId, RealmId realmId, int attempt) { return; } TaskHandler handler = handlerOpt.get(); - boolean success = handler.handleTask(task, realmId); + boolean success = handler.handleTask(task, realmContext); if (success) { LOGGER .atInfo() diff --git a/service/common/src/main/java/org/apache/polaris/service/task/TaskFileIOSupplier.java b/service/common/src/main/java/org/apache/polaris/service/task/TaskFileIOSupplier.java index 7fa5802e4..5cfbfb547 100644 --- a/service/common/src/main/java/org/apache/polaris/service/task/TaskFileIOSupplier.java +++ b/service/common/src/main/java/org/apache/polaris/service/task/TaskFileIOSupplier.java @@ -28,7 +28,7 @@ import org.apache.iceberg.CatalogProperties; import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.io.FileIO; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisTaskConstants; import org.apache.polaris.core.entity.TableLikeEntity; import org.apache.polaris.core.entity.TaskEntity; @@ -38,7 +38,7 @@ import org.apache.polaris.service.catalog.io.FileIOFactory; @ApplicationScoped -public class TaskFileIOSupplier implements BiFunction { +public class TaskFileIOSupplier implements BiFunction { private final FileIOFactory fileIOFactory; @Inject @@ -47,7 +47,7 @@ public TaskFileIOSupplier(FileIOFactory fileIOFactory) { } @Override - public FileIO apply(TaskEntity task, RealmId realmId) { + public FileIO apply(TaskEntity task, RealmContext realmContext) { Map internalProperties = task.getInternalPropertiesAsMap(); Map properties = new HashMap<>(internalProperties); @@ -66,6 +66,6 @@ public FileIO apply(TaskEntity task, RealmId realmId) { CatalogProperties.FILE_IO_IMPL, "org.apache.iceberg.io.ResolvingFileIO"); return fileIOFactory.loadFileIO( - realmId, ioImpl, properties, identifier, locations, storageActions, resolvedPath); + realmContext, ioImpl, properties, identifier, locations, storageActions, resolvedPath); } } diff --git a/service/common/src/main/java/org/apache/polaris/service/task/TaskHandler.java b/service/common/src/main/java/org/apache/polaris/service/task/TaskHandler.java index 2c8dbfab0..f0d331494 100644 --- a/service/common/src/main/java/org/apache/polaris/service/task/TaskHandler.java +++ b/service/common/src/main/java/org/apache/polaris/service/task/TaskHandler.java @@ -18,11 +18,11 @@ */ package org.apache.polaris.service.task; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.TaskEntity; public interface TaskHandler { boolean canHandleTask(TaskEntity task); - boolean handleTask(TaskEntity task, RealmId realmId); + boolean handleTask(TaskEntity task, RealmContext realmContext); } diff --git a/service/common/src/test/java/org/apache/polaris/service/catalog/io/FileIOFactoryTest.java b/service/common/src/test/java/org/apache/polaris/service/catalog/io/FileIOFactoryTest.java index 6455c1682..326e6a1ee 100644 --- a/service/common/src/test/java/org/apache/polaris/service/catalog/io/FileIOFactoryTest.java +++ b/service/common/src/test/java/org/apache/polaris/service/catalog/io/FileIOFactoryTest.java @@ -40,9 +40,8 @@ import org.apache.polaris.core.admin.model.CreateCatalogRequest; import org.apache.polaris.core.admin.model.PolarisCatalog; import org.apache.polaris.core.admin.model.StorageConfigInfo; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.*; -import org.apache.polaris.core.persistence.*; import org.apache.polaris.service.TestServices; import org.apache.polaris.service.catalog.BasePolarisCatalog; import org.apache.polaris.service.catalog.PolarisPassthroughResolutionView; @@ -71,7 +70,7 @@ public class FileIOFactoryTest { public static final String SECRET_ACCESS_KEY = "secret_access_key"; public static final String SESSION_TOKEN = "session_token"; - private RealmId realmId; + private RealmContext realmContext; private StsClient stsClient; private TestServices testServices; @@ -81,7 +80,7 @@ public void before(TestInfo testInfo) { "realm_%s_%s" .formatted( testInfo.getTestMethod().map(Method::getName).orElse("test"), System.nanoTime()); - realmId = RealmId.newRealmId(realmName); + realmContext = () -> realmName; // Mock get subscoped creds stsClient = Mockito.mock(StsClient.class); @@ -117,7 +116,7 @@ FileIO loadFileIOInternal( testServices = TestServices.builder() .config(Map.of("ALLOW_SPECIFYING_FILE_IO_IMPL", true)) - .realmId(realmId) + .realmId(realmContext) .stsClient(stsClient) .fileIOFactorySupplier(fileIOFactorySupplier) .build(); @@ -154,15 +153,19 @@ public void testLoadFileIOForCleanupTask() { List tasks = testServices .metaStoreManagerFactory() - .getOrCreateMetaStoreManager(realmId) + .getOrCreateMetaStoreManager(realmContext) .loadTasks( - testServices.metaStoreManagerFactory().getOrCreateSessionSupplier(realmId).get(), + testServices + .metaStoreManagerFactory() + .getOrCreateSessionSupplier(realmContext) + .get(), "testExecutor", 1) .getEntities(); Assertions.assertThat(tasks).hasSize(1); TaskEntity taskEntity = TaskEntity.of(tasks.get(0)); - FileIO fileIO = new TaskFileIOSupplier(testServices.fileIOFactory()).apply(taskEntity, realmId); + FileIO fileIO = + new TaskFileIOSupplier(testServices.fileIOFactory()).apply(taskEntity, realmContext); Assertions.assertThat(fileIO).isNotNull().isInstanceOf(InMemoryFileIO.class); // 1. BasePolarisCatalog:doCommit: for writing the table during the creation @@ -199,20 +202,20 @@ BasePolarisCatalog createCatalog(TestServices services) { services .catalogsApi() .createCatalog( - new CreateCatalogRequest(catalog), services.realmId(), services.securityContext()); + new CreateCatalogRequest(catalog), services.realmContext(), services.securityContext()); PolarisPassthroughResolutionView passthroughView = new PolarisPassthroughResolutionView( - services.entityManagerFactory().getOrCreateEntityManager(realmId), - services.metaStoreManagerFactory().getOrCreateSessionSupplier(realmId).get(), + services.entityManagerFactory().getOrCreateEntityManager(realmContext), + services.metaStoreManagerFactory().getOrCreateSessionSupplier(realmContext).get(), services.securityContext(), CATALOG_NAME); BasePolarisCatalog polarisCatalog = new BasePolarisCatalog( - services.realmId(), - services.entityManagerFactory().getOrCreateEntityManager(realmId), - services.metaStoreManagerFactory().getOrCreateMetaStoreManager(realmId), - services.metaStoreManagerFactory().getOrCreateSessionSupplier(realmId).get(), + services.realmContext(), + services.entityManagerFactory().getOrCreateEntityManager(realmContext), + services.metaStoreManagerFactory().getOrCreateMetaStoreManager(realmContext), + services.metaStoreManagerFactory().getOrCreateSessionSupplier(realmContext).get(), services.configurationStore(), services.polarisDiagnostics(), passthroughView, diff --git a/service/common/src/testFixtures/java/org/apache/polaris/service/TestServices.java b/service/common/src/testFixtures/java/org/apache/polaris/service/TestServices.java index c09efeded..8dd43bf37 100644 --- a/service/common/src/testFixtures/java/org/apache/polaris/service/TestServices.java +++ b/service/common/src/testFixtures/java/org/apache/polaris/service/TestServices.java @@ -31,7 +31,7 @@ import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.auth.AuthenticatedPolarisPrincipal; import org.apache.polaris.core.auth.PolarisAuthorizer; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.entity.PolarisEntity; import org.apache.polaris.core.entity.PrincipalEntity; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; @@ -61,13 +61,12 @@ public record TestServices( PolarisDiagnostics polarisDiagnostics, RealmEntityManagerFactory entityManagerFactory, MetaStoreManagerFactory metaStoreManagerFactory, - RealmId realmId, + RealmContext realmContext, SecurityContext securityContext, FileIOFactory fileIOFactory, TaskExecutor taskExecutor) { - private static final RealmId TEST_REALM = - org.apache.polaris.core.context.RealmId.newRealmId("test-realm"); + private static final RealmContext TEST_REALM = () -> "test-realm"; private static final String GCP_ACCESS_TOKEN = "abc"; @FunctionalInterface @@ -83,15 +82,15 @@ public static Builder builder() { } public static class Builder { - private RealmId realmId = TEST_REALM; + private RealmContext realm = TEST_REALM; private Map config = Map.of(); private StsClient stsClient = Mockito.mock(StsClient.class); private FileIOFactorySupplier fileIOFactorySupplier = MeasuredFileIOFactory::new; private Builder() {} - public Builder realmId(RealmId realmId) { - this.realmId = realmId; + public Builder realmId(RealmContext realmId) { + this.realm = realmId; return this; } @@ -131,11 +130,11 @@ public TestServices build() { new RealmEntityManagerFactory(metaStoreManagerFactory, polarisDiagnostics) {}; PolarisEntityManager entityManager = - realmEntityManagerFactory.getOrCreateEntityManager(realmId); + realmEntityManagerFactory.getOrCreateEntityManager(realm); PolarisMetaStoreManager metaStoreManager = - metaStoreManagerFactory.getOrCreateMetaStoreManager(realmId); + metaStoreManagerFactory.getOrCreateMetaStoreManager(realm); PolarisMetaStoreSession metaStoreSession = - metaStoreManagerFactory.getOrCreateSessionSupplier(realmId).get(); + metaStoreManagerFactory.getOrCreateSessionSupplier(realm).get(); FileIOFactory fileIOFactory = fileIOFactorySupplier.apply( @@ -144,7 +143,7 @@ public TestServices build() { TaskExecutor taskExecutor = Mockito.mock(TaskExecutor.class); IcebergRestCatalogApiService service = new IcebergCatalogAdapter( - realmId, + realm, entityManager, metaStoreManager, metaStoreSession, @@ -208,7 +207,7 @@ public String getAuthenticationScheme() { polarisDiagnostics, realmEntityManagerFactory, metaStoreManagerFactory, - realmId, + realm, securityContext, fileIOFactory, taskExecutor); diff --git a/service/common/src/testFixtures/java/org/apache/polaris/service/catalog/io/MeasuredFileIOFactory.java b/service/common/src/testFixtures/java/org/apache/polaris/service/catalog/io/MeasuredFileIOFactory.java index 5826c1dac..943967c9c 100644 --- a/service/common/src/testFixtures/java/org/apache/polaris/service/catalog/io/MeasuredFileIOFactory.java +++ b/service/common/src/testFixtures/java/org/apache/polaris/service/catalog/io/MeasuredFileIOFactory.java @@ -30,7 +30,7 @@ import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.io.FileIO; import org.apache.polaris.core.PolarisConfigurationStore; -import org.apache.polaris.core.context.RealmId; +import org.apache.polaris.core.context.RealmContext; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisResolvedPathWrapper; import org.apache.polaris.core.storage.PolarisStorageActions; @@ -64,7 +64,7 @@ public MeasuredFileIOFactory( @Override public FileIO loadFileIO( - @Nonnull RealmId realmId, + @Nonnull RealmContext realmContext, @Nonnull String ioImplClassName, @Nonnull Map properties, @Nonnull TableIdentifier identifier, @@ -79,7 +79,7 @@ public FileIO loadFileIO( MeasuredFileIO wrapped = new MeasuredFileIO( defaultFileIOFactory.loadFileIO( - realmId, + realmContext, ioImplClassName, properties, identifier, diff --git a/site/content/in-dev/unreleased/configuring-polaris-for-production.md b/site/content/in-dev/unreleased/configuring-polaris-for-production.md index 02f2a4ff5..e02be618f 100644 --- a/site/content/in-dev/unreleased/configuring-polaris-for-production.md +++ b/site/content/in-dev/unreleased/configuring-polaris-for-production.md @@ -23,188 +23,116 @@ type: docs weight: 600 --- -## Configuring Polaris for Production +The default `polaris-server.yml` configuration is intended for development and testing. When deploying Polaris in production, there are several best practices to keep in mind. -The default server configuration is intended for development and testing. When deploying Polaris in -production, there are several best practices to keep in mind. +## Security + +### Configurations Notable configuration used to secure a Polaris deployment are outlined below. -For more information on how to configure Polaris and what configuration options are available, -refer to the [configuration reference page]({{% ref "configuration" %}}). +#### oauth2 -### OAuth2 +> [!WARNING] +> Ensure that the `tokenBroker` setting reflects the token broker specified in `authenticator` below. -Polaris authentication requires specifying a token broker factory type. Two implementations are -supported out of the box: +* Configure [OAuth](https://oauth.net/2/) with this setting. Remove the `TestInlineBearerTokenPolarisAuthenticator` option and uncomment the `DefaultPolarisAuthenticator` authenticator option beneath it. +* Then, configure the token broker. You can configure the token broker to use either [asymmetric](https://github.com/apache/polaris/blob/b482617bf8cc508b37dbedf3ebc81a9408160a5e/polaris-service/src/main/java/io/polaris/service/auth/JWTRSAKeyPair.java#L24) or [symmetric](https://github.com/apache/polaris/blob/b482617bf8cc508b37dbedf3ebc81a9408160a5e/polaris-service/src/main/java/io/polaris/service/auth/JWTSymmetricKeyBroker.java#L23) keys. -- [rsa-key-pair] uses a pair of public and private keys; -- [symmetric-key] uses a shared secret. +#### authenticator.tokenBroker -[rsa-key-pair]: https://github.com/apache/polaris/blob/390f1fa57bb1af24a21aa95fdbff49a46e31add7/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java -[symmetric-key]: https://github.com/apache/polaris/blob/390f1fa57bb1af24a21aa95fdbff49a46e31add7/service/common/src/main/java/org/apache/polaris/service/auth/JWTSymmetricKeyFactory.java +> [!WARNING] +> Ensure that the `tokenBroker` setting reflects the token broker specified in `oauth2` above. -By default, Polaris uses `rsa-key-pair`, with randomly generated keys. +#### callContextResolver & realmContextResolver +* Use these configurations to specify a service that can resolve a realm from bearer tokens. +* The service(s) used here must implement the relevant interfaces (i.e. [CallContextResolver](https://github.com/apache/polaris/blob/8290019c10290a600e40b35ddb1e2f54bf99e120/polaris-service/src/main/java/io/polaris/service/context/CallContextResolver.java#L27) and [RealmContextResolver](https://github.com/apache/polaris/blob/7ce86f10a68a3b56aed766235c88d6027c0de038/polaris-service/src/main/java/io/polaris/service/context/RealmContextResolver.java)). -> [!IMPORTANT] -> The default `rsa-key-pair` configuration is not suitable when deploying many replicas of Polaris, -> as each replica will have its own set of keys. This will cause token validation to fail when a -> request is routed to a different replica than the one that issued the token. +## Metastore Management -It is highly recommended to configure Polaris with previously-generated RSA keys. This can be done -by setting the following properties: +> [!IMPORTANT] +> The default `in-memory` implementation for `metastoreManager` is meant for testing and not suitable for production usage. Instead, consider an implementation such as `eclipse-link` which allows you to store metadata in a remote database. -```properties -polaris.authentication.token-broker.type=rsa-key-pair -polaris.authentication.token-broker.rsa-key-pair.public-key-file=/tmp/public.key -polaris.authentication.token-broker.rsa-key-pair.private-key-file=/tmp/private.key -``` +A Metastore Manger should be configured with an implementation that durably persists Polaris entities. Use the configuration `metaStoreManager` to configure a [MetastoreManager](https://github.com/apache/polaris/blob/627dc602eb15a3258dcc32babf8def34cf6de0e9/polaris-core/src/main/java/io/polaris/core/persistence/PolarisMetaStoreManager.java#L47) implementation where Polaris entities will be persisted. -To generate an RSA key pair, you can use the following commands: +Be sure to secure your metastore backend since it will be storing credentials and catalog metadata. -```shell -openssl genrsa -out private.key 2048 -openssl rsa -in private.key -pubout -out public.key -``` +### Configuring EclipseLink -Alternatively, you can use a symmetric key by setting the following properties: +To use EclipseLink for metastore management, specify the configuration `metaStoreManager.conf-file` to point to an EclipseLink `persistence.xml` file. This file, local to the Polaris service, contains details of the database used for metastore management and the connection settings. For more information, refer to the [metastore documentation]({{% ref "metastores" %}}). -```properties -polaris.authentication.token-broker.type=symmetric-key -polaris.authentication.token-broker.symmetric-key.file=/tmp/symmetric.key -``` +> [!IMPORTANT] +> EclipseLink requires +> 1. Building the JAR for the EclipseLink extension +> 2. Setting the `eclipseLink` gradle property to `true`. +> +> This can be achieved by setting `eclipseLink=true` in the `gradle.properties` file, or by passing the property explicitly while building all JARs, e.g.: `./gradlew -PeclipseLink=true clean assemble` -Note: it is also possible to set the symmetric key secret directly in the configuration file. If -possible, pass the secret as an environment variable to avoid storing sensitive information in the -configuration file: +### Bootstrapping -```properties -polaris.authentication.token-broker.symmetric-key.secret=${POLARIS_SYMMETRIC_KEY_SECRET} -``` +Before using Polaris when using a metastore manager other than `in-memory`, you must **bootstrap** the metastore manager. This is a manual operation that must be performed **only once** in order to prepare the metastore manager to integrate with Polaris. When the metastore manager is bootstrapped, any existing Polaris entities in the metastore manager may be **purged**. -Finally, you can also configure the token broker to use a maximum lifespan by setting the following -property: +By default, Polaris will create randomised `CLIENT_ID` and `CLIENT_SECRET` for the `root` principal and store their hashes in the metastore backend. In order to provide your own credentials for `root` principal (so you can request tokens via `api/catalog/v1/oauth/tokens`), set the `POLARIS_BOOTSTRAP_CREDENTIALS` environment variable as follows: -```properties -polaris.authentication.token-broker.max-token-generation=PT1H ``` - -Typically, in Kubernetes, you would define the keys as a `Secret` and mount them as files in the -container. - -### Realm Id Resolver - -By default, Polaris resolves realms based on incoming request headers. You can configure the realm -context resolver by setting the following properties in `application.properties`: - -```properties -polaris.realm-context.realms=POLARIS,MY-REALM -polaris.realm-context.header-name=Polaris-Realm +export POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,root,my-client-id,my-client-secret ``` -Where: - -- `realms` is a comma-separated list of allowed realms. This setting _must_ be correctly configured. - At least one realm must be specified. -- `header-name` is the name of the header used to resolve the realm; by default, it is - `Polaris-Realm`. +The format of the environment variable is `realm,principal,client_id,client_secret`. You can provide multiple credentials separated by `;`. For example, to provide credentials for two realms `my_realm` and `my_realm2`: -If a request does not contain the specified header, Polaris will use the first realm in the list as -the default realm. In the above example, `POLARIS` is the default realm. - -### Metastore Configuration - -A metastore should be configured with an implementation that durably persists Polaris entities. By -default, Polaris uses an in-memory metastore. - -> [!IMPORTANT] -> The default in-memory metastore is not suitable for production use, as it will lose all data -> when the server is restarted; it is also unusable when multiple Polaris replicas are used. - -To use a durable metastore, you need to switch to the EclipseLink metastore, and provide your own -`persistence.xml` file. This file contains details of the database used for metastore management and -the connection settings. For more information, refer to the [metastore documentation]({{% ref -"metastores" %}}). - -Then, configure Polaris to use your metastore by setting the following properties: - -```properties -polaris.persistence.type=eclipse-link -polaris.persistence.eclipselink.configuration-file=/path/to/persistence.xml -polaris.persistence.eclipselink.persistence-unit=polaris +``` +export POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,root,my-client-id,my-client-secret;my_realm2,root,my-client-id2,my-client-secret2 ``` -Where: +You can also provide credentials for other users too. -- `polaris.persistence.type` indicates that we are using the EclipseLink metastore. -- `polaris.persistence.eclipselink.configuration-file` is the path to the `persistence.xml` file. -- `polaris.persistence.eclipselink.persistence-unit` is the name of the persistence unit to use (in - case the configuration file has many persistence units). +It is also possible to use system properties to provide the credentials: -Typically, in Kubernetes, you would define the `persistence.xml` file as a `ConfigMap` and set the -`polaris.persistence.eclipselink.configuration-file` property to the path of the mounted file in -the container. +``` +java -Dpolaris.bootstrap.credentials=my_realm,root,my-client-id,my-client-secret -jar /path/to/jar/polaris-service-all.jar bootstrap polaris-server.yml +``` -> [!IMPORTANT] -> Be sure to secure your metastore backend since it will be storing sensitive data and catalog -> metadata. +Now, to bootstrap Polaris, run: -### Bootstrapping +```bash +java -jar /path/to/jar/polaris-service-all.jar bootstrap polaris-server.yml +``` -Before using Polaris, you must **bootstrap** the metastore. This is a manual operation that must be -performed **only once** for each realm in order to prepare the metastore to integrate with Polaris. +or in a container: -By default, when bootstrapping a new realm, Polaris will create randomised `CLIENT_ID` and -`CLIENT_SECRET` for the `root` principal and store their hashes in the metastore backend. +```bash +bin/polaris-service bootstrap config/polaris-server.yml +``` -Depending on your database, this may not be convenient as the generated credentials are not stored -in clear text in the database. +Afterward, Polaris can be launched normally: -In order to provide your own credentials for `root` principal (so you can request tokens via -`api/catalog/v1/oauth/tokens`), use the [Polaris Admin Tool]({{% ref "admin-tool" %}}) +```bash +java -jar /path/to/jar/polaris-service-all.jar server polaris-server.yml +``` You can verify the setup by attempting a token issue for the `root` principal: ```bash -curl -X POST http://localhost:8181/api/catalog/v1/oauth/tokens \ - -d "grant_type=client_credentials" \ - -d "client_id=my-client-id" \ - -d "client_secret=my-client-secret" \ - -d "scope=PRINCIPAL_ROLE:ALL" +curl -X POST http://localhost:8181/api/catalog/v1/oauth/tokens -d "grant_type=client_credentials&client_id=my-client-id&client_secret=my-client-secret&scope=PRINCIPAL_ROLE:ALL" ``` -Which should return an access token: +which should return: ```json -{ - "access_token": "...", - "token_type": "bearer", - "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", - "expires_in": 3600 -} +{"access_token":"...","token_type":"bearer","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","expires_in":3600} ``` -If you used a non-default realm name, add the appropriate request header to the `curl` command, -otherwise Polaris will resolve the realm to the first one in the configuration -`polaris.realm-context.realms`. Here is an example to set realm header: - +Note that if you used non-default realm name, for example, `iceberg` instead of `default-realm` in your `polaris-server.yml`, then you should add an appropriate request header: ```bash -curl -X POST http://localhost:8181/api/catalog/v1/oauth/tokens \ - -H "Polaris-Realm: my-realm" \ - -d "grant_type=client_credentials" \ - -d "client_id=my-client-id" \ - -d "client_secret=my-client-secret" \ - -d "scope=PRINCIPAL_ROLE:ALL" +curl -X POST -H 'realm: iceberg' http://localhost:8181/api/catalog/v1/oauth/tokens -d "grant_type=client_credentials&client_id=my-client-id&client_secret=my-client-secret&scope=PRINCIPAL_ROLE:ALL" ``` ## Other Configurations When deploying Polaris in production, consider adjusting the following configurations: -#### `polaris.features.defaults."SUPPORTED_CATALOG_STORAGE_TYPES"` - -- By default, Polaris catalogs are allowed to be located in local filesystem with the `FILE` storage - type. This should be disabled for production systems. -- Use this configuration to additionally disable any other storage types that will not be in use. +#### featureConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES + - By default Polaris catalogs are allowed to be located in local filesystem with the `FILE` storage type. This should be disabled for production systems. + - Use this configuration to additionally disable any other storage types that will not be in use. From d9d7c0a080989db650a8f1d187c88247a6089ba7 Mon Sep 17 00:00:00 2001 From: Yufei Gu Date: Thu, 30 Jan 2025 23:16:45 -0800 Subject: [PATCH 3/3] Resolve comments --- .../service/quarkus/catalog/TestUtil.java | 201 ------------------ .../configuring-polaris-for-production.md | 191 +++++++++++------ 2 files changed, 131 insertions(+), 261 deletions(-) delete mode 100644 quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/TestUtil.java diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/TestUtil.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/TestUtil.java deleted file mode 100644 index 5d4ee4466..000000000 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/TestUtil.java +++ /dev/null @@ -1,201 +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.service.quarkus.catalog; - -import static org.apache.polaris.service.context.TestRealmContextResolver.REALM_PROPERTY_KEY; -import static org.assertj.core.api.Assertions.assertThat; - -import com.google.common.collect.ImmutableMap; -import jakarta.ws.rs.client.Entity; -import jakarta.ws.rs.core.Response; -import java.net.URI; -import java.util.Map; -import org.apache.iceberg.CatalogProperties; -import org.apache.iceberg.catalog.SessionCatalog; -import org.apache.iceberg.rest.HTTPClient; -import org.apache.iceberg.rest.RESTCatalog; -import org.apache.iceberg.rest.auth.OAuth2Properties; -import org.apache.polaris.core.admin.model.Catalog; -import org.apache.polaris.core.admin.model.CatalogGrant; -import org.apache.polaris.core.admin.model.CatalogPrivilege; -import org.apache.polaris.core.admin.model.CatalogRole; -import org.apache.polaris.core.admin.model.GrantResource; -import org.apache.polaris.service.auth.BasePolarisAuthenticator; -import org.apache.polaris.service.quarkus.test.PolarisIntegrationTestFixture; -import org.apache.polaris.service.quarkus.test.TestEnvironment; - -/** Test utilities for catalog tests */ -public class TestUtil { - - /** - * Creates a catalog and grants the snowman principal permission to manage it. - * - * @return A client to interact with the catalog. - */ - public static RESTCatalog createSnowmanManagedCatalog( - TestEnvironment testEnv, - PolarisIntegrationTestFixture fixture, - Catalog catalog, - Map extraProperties) { - return createSnowmanManagedCatalog( - testEnv, fixture, testEnv.baseUri(), fixture.realm, catalog, extraProperties); - } - - /** - * Creates a catalog and grants the snowman principal permission to manage it. - * - * @return A client to interact with the catalog. - */ - public static RESTCatalog createSnowmanManagedCatalog( - TestEnvironment testEnv, - PolarisIntegrationTestFixture fixture, - URI baseUri, - String realm, - Catalog catalog, - Map extraProperties) { - String currentCatalogName = catalog.getName(); - try (Response response = - fixture - .client - .target(String.format("%s/api/management/v1/catalogs", baseUri)) - .request("application/json") - .header("Authorization", "Bearer " + fixture.adminToken) - .header(REALM_PROPERTY_KEY, realm) - .post(Entity.json(catalog))) { - assertStatusCode(response, Response.Status.CREATED.getStatusCode()); - } - - // Create a new CatalogRole that has CATALOG_MANAGE_CONTENT and CATALOG_MANAGE_ACCESS - CatalogRole newRole = new CatalogRole("custom-admin"); - try (Response response = - fixture - .client - .target( - String.format( - "%s/api/management/v1/catalogs/%s/catalog-roles", baseUri, currentCatalogName)) - .request("application/json") - .header("Authorization", "Bearer " + fixture.adminToken) - .header(REALM_PROPERTY_KEY, realm) - .post(Entity.json(newRole))) { - assertStatusCode(response, Response.Status.CREATED.getStatusCode()); - } - CatalogGrant grantResource = - new CatalogGrant(CatalogPrivilege.CATALOG_MANAGE_CONTENT, GrantResource.TypeEnum.CATALOG); - try (Response response = - fixture - .client - .target( - String.format( - "%s/api/management/v1/catalogs/%s/catalog-roles/custom-admin/grants", - baseUri, currentCatalogName)) - .request("application/json") - .header("Authorization", "Bearer " + fixture.adminToken) - .header(REALM_PROPERTY_KEY, realm) - .put(Entity.json(grantResource))) { - assertStatusCode(response, Response.Status.CREATED.getStatusCode()); - } - CatalogGrant grantAccessResource = - new CatalogGrant(CatalogPrivilege.CATALOG_MANAGE_ACCESS, GrantResource.TypeEnum.CATALOG); - try (Response response = - fixture - .client - .target( - String.format( - "%s/api/management/v1/catalogs/%s/catalog-roles/custom-admin/grants", - baseUri, currentCatalogName)) - .request("application/json") - .header("Authorization", "Bearer " + fixture.adminToken) - .header(REALM_PROPERTY_KEY, realm) - .put(Entity.json(grantAccessResource))) { - assertStatusCode(response, Response.Status.CREATED.getStatusCode()); - } - - // Assign this new CatalogRole to the service_admin PrincipalRole - try (Response response = - fixture - .client - .target( - String.format( - "%s/api/management/v1/catalogs/%s/catalog-roles/custom-admin", - baseUri, currentCatalogName)) - .request("application/json") - .header("Authorization", "Bearer " + fixture.adminToken) - .header(REALM_PROPERTY_KEY, realm) - .get()) { - assertStatusCode(response, Response.Status.OK.getStatusCode()); - - CatalogRole catalogRole = response.readEntity(CatalogRole.class); - try (Response assignResponse = - fixture - .client - .target( - String.format( - "%s/api/management/v1/principal-roles/%s/catalog-roles/%s", - baseUri, - fixture.snowmanCredentials.identifier().principalRoleName(), - currentCatalogName)) - .request("application/json") - .header("Authorization", "Bearer " + fixture.adminToken) - .header(REALM_PROPERTY_KEY, realm) - .put(Entity.json(catalogRole))) { - assertStatusCode(assignResponse, Response.Status.CREATED.getStatusCode()); - } - } - - SessionCatalog.SessionContext context = SessionCatalog.SessionContext.createEmpty(); - RESTCatalog restCatalog = - new RESTCatalog( - context, - (config) -> HTTPClient.builder(config).uri(config.get(CatalogProperties.URI)).build()); - - ImmutableMap.Builder propertiesBuilder = - ImmutableMap.builder() - .put(CatalogProperties.URI, baseUri + "/api/catalog") - .put( - OAuth2Properties.CREDENTIAL, - fixture.snowmanCredentials.clientId() - + ":" - + fixture.snowmanCredentials.clientSecret()) - .put(OAuth2Properties.SCOPE, BasePolarisAuthenticator.PRINCIPAL_ROLE_ALL) - .put(CatalogProperties.FILE_IO_IMPL, "org.apache.iceberg.inmemory.InMemoryFileIO") - .put("warehouse", currentCatalogName) - .put("header." + REALM_PROPERTY_KEY, realm) - .putAll(extraProperties); - restCatalog.initialize("polaris", propertiesBuilder.buildKeepingLast()); - return restCatalog; - } - - /** - * Asserts that the response has the expected status code, with a fail message if the assertion - * fails. The response entity is buffered so it can be read multiple times. - * - * @param response The response to check - * @param expectedStatusCode The expected status code - */ - private static void assertStatusCode(Response response, int expectedStatusCode) { - // Buffer the entity so we can read it multiple times - response.bufferEntity(); - - assertThat(response) - .withFailMessage( - "Expected status code %s but got %s with message: %s", - expectedStatusCode, response.getStatus(), response.readEntity(String.class)) - .returns(expectedStatusCode, Response::getStatus); - } -} diff --git a/site/content/in-dev/unreleased/configuring-polaris-for-production.md b/site/content/in-dev/unreleased/configuring-polaris-for-production.md index e02be618f..5b83bbe58 100644 --- a/site/content/in-dev/unreleased/configuring-polaris-for-production.md +++ b/site/content/in-dev/unreleased/configuring-polaris-for-production.md @@ -23,116 +23,187 @@ type: docs weight: 600 --- -The default `polaris-server.yml` configuration is intended for development and testing. When deploying Polaris in production, there are several best practices to keep in mind. +## Configuring Polaris for Production -## Security - -### Configurations +The default server configuration is intended for development and testing. When deploying Polaris in +production, there are several best practices to keep in mind. Notable configuration used to secure a Polaris deployment are outlined below. -#### oauth2 +For more information on how to configure Polaris and what configuration options are available, +refer to the [configuration reference page]({{% ref "configuration" %}}). -> [!WARNING] -> Ensure that the `tokenBroker` setting reflects the token broker specified in `authenticator` below. +### OAuth2 -* Configure [OAuth](https://oauth.net/2/) with this setting. Remove the `TestInlineBearerTokenPolarisAuthenticator` option and uncomment the `DefaultPolarisAuthenticator` authenticator option beneath it. -* Then, configure the token broker. You can configure the token broker to use either [asymmetric](https://github.com/apache/polaris/blob/b482617bf8cc508b37dbedf3ebc81a9408160a5e/polaris-service/src/main/java/io/polaris/service/auth/JWTRSAKeyPair.java#L24) or [symmetric](https://github.com/apache/polaris/blob/b482617bf8cc508b37dbedf3ebc81a9408160a5e/polaris-service/src/main/java/io/polaris/service/auth/JWTSymmetricKeyBroker.java#L23) keys. +Polaris authentication requires specifying a token broker factory type. Two implementations are +supported out of the box: -#### authenticator.tokenBroker +- [rsa-key-pair] uses a pair of public and private keys; +- [symmetric-key] uses a shared secret. -> [!WARNING] -> Ensure that the `tokenBroker` setting reflects the token broker specified in `oauth2` above. +[rsa-key-pair]: https://github.com/apache/polaris/blob/390f1fa57bb1af24a21aa95fdbff49a46e31add7/service/common/src/main/java/org/apache/polaris/service/auth/JWTRSAKeyPairFactory.java +[symmetric-key]: https://github.com/apache/polaris/blob/390f1fa57bb1af24a21aa95fdbff49a46e31add7/service/common/src/main/java/org/apache/polaris/service/auth/JWTSymmetricKeyFactory.java -#### callContextResolver & realmContextResolver -* Use these configurations to specify a service that can resolve a realm from bearer tokens. -* The service(s) used here must implement the relevant interfaces (i.e. [CallContextResolver](https://github.com/apache/polaris/blob/8290019c10290a600e40b35ddb1e2f54bf99e120/polaris-service/src/main/java/io/polaris/service/context/CallContextResolver.java#L27) and [RealmContextResolver](https://github.com/apache/polaris/blob/7ce86f10a68a3b56aed766235c88d6027c0de038/polaris-service/src/main/java/io/polaris/service/context/RealmContextResolver.java)). +By default, Polaris uses `rsa-key-pair`, with randomly generated keys. -## Metastore Management +> [!IMPORTANT] +> The default `rsa-key-pair` configuration is not suitable when deploying many replicas of Polaris, +> as each replica will have its own set of keys. This will cause token validation to fail when a +> request is routed to a different replica than the one that issued the token. -> [!IMPORTANT] -> The default `in-memory` implementation for `metastoreManager` is meant for testing and not suitable for production usage. Instead, consider an implementation such as `eclipse-link` which allows you to store metadata in a remote database. +It is highly recommended to configure Polaris with previously-generated RSA keys. This can be done +by setting the following properties: -A Metastore Manger should be configured with an implementation that durably persists Polaris entities. Use the configuration `metaStoreManager` to configure a [MetastoreManager](https://github.com/apache/polaris/blob/627dc602eb15a3258dcc32babf8def34cf6de0e9/polaris-core/src/main/java/io/polaris/core/persistence/PolarisMetaStoreManager.java#L47) implementation where Polaris entities will be persisted. +```properties +polaris.authentication.token-broker.type=rsa-key-pair +polaris.authentication.token-broker.rsa-key-pair.public-key-file=/tmp/public.key +polaris.authentication.token-broker.rsa-key-pair.private-key-file=/tmp/private.key +``` -Be sure to secure your metastore backend since it will be storing credentials and catalog metadata. +To generate an RSA key pair, you can use the following commands: -### Configuring EclipseLink +```shell +openssl genrsa -out private.key 2048 +openssl rsa -in private.key -pubout -out public.key +``` -To use EclipseLink for metastore management, specify the configuration `metaStoreManager.conf-file` to point to an EclipseLink `persistence.xml` file. This file, local to the Polaris service, contains details of the database used for metastore management and the connection settings. For more information, refer to the [metastore documentation]({{% ref "metastores" %}}). +Alternatively, you can use a symmetric key by setting the following properties: -> [!IMPORTANT] -> EclipseLink requires -> 1. Building the JAR for the EclipseLink extension -> 2. Setting the `eclipseLink` gradle property to `true`. -> -> This can be achieved by setting `eclipseLink=true` in the `gradle.properties` file, or by passing the property explicitly while building all JARs, e.g.: `./gradlew -PeclipseLink=true clean assemble` +```properties +polaris.authentication.token-broker.type=symmetric-key +polaris.authentication.token-broker.symmetric-key.file=/tmp/symmetric.key +``` -### Bootstrapping +Note: it is also possible to set the symmetric key secret directly in the configuration file. If +possible, pass the secret as an environment variable to avoid storing sensitive information in the +configuration file: -Before using Polaris when using a metastore manager other than `in-memory`, you must **bootstrap** the metastore manager. This is a manual operation that must be performed **only once** in order to prepare the metastore manager to integrate with Polaris. When the metastore manager is bootstrapped, any existing Polaris entities in the metastore manager may be **purged**. +```properties +polaris.authentication.token-broker.symmetric-key.secret=${POLARIS_SYMMETRIC_KEY_SECRET} +``` -By default, Polaris will create randomised `CLIENT_ID` and `CLIENT_SECRET` for the `root` principal and store their hashes in the metastore backend. In order to provide your own credentials for `root` principal (so you can request tokens via `api/catalog/v1/oauth/tokens`), set the `POLARIS_BOOTSTRAP_CREDENTIALS` environment variable as follows: +Finally, you can also configure the token broker to use a maximum lifespan by setting the following +property: -``` -export POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,root,my-client-id,my-client-secret +```properties +polaris.authentication.token-broker.max-token-generation=PT1H ``` -The format of the environment variable is `realm,principal,client_id,client_secret`. You can provide multiple credentials separated by `;`. For example, to provide credentials for two realms `my_realm` and `my_realm2`: +Typically, in Kubernetes, you would define the keys as a `Secret` and mount them as files in the +container. -``` -export POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,root,my-client-id,my-client-secret;my_realm2,root,my-client-id2,my-client-secret2 +### Realm Id Resolver + +By default, Polaris resolves realms based on incoming request headers. You can configure the realm +context resolver by setting the following properties in `application.properties`: + +```properties +polaris.realm-context.realms=POLARIS,MY-REALM +polaris.realm-context.header-name=Polaris-Realm ``` -You can also provide credentials for other users too. +Where: -It is also possible to use system properties to provide the credentials: +- `realms` is a comma-separated list of allowed realms. This setting _must_ be correctly configured. + At least one realm must be specified. +- `header-name` is the name of the header used to resolve the realm; by default, it is + `Polaris-Realm`. -``` -java -Dpolaris.bootstrap.credentials=my_realm,root,my-client-id,my-client-secret -jar /path/to/jar/polaris-service-all.jar bootstrap polaris-server.yml -``` +If a request does not contain the specified header, Polaris will use the first realm in the list as +the default realm. In the above example, `POLARIS` is the default realm. -Now, to bootstrap Polaris, run: +### Metastore Configuration -```bash -java -jar /path/to/jar/polaris-service-all.jar bootstrap polaris-server.yml -``` +A metastore should be configured with an implementation that durably persists Polaris entities. By +default, Polaris uses an in-memory metastore. -or in a container: +> [!IMPORTANT] +> The default in-memory metastore is not suitable for production use, as it will lose all data +> when the server is restarted; it is also unusable when multiple Polaris replicas are used. -```bash -bin/polaris-service bootstrap config/polaris-server.yml -``` +To use a durable metastore, you need to switch to the EclipseLink metastore, and provide your own +`persistence.xml` file. This file contains details of the database used for metastore management and +the connection settings. For more information, refer to the [metastore documentation]({{% ref +"metastores" %}}). -Afterward, Polaris can be launched normally: +Then, configure Polaris to use your metastore by setting the following properties: -```bash -java -jar /path/to/jar/polaris-service-all.jar server polaris-server.yml +```properties +polaris.persistence.type=eclipse-link +polaris.persistence.eclipselink.configuration-file=/path/to/persistence.xml +polaris.persistence.eclipselink.persistence-unit=polaris ``` +Where: + +- `polaris.persistence.type` indicates that we are using the EclipseLink metastore. +- `polaris.persistence.eclipselink.configuration-file` is the path to the `persistence.xml` file. +- `polaris.persistence.eclipselink.persistence-unit` is the name of the persistence unit to use (in + case the configuration file has many persistence units). + +Typically, in Kubernetes, you would define the `persistence.xml` file as a `ConfigMap` and set the +`polaris.persistence.eclipselink.configuration-file` property to the path of the mounted file in +the container. + +> [!IMPORTANT] +> Be sure to secure your metastore backend since it will be storing sensitive data and catalog +> metadata. + +### Bootstrapping + +Before using Polaris, you must **bootstrap** the metastore. This is a manual operation that must be +performed **only once** for each realm in order to prepare the metastore to integrate with Polaris. + +By default, when bootstrapping a new realm, Polaris will create randomised `CLIENT_ID` and +`CLIENT_SECRET` for the `root` principal and store their hashes in the metastore backend. + +Depending on your database, this may not be convenient as the generated credentials are not stored +in clear text in the database. + +In order to provide your own credentials for `root` principal (so you can request tokens via +`api/catalog/v1/oauth/tokens`), use the [Polaris Admin Tool]({{% ref "admin-tool" %}}) + You can verify the setup by attempting a token issue for the `root` principal: ```bash -curl -X POST http://localhost:8181/api/catalog/v1/oauth/tokens -d "grant_type=client_credentials&client_id=my-client-id&client_secret=my-client-secret&scope=PRINCIPAL_ROLE:ALL" +curl -X POST http://localhost:8181/api/catalog/v1/oauth/tokens \ + -d "grant_type=client_credentials" \ + -d "client_id=my-client-id" \ + -d "client_secret=my-client-secret" \ + -d "scope=PRINCIPAL_ROLE:ALL" ``` -which should return: +Which should return an access token: ```json -{"access_token":"...","token_type":"bearer","issued_token_type":"urn:ietf:params:oauth:token-type:access_token","expires_in":3600} +{ + "access_token": "...", + "token_type": "bearer", + "issued_token_type": "urn:ietf:params:oauth:token-type:access_token", + "expires_in": 3600 +} ``` -Note that if you used non-default realm name, for example, `iceberg` instead of `default-realm` in your `polaris-server.yml`, then you should add an appropriate request header: +If you used a non-default realm name, add the appropriate request header to the `curl` command, +otherwise Polaris will resolve the realm to the first one in the configuration +`polaris.realm-context.realms`. Here is an example to set realm header: + ```bash -curl -X POST -H 'realm: iceberg' http://localhost:8181/api/catalog/v1/oauth/tokens -d "grant_type=client_credentials&client_id=my-client-id&client_secret=my-client-secret&scope=PRINCIPAL_ROLE:ALL" +curl -X POST http://localhost:8181/api/catalog/v1/oauth/tokens \ + -H "Polaris-Realm: my-realm" \ + -d "grant_type=client_credentials" \ + -d "client_id=my-client-id" \ + -d "client_secret=my-client-secret" \ + -d "scope=PRINCIPAL_ROLE:ALL" ``` ## Other Configurations When deploying Polaris in production, consider adjusting the following configurations: -#### featureConfiguration.SUPPORTED_CATALOG_STORAGE_TYPES - - By default Polaris catalogs are allowed to be located in local filesystem with the `FILE` storage type. This should be disabled for production systems. - - Use this configuration to additionally disable any other storage types that will not be in use. +#### `polaris.features.defaults."SUPPORTED_CATALOG_STORAGE_TYPES"` +- By default, Polaris catalogs are allowed to be located in local filesystem with the `FILE` storage + type. This should be disabled for production systems. +- Use this configuration to additionally disable any other storage types that will not be in use.