Skip to content

Commit b1f7c68

Browse files
authored
Policy Store: Add PolicyEntity and PolicyTypes (#1133)
1 parent d334548 commit b1f7c68

File tree

8 files changed

+562
-4
lines changed

8 files changed

+562
-4
lines changed

polaris-core/src/main/java/org/apache/polaris/core/entity/PolarisEntityType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ public enum PolarisEntityType {
3434
// generic table is either a view or a real table
3535
TABLE_LIKE(7, NAMESPACE, false, false),
3636
TASK(8, ROOT, false, false),
37-
FILE(9, TABLE_LIKE, false, false);
37+
FILE(9, TABLE_LIKE, false, false),
38+
POLICY(10, NAMESPACE, false, false);
3839

3940
// to efficiently map a code to its corresponding entity type, use a reverse array which
4041
// is initialized below
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.core.policy;
20+
21+
import com.fasterxml.jackson.annotation.JsonIgnore;
22+
import com.google.common.base.Preconditions;
23+
import org.apache.iceberg.catalog.Namespace;
24+
import org.apache.iceberg.rest.RESTUtil;
25+
import org.apache.polaris.core.entity.NamespaceEntity;
26+
import org.apache.polaris.core.entity.PolarisBaseEntity;
27+
import org.apache.polaris.core.entity.PolarisEntity;
28+
import org.apache.polaris.core.entity.PolarisEntityType;
29+
30+
public class PolicyEntity extends PolarisEntity {
31+
32+
public static final String POLICY_TYPE_CODE_KEY = "policy-type-code";
33+
public static final String POLICY_DESCRIPTION_KEY = "policy-description";
34+
public static final String POLICY_VERSION_KEY = "policy-version";
35+
public static final String POLICY_CONTENT_KEY = "policy-content";
36+
37+
PolicyEntity(PolarisBaseEntity sourceEntity) {
38+
super(sourceEntity);
39+
}
40+
41+
public static PolicyEntity of(PolarisBaseEntity sourceEntity) {
42+
if (sourceEntity != null) {
43+
return new PolicyEntity(sourceEntity);
44+
}
45+
46+
return null;
47+
}
48+
49+
@JsonIgnore
50+
public PolicyType getPolicyType() {
51+
return PolicyType.fromCode(getPolicyTypeCode());
52+
}
53+
54+
@JsonIgnore
55+
public int getPolicyTypeCode() {
56+
Preconditions.checkArgument(
57+
getPropertiesAsMap().containsKey(POLICY_TYPE_CODE_KEY),
58+
"Invalid policy entity: policy type must exist");
59+
String policyTypeCode = getPropertiesAsMap().get(POLICY_TYPE_CODE_KEY);
60+
return Integer.parseInt(policyTypeCode);
61+
}
62+
63+
@JsonIgnore
64+
public String getDescription() {
65+
return getPropertiesAsMap().get(POLICY_DESCRIPTION_KEY);
66+
}
67+
68+
@JsonIgnore
69+
public String getContent() {
70+
return getPropertiesAsMap().get(POLICY_CONTENT_KEY);
71+
}
72+
73+
@JsonIgnore
74+
public int getPolicyVersion() {
75+
return Integer.parseInt(getPropertiesAsMap().get(POLICY_VERSION_KEY));
76+
}
77+
78+
public static class Builder extends PolarisEntity.BaseBuilder<PolicyEntity, Builder> {
79+
public Builder(Namespace namespace, String policyName, PolicyType policyType) {
80+
super();
81+
setType(PolarisEntityType.POLICY);
82+
setParentNamespace(namespace);
83+
setName(policyName);
84+
setPolicyType(policyType);
85+
setPolicyVersion(0);
86+
}
87+
88+
public Builder(PolicyEntity original) {
89+
super(original);
90+
}
91+
92+
@Override
93+
public PolicyEntity build() {
94+
Preconditions.checkArgument(
95+
properties.containsKey(POLICY_TYPE_CODE_KEY), "Policy type must be specified");
96+
97+
return new PolicyEntity(buildBase());
98+
}
99+
100+
public Builder setParentNamespace(Namespace namespace) {
101+
if (namespace != null && !namespace.isEmpty()) {
102+
internalProperties.put(
103+
NamespaceEntity.PARENT_NAMESPACE_KEY, RESTUtil.encodeNamespace(namespace));
104+
}
105+
return this;
106+
}
107+
108+
public Builder setPolicyType(PolicyType policyType) {
109+
Preconditions.checkArgument(policyType != null, "Policy type must be specified");
110+
properties.put(POLICY_TYPE_CODE_KEY, Integer.toString(policyType.getCode()));
111+
return this;
112+
}
113+
114+
public Builder setDescription(String description) {
115+
properties.put(POLICY_DESCRIPTION_KEY, description);
116+
return this;
117+
}
118+
119+
public Builder setPolicyVersion(int version) {
120+
properties.put(POLICY_VERSION_KEY, Integer.toString(version));
121+
return this;
122+
}
123+
124+
public Builder setContent(String content) {
125+
properties.put(POLICY_CONTENT_KEY, content);
126+
return this;
127+
}
128+
}
129+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.core.policy;
20+
21+
import com.fasterxml.jackson.annotation.JsonCreator;
22+
import com.fasterxml.jackson.annotation.JsonValue;
23+
import jakarta.annotation.Nullable;
24+
25+
/**
26+
* Represents a policy type in Polaris. A policy type defines a category of policies that may be
27+
* either predefined or custom (user-defined).
28+
*
29+
* <p>A policy type can be either inheritable or non-inheritable. Inheritable policies are passed
30+
* down to lower-level entities (e.g., from a namespace to a table).
31+
*/
32+
public interface PolicyType {
33+
34+
/**
35+
* Retrieves the unique type code associated with this policy type.
36+
*
37+
* @return the type code of the policy type
38+
*/
39+
@JsonValue
40+
int getCode();
41+
42+
/**
43+
* Retrieves the human-readable name of this policy type.
44+
*
45+
* @return the name of the policy type
46+
*/
47+
String getName();
48+
49+
/**
50+
* Determines whether this policy type is inheritable.
51+
*
52+
* @return {@code true} if the policy type is inheritable, otherwise {@code false}
53+
*/
54+
boolean isInheritable();
55+
56+
/**
57+
* Retrieves a {@link PolicyType} instance corresponding to the given type code.
58+
*
59+
* <p>This method searches for the policy type in predefined policy types. If a custom policy type
60+
* storage mechanism is implemented in the future, it may also check registered custom policy
61+
* types.
62+
*
63+
* @param code the type code of the policy type
64+
* @return the corresponding {@link PolicyType}, or {@code null} if no matching type is found
65+
*/
66+
@JsonCreator
67+
static @Nullable PolicyType fromCode(int code) {
68+
return PredefinedPolicyTypes.fromCode(code);
69+
}
70+
71+
/**
72+
* Retrieves a {@link PolicyType} instance corresponding to the given policy name.
73+
*
74+
* <p>This method searches for the policy type in predefined policy types. If a custom policy type
75+
* storage mechanism is implemented in the future, it may also check registered custom policy
76+
* types.
77+
*
78+
* @param name the name of the policy type
79+
* @return the corresponding {@link PolicyType}, or {@code null} if no matching type is found
80+
*/
81+
static @Nullable PolicyType fromName(String name) {
82+
return PredefinedPolicyTypes.fromName(name);
83+
}
84+
}
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
package org.apache.polaris.core.policy;
20+
21+
import com.fasterxml.jackson.annotation.JsonCreator;
22+
import com.fasterxml.jackson.annotation.JsonValue;
23+
import com.google.common.collect.ImmutableMap;
24+
import jakarta.annotation.Nullable;
25+
26+
/* Represents all predefined policy types in Polaris */
27+
public enum PredefinedPolicyTypes implements PolicyType {
28+
DATA_COMPACTION(0, "system.data-compaction", true),
29+
METADATA_COMPACTION(1, "system.metadata-compaction", true),
30+
ORPHAN_FILE_REMOVAL(2, "system.orphan-file-removal", true),
31+
SNAPSHOT_RETENTION(3, "system.snapshot-retention", true);
32+
33+
private final int code;
34+
private final String name;
35+
private final boolean isInheritable;
36+
private static final PredefinedPolicyTypes[] REVERSE_CODE_MAPPING_ARRAY;
37+
private static final ImmutableMap<String, PredefinedPolicyTypes> REVERSE_NAME_MAPPING_ARRAY;
38+
39+
static {
40+
int maxId = 0;
41+
for (PredefinedPolicyTypes policyType : PredefinedPolicyTypes.values()) {
42+
if (maxId < policyType.code) {
43+
maxId = policyType.code;
44+
}
45+
}
46+
47+
REVERSE_CODE_MAPPING_ARRAY = new PredefinedPolicyTypes[maxId + 1];
48+
ImmutableMap.Builder<String, PredefinedPolicyTypes> builder = ImmutableMap.builder();
49+
// populate both
50+
for (PredefinedPolicyTypes policyType : PredefinedPolicyTypes.values()) {
51+
REVERSE_CODE_MAPPING_ARRAY[policyType.code] = policyType;
52+
builder.put(policyType.name, policyType);
53+
}
54+
REVERSE_NAME_MAPPING_ARRAY = builder.build();
55+
}
56+
57+
PredefinedPolicyTypes(int code, String name, boolean isInheritable) {
58+
this.code = code;
59+
this.name = name;
60+
this.isInheritable = isInheritable;
61+
}
62+
63+
/** {@inheritDoc} */
64+
@Override
65+
@JsonValue
66+
public int getCode() {
67+
return code;
68+
}
69+
70+
/** {@inheritDoc} */
71+
@Override
72+
public String getName() {
73+
return name;
74+
}
75+
76+
/** {@inheritDoc} */
77+
@Override
78+
public boolean isInheritable() {
79+
return isInheritable;
80+
}
81+
82+
/**
83+
* Retrieves a {@link PredefinedPolicyTypes} instance corresponding to the given type code.
84+
*
85+
* @param code the type code of the predefined policy type
86+
* @return the corresponding {@link PredefinedPolicyTypes}, or {@code null} if no matching type is
87+
* found
88+
*/
89+
@JsonCreator
90+
public static @Nullable PredefinedPolicyTypes fromCode(int code) {
91+
if (code >= REVERSE_CODE_MAPPING_ARRAY.length) {
92+
return null;
93+
}
94+
95+
return REVERSE_CODE_MAPPING_ARRAY[code];
96+
}
97+
98+
/**
99+
* Retrieves a {@link PredefinedPolicyTypes} instance corresponding to the given policy name.
100+
*
101+
* @param name the name of the predefined policy type
102+
* @return the corresponding {@link PredefinedPolicyTypes}, or {@code null} if no matching type is
103+
* found
104+
*/
105+
public static @Nullable PredefinedPolicyTypes fromName(String name) {
106+
return REVERSE_NAME_MAPPING_ARRAY.get(name);
107+
}
108+
}

polaris-core/src/test/java/org/apache/polaris/core/persistence/ResolverTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ public class ResolverTest {
105105
* - (N1/N4)
106106
* - N5/N6/T5
107107
* - N5/N6/T6
108+
* - N7/N8/POL1
109+
* - N7/N8/POL2
110+
* - N7/POL3
108111
* - R1(TABLE_READ on N1/N2, VIEW_CREATE on C, TABLE_LIST on N2, TABLE_DROP on N5/N6/T5)
109112
* - R2(TABLE_WRITE_DATA on N5, VIEW_LIST on C)
110113
* - PR1(R1, R2)
@@ -230,6 +233,19 @@ void testResolvePath(boolean useCache) {
230233
new ResolverPath(List.of("N5", "N6", "T5"), PolarisEntityType.TABLE_LIKE);
231234
this.resolveDriver(this.cache, "test", N5_N6_T5, null, null);
232235

236+
// N7/N8 which exists
237+
ResolverPath N7_N8 = new ResolverPath(List.of("N7", "N8"), PolarisEntityType.NAMESPACE);
238+
this.resolveDriver(this.cache, "test", N7_N8, null, null);
239+
240+
// N7/N8/POL1 which exists
241+
ResolverPath N7_N8_POL1 =
242+
new ResolverPath(List.of("N7", "N8", "POL1"), PolarisEntityType.POLICY);
243+
this.resolveDriver(this.cache, "test", N7_N8_POL1, null, null);
244+
245+
// N7/POL3 which exists
246+
ResolverPath N7_POL3 = new ResolverPath(List.of("N7", "POL3"), PolarisEntityType.POLICY);
247+
this.resolveDriver(this.cache, "test", N7_POL3, null, null);
248+
233249
// Error scenarios: N5/N6/T8 which does not exists
234250
ResolverPath N5_N6_T8 =
235251
new ResolverPath(List.of("N5", "N6", "T8"), PolarisEntityType.TABLE_LIKE);

0 commit comments

Comments
 (0)