Skip to content

Commit 3ad02bb

Browse files
Move static functions back to UserSecretReference
1 parent be1ff66 commit 3ad02bb

File tree

5 files changed

+119
-171
lines changed

5 files changed

+119
-171
lines changed

polaris-core/src/main/java/org/apache/polaris/core/secrets/UnsafeInMemorySecretsManager.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -105,10 +105,7 @@ public UserSecretReference writeSecret(
105105
@Override
106106
@Nonnull
107107
public String readSecret(@Nonnull UserSecretReference secretReference) {
108-
String urn = secretReference.getUrn();
109-
Preconditions.checkState(UserSecretReferenceUrnHelper.isValid(urn), "Invalid URN: " + urn);
110-
111-
String secretManagerType = UserSecretReferenceUrnHelper.getSecretManagerType(urn);
108+
String secretManagerType = secretReference.getUserSecretManagerType();
112109
Preconditions.checkState(
113110
secretManagerType.equals(SECRET_MANAGER_TYPE),
114111
"Invalid secret manager type, expected: "

polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretReference.java

Lines changed: 104 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@
2727
import java.util.HashMap;
2828
import java.util.Map;
2929
import java.util.Objects;
30+
import java.util.regex.Matcher;
31+
import java.util.regex.Pattern;
3032

3133
/**
3234
* Represents a "wrapped reference" to a user-owned secret that holds an identifier to retrieve
@@ -56,6 +58,43 @@ public class UserSecretReference {
5658
@JsonProperty(value = "referencePayload")
5759
private final Map<String, String> referencePayload;
5860

61+
private static final String URN_SCHEME = "urn";
62+
private static final String URN_NAMESPACE = "polaris-secret";
63+
private static final String SECRET_MANAGER_TYPE_REGEX = "([a-zA-Z0-9_-]+)";
64+
private static final String TYPE_SPECIFIC_IDENTIFIER_REGEX =
65+
"([a-zA-Z0-9_-]+(?::[a-zA-Z0-9_-]+)*)";
66+
67+
/**
68+
* Precompiled regex pattern for validating the secret manager type and type-specific identifier.
69+
*/
70+
private static final Pattern SECRET_MANAGER_TYPE_PATTERN =
71+
Pattern.compile("^" + SECRET_MANAGER_TYPE_REGEX + "$");
72+
73+
private static final Pattern TYPE_SPECIFIC_IDENTIFIER_PATTERN =
74+
Pattern.compile("^" + TYPE_SPECIFIC_IDENTIFIER_REGEX + "$");
75+
76+
/**
77+
* Precompiled regex pattern for validating and parsing UserSecretReference URNs. Expected format:
78+
* urn:polaris-secret:<secret-manager-type>:<identifier1>(:<identifier2>:...).
79+
*
80+
* <p>Groups:
81+
*
82+
* <p>Group 1: secret-manager-type (alphanumeric, hyphens, underscores).
83+
*
84+
* <p>Group 2: type-specific-identifier (one or more colon-separated alphanumeric components).
85+
*/
86+
private static final Pattern URN_PATTERN =
87+
Pattern.compile(
88+
"^"
89+
+ URN_SCHEME
90+
+ ":"
91+
+ URN_NAMESPACE
92+
+ ":"
93+
+ SECRET_MANAGER_TYPE_REGEX
94+
+ ":"
95+
+ TYPE_SPECIFIC_IDENTIFIER_REGEX
96+
+ "$");
97+
5998
/**
6099
* @param urn A string which should be self-sufficient to retrieve whatever secret material that
61100
* is stored in the remote secret store and also to identify an implementation of the
@@ -71,24 +110,82 @@ public UserSecretReference(
71110
@JsonProperty(value = "urn", required = true) @Nonnull String urn,
72111
@JsonProperty(value = "referencePayload") @Nullable Map<String, String> referencePayload) {
73112
Preconditions.checkArgument(
74-
UserSecretReferenceUrnHelper.isValid(urn),
75-
"Invalid secret URN: "
76-
+ urn
77-
+ "; must be of the form: "
78-
+ UserSecretReferenceUrnHelper.getUrnPattern());
113+
urnIsValid(urn),
114+
"Invalid secret URN: " + urn + "; must be of the form: " + URN_PATTERN.toString());
79115
this.urn = urn;
80116
this.referencePayload = Objects.requireNonNullElse(referencePayload, new HashMap<>());
81117
}
82118

119+
/**
120+
* Validates whether the given URN string matches the expected format for UserSecretReference
121+
* URNs.
122+
*
123+
* @param urn The URN string to validate.
124+
* @return true if the URN is valid, false otherwise.
125+
*/
126+
private static boolean urnIsValid(@Nonnull String urn) {
127+
return urn.trim().isEmpty() ? false : URN_PATTERN.matcher(urn).matches();
128+
}
129+
130+
/**
131+
* Builds a URN string from the given secret manager type and type-specific identifier. Validates
132+
* the inputs to ensure they conform to the expected pattern.
133+
*
134+
* @param secretManagerType The secret manager type (alphanumeric, hyphens, underscores).
135+
* @param typeSpecificIdentifier The type-specific identifier (colon-separated alphanumeric
136+
* components).
137+
* @return The constructed URN string.
138+
*/
139+
@Nonnull
140+
public static String buildUrnString(
141+
@Nonnull String secretManagerType, @Nonnull String typeSpecificIdentifier) {
142+
143+
Preconditions.checkArgument(
144+
!secretManagerType.trim().isEmpty(), "Secret manager type cannot be empty");
145+
Preconditions.checkArgument(
146+
SECRET_MANAGER_TYPE_PATTERN.matcher(secretManagerType).matches(),
147+
"Invalid secret manager type '%s'; must contain only alphanumeric characters, hyphens, and underscores",
148+
secretManagerType);
149+
150+
Preconditions.checkArgument(
151+
!typeSpecificIdentifier.trim().isEmpty(), "Type-specific identifier cannot be empty");
152+
Preconditions.checkArgument(
153+
TYPE_SPECIFIC_IDENTIFIER_PATTERN.matcher(typeSpecificIdentifier).matches(),
154+
"Invalid type-specific identifier '%s'; must be colon-separated alphanumeric components (hyphens and underscores allowed)",
155+
typeSpecificIdentifier);
156+
157+
return URN_SCHEME
158+
+ ":"
159+
+ URN_NAMESPACE
160+
+ ":"
161+
+ secretManagerType
162+
+ ":"
163+
+ typeSpecificIdentifier;
164+
}
165+
83166
/**
84167
* Since UserSecretReference objects are specific to UserSecretManager implementations, the
85168
* "secret-manager-type" portion of the URN should be used to validate that a URN is valid for a
86169
* given implementation and to dispatch to the correct implementation at runtime if multiple
87170
* concurrent implementations are possible in a given runtime environment.
88171
*/
89172
@JsonIgnore
90-
public String getUserSecretManagerTypeFromUrn() {
91-
return UserSecretReferenceUrnHelper.getSecretManagerType(urn);
173+
public String getUserSecretManagerType() {
174+
Matcher matcher = URN_PATTERN.matcher(urn);
175+
Preconditions.checkState(matcher.matches(), "Invalid secret URN: " + urn);
176+
return matcher.group(1);
177+
}
178+
179+
/**
180+
* Returns the type-specific identifier from the URN. Since the format is specific to the
181+
* UserSecretManager implementation, this method does not validate the identifier. It is the
182+
* responsibility of the caller to validate it.
183+
*/
184+
@JsonIgnore
185+
public String getTypeSpecificIdentifier() {
186+
Matcher matcher = URN_PATTERN.matcher(urn);
187+
Preconditions.checkState(matcher.matches(), "Invalid secret URN: " + urn);
188+
return matcher.group(2);
92189
}
93190

94191
public @Nonnull String getUrn() {

polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretReferenceUrnHelper.java

Lines changed: 0 additions & 147 deletions
This file was deleted.

polaris-core/src/main/java/org/apache/polaris/core/secrets/UserSecretsManager.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,6 @@ public interface UserSecretsManager {
7474
@Nonnull
7575
default String buildUrn(
7676
@Nonnull String secretManagerType, @Nonnull String typeSpecificIdentifier) {
77-
return UserSecretReferenceUrnHelper.buildUrn(secretManagerType, typeSpecificIdentifier);
77+
return UserSecretReference.buildUrnString(secretManagerType, typeSpecificIdentifier);
7878
}
7979
}
Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,13 @@
1919
package org.apache.polaris.core.secrets;
2020

2121
import static org.assertj.core.api.Assertions.assertThat;
22+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2223

2324
import org.junit.jupiter.api.Test;
2425
import org.junit.jupiter.params.ParameterizedTest;
2526
import org.junit.jupiter.params.provider.ValueSource;
2627

27-
public class UserSecretReferenceUrnHelperTest {
28+
public class UserSecretReferenceTest {
2829

2930
@ParameterizedTest
3031
@ValueSource(
@@ -35,7 +36,7 @@ public class UserSecretReferenceUrnHelperTest {
3536
"urn:polaris-secret:vault:project:env:service:key"
3637
})
3738
public void testValidUrns(String validUrn) {
38-
assertThat(UserSecretReferenceUrnHelper.isValid(validUrn)).isTrue();
39+
assertThat(new UserSecretReference(validUrn, null)).isNotNull();
3940
}
4041

4142
@ParameterizedTest
@@ -53,30 +54,30 @@ public void testValidUrns(String validUrn) {
5354
"urn:polaris-secret:unsafe-in-memory:key::"
5455
})
5556
public void testInvalidUrns(String invalidUrn) {
56-
assertThat(UserSecretReferenceUrnHelper.isValid(invalidUrn))
57-
.as("URN should be invalid: %s", invalidUrn)
58-
.isFalse();
57+
assertThatThrownBy(() -> new UserSecretReference(invalidUrn, null))
58+
.isInstanceOf(IllegalArgumentException.class)
59+
.hasMessageContaining("Invalid secret URN: " + invalidUrn);
5960
}
6061

6162
@Test
6263
public void tesGetUrnComponents() {
6364
String urn = "urn:polaris-secret:unsafe-in-memory:key1:value1";
64-
assertThat(UserSecretReferenceUrnHelper.getSecretManagerType(urn))
65-
.isEqualTo("unsafe-in-memory");
66-
assertThat(UserSecretReferenceUrnHelper.getTypeSpecificIdentifier(urn))
67-
.isEqualTo("key1:value1");
65+
UserSecretReference reference = new UserSecretReference(urn, null);
66+
67+
assertThat(reference.getUserSecretManagerType()).isEqualTo("unsafe-in-memory");
68+
assertThat(reference.getTypeSpecificIdentifier()).isEqualTo("key1:value1");
6869
}
6970

7071
@Test
7172
public void testBuildUrn() {
72-
String urn = UserSecretReferenceUrnHelper.buildUrn("aws-secrets", "my-key");
73+
String urn = UserSecretReference.buildUrnString("aws-secrets", "my-key");
7374
assertThat(urn).isEqualTo("urn:polaris-secret:aws-secrets:my-key");
7475

7576
String urnWithMultipleIdentifiers =
76-
UserSecretReferenceUrnHelper.buildUrn("vault", "project:service");
77+
UserSecretReference.buildUrnString("vault", "project:service");
7778
assertThat(urnWithMultipleIdentifiers).isEqualTo("urn:polaris-secret:vault:project:service");
7879

79-
String urnWithNumbers = UserSecretReferenceUrnHelper.buildUrn("type_123", "456:789");
80+
String urnWithNumbers = UserSecretReference.buildUrnString("type_123", "456:789");
8081
assertThat(urnWithNumbers).isEqualTo("urn:polaris-secret:type_123:456:789");
8182
}
8283
}

0 commit comments

Comments
 (0)