Skip to content

Commit aeae51f

Browse files
authored
[Catalog Federation] Add feature flag to disallow setting sub-RBAC for federated catalog at catalog level (#2696)
In #2688 (comment), we've identified that configuring polaris.config.enable-sub-catalog-rbac-for-federated-catalogs at catalog level should not be allowed in all cases, especially when the owner is not the same subject as the catalog user or admin. This PR add a feature flag, ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS to allow owner to disable catalog level setting polaris.config.enable-sub-catalog-rbac-for-federated-catalogs
1 parent 5ea215a commit aeae51f

File tree

4 files changed

+172
-2
lines changed

4 files changed

+172
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ request adding CHANGELOG notes for breaking (!) changes and possibly other secti
4040
### New Features
4141

4242
- Added a Management API endpoint to reset principal credentials, controlled by the `ENABLE_CREDENTIAL_RESET` (default: true) feature flag.
43+
- The `ENABLE_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS` was added to support sub-catalog (initially namespace and table) RBAC for federated catalogs.
44+
The setting can be configured on a per-catalog basis by setting the catalog property: `polaris.config.enable-sub-catalog-rbac-for-federated-catalogs`.
45+
The realm-level feature flag `ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS` (default: true) controls whether this functionality can be enabled or modified at the catalog level.
4346

4447
- Added support for S3-compatible storage that does not have STS (use `stsUavailable: true` in catalog storage configuration)
4548

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -386,4 +386,15 @@ public static void enforceFeatureEnabledOrThrow(
386386
+ "Defaults to enabled, but service providers may want to disable it.")
387387
.defaultValue(true)
388388
.buildFeatureConfiguration();
389+
390+
public static final FeatureConfiguration<Boolean>
391+
ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS =
392+
PolarisConfiguration.<Boolean>builder()
393+
.key("ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS")
394+
.description(
395+
"If set to true (default), Polaris will allow setting or changing "
396+
+ "catalog property polaris.config.enable-sub-catalog-rbac-for-federated-catalogs."
397+
+ "If set to false, Polaris will disallow setting or changing the above catalog property")
398+
.defaultValue(true)
399+
.buildFeatureConfiguration();
389400
}

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

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import jakarta.ws.rs.core.SecurityContext;
2626
import java.util.List;
2727
import java.util.Locale;
28+
import java.util.Map;
2829
import org.apache.iceberg.catalog.Namespace;
2930
import org.apache.iceberg.catalog.TableIdentifier;
3031
import org.apache.iceberg.rest.responses.ErrorResponse;
@@ -124,6 +125,7 @@ public Response createCatalog(
124125
Catalog catalog = request.getCatalog();
125126
validateStorageConfig(catalog.getStorageConfigInfo());
126127
validateExternalCatalog(catalog);
128+
validateCatalogProperties(catalog.getProperties());
127129
Catalog newCatalog = CatalogEntity.of(adminService.createCatalog(request)).asCatalog();
128130
LOGGER.info("Created new catalog {}", newCatalog);
129131
return Response.status(Response.Status.CREATED).entity(newCatalog).build();
@@ -180,6 +182,23 @@ private void validateExternalCatalog(Catalog catalog) {
180182
}
181183
}
182184

185+
private void validateCatalogProperties(Map<String, String> catalogProperties) {
186+
if (catalogProperties != null) {
187+
if (!realmConfig.getConfig(
188+
FeatureConfiguration.ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS)
189+
&& catalogProperties.containsKey(
190+
FeatureConfiguration.ENABLE_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS
191+
.catalogConfig())) {
192+
193+
throw new IllegalArgumentException(
194+
String.format(
195+
"Explicitly setting %s is not allowed because %s is set to false.",
196+
FeatureConfiguration.ENABLE_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS.catalogConfig(),
197+
FeatureConfiguration.ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS.key()));
198+
}
199+
}
200+
}
201+
183202
private void validateConnectionConfigInfo(ConnectionConfigInfo connectionConfigInfo) {
184203

185204
String connectionType = connectionConfigInfo.getConnectionType().name();
@@ -231,6 +250,7 @@ public Response updateCatalog(
231250
if (updateRequest.getStorageConfigInfo() != null) {
232251
validateStorageConfig(updateRequest.getStorageConfigInfo());
233252
}
253+
validateCatalogProperties(updateRequest.getProperties());
234254
return Response.ok(adminService.updateCatalog(catalogName, updateRequest).asCatalog()).build();
235255
}
236256

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

Lines changed: 138 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,16 @@
3131
import java.util.function.Supplier;
3232
import org.apache.iceberg.exceptions.ValidationException;
3333
import org.apache.polaris.core.PolarisCallContext;
34+
import org.apache.polaris.core.admin.model.AuthenticationParameters;
3435
import org.apache.polaris.core.admin.model.AwsStorageConfigInfo;
3536
import org.apache.polaris.core.admin.model.Catalog;
3637
import org.apache.polaris.core.admin.model.CatalogProperties;
38+
import org.apache.polaris.core.admin.model.ConnectionConfigInfo;
3739
import org.apache.polaris.core.admin.model.CreateCatalogRequest;
40+
import org.apache.polaris.core.admin.model.ExternalCatalog;
3841
import org.apache.polaris.core.admin.model.FileStorageConfigInfo;
42+
import org.apache.polaris.core.admin.model.IcebergRestConnectionConfigInfo;
43+
import org.apache.polaris.core.admin.model.OAuthClientCredentialsParameters;
3944
import org.apache.polaris.core.admin.model.PolarisCatalog;
4045
import org.apache.polaris.core.admin.model.StorageConfigInfo;
4146
import org.apache.polaris.core.admin.model.UpdateCatalogRequest;
@@ -66,8 +71,16 @@ public class ManagementServiceTest {
6671
public void setup() {
6772
services =
6873
TestServices.builder()
69-
.config(Map.of("SUPPORTED_CATALOG_STORAGE_TYPES", List.of("S3", "GCS", "AZURE")))
70-
.config(Map.of("ALLOW_SETTING_S3_ENDPOINTS", Boolean.FALSE))
74+
.config(
75+
Map.of(
76+
"SUPPORTED_CATALOG_STORAGE_TYPES",
77+
List.of("S3", "GCS", "AZURE"),
78+
"ALLOW_SETTING_S3_ENDPOINTS",
79+
Boolean.FALSE,
80+
"ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS",
81+
Boolean.FALSE,
82+
"ENABLE_CATALOG_FEDERATION",
83+
Boolean.TRUE))
7184
.build();
7285
}
7386

@@ -232,6 +245,129 @@ public void testUpdateCatalogWithDisallowedStorageConfig() {
232245
.hasMessage("Explicitly setting S3 endpoints is not allowed.");
233246
}
234247

248+
@Test
249+
public void testCreateCatalogWithDisallowedConfigs() {
250+
AwsStorageConfigInfo awsConfigModel =
251+
AwsStorageConfigInfo.builder()
252+
.setRoleArn("arn:aws:iam::123456789012:role/my-role")
253+
.setExternalId("externalId")
254+
.setUserArn("userArn")
255+
.setStorageType(StorageConfigInfo.StorageTypeEnum.S3)
256+
.setAllowedLocations(List.of("s3://my-old-bucket/path/to/data"))
257+
.build();
258+
ConnectionConfigInfo connectionConfigInfo =
259+
IcebergRestConnectionConfigInfo.builder(
260+
ConnectionConfigInfo.ConnectionTypeEnum.ICEBERG_REST)
261+
.setUri("https://myorg-my_account.snowflakecomputing.com/polaris/api/catalog")
262+
.setRemoteCatalogName("my-remote-catalog")
263+
.setAuthenticationParameters(
264+
OAuthClientCredentialsParameters.builder(
265+
AuthenticationParameters.AuthenticationTypeEnum.OAUTH)
266+
.setClientId("my-client-id")
267+
.setClientSecret("my-client-secret")
268+
.setScopes(List.of("PRINCIPAL_ROLE:ALL"))
269+
.build())
270+
.build();
271+
String catalogName = "mycatalog";
272+
CatalogProperties catalogProperties =
273+
CatalogProperties.builder("s3://bucket/path/to/data")
274+
.addProperty("polaris.config.enable-sub-catalog-rbac-for-federated-catalogs", "true")
275+
.build();
276+
Catalog catalog =
277+
ExternalCatalog.builder()
278+
.setType(Catalog.TypeEnum.EXTERNAL)
279+
.setName(catalogName)
280+
.setProperties(catalogProperties)
281+
.setStorageConfigInfo(awsConfigModel)
282+
.setConnectionConfigInfo(connectionConfigInfo)
283+
.build();
284+
Supplier<Response> createCatalog =
285+
() ->
286+
services
287+
.catalogsApi()
288+
.createCatalog(
289+
new CreateCatalogRequest(catalog),
290+
services.realmContext(),
291+
services.securityContext());
292+
assertThatThrownBy(createCatalog::get)
293+
.isInstanceOf(IllegalArgumentException.class)
294+
.hasMessage(
295+
"Explicitly setting polaris.config.enable-sub-catalog-rbac-for-federated-catalogs is not allowed because ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS is set to false.");
296+
}
297+
298+
@Test
299+
public void testUpdateCatalogWithDisallowedConfigs() {
300+
AwsStorageConfigInfo awsConfigModel =
301+
AwsStorageConfigInfo.builder()
302+
.setRoleArn("arn:aws:iam::123456789012:role/my-role")
303+
.setExternalId("externalId")
304+
.setUserArn("userArn")
305+
.setStorageType(StorageConfigInfo.StorageTypeEnum.S3)
306+
.setAllowedLocations(List.of("s3://my-old-bucket/path/to/data"))
307+
.build();
308+
ConnectionConfigInfo connectionConfigInfo =
309+
IcebergRestConnectionConfigInfo.builder(
310+
ConnectionConfigInfo.ConnectionTypeEnum.ICEBERG_REST)
311+
.setUri("https://myorg-my_account.snowflakecomputing.com/polaris/api/catalog")
312+
.setRemoteCatalogName("my-remote-catalog")
313+
.setAuthenticationParameters(
314+
OAuthClientCredentialsParameters.builder(
315+
AuthenticationParameters.AuthenticationTypeEnum.OAUTH)
316+
.setClientId("my-client-id")
317+
.setClientSecret("my-client-secret")
318+
.setScopes(List.of("PRINCIPAL_ROLE:ALL"))
319+
.build())
320+
.build();
321+
String catalogName = "mycatalog";
322+
CatalogProperties catalogProperties =
323+
CatalogProperties.builder("s3://bucket/path/to/data").build();
324+
Catalog catalog =
325+
ExternalCatalog.builder()
326+
.setType(Catalog.TypeEnum.EXTERNAL)
327+
.setName(catalogName)
328+
.setProperties(catalogProperties)
329+
.setStorageConfigInfo(awsConfigModel)
330+
.setConnectionConfigInfo(connectionConfigInfo)
331+
.build();
332+
try (Response response =
333+
services
334+
.catalogsApi()
335+
.createCatalog(
336+
new CreateCatalogRequest(catalog),
337+
services.realmContext(),
338+
services.securityContext())) {
339+
assertThat(response).returns(Response.Status.CREATED.getStatusCode(), Response::getStatus);
340+
}
341+
Catalog fetchedCatalog;
342+
try (Response response =
343+
services
344+
.catalogsApi()
345+
.getCatalog(catalogName, services.realmContext(), services.securityContext())) {
346+
assertThat(response).returns(Response.Status.OK.getStatusCode(), Response::getStatus);
347+
fetchedCatalog = (Catalog) response.getEntity();
348+
349+
assertThat(fetchedCatalog.getName()).isEqualTo(catalogName);
350+
assertThat(fetchedCatalog.getProperties().toMap())
351+
.isEqualTo(Map.of("default-base-location", "s3://bucket/path/to/data"));
352+
assertThat(fetchedCatalog.getEntityVersion()).isGreaterThan(0);
353+
}
354+
355+
UpdateCatalogRequest update =
356+
UpdateCatalogRequest.builder()
357+
.setProperties(
358+
Map.of("polaris.config.enable-sub-catalog-rbac-for-federated-catalogs", "true"))
359+
.build();
360+
assertThatThrownBy(
361+
() ->
362+
services
363+
.catalogsApi()
364+
.updateCatalog(
365+
catalogName, update, services.realmContext(), services.securityContext()))
366+
.isInstanceOf(IllegalArgumentException.class)
367+
.hasMessage(
368+
"Explicitly setting polaris.config.enable-sub-catalog-rbac-for-federated-catalogs is not allowed because ALLOW_SETTING_SUB_CATALOG_RBAC_FOR_FEDERATED_CATALOGS is set to false.");
369+
}
370+
235371
private PolarisAdminService setupPolarisAdminService(
236372
PolarisMetaStoreManager metaStoreManager, PolarisCallContext callContext) {
237373
return new PolarisAdminService(

0 commit comments

Comments
 (0)