diff --git a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizableOperation.java b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizableOperation.java index 1eaea6c3f9..e7f5a6cc00 100644 --- a/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizableOperation.java +++ b/polaris-core/src/main/java/org/apache/polaris/core/auth/PolarisAuthorizableOperation.java @@ -208,9 +208,7 @@ public enum PolarisAuthorizableOperation { DETACH_POLICY_FROM_TABLE(POLICY_DETACH, TABLE_DETACH_POLICY), GET_APPLICABLE_POLICIES_ON_CATALOG(CATALOG_READ_PROPERTIES), GET_APPLICABLE_POLICIES_ON_NAMESPACE(NAMESPACE_READ_PROPERTIES), - GET_APPLICABLE_POLICIES_ON_TABLE(TABLE_READ_PROPERTIES), - GET_APPLICABLE_POLICIES_ON_VIEW(VIEW_READ_PROPERTIES), - ; + GET_APPLICABLE_POLICIES_ON_TABLE(TABLE_READ_PROPERTIES); private final EnumSet privilegesOnTarget; private final EnumSet privilegesOnSecondary; diff --git a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolicyCatalogHandlerAuthzTest.java b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolicyCatalogHandlerAuthzTest.java index b821be31e5..c2aea07e3b 100644 --- a/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolicyCatalogHandlerAuthzTest.java +++ b/quarkus/service/src/test/java/org/apache/polaris/service/quarkus/catalog/PolicyCatalogHandlerAuthzTest.java @@ -722,4 +722,68 @@ public void testDetachFromPolicyInsufficientPrivileges() { newWrapper(Set.of(PRINCIPAL_ROLE2)).detachPolicy(POLICY_NS1_1, detachPolicyRequest); } + + @Test + public void testGetApplicablePoliciesOnCatalogSufficientPrivileges() { + doTestSufficientPrivileges( + List.of( + PolarisPrivilege.CATALOG_READ_PROPERTIES, + PolarisPrivilege.CATALOG_WRITE_PROPERTIES, + PolarisPrivilege.CATALOG_MANAGE_METADATA), + () -> newWrapper().getApplicablePolicies(null, null, null), + null /* cleanupAction */); + } + + @Test + public void testGetApplicablePoliciesOnCatalogInsufficientPrivileges() { + doTestInsufficientPrivileges( + List.of( + PolarisPrivilege.NAMESPACE_READ_PROPERTIES, + PolarisPrivilege.POLICY_READ, + PolarisPrivilege.TABLE_READ_PROPERTIES), + () -> newWrapper().getApplicablePolicies(null, null, null)); + } + + @Test + public void testGetApplicablePoliciesOnNamespaceSufficientPrivileges() { + doTestSufficientPrivileges( + List.of( + PolarisPrivilege.NAMESPACE_READ_PROPERTIES, + PolarisPrivilege.NAMESPACE_WRITE_PROPERTIES, + PolarisPrivilege.CATALOG_MANAGE_METADATA), + () -> newWrapper().getApplicablePolicies(NS1, null, null), + null /* cleanupAction */); + } + + @Test + public void testGetApplicablePoliciesOnNamespaceInSufficientPrivileges() { + doTestInsufficientPrivileges( + List.of( + PolarisPrivilege.CATALOG_READ_PROPERTIES, + PolarisPrivilege.POLICY_READ, + PolarisPrivilege.TABLE_READ_PROPERTIES), + () -> newWrapper().getApplicablePolicies(NS1, null, null)); + } + + @Test + public void testGetApplicablePoliciesOnTableSufficientPrivileges() { + doTestSufficientPrivileges( + List.of( + PolarisPrivilege.TABLE_READ_PROPERTIES, + PolarisPrivilege.TABLE_WRITE_PROPERTIES, + PolarisPrivilege.CATALOG_MANAGE_METADATA), + () -> newWrapper().getApplicablePolicies(TABLE_NS1_1.namespace(), TABLE_NS1_1.name(), null), + null /* cleanupAction */); + } + + @Test + public void testGetApplicablePoliciesOnTableInsufficientPrivileges() { + doTestInsufficientPrivileges( + List.of( + PolarisPrivilege.CATALOG_READ_PROPERTIES, + PolarisPrivilege.POLICY_READ, + PolarisPrivilege.NAMESPACE_READ_PROPERTIES), + () -> + newWrapper().getApplicablePolicies(TABLE_NS1_1.namespace(), TABLE_NS1_1.name(), null)); + } } diff --git a/service/common/src/main/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandler.java b/service/common/src/main/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandler.java index fb0c71f2e7..f4dea27b43 100644 --- a/service/common/src/main/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandler.java +++ b/service/common/src/main/java/org/apache/polaris/service/catalog/policy/PolicyCatalogHandler.java @@ -18,6 +18,8 @@ */ package org.apache.polaris.service.catalog.policy; +import com.google.common.base.Strings; +import jakarta.annotation.Nullable; import jakarta.ws.rs.core.SecurityContext; import java.util.Arrays; import java.util.HashSet; @@ -26,6 +28,7 @@ import org.apache.iceberg.catalog.TableIdentifier; import org.apache.iceberg.exceptions.NoSuchNamespaceException; import org.apache.iceberg.exceptions.NoSuchTableException; +import org.apache.iceberg.exceptions.NotFoundException; import org.apache.polaris.core.auth.PolarisAuthorizableOperation; import org.apache.polaris.core.auth.PolarisAuthorizer; import org.apache.polaris.core.catalog.PolarisCatalogHelpers; @@ -43,6 +46,7 @@ import org.apache.polaris.service.types.AttachPolicyRequest; import org.apache.polaris.service.types.CreatePolicyRequest; import org.apache.polaris.service.types.DetachPolicyRequest; +import org.apache.polaris.service.types.GetApplicablePoliciesResponse; import org.apache.polaris.service.types.ListPoliciesResponse; import org.apache.polaris.service.types.LoadPolicyResponse; import org.apache.polaris.service.types.PolicyAttachmentTarget; @@ -134,6 +138,16 @@ public boolean detachPolicy(PolicyIdentifier identifier, DetachPolicyRequest req return policyCatalog.detachPolicy(identifier, request.getTarget()); } + public GetApplicablePoliciesResponse getApplicablePolicies( + @Nullable Namespace namespace, @Nullable String targetName, @Nullable PolicyType policyType) { + authorizeGetApplicablePoliciesOperationOrThrow(namespace, targetName); + + return GetApplicablePoliciesResponse.builder() + .setApplicablePolicies( + new HashSet<>(policyCatalog.getApplicablePolicies(namespace, targetName, policyType))) + .build(); + } + private void authorizeBasicPolicyOperationOrThrow( PolarisAuthorizableOperation op, PolicyIdentifier identifier) { resolutionManifest = @@ -161,6 +175,49 @@ private void authorizeBasicPolicyOperationOrThrow( initializeCatalog(); } + private void authorizeGetApplicablePoliciesOperationOrThrow( + @Nullable Namespace namespace, @Nullable String targetName) { + if (namespace == null || namespace.isEmpty()) { + // catalog + PolarisAuthorizableOperation op = + PolarisAuthorizableOperation.GET_APPLICABLE_POLICIES_ON_CATALOG; + authorizeBasicCatalogOperationOrThrow(op); + } else if (Strings.isNullOrEmpty(targetName)) { + // namespace + PolarisAuthorizableOperation op = + PolarisAuthorizableOperation.GET_APPLICABLE_POLICIES_ON_NAMESPACE; + authorizeBasicNamespaceOperationOrThrow(op, namespace); + } else { + // table + TableIdentifier tableIdentifier = TableIdentifier.of(namespace, targetName); + PolarisAuthorizableOperation op = + PolarisAuthorizableOperation.GET_APPLICABLE_POLICIES_ON_TABLE; + // only Iceberg tables are supported + authorizeBasicTableLikeOperationOrThrow( + op, PolarisEntitySubType.ICEBERG_TABLE, tableIdentifier); + } + } + + private void authorizeBasicCatalogOperationOrThrow(PolarisAuthorizableOperation op) { + resolutionManifest = + entityManager.prepareResolutionManifest(callContext, securityContext, catalogName); + resolutionManifest.resolveAll(); + + PolarisResolvedPathWrapper targetCatalog = + resolutionManifest.getResolvedReferenceCatalogEntity(); + if (targetCatalog == null) { + throw new NotFoundException("Catalog not found"); + } + authorizer.authorizeOrThrow( + authenticatedPrincipal, + resolutionManifest.getAllActivatedCatalogRoleAndPrincipalRoles(), + op, + targetCatalog, + null); + + initializeCatalog(); + } + private void authorizePolicyMappingOperationOrThrow( PolicyIdentifier identifier, PolicyAttachmentTarget target, boolean isAttach) { resolutionManifest =