Skip to content

Commit 3ea8db4

Browse files
authored
Revert "Simplify credentials format when bootstrapping (#806)"
This reverts commit 4eee4fe.
1 parent 4eee4fe commit 3ea8db4

File tree

7 files changed

+92
-74
lines changed

7 files changed

+92
-74
lines changed

polaris-core/src/main/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrap.java

Lines changed: 31 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@
2525
import java.util.HashMap;
2626
import java.util.List;
2727
import java.util.Map;
28+
import java.util.Map.Entry;
2829
import java.util.Optional;
29-
import org.apache.polaris.core.entity.PolarisEntityConstants;
3030
import org.apache.polaris.core.entity.PolarisPrincipalSecrets;
3131

3232
/**
@@ -54,7 +54,7 @@ public static PolarisCredentialsBootstrap fromEnvironment() {
5454
* Parse a string of credentials in the format:
5555
*
5656
* <pre>
57-
* realm1,client1,secret1;realm2,client2,secret2;...
57+
* realm1,user1a,client1a,secret1a;realm1,user1b,client1b,secret1b;realm2,user2a,client2a,secret2a;...
5858
* </pre>
5959
*/
6060
public static PolarisCredentialsBootstrap fromString(@Nullable String credentialsString) {
@@ -65,51 +65,52 @@ public static PolarisCredentialsBootstrap fromString(@Nullable String credential
6565

6666
/**
6767
* Parse a list of credentials; each element should be in the format: {@code
68-
* realm,clientId,clientSecret}.
68+
* realm,principal,clientId,clientSecret}.
6969
*/
7070
public static PolarisCredentialsBootstrap fromList(List<String> credentialsList) {
71-
Map<String, Map.Entry<String, String>> credentials = new HashMap<>();
72-
for (String triplet : credentialsList) {
73-
if (!triplet.isBlank()) {
74-
List<String> parts = Splitter.on(',').trimResults().splitToList(triplet);
75-
if (parts.size() != 3) {
76-
throw new IllegalArgumentException("Invalid credentials format: " + triplet);
71+
Map<String, Map<String, Map.Entry<String, String>>> credentials = new HashMap<>();
72+
for (String quadruple : credentialsList) {
73+
if (!quadruple.isBlank()) {
74+
List<String> parts = Splitter.on(',').trimResults().splitToList(quadruple);
75+
if (parts.size() != 4) {
76+
throw new IllegalArgumentException("Invalid credentials format: " + quadruple);
7777
}
7878
String realmName = parts.get(0);
79-
String clientId = parts.get(1);
80-
String clientSecret = parts.get(2);
81-
82-
if (credentials.containsKey(realmName)) {
83-
throw new IllegalArgumentException("Duplicate realm: " + realmName);
84-
}
85-
credentials.put(realmName, new SimpleEntry<>(clientId, clientSecret));
79+
String principalName = parts.get(1);
80+
String clientId = parts.get(2);
81+
String clientSecret = parts.get(3);
82+
credentials
83+
.computeIfAbsent(realmName, k -> new HashMap<>())
84+
.merge(
85+
principalName,
86+
new SimpleEntry<>(clientId, clientSecret),
87+
(a, b) -> {
88+
throw new IllegalArgumentException("Duplicate principal: " + principalName);
89+
});
8690
}
8791
}
8892
return credentials.isEmpty() ? EMPTY : new PolarisCredentialsBootstrap(credentials);
8993
}
9094

91-
@VisibleForTesting final Map<String, Map.Entry<String, String>> credentials;
95+
@VisibleForTesting final Map<String, Map<String, Map.Entry<String, String>>> credentials;
9296

93-
private PolarisCredentialsBootstrap(Map<String, Map.Entry<String, String>> credentials) {
97+
private PolarisCredentialsBootstrap(Map<String, Map<String, Entry<String, String>>> credentials) {
9498
this.credentials = credentials;
9599
}
96100

97101
/**
98102
* Get the secrets for the specified principal in the specified realm, if available among the
99-
* credentials that were supplied for bootstrap. Only credentials for the root principal are
100-
* supported.
103+
* credentials that were supplied for bootstrap.
101104
*/
102105
public Optional<PolarisPrincipalSecrets> getSecrets(
103106
String realmName, long principalId, String principalName) {
104-
if (principalName.equals(PolarisEntityConstants.getRootPrincipalName())) {
105-
return Optional.ofNullable(credentials.get(realmName))
106-
.map(
107-
credentials -> {
108-
String clientId = credentials.getKey();
109-
String secret = credentials.getValue();
110-
return new PolarisPrincipalSecrets(principalId, clientId, secret, secret);
111-
});
112-
}
113-
return Optional.empty();
107+
return Optional.ofNullable(credentials.get(realmName))
108+
.flatMap(principals -> Optional.ofNullable(principals.get(principalName)))
109+
.map(
110+
credentials -> {
111+
String clientId = credentials.getKey();
112+
String secret = credentials.getValue();
113+
return new PolarisPrincipalSecrets(principalId, clientId, secret, secret);
114+
});
114115
}
115116
}

polaris-core/src/test/java/org/apache/polaris/core/persistence/PolarisCredentialsBootstrapTest.java

Lines changed: 47 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,22 @@
2121
import static org.assertj.core.api.Assertions.assertThat;
2222
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2323

24+
import java.util.Comparator;
2425
import java.util.List;
26+
import org.apache.polaris.core.entity.PolarisPrincipalSecrets;
2527
import org.junit.jupiter.api.Test;
2628

2729
class PolarisCredentialsBootstrapTest {
2830

31+
private final Comparator<PolarisPrincipalSecrets> comparator =
32+
(a, b) ->
33+
a.getPrincipalId() == b.getPrincipalId()
34+
&& a.getPrincipalClientId().equals(b.getPrincipalClientId())
35+
&& a.getMainSecret().equals(b.getMainSecret())
36+
&& a.getSecondarySecret().equals(b.getSecondarySecret())
37+
? 0
38+
: 1;
39+
2940
@Test
3041
void nullString() {
3142
PolarisCredentialsBootstrap credentials = PolarisCredentialsBootstrap.fromString(null);
@@ -51,63 +62,67 @@ void invalidString() {
5162
}
5263

5364
@Test
54-
void duplicateRealm() {
65+
void duplicatePrincipal() {
5566
assertThatThrownBy(
5667
() ->
5768
PolarisCredentialsBootstrap.fromString(
58-
"realm1,client1a,secret1a;realm1,client1b,secret1b"))
59-
.hasMessage("Duplicate realm: realm1");
69+
"realm1,user1a,client1a,secret1a;realm1,user1a,client1b,secret1b"))
70+
.hasMessage("Duplicate principal: user1a");
6071
}
6172

6273
@Test
6374
void getSecretsValidString() {
6475
PolarisCredentialsBootstrap credentials =
6576
PolarisCredentialsBootstrap.fromString(
66-
" ; realm1 , client1 , secret1 ; realm2 , client2 , secret2 ; ");
67-
assertCredentials(credentials);
77+
" ; realm1 , user1a , client1a , secret1a ; realm1 , user1b , client1b , secret1b ; realm2 , user2a , client2a , secret2a ; ");
78+
assertThat(credentials.getSecrets("realm1", 123, "nonexistent")).isEmpty();
79+
assertThat(credentials.getSecrets("nonexistent", 123, "user1a")).isEmpty();
80+
assertThat(credentials.getSecrets("realm1", 123, "user1a"))
81+
.usingValueComparator(comparator)
82+
.contains(new PolarisPrincipalSecrets(123, "client1a", "secret1a", "secret1a"));
83+
assertThat(credentials.getSecrets("realm1", 123, "user1b"))
84+
.usingValueComparator(comparator)
85+
.contains(new PolarisPrincipalSecrets(123, "client1b", "secret1b", "secret1b"));
86+
assertThat(credentials.getSecrets("realm2", 123, "user2a"))
87+
.usingValueComparator(comparator)
88+
.contains(new PolarisPrincipalSecrets(123, "client2a", "secret2a", "secret2a"));
6889
}
6990

7091
@Test
7192
void getSecretsValidList() {
7293
PolarisCredentialsBootstrap credentials =
7394
PolarisCredentialsBootstrap.fromList(
74-
List.of("realm1,client1,secret1", "realm2,client2,secret2"));
75-
assertCredentials(credentials);
95+
List.of(
96+
"realm1,user1a,client1a,secret1a",
97+
"realm1,user1b,client1b,secret1b",
98+
"realm2,user2a,client2a,secret2a"));
99+
assertThat(credentials.getSecrets("realm1", 123, "nonexistent")).isEmpty();
100+
assertThat(credentials.getSecrets("nonexistent", 123, "user1a")).isEmpty();
101+
assertThat(credentials.getSecrets("realm1", 123, "user1a"))
102+
.usingValueComparator(comparator)
103+
.contains(new PolarisPrincipalSecrets(123, "client1a", "secret1a", "secret1a"));
104+
assertThat(credentials.getSecrets("realm1", 123, "user1b"))
105+
.usingValueComparator(comparator)
106+
.contains(new PolarisPrincipalSecrets(123, "client1b", "secret1b", "secret1b"));
107+
assertThat(credentials.getSecrets("realm2", 123, "user2a"))
108+
.usingValueComparator(comparator)
109+
.contains(new PolarisPrincipalSecrets(123, "client2a", "secret2a", "secret2a"));
76110
}
77111

78112
@Test
79113
void getSecretsValidSystemProperty() {
80114
PolarisCredentialsBootstrap credentials = PolarisCredentialsBootstrap.fromEnvironment();
81115
assertThat(credentials.credentials).isEmpty();
82116
try {
83-
System.setProperty(
84-
"polaris.bootstrap.credentials", "realm1,client1,secret1;realm2,client2,secret2");
117+
System.setProperty("polaris.bootstrap.credentials", "realm1,user1a,client1a,secret1a");
85118
credentials = PolarisCredentialsBootstrap.fromEnvironment();
86-
assertCredentials(credentials);
119+
assertThat(credentials.getSecrets("realm1", 123, "nonexistent")).isEmpty();
120+
assertThat(credentials.getSecrets("nonexistent", 123, "user1a")).isEmpty();
121+
assertThat(credentials.getSecrets("realm1", 123, "user1a"))
122+
.usingValueComparator(comparator)
123+
.contains(new PolarisPrincipalSecrets(123, "client1a", "secret1a", "secret1a"));
87124
} finally {
88125
System.clearProperty("polaris.bootstrap.credentials");
89126
}
90127
}
91-
92-
private void assertCredentials(PolarisCredentialsBootstrap credentials) {
93-
assertThat(credentials.getSecrets("realm3", 123, "root")).isEmpty();
94-
assertThat(credentials.getSecrets("nonexistent", 123, "root")).isEmpty();
95-
assertThat(credentials.getSecrets("realm1", 123, "non-root")).isEmpty();
96-
assertThat(credentials.getSecrets("realm1", 123, "root"))
97-
.hasValueSatisfying(
98-
secrets -> {
99-
assertThat(secrets.getPrincipalId()).isEqualTo(123);
100-
assertThat(secrets.getPrincipalClientId()).isEqualTo("client1");
101-
assertThat(secrets.getMainSecret()).isEqualTo("secret1");
102-
assertThat(secrets.getSecondarySecret()).isEqualTo("secret1");
103-
});
104-
assertThat(credentials.getSecrets("realm2", 123, "root"))
105-
.hasValueSatisfying(
106-
secrets -> {
107-
assertThat(secrets.getPrincipalId()).isEqualTo(123);
108-
assertThat(secrets.getPrincipalClientId()).isEqualTo("client2");
109-
assertThat(secrets.getMainSecret()).isEqualTo("secret2");
110-
assertThat(secrets.getSecondarySecret()).isEqualTo("secret2");
111-
});
112-
}
113128
}

polaris-core/src/test/java/org/apache/polaris/core/persistence/PrincipalSecretsGeneratorTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ void testRandomSecrets() {
3939
@Test
4040
void testSecretOverride() {
4141
PrincipalSecretsGenerator gen =
42-
bootstrap("test-Realm", PolarisCredentialsBootstrap.fromString("test-Realm,client1,sec2"));
43-
PolarisPrincipalSecrets s = gen.produceSecrets("root", 123);
42+
bootstrap(
43+
"test-Realm", PolarisCredentialsBootstrap.fromString("test-Realm,user1,client1,sec2"));
44+
PolarisPrincipalSecrets s = gen.produceSecrets("user1", 123);
4445
assertThat(s).isNotNull();
4546
assertThat(s.getPrincipalId()).isEqualTo(123);
4647
assertThat(s.getPrincipalClientId()).isEqualTo("client1");

quarkus/admin/src/main/java/org/apache/polaris/admintool/BootstrapCommand.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ public class BootstrapCommand extends BaseCommand {
3939
@CommandLine.Option(
4040
names = {"-c", "--credential"},
4141
description =
42-
"Root principal credentials to bootstrap. Must be of the form 'realm,clientId,clientSecret'.")
42+
"Principal credentials to bootstrap. Must be of the form 'realm,userName,clientId,clientSecret'.")
4343
List<String> credentials;
4444

4545
@Override

quarkus/admin/src/test/java/org/apache/polaris/admintool/BootstrapCommandTest.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,9 +37,9 @@ class BootstrapCommandTest {
3737
"-r",
3838
"realm2",
3939
"-c",
40-
"realm1,root,s3cr3t",
40+
"realm1,root,root,s3cr3t",
4141
"-c",
42-
"realm2,root,s3cr3t"
42+
"realm2,root,root,s3cr3t"
4343
})
4444
public void testBootstrap(LaunchResult result) {
4545
assertThat(result.getOutput()).contains("Bootstrap completed successfully.");

quarkus/service/build.gradle.kts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,9 +150,8 @@ tasks.withType(Test::class.java).configureEach {
150150
if (System.getenv("AWS_REGION") == null) {
151151
environment("AWS_REGION", "us-west-2")
152152
}
153-
// Note: the test secrets are referenced in
154-
// org.apache.polaris.service.quarkus.it.QuarkusServerManager
155-
environment("POLARIS_BOOTSTRAP_CREDENTIALS", "POLARIS,test-admin,test-secret")
153+
// Note: the test secrets are referenced in DropwizardServerManager
154+
environment("POLARIS_BOOTSTRAP_CREDENTIALS", "POLARIS,root,test-admin,test-secret")
156155
jvmArgs("--add-exports", "java.base/sun.nio.ch=ALL-UNNAMED")
157156
// Need to allow a java security manager after Java 21, for Subject.getSubject to work
158157
// "getSubject is supported only if a security manager is allowed".

site/content/in-dev/unreleased/configuring-polaris-for-production.md

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,19 +75,21 @@ Before using Polaris when using a metastore manager other than `in-memory`, you
7575
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:
7676

7777
```
78-
export POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,my-client-id,my-client-secret
78+
export POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,root,my-client-id,my-client-secret
7979
```
8080

81-
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`:
81+
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`:
8282

8383
```
84-
export POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,my-client-id,my-client-secret;my_realm2,my-client-id2,my-client-secret2
84+
export POLARIS_BOOTSTRAP_CREDENTIALS=my_realm,root,my-client-id,my-client-secret;my_realm2,root,my-client-id2,my-client-secret2
8585
```
8686

87+
You can also provide credentials for other users too.
88+
8789
It is also possible to use system properties to provide the credentials:
8890

8991
```
90-
java -Dpolaris.bootstrap.credentials=my_realm,my-client-id,my-client-secret -jar /path/to/jar/polaris-service-all.jar bootstrap polaris-server.yml
92+
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
9193
```
9294

9395
Now, to bootstrap Polaris, run:

0 commit comments

Comments
 (0)