From 3d5640f9bdbb50f141eb9f025b0fd806ce65a63a Mon Sep 17 00:00:00 2001 From: Dmitri Bourlatchkov Date: Fri, 8 Nov 2024 13:04:56 -0500 Subject: [PATCH] review: accept initial secrets via env. in the bootstrap command --- .../PrincipalSecretsGenerator.java | 20 +++++++ .../PrincipalSecretsGeneratorTest.java | 59 +++++++++++++++++++ .../service/BootstrapRealmsCommand.java | 6 +- ...nMemoryPolarisMetaStoreManagerFactory.java | 47 ++++----------- 4 files changed, 95 insertions(+), 37 deletions(-) create mode 100644 polaris-core/src/test/java/org/apache/polaris/core/persistence/PrincipalSecretsGeneratorTest.java diff --git a/polaris-core/src/main/java/org/apache/polaris/core/persistence/PrincipalSecretsGenerator.java b/polaris-core/src/main/java/org/apache/polaris/core/persistence/PrincipalSecretsGenerator.java index ffb8a769f..abe364b93 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/persistence/PrincipalSecretsGenerator.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/persistence/PrincipalSecretsGenerator.java @@ -18,6 +18,8 @@ */ package org.apache.polaris.core.persistence; +import java.util.Locale; +import java.util.function.Function; import org.apache.polaris.core.entity.PolarisPrincipalSecrets; import org.jetbrains.annotations.NotNull; @@ -27,4 +29,22 @@ public interface PrincipalSecretsGenerator { PrincipalSecretsGenerator RANDOM_SECRETS = (name, id) -> new PolarisPrincipalSecrets(id); PolarisPrincipalSecrets produceSecrets(@NotNull String principalName, long principalId); + + static Function bootstrap(Function config) { + return (realmName) -> + (principalName, principalId) -> { + String propId = + String.format("POLARIS_BOOTSTRAP_%s_%s_CLIENT_ID", realmName, principalName); + String propSecret = + String.format("POLARIS_BOOTSTRAP_%s_%s_CLIENT_SECRET", realmName, principalName); + String clientId = config.apply(propId.toUpperCase(Locale.ROOT)); + String secret = config.apply(propSecret.toUpperCase(Locale.ROOT)); + if (clientId == null || secret == null) { + return new PolarisPrincipalSecrets(principalId); // random secrets + } else { + // Use the same secondary secret for bootstrapping. It can be rotated later, if required + return new PolarisPrincipalSecrets(principalId, clientId, secret, secret); + } + }; + } } diff --git a/polaris-core/src/test/java/org/apache/polaris/core/persistence/PrincipalSecretsGeneratorTest.java b/polaris-core/src/test/java/org/apache/polaris/core/persistence/PrincipalSecretsGeneratorTest.java new file mode 100644 index 000000000..14aa959d5 --- /dev/null +++ b/polaris-core/src/test/java/org/apache/polaris/core/persistence/PrincipalSecretsGeneratorTest.java @@ -0,0 +1,59 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.polaris.core.persistence; + +import static org.apache.polaris.core.persistence.PrincipalSecretsGenerator.bootstrap; +import static org.assertj.core.api.Assertions.assertThat; + +import java.util.Map; +import org.apache.polaris.core.entity.PolarisPrincipalSecrets; +import org.junit.jupiter.api.Test; + +class PrincipalSecretsGeneratorTest { + + @Test + void testRandomSecrets() { + PolarisPrincipalSecrets s = + bootstrap((name) -> null).apply("test").produceSecrets("name1", 123); + assertThat(s).isNotNull(); + assertThat(s.getPrincipalId()).isEqualTo(123); + assertThat(s.getPrincipalClientId()).isNotNull(); + assertThat(s.getMainSecret()).isNotNull(); + assertThat(s.getSecondarySecret()).isNotNull(); + } + + @Test + void testSecretOverride() { + PolarisPrincipalSecrets s = + bootstrap( + Map.of( + "POLARIS_BOOTSTRAP_TEST-REALM_USER1_CLIENT_ID", + "client1", + "POLARIS_BOOTSTRAP_TEST-REALM_USER1_CLIENT_SECRET", + "sec2") + ::get) + .apply("test-Realm") + .produceSecrets("user1", 123); + assertThat(s).isNotNull(); + assertThat(s.getPrincipalId()).isEqualTo(123); + assertThat(s.getPrincipalClientId()).isEqualTo("client1"); + assertThat(s.getMainSecret()).isEqualTo("sec2"); + assertThat(s.getSecondarySecret()).isEqualTo("sec2"); + } +} diff --git a/polaris-service/src/main/java/org/apache/polaris/service/BootstrapRealmsCommand.java b/polaris-service/src/main/java/org/apache/polaris/service/BootstrapRealmsCommand.java index f727f0b98..a8754488a 100644 --- a/polaris-service/src/main/java/org/apache/polaris/service/BootstrapRealmsCommand.java +++ b/polaris-service/src/main/java/org/apache/polaris/service/BootstrapRealmsCommand.java @@ -18,8 +18,6 @@ */ package org.apache.polaris.service; -import static org.apache.polaris.core.persistence.PrincipalSecretsGenerator.RANDOM_SECRETS; - import io.dropwizard.core.cli.ConfiguredCommand; import io.dropwizard.core.setup.Bootstrap; import java.util.Map; @@ -27,6 +25,7 @@ import org.apache.polaris.core.PolarisConfigurationStore; import org.apache.polaris.core.persistence.MetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; +import org.apache.polaris.core.persistence.PrincipalSecretsGenerator; import org.apache.polaris.service.config.ConfigurationStoreAware; import org.apache.polaris.service.config.PolarisApplicationConfig; import org.apache.polaris.service.context.CallContextResolver; @@ -65,7 +64,8 @@ protected void run( // Execute the bootstrap Map results = metaStoreManagerFactory.bootstrapRealms( - configuration.getDefaultRealms(), name -> RANDOM_SECRETS); + configuration.getDefaultRealms(), + PrincipalSecretsGenerator.bootstrap(System.getenv()::get)); // Log any errors: boolean success = true; diff --git a/polaris-service/src/main/java/org/apache/polaris/service/persistence/InMemoryPolarisMetaStoreManagerFactory.java b/polaris-service/src/main/java/org/apache/polaris/service/persistence/InMemoryPolarisMetaStoreManagerFactory.java index 2c565b7d7..89bcae235 100644 --- a/polaris-service/src/main/java/org/apache/polaris/service/persistence/InMemoryPolarisMetaStoreManagerFactory.java +++ b/polaris-service/src/main/java/org/apache/polaris/service/persistence/InMemoryPolarisMetaStoreManagerFactory.java @@ -18,19 +18,14 @@ */ package org.apache.polaris.service.persistence; -import static org.apache.polaris.core.persistence.PrincipalSecretsGenerator.RANDOM_SECRETS; - import com.fasterxml.jackson.annotation.JsonTypeName; import java.util.Collections; import java.util.HashSet; import java.util.Map; -import java.util.Random; import java.util.Set; -import java.util.function.Function; import java.util.function.Supplier; import org.apache.polaris.core.PolarisDiagnostics; import org.apache.polaris.core.context.RealmContext; -import org.apache.polaris.core.entity.PolarisPrincipalSecrets; import org.apache.polaris.core.persistence.LocalPolarisMetaStoreManagerFactory; import org.apache.polaris.core.persistence.PolarisMetaStoreManager; import org.apache.polaris.core.persistence.PolarisMetaStoreSession; @@ -76,36 +71,20 @@ public synchronized Supplier getOrCreateSessionSupplier } private void bootstrapRealmAndPrintCredentials(String realmId) { - Function rootSecrets = - realm -> { - Random r = new Random(); // does not have to be secure in this case - return (name, id) -> { - // Allow env-based root ID definition for testing - String rootClientId = System.getenv("POLARIS_UNSAFE_BOOTSTRAP_ROOT_CLIENT_ID"); - PolarisPrincipalSecrets secrets; - if (rootClientId == null) { - secrets = RANDOM_SECRETS.produceSecrets(name, id); - } else { - secrets = - new PolarisPrincipalSecrets( - id, - rootClientId, - Long.toHexString(r.nextLong()), - Long.toHexString(r.nextLong())); - } - - String msg = - String.format( - "realm: %1s root principal credentials: %2s:%3s", - realm, secrets.getPrincipalClientId(), secrets.getMainSecret()); - System.out.println(msg); - - return secrets; - }; - }; - Map results = - this.bootstrapRealms(Collections.singletonList(realmId), rootSecrets); + this.bootstrapRealms( + Collections.singletonList(realmId), + PrincipalSecretsGenerator.bootstrap(System.getenv()::get)); bootstrappedRealms.add(realmId); + + PolarisMetaStoreManager.PrincipalSecretsResult principalSecrets = results.get(realmId); + + String msg = + String.format( + "realm: %1s root principal credentials: %2s:%3s", + realmId, + principalSecrets.getPrincipalSecrets().getPrincipalClientId(), + principalSecrets.getPrincipalSecrets().getMainSecret()); + System.out.println(msg); } }