This is an automated email from the ASF dual-hosted git repository.

adutra pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/polaris.git


The following commit(s) were added to refs/heads/main by this push:
     new 4eee4fe0 Simplify credentials format when bootstrapping (#806)
4eee4fe0 is described below

commit 4eee4fe00eb834655f24c57d697764984f3f4731
Author: Alexandre Dutra <[email protected]>
AuthorDate: Wed Jan 22 12:31:57 2025 +0100

    Simplify credentials format when bootstrapping (#806)
    
    It turns out that only `root` credentials can be created
    during bootstrap, since the principal name is hard-coded
    to `PolarisEntityConstants.getRootPrincipalName()`:
    
    
https://github.com/apache/polaris/blob/390f1fa57bb1af24a21aa95fdbff49a46e31add7/polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisMetaStoreManagerImpl.java#L612-L615
    
    Because of that, the second element in the quadruplet 
`realm,name,id,secret` is useless and can be removed.
---
 .../persistence/PolarisCredentialsBootstrap.java   | 61 ++++++++---------
 .../PolarisCredentialsBootstrapTest.java           | 79 +++++++++-------------
 .../persistence/PrincipalSecretsGeneratorTest.java |  5 +-
 .../apache/polaris/admintool/BootstrapCommand.java |  2 +-
 .../polaris/admintool/BootstrapCommandTest.java    |  4 +-
 quarkus/service/build.gradle.kts                   |  5 +-
 .../configuring-polaris-for-production.md          | 10 ++-
 7 files changed, 74 insertions(+), 92 deletions(-)

diff --git 
a/polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrap.java
 
b/polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrap.java
index dc4f5b87..f12f58db 100644
--- 
a/polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrap.java
+++ 
b/polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrap.java
@@ -25,8 +25,8 @@ import java.util.AbstractMap.SimpleEntry;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Map.Entry;
 import java.util.Optional;
+import org.apache.polaris.core.entity.PolarisEntityConstants;
 import org.apache.polaris.core.entity.PolarisPrincipalSecrets;
 
 /**
@@ -54,7 +54,7 @@ public class PolarisCredentialsBootstrap {
    * Parse a string of credentials in the format:
    *
    * <pre>
-   * 
realm1,user1a,client1a,secret1a;realm1,user1b,client1b,secret1b;realm2,user2a,client2a,secret2a;...
+   * realm1,client1,secret1;realm2,client2,secret2;...
    * </pre>
    */
   public static PolarisCredentialsBootstrap fromString(@Nullable String 
credentialsString) {
@@ -65,52 +65,51 @@ public class PolarisCredentialsBootstrap {
 
   /**
    * Parse a list of credentials; each element should be in the format: {@code
-   * realm,principal,clientId,clientSecret}.
+   * realm,clientId,clientSecret}.
    */
   public static PolarisCredentialsBootstrap fromList(List<String> 
credentialsList) {
-    Map<String, Map<String, Map.Entry<String, String>>> credentials = new 
HashMap<>();
-    for (String quadruple : credentialsList) {
-      if (!quadruple.isBlank()) {
-        List<String> parts = 
Splitter.on(',').trimResults().splitToList(quadruple);
-        if (parts.size() != 4) {
-          throw new IllegalArgumentException("Invalid credentials format: " + 
quadruple);
+    Map<String, Map.Entry<String, String>> credentials = new HashMap<>();
+    for (String triplet : credentialsList) {
+      if (!triplet.isBlank()) {
+        List<String> parts = 
Splitter.on(',').trimResults().splitToList(triplet);
+        if (parts.size() != 3) {
+          throw new IllegalArgumentException("Invalid credentials format: " + 
triplet);
         }
         String realmName = parts.get(0);
-        String principalName = parts.get(1);
-        String clientId = parts.get(2);
-        String clientSecret = parts.get(3);
-        credentials
-            .computeIfAbsent(realmName, k -> new HashMap<>())
-            .merge(
-                principalName,
-                new SimpleEntry<>(clientId, clientSecret),
-                (a, b) -> {
-                  throw new IllegalArgumentException("Duplicate principal: " + 
principalName);
-                });
+        String clientId = parts.get(1);
+        String clientSecret = parts.get(2);
+
+        if (credentials.containsKey(realmName)) {
+          throw new IllegalArgumentException("Duplicate realm: " + realmName);
+        }
+        credentials.put(realmName, new SimpleEntry<>(clientId, clientSecret));
       }
     }
     return credentials.isEmpty() ? EMPTY : new 
PolarisCredentialsBootstrap(credentials);
   }
 
-  @VisibleForTesting final Map<String, Map<String, Map.Entry<String, String>>> 
credentials;
+  @VisibleForTesting final Map<String, Map.Entry<String, String>> credentials;
 
-  private PolarisCredentialsBootstrap(Map<String, Map<String, Entry<String, 
String>>> credentials) {
+  private PolarisCredentialsBootstrap(Map<String, Map.Entry<String, String>> 
credentials) {
     this.credentials = credentials;
   }
 
   /**
    * Get the secrets for the specified principal in the specified realm, if 
available among the
-   * credentials that were supplied for bootstrap.
+   * credentials that were supplied for bootstrap. Only credentials for the 
root principal are
+   * supported.
    */
   public Optional<PolarisPrincipalSecrets> getSecrets(
       String realmName, long principalId, String principalName) {
-    return Optional.ofNullable(credentials.get(realmName))
-        .flatMap(principals -> 
Optional.ofNullable(principals.get(principalName)))
-        .map(
-            credentials -> {
-              String clientId = credentials.getKey();
-              String secret = credentials.getValue();
-              return new PolarisPrincipalSecrets(principalId, clientId, 
secret, secret);
-            });
+    if (principalName.equals(PolarisEntityConstants.getRootPrincipalName())) {
+      return Optional.ofNullable(credentials.get(realmName))
+          .map(
+              credentials -> {
+                String clientId = credentials.getKey();
+                String secret = credentials.getValue();
+                return new PolarisPrincipalSecrets(principalId, clientId, 
secret, secret);
+              });
+    }
+    return Optional.empty();
   }
 }
diff --git 
a/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrapTest.java
 
b/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrapTest.java
index 4cba20a7..bd4ecec6 100644
--- 
a/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrapTest.java
+++ 
b/polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrapTest.java
@@ -21,22 +21,11 @@ package org.apache.polaris.core.persistence;
 import static org.assertj.core.api.Assertions.assertThat;
 import static org.assertj.core.api.Assertions.assertThatThrownBy;
 
-import java.util.Comparator;
 import java.util.List;
-import org.apache.polaris.core.entity.PolarisPrincipalSecrets;
 import org.junit.jupiter.api.Test;
 
 class PolarisCredentialsBootstrapTest {
 
-  private final Comparator<PolarisPrincipalSecrets> comparator =
-      (a, b) ->
-          a.getPrincipalId() == b.getPrincipalId()
-                  && a.getPrincipalClientId().equals(b.getPrincipalClientId())
-                  && a.getMainSecret().equals(b.getMainSecret())
-                  && a.getSecondarySecret().equals(b.getSecondarySecret())
-              ? 0
-              : 1;
-
   @Test
   void nullString() {
     PolarisCredentialsBootstrap credentials = 
PolarisCredentialsBootstrap.fromString(null);
@@ -62,51 +51,28 @@ class PolarisCredentialsBootstrapTest {
   }
 
   @Test
-  void duplicatePrincipal() {
+  void duplicateRealm() {
     assertThatThrownBy(
             () ->
                 PolarisCredentialsBootstrap.fromString(
-                    
"realm1,user1a,client1a,secret1a;realm1,user1a,client1b,secret1b"))
-        .hasMessage("Duplicate principal: user1a");
+                    "realm1,client1a,secret1a;realm1,client1b,secret1b"))
+        .hasMessage("Duplicate realm: realm1");
   }
 
   @Test
   void getSecretsValidString() {
     PolarisCredentialsBootstrap credentials =
         PolarisCredentialsBootstrap.fromString(
-            " ; realm1 , user1a , client1a , secret1a ; realm1 , user1b , 
client1b , secret1b ; realm2 , user2a , client2a , secret2a ; ");
-    assertThat(credentials.getSecrets("realm1", 123, "nonexistent")).isEmpty();
-    assertThat(credentials.getSecrets("nonexistent", 123, "user1a")).isEmpty();
-    assertThat(credentials.getSecrets("realm1", 123, "user1a"))
-        .usingValueComparator(comparator)
-        .contains(new PolarisPrincipalSecrets(123, "client1a", "secret1a", 
"secret1a"));
-    assertThat(credentials.getSecrets("realm1", 123, "user1b"))
-        .usingValueComparator(comparator)
-        .contains(new PolarisPrincipalSecrets(123, "client1b", "secret1b", 
"secret1b"));
-    assertThat(credentials.getSecrets("realm2", 123, "user2a"))
-        .usingValueComparator(comparator)
-        .contains(new PolarisPrincipalSecrets(123, "client2a", "secret2a", 
"secret2a"));
+            " ; realm1 , client1 , secret1 ; realm2 , client2 , secret2 ; ");
+    assertCredentials(credentials);
   }
 
   @Test
   void getSecretsValidList() {
     PolarisCredentialsBootstrap credentials =
         PolarisCredentialsBootstrap.fromList(
-            List.of(
-                "realm1,user1a,client1a,secret1a",
-                "realm1,user1b,client1b,secret1b",
-                "realm2,user2a,client2a,secret2a"));
-    assertThat(credentials.getSecrets("realm1", 123, "nonexistent")).isEmpty();
-    assertThat(credentials.getSecrets("nonexistent", 123, "user1a")).isEmpty();
-    assertThat(credentials.getSecrets("realm1", 123, "user1a"))
-        .usingValueComparator(comparator)
-        .contains(new PolarisPrincipalSecrets(123, "client1a", "secret1a", 
"secret1a"));
-    assertThat(credentials.getSecrets("realm1", 123, "user1b"))
-        .usingValueComparator(comparator)
-        .contains(new PolarisPrincipalSecrets(123, "client1b", "secret1b", 
"secret1b"));
-    assertThat(credentials.getSecrets("realm2", 123, "user2a"))
-        .usingValueComparator(comparator)
-        .contains(new PolarisPrincipalSecrets(123, "client2a", "secret2a", 
"secret2a"));
+            List.of("realm1,client1,secret1", "realm2,client2,secret2"));
+    assertCredentials(credentials);
   }
 
   @Test
@@ -114,15 +80,34 @@ class PolarisCredentialsBootstrapTest {
     PolarisCredentialsBootstrap credentials = 
PolarisCredentialsBootstrap.fromEnvironment();
     assertThat(credentials.credentials).isEmpty();
     try {
-      System.setProperty("polaris.bootstrap.credentials", 
"realm1,user1a,client1a,secret1a");
+      System.setProperty(
+          "polaris.bootstrap.credentials", 
"realm1,client1,secret1;realm2,client2,secret2");
       credentials = PolarisCredentialsBootstrap.fromEnvironment();
-      assertThat(credentials.getSecrets("realm1", 123, 
"nonexistent")).isEmpty();
-      assertThat(credentials.getSecrets("nonexistent", 123, 
"user1a")).isEmpty();
-      assertThat(credentials.getSecrets("realm1", 123, "user1a"))
-          .usingValueComparator(comparator)
-          .contains(new PolarisPrincipalSecrets(123, "client1a", "secret1a", 
"secret1a"));
+      assertCredentials(credentials);
     } finally {
       System.clearProperty("polaris.bootstrap.credentials");
     }
   }
+
+  private void assertCredentials(PolarisCredentialsBootstrap credentials) {
+    assertThat(credentials.getSecrets("realm3", 123, "root")).isEmpty();
+    assertThat(credentials.getSecrets("nonexistent", 123, "root")).isEmpty();
+    assertThat(credentials.getSecrets("realm1", 123, "non-root")).isEmpty();
+    assertThat(credentials.getSecrets("realm1", 123, "root"))
+        .hasValueSatisfying(
+            secrets -> {
+              assertThat(secrets.getPrincipalId()).isEqualTo(123);
+              assertThat(secrets.getPrincipalClientId()).isEqualTo("client1");
+              assertThat(secrets.getMainSecret()).isEqualTo("secret1");
+              assertThat(secrets.getSecondarySecret()).isEqualTo("secret1");
+            });
+    assertThat(credentials.getSecrets("realm2", 123, "root"))
+        .hasValueSatisfying(
+            secrets -> {
+              assertThat(secrets.getPrincipalId()).isEqualTo(123);
+              assertThat(secrets.getPrincipalClientId()).isEqualTo("client2");
+              assertThat(secrets.getMainSecret()).isEqualTo("secret2");
+              assertThat(secrets.getSecondarySecret()).isEqualTo("secret2");
+            });
+  }
 }
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
index b03b368b..d220d7ef 100644
--- 
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
@@ -39,9 +39,8 @@ class PrincipalSecretsGeneratorTest {
   @Test
   void testSecretOverride() {
     PrincipalSecretsGenerator gen =
-        bootstrap(
-            "test-Realm", 
PolarisCredentialsBootstrap.fromString("test-Realm,user1,client1,sec2"));
-    PolarisPrincipalSecrets s = gen.produceSecrets("user1", 123);
+        bootstrap("test-Realm", 
PolarisCredentialsBootstrap.fromString("test-Realm,client1,sec2"));
+    PolarisPrincipalSecrets s = gen.produceSecrets("root", 123);
     assertThat(s).isNotNull();
     assertThat(s.getPrincipalId()).isEqualTo(123);
     assertThat(s.getPrincipalClientId()).isEqualTo("client1");
diff --git 
a/quarkus/admin/src/main/java/org/apache/polaris/admintool/BootstrapCommand.java
 
b/quarkus/admin/src/main/java/org/apache/polaris/admintool/BootstrapCommand.java
index 22a2e8ac..c9765420 100644
--- 
a/quarkus/admin/src/main/java/org/apache/polaris/admintool/BootstrapCommand.java
+++ 
b/quarkus/admin/src/main/java/org/apache/polaris/admintool/BootstrapCommand.java
@@ -39,7 +39,7 @@ public class BootstrapCommand extends BaseCommand {
   @CommandLine.Option(
       names = {"-c", "--credential"},
       description =
-          "Principal credentials to bootstrap. Must be of the form 
'realm,userName,clientId,clientSecret'.")
+          "Root principal credentials to bootstrap. Must be of the form 
'realm,clientId,clientSecret'.")
   List<String> credentials;
 
   @Override
diff --git 
a/quarkus/admin/src/test/java/org/apache/polaris/admintool/BootstrapCommandTest.java
 
b/quarkus/admin/src/test/java/org/apache/polaris/admintool/BootstrapCommandTest.java
index cf290823..fd503cb9 100644
--- 
a/quarkus/admin/src/test/java/org/apache/polaris/admintool/BootstrapCommandTest.java
+++ 
b/quarkus/admin/src/test/java/org/apache/polaris/admintool/BootstrapCommandTest.java
@@ -37,9 +37,9 @@ class BootstrapCommandTest {
         "-r",
         "realm2",
         "-c",
-        "realm1,root,root,s3cr3t",
+        "realm1,root,s3cr3t",
         "-c",
-        "realm2,root,root,s3cr3t"
+        "realm2,root,s3cr3t"
       })
   public void testBootstrap(LaunchResult result) {
     assertThat(result.getOutput()).contains("Bootstrap completed 
successfully.");
diff --git a/quarkus/service/build.gradle.kts b/quarkus/service/build.gradle.kts
index 669e54c5..99d72e8c 100644
--- a/quarkus/service/build.gradle.kts
+++ b/quarkus/service/build.gradle.kts
@@ -150,8 +150,9 @@ tasks.withType(Test::class.java).configureEach {
   if (System.getenv("AWS_REGION") == null) {
     environment("AWS_REGION", "us-west-2")
   }
-  // Note: the test secrets are referenced in DropwizardServerManager
-  environment("POLARIS_BOOTSTRAP_CREDENTIALS", 
"POLARIS,root,test-admin,test-secret")
+  // Note: the test secrets are referenced in
+  // org.apache.polaris.service.quarkus.it.QuarkusServerManager
+  environment("POLARIS_BOOTSTRAP_CREDENTIALS", 
"POLARIS,test-admin,test-secret")
   jvmArgs("--add-exports", "java.base/sun.nio.ch=ALL-UNNAMED")
   // Need to allow a java security manager after Java 21, for 
Subject.getSubject to work
   // "getSubject is supported only if a security manager is allowed".
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 ebf4c9d4..ed43052d 100644
--- a/site/content/in-dev/unreleased/configuring-polaris-for-production.md
+++ b/site/content/in-dev/unreleased/configuring-polaris-for-production.md
@@ -75,21 +75,19 @@ Before using Polaris when using a metastore manager other 
than `in-memory`, you
 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:
 
 ```
-export 
POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,root,my-client-id,my-client-secret
+export POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,my-client-id,my-client-secret
 ```
 
-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`:
+The format of the environment variable is `realm,client_id,client_secret`. You 
can provide multiple credentials separated by `;`. For example, to provide 
credentials for two realms `my_realm` and `my_realm2`:
 
 ```
-export 
POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,root,my-client-id,my-client-secret;my_realm2,root,my-client-id2,my-client-secret2
+export 
POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,my-client-id,my-client-secret;my_realm2,my-client-id2,my-client-secret2
 ```
 
-You can also provide credentials for other users too. 
-
 It is also possible to use system properties to provide the credentials:
 
 ```
-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
+java -Dpolaris.bootstrap.credentials=my_realm,my-client-id,my-client-secret 
-jar /path/to/jar/polaris-service-all.jar bootstrap polaris-server.yml
 ```
 
 Now, to bootstrap Polaris, run:

Reply via email to