Skip to content

Commit 548e086

Browse files
committed
Ensure client_id/client_secret patterns are configurable
1 parent 2929a99 commit 548e086

File tree

6 files changed

+144
-9
lines changed

6 files changed

+144
-9
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ request adding CHANGELOG notes for breaking (!) changes and possibly other secti
6060
- Enhanced catalog federation with SigV4 authentication support, additional authentication types for credential vending, and location-based access restrictions to block credential vending for remote tables outside allowed location lists.
6161
- Added `topologySpreadConstraints` support in Helm chart.
6262
- Added support for including principal name in subscoped credentials. `INCLUDE_PRINCIPAL_NAME_IN_SUBSCOPED_CREDENTIAL` (default: false) can be used to toggle this feature. If enabled, cached credentials issued to one principal will no longer be available for others.
63+
- Added `CREDENTIAL_RESET_CLIENT_ID_PATTERN` and `CREDENTIAL_RESET_CLIENT_SECRET_PATTERN` (default being backward compatible) feature configuration to enable to change (enforce/relax) `client_id`/`client_secret` patterns on reset calls.
6364

6465
### Changes
6566

polaris-core/src/main/java/org/apache/polaris/core/config/FeatureConfiguration.java

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,20 @@ public static void enforceFeatureEnabledOrThrow(
409409
+ "Defaults to enabled, but service providers may want to disable it.")
410410
.defaultValue(true)
411411
.buildFeatureConfiguration();
412+
public static final FeatureConfiguration<String> CREDENTIAL_RESET_CLIENT_ID_PATTERN =
413+
PolarisConfiguration.<String>builder()
414+
.key("CREDENTIAL_RESET_CLIENT_ID_PATTERN")
415+
.description(
416+
"Java regex (Pattern) client_id must match to pass ENABLE_CREDENTIAL_RESET validations.")
417+
.defaultValue("^[0-9a-f]{16}$")
418+
.buildFeatureConfiguration();
419+
public static final FeatureConfiguration<String> CREDENTIAL_RESET_CLIENT_SECRET_PATTERN =
420+
PolarisConfiguration.<String>builder()
421+
.key("CREDENTIAL_RESET_CLIENT_SECRET_PATTERN")
422+
.description(
423+
"Java regex (Pattern) client_secret must match to pass ENABLE_CREDENTIAL_RESET validations.")
424+
.defaultValue("^[0-9a-f]{32}$")
425+
.buildFeatureConfiguration();
412426

413427
public static final FeatureConfiguration<Boolean>
414428
ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS =

polaris-core/src/main/java/org/apache/polaris/core/config/PolarisConfiguration.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,6 @@
2424
import java.util.function.Function;
2525
import java.util.stream.Collectors;
2626
import java.util.stream.Stream;
27-
import org.slf4j.Logger;
28-
import org.slf4j.LoggerFactory;
2927

3028
/**
3129
* An ABC for Polaris configurations that alter the service's behavior TODO: deprecate unsafe
@@ -34,9 +32,6 @@
3432
* @param <T> The type of the configuration
3533
*/
3634
public abstract class PolarisConfiguration<T> {
37-
38-
private static final Logger LOGGER = LoggerFactory.getLogger(PolarisConfiguration.class);
39-
4035
private static final List<PolarisConfiguration<?>> allConfigurations = new ArrayList<>();
4136

4237
private final String key;

runtime/service/src/main/java/org/apache/polaris/service/admin/PolarisServiceImpl.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -137,13 +137,15 @@ public Response createCatalog(
137137
}
138138

139139
private void validateClientId(String clientId) {
140-
if (!clientId.matches("^[0-9a-f]{16}$")) {
140+
if (!clientId.matches(
141+
realmConfig.getConfig(FeatureConfiguration.CREDENTIAL_RESET_CLIENT_ID_PATTERN))) {
141142
throw new IllegalArgumentException("Invalid clientId format");
142143
}
143144
}
144145

145146
private void validateClientSecret(String clientSecret) {
146-
if (!clientSecret.matches("^[0-9a-f]{32}$")) {
147+
if (!clientSecret.matches(
148+
realmConfig.getConfig(FeatureConfiguration.CREDENTIAL_RESET_CLIENT_SECRET_PATTERN))) {
147149
throw new IllegalArgumentException("Invalid clientSecret format");
148150
}
149151
}

runtime/service/src/test/java/org/apache/polaris/service/admin/PolarisServiceImplTest.java

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818
*/
1919
package org.apache.polaris.service.admin;
2020

21+
import static org.assertj.core.api.Assertions.assertThat;
2122
import static org.assertj.core.api.Assertions.assertThatCode;
23+
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
2224
import static org.assertj.core.api.Assertions.assertThatThrownBy;
2325
import static org.mockito.Mockito.when;
2426

27+
import jakarta.annotation.Nonnull;
2528
import java.lang.reflect.Method;
2629
import java.util.List;
2730
import org.apache.polaris.core.admin.model.AuthenticationParameters;
@@ -31,6 +34,8 @@
3134
import org.apache.polaris.core.admin.model.ExternalCatalog;
3235
import org.apache.polaris.core.admin.model.FileStorageConfigInfo;
3336
import org.apache.polaris.core.admin.model.PolarisCatalog;
37+
import org.apache.polaris.core.admin.model.PrincipalWithCredentials;
38+
import org.apache.polaris.core.admin.model.ResetPrincipalRequest;
3439
import org.apache.polaris.core.admin.model.StorageConfigInfo;
3540
import org.apache.polaris.core.auth.PolarisAuthorizer;
3641
import org.apache.polaris.core.auth.PolarisPrincipal;
@@ -78,6 +83,10 @@ void setUp() {
7883
when(realmConfig.getConfig(
7984
FeatureConfiguration.SUPPORTED_EXTERNAL_CATALOG_AUTHENTICATION_TYPES))
8085
.thenReturn(List.of("OAUTH"));
86+
when(realmConfig.getConfig(FeatureConfiguration.CREDENTIAL_RESET_CLIENT_ID_PATTERN))
87+
.thenReturn("^[0-9a-f]{16}$");
88+
when(realmConfig.getConfig(FeatureConfiguration.CREDENTIAL_RESET_CLIENT_SECRET_PATTERN))
89+
.thenReturn("^[0-9a-f]{32}$");
8190

8291
adminService =
8392
new PolarisAdminService(
@@ -94,6 +103,95 @@ void setUp() {
94103
realmConfig, reservedProperties, adminService, serviceIdentityProvider);
95104
}
96105

106+
@Test
107+
void testResetCredentialsValidatesClientId() {
108+
// just fake the admin service since we do validate the validations, not the service
109+
final var service = noAdminResetCredentialPolarisService();
110+
111+
// ok
112+
assertThat(
113+
service.resetCredentials(
114+
"pcp",
115+
new ResetPrincipalRequest("aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
116+
null,
117+
null))
118+
.isNotNull();
119+
120+
// too short
121+
assertThatExceptionOfType(IllegalArgumentException.class)
122+
.isThrownBy(
123+
() ->
124+
service.resetCredentials(
125+
"pcp",
126+
new ResetPrincipalRequest(
127+
"aaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
128+
null,
129+
null));
130+
131+
// too long
132+
assertThatExceptionOfType(IllegalArgumentException.class)
133+
.isThrownBy(
134+
() ->
135+
service.resetCredentials(
136+
"pcp",
137+
new ResetPrincipalRequest(
138+
"aaaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
139+
null,
140+
null));
141+
142+
// bad char
143+
assertThatExceptionOfType(IllegalArgumentException.class)
144+
.isThrownBy(
145+
() ->
146+
service.resetCredentials(
147+
"pcp",
148+
new ResetPrincipalRequest(
149+
"aaaaaaaaaaaaaaag", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
150+
null,
151+
null));
152+
}
153+
154+
@Test
155+
void testResetCredentialsValidatesClientSecret() {
156+
// just fake the admin service since we do validate the validations, not the service
157+
final var service = noAdminResetCredentialPolarisService();
158+
159+
// Note: ok already tested for client_id so not repeating there
160+
161+
// too short
162+
assertThatExceptionOfType(IllegalArgumentException.class)
163+
.isThrownBy(
164+
() ->
165+
service.resetCredentials(
166+
"pcp",
167+
new ResetPrincipalRequest(
168+
"aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
169+
null,
170+
null));
171+
172+
// too long
173+
assertThatExceptionOfType(IllegalArgumentException.class)
174+
.isThrownBy(
175+
() ->
176+
service.resetCredentials(
177+
"pcp",
178+
new ResetPrincipalRequest(
179+
"aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"),
180+
null,
181+
null));
182+
183+
// bad char
184+
assertThatExceptionOfType(IllegalArgumentException.class)
185+
.isThrownBy(
186+
() ->
187+
service.resetCredentials(
188+
"pcp",
189+
new ResetPrincipalRequest(
190+
"aaaaaaaaaaaaaaaa", "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaag"),
191+
null,
192+
null));
193+
}
194+
97195
@Test
98196
void testValidateExternalCatalog_InternalCatalog() {
99197
StorageConfigInfo storageConfig =
@@ -237,4 +335,27 @@ private void invokeValidateExternalCatalog(PolarisServiceImpl service, Catalog c
237335
}
238336
}
239337
}
338+
339+
private PolarisServiceImpl noAdminResetCredentialPolarisService() {
340+
return new PolarisServiceImpl(
341+
realmConfig,
342+
reservedProperties,
343+
new PolarisAdminService(
344+
callContext,
345+
resolutionManifestFactory,
346+
metaStoreManager,
347+
userSecretsManager,
348+
serviceIdentityProvider,
349+
Mockito.mock(PolarisPrincipal.class),
350+
polarisAuthorizer,
351+
reservedProperties) {
352+
@Nonnull
353+
@Override
354+
public PrincipalWithCredentials resetCredentials(
355+
final String principalName, final ResetPrincipalRequest resetPrincipalRequest) {
356+
return new PrincipalWithCredentials(null, null);
357+
}
358+
},
359+
serviceIdentityProvider);
360+
}
240361
}

0 commit comments

Comments
 (0)