Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,15 @@ public int getPolicyVersion() {
return Integer.parseInt(getPropertiesAsMap().get(POLICY_VERSION_KEY));
}

@JsonIgnore
public Namespace getParentNamespace() {
String parentNamespace = getInternalPropertiesAsMap().get(NamespaceEntity.PARENT_NAMESPACE_KEY);
if (parentNamespace != null) {
return RESTUtil.decodeNamespace(parentNamespace);
}
return null;
}

public static class Builder extends PolarisEntity.BaseBuilder<PolicyEntity, Builder> {
public Builder(Namespace namespace, String policyName, PolicyType policyType) {
super();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import java.io.IOException;
import java.lang.reflect.Method;
import java.time.Clock;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.Set;
Expand Down Expand Up @@ -94,6 +95,7 @@
import org.apache.polaris.service.config.RealmEntityManagerFactory;
import org.apache.polaris.service.storage.PolarisStorageIntegrationProviderImpl;
import org.apache.polaris.service.task.TaskExecutor;
import org.apache.polaris.service.types.ApplicablePolicy;
import org.apache.polaris.service.types.Policy;
import org.apache.polaris.service.types.PolicyAttachmentTarget;
import org.apache.polaris.service.types.PolicyIdentifier;
Expand Down Expand Up @@ -585,8 +587,8 @@ public void testPolicyInheritance() {
policyCatalog.attachPolicy(POLICY2, POLICY_ATTACH_TARGET_NS, null);
var applicablePolicies = policyCatalog.getApplicablePolicies(NS, null, null);
assertThat(applicablePolicies.size()).isEqualTo(2);
assertThat(applicablePolicies.contains(p1)).isTrue();
assertThat(applicablePolicies.contains(p2)).isTrue();
assertThat(applicablePolicies.contains(policyToApplicablePolicy(p1, true, NS))).isTrue();
assertThat(applicablePolicies.contains(policyToApplicablePolicy(p2, false, NS))).isTrue();

// attach policies to a table
icebergCatalog.createTable(TABLE, SCHEMA);
Expand All @@ -605,8 +607,8 @@ public void testPolicyInheritance() {
policyCatalog.attachPolicy(POLICY4, POLICY_ATTACH_TARGET_TBL, null);
applicablePolicies = policyCatalog.getApplicablePolicies(NS, TABLE.name(), null);
// p2 should be overwritten by p4, as they are the same type
assertThat(applicablePolicies.contains(p4)).isTrue();
assertThat(applicablePolicies.contains(p2)).isFalse();
assertThat(applicablePolicies.contains(policyToApplicablePolicy(p4, false, NS))).isTrue();
assertThat(applicablePolicies.contains(policyToApplicablePolicy(p2, true, NS))).isFalse();
}

@Test
Expand All @@ -626,6 +628,19 @@ public void testGetApplicablePoliciesFilterOnType() {
policyCatalog.attachPolicy(POLICY2, POLICY_ATTACH_TARGET_NS, null);
var applicablePolicies = policyCatalog.getApplicablePolicies(NS, null, DATA_COMPACTION);
// only p2 is with the type fetched
assertThat(applicablePolicies.contains(p2)).isTrue();
assertThat(applicablePolicies.contains(policyToApplicablePolicy(p2, false, NS))).isTrue();
}

private static ApplicablePolicy policyToApplicablePolicy(
Policy policy, boolean inherited, Namespace parent) {
return new ApplicablePolicy(
policy.getPolicyType(),
policy.getInheritable(),
policy.getName(),
policy.getDescription(),
policy.getContent(),
policy.getVersion(),
inherited,
Arrays.asList(parent.levels()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,15 @@
import com.google.common.base.Strings;
import jakarta.annotation.Nonnull;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Stream;
import org.apache.iceberg.catalog.Namespace;
import org.apache.iceberg.catalog.TableIdentifier;
import org.apache.iceberg.exceptions.AlreadyExistsException;
Expand All @@ -52,6 +56,7 @@
import org.apache.polaris.core.policy.exceptions.PolicyAttachException;
import org.apache.polaris.core.policy.exceptions.PolicyVersionMismatchException;
import org.apache.polaris.core.policy.validator.PolicyValidators;
import org.apache.polaris.service.types.ApplicablePolicy;
import org.apache.polaris.service.types.Policy;
import org.apache.polaris.service.types.PolicyAttachmentTarget;
import org.apache.polaris.service.types.PolicyIdentifier;
Expand Down Expand Up @@ -342,7 +347,7 @@ public boolean detachPolicy(PolicyIdentifier policyIdentifier, PolicyAttachmentT
return true;
}

public List<Policy> getApplicablePolicies(
public List<ApplicablePolicy> getApplicablePolicies(
Namespace namespace, String targetName, PolicyType policyType) {
var targetFullPath = getFullPath(namespace, targetName);
return getEffectivePolicies(targetFullPath, policyType);
Expand All @@ -369,14 +374,15 @@ public List<Policy> getApplicablePolicies(
* @return a list of effective policies, combining inherited policies from upper levels and
* non-inheritable policies from the final entity
*/
private List<Policy> getEffectivePolicies(List<PolarisEntity> path, PolicyType policyType) {
private List<ApplicablePolicy> getEffectivePolicies(
List<PolarisEntity> path, PolicyType policyType) {
if (path == null || path.isEmpty()) {
return List.of();
}

Map<String, PolicyEntity> inheritedPolicies = new LinkedHashMap<>();
// Final list of effective policies (inheritable + last-entity non-inheritable)
List<PolicyEntity> finalPolicies = new ArrayList<>();
Map<String, PolicyEntity> inheritablePolicies = new LinkedHashMap<>();
Set<Long> directAttachedInheritablePolicies = new HashSet<>();
List<PolicyEntity> nonInheritablePolicies = new ArrayList<>();

// Process all entities except the last one
for (int i = 0; i < path.size() - 1; i++) {
Expand All @@ -387,7 +393,7 @@ private List<Policy> getEffectivePolicies(List<PolarisEntity> path, PolicyType p
// For non-last entities, we only carry forward inheritable policies
if (policy.getPolicyType().isInheritable()) {
// Put in map; overwrites by policyType if encountered again
inheritedPolicies.put(policy.getPolicyType().getName(), policy);
inheritablePolicies.put(policy.getPolicyType().getName(), policy);
}
}
}
Expand All @@ -398,17 +404,22 @@ private List<Policy> getEffectivePolicies(List<PolarisEntity> path, PolicyType p
for (var policy : lastPolicies) {
if (policy.getPolicyType().isInheritable()) {
// Overwrite anything by the same policyType in the inherited map
inheritedPolicies.put(policy.getPolicyType().getName(), policy);
inheritablePolicies.put(policy.getPolicyType().getName(), policy);
directAttachedInheritablePolicies.add(policy.getId());
} else {
// Non-inheritable => goes directly to final list
finalPolicies.add(policy);
nonInheritablePolicies.add(policy);
}
}

// Append all inherited policies at the end, preserving insertion order
finalPolicies.addAll(inheritedPolicies.values());

return finalPolicies.stream().map(PolicyCatalog::constructPolicy).toList();
return Stream.concat(
nonInheritablePolicies.stream().map(policy -> constructApplicablePolicy(policy, false)),
inheritablePolicies.values().stream()
.map(
policy ->
constructApplicablePolicy(
policy, !directAttachedInheritablePolicies.contains(policy.getId()))))
.toList();
}

private List<PolicyEntity> getPolicies(PolarisEntity target, PolicyType policyType) {
Expand Down Expand Up @@ -506,4 +517,20 @@ private static Policy constructPolicy(PolicyEntity policyEntity) {
.setVersion(policyEntity.getPolicyVersion())
.build();
}

private static ApplicablePolicy constructApplicablePolicy(
PolicyEntity policyEntity, boolean inherited) {
Namespace parentNamespace = policyEntity.getParentNamespace();

return ApplicablePolicy.builder()
.setPolicyType(policyEntity.getPolicyType().getName())
.setInheritable(policyEntity.getPolicyType().isInheritable())
.setName(policyEntity.getName())
.setDescription(policyEntity.getDescription())
.setContent(policyEntity.getContent())
.setVersion(policyEntity.getPolicyVersion())
.setInherited(inherited)
.setNamespace(Arrays.asList(parentNamespace.levels()))
.build();
}
}