This is an automated email from the ASF dual-hosted git repository.
roryqi pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/main by this push:
new f052358ad7 [#9071] feat(authz): Add job and job template privileges
interfaces and documents (#9177)
f052358ad7 is described below
commit f052358ad76b8f37f6f30052179eea397ed2b247
Author: roryqi <[email protected]>
AuthorDate: Tue Nov 25 21:54:52 2025 +0800
[#9071] feat(authz): Add job and job template privileges interfaces and
documents (#9177)
### What changes were proposed in this pull request?
Add job and job template privileges interfaces and documents
### Why are the changes needed?
Fix: #9071
### Does this PR introduce _any_ user-facing change?
Added the documents.
### How was this patch tested?
Added unit tests.
---
.../java/org/apache/gravitino/MetadataObject.java | 5 +-
.../java/org/apache/gravitino/MetadataObjects.java | 83 ++++++++------
.../apache/gravitino/authorization/Privilege.java | 7 +-
.../apache/gravitino/authorization/Privileges.java | 125 ++++++++++++++++++++-
.../gravitino/authorization/SecurableObjects.java | 34 ++++++
.../org/apache/gravitino/TestMetadataObjects.java | 28 +++++
.../authorization/TestSecurableObjects.java | 49 +++++++-
.../java/org/apache/gravitino/tag/TagManager.java | 13 ++-
docs/security/access-control.md | 27 ++++-
9 files changed, 325 insertions(+), 46 deletions(-)
diff --git a/api/src/main/java/org/apache/gravitino/MetadataObject.java
b/api/src/main/java/org/apache/gravitino/MetadataObject.java
index 4df271d049..d773205065 100644
--- a/api/src/main/java/org/apache/gravitino/MetadataObject.java
+++ b/api/src/main/java/org/apache/gravitino/MetadataObject.java
@@ -68,7 +68,10 @@ public interface MetadataObject {
* A policy can be associated with a metadata object for data governance
and similar purposes.
*/
POLICY,
- ;
+ /** A job represents a data processing task in Gravitino. */
+ JOB,
+ /** A job template represents a reusable template for creating jobs in
Gravitino. */
+ JOB_TEMPLATE;
}
/**
diff --git a/api/src/main/java/org/apache/gravitino/MetadataObjects.java
b/api/src/main/java/org/apache/gravitino/MetadataObjects.java
index c3f7e8a7c3..586e1785b4 100644
--- a/api/src/main/java/org/apache/gravitino/MetadataObjects.java
+++ b/api/src/main/java/org/apache/gravitino/MetadataObjects.java
@@ -21,8 +21,12 @@ package org.apache.gravitino;
import com.google.common.base.Joiner;
import com.google.common.base.Preconditions;
import com.google.common.base.Splitter;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.Sets;
import java.util.Collections;
import java.util.List;
+import java.util.Map;
+import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.lang3.StringUtils;
@@ -36,6 +40,36 @@ public class MetadataObjects {
private static final Joiner DOT_JOINER = Joiner.on('.');
+ private static final Set<MetadataObject.Type> VALID_SINGLE_LEVEL_NAME_TYPES =
+ Sets.newHashSet(
+ MetadataObject.Type.CATALOG,
+ MetadataObject.Type.METALAKE,
+ MetadataObject.Type.ROLE,
+ MetadataObject.Type.TAG,
+ MetadataObject.Type.JOB,
+ MetadataObject.Type.JOB_TEMPLATE,
+ MetadataObject.Type.POLICY);
+
+ private static final Set<MetadataObject.Type> VALID_TWO_LEVEL_NAME_TYPES =
+ Sets.newHashSet(MetadataObject.Type.SCHEMA);
+
+ private static final Set<MetadataObject.Type> VALID_THREE_LEVEL_NAME_TYPES =
+ Sets.newHashSet(
+ MetadataObject.Type.FILESET,
+ MetadataObject.Type.TABLE,
+ MetadataObject.Type.TOPIC,
+ MetadataObject.Type.MODEL);
+
+ private static final Set<MetadataObject.Type> VALID_FOUR_LEVEL_NAME_TYPES =
+ Sets.newHashSet(MetadataObject.Type.COLUMN);
+
+ private static final Map<Set<MetadataObject.Type>, Integer>
TYPE_TO_EXPECT_LENGTH =
+ ImmutableMap.of(
+ VALID_SINGLE_LEVEL_NAME_TYPES, 1,
+ VALID_TWO_LEVEL_NAME_TYPES, 2,
+ VALID_THREE_LEVEL_NAME_TYPES, 3,
+ VALID_FOUR_LEVEL_NAME_TYPES, 4);
+
private MetadataObjects() {}
/**
@@ -49,6 +83,10 @@ public class MetadataObjects {
public static MetadataObject of(String parent, String name,
MetadataObject.Type type) {
Preconditions.checkArgument(name != null, "Cannot create a metadata object
with null name");
Preconditions.checkArgument(type != null, "Cannot create a metadata object
with no type");
+ if (VALID_SINGLE_LEVEL_NAME_TYPES.contains(type)) {
+ Preconditions.checkArgument(
+ parent == null, "If the type is " + type + ", parent must be null");
+ }
String fullName = parent == null ? name : DOT_JOINER.join(parent, name);
return parse(fullName, type);
@@ -64,35 +102,19 @@ public class MetadataObjects {
public static MetadataObject of(List<String> names, MetadataObject.Type
type) {
Preconditions.checkArgument(names != null, "Cannot create a metadata
object with null names");
Preconditions.checkArgument(!names.isEmpty(), "Cannot create a metadata
object with no names");
- Preconditions.checkArgument(
- names.size() <= 4,
- "Cannot create a metadata object with the name length which is greater
than 4");
Preconditions.checkArgument(type != null, "Cannot create a metadata object
with no type");
- Preconditions.checkArgument(
- names.size() != 1
- || type == MetadataObject.Type.CATALOG
- || type == MetadataObject.Type.METALAKE
- || type == MetadataObject.Type.ROLE
- || type == MetadataObject.Type.TAG
- || type == MetadataObject.Type.POLICY,
- "If the length of names is 1, it must be the CATALOG, METALAKE,TAG, or
ROLE type");
+ Integer expectedLength =
+ TYPE_TO_EXPECT_LENGTH.entrySet().stream()
+ .filter(entry -> entry.getKey().contains(type))
+ .map(Map.Entry::getValue)
+ .findFirst()
+ .orElseThrow(
+ () -> new IllegalArgumentException("Unsupported metadata
object type: " + type));
Preconditions.checkArgument(
- names.size() != 2 || type == MetadataObject.Type.SCHEMA,
- "If the length of names is 2, it must be the SCHEMA type");
-
- Preconditions.checkArgument(
- names.size() != 3
- || type == MetadataObject.Type.FILESET
- || type == MetadataObject.Type.TABLE
- || type == MetadataObject.Type.TOPIC
- || type == MetadataObject.Type.MODEL,
- "If the length of names is 3, it must be FILESET, TABLE, TOPIC or
MODEL");
-
- Preconditions.checkArgument(
- names.size() != 4 || type == MetadataObject.Type.COLUMN,
- "If the length of names is 4, it must be COLUMN");
+ names.size() == expectedLength,
+ "If the type is " + type + ", the length of names must be " +
expectedLength);
for (String name : names) {
checkName(name);
@@ -114,9 +136,7 @@ public class MetadataObjects {
}
// Return null if the object is the root object
- if (object.type() == MetadataObject.Type.METALAKE
- || object.type() == MetadataObject.Type.CATALOG
- || object.type() == MetadataObject.Type.ROLE) {
+ if (VALID_SINGLE_LEVEL_NAME_TYPES.contains(object.type())) {
return null;
}
@@ -155,11 +175,8 @@ public class MetadataObjects {
StringUtils.isNotBlank(fullName), "Metadata object full name cannot be
blank");
List<String> parts = DOT_SPLITTER.splitToList(fullName);
- if (type == MetadataObject.Type.ROLE) {
- return MetadataObjects.of(Collections.singletonList(fullName),
MetadataObject.Type.ROLE);
- }
- if (type == MetadataObject.Type.TAG) {
- return MetadataObjects.of(Collections.singletonList(fullName),
MetadataObject.Type.TAG);
+ if (VALID_SINGLE_LEVEL_NAME_TYPES.contains(type)) {
+ return of(Collections.singletonList(fullName), type);
}
return MetadataObjects.of(parts, type);
diff --git
a/api/src/main/java/org/apache/gravitino/authorization/Privilege.java
b/api/src/main/java/org/apache/gravitino/authorization/Privilege.java
index 23a7c4a2cc..332ff3be58 100644
--- a/api/src/main/java/org/apache/gravitino/authorization/Privilege.java
+++ b/api/src/main/java/org/apache/gravitino/authorization/Privilege.java
@@ -103,7 +103,12 @@ public interface Privilege {
CREATE_POLICY(0L, 1L << 23),
/** The privilege to apply a policy */
APPLY_POLICY(0L, 1L << 24),
- ;
+ /** The privilege to register a job template */
+ REGISTER_JOB_TEMPLATE(0L, 1L << 25),
+ /** The privilege to use a job template */
+ USE_JOB_TEMPLATE(0L, 1L << 26),
+ /** The privilege to run a job */
+ RUN_JOB(0L, 1L << 27);
private final long highBits;
private final long lowBits;
diff --git
a/api/src/main/java/org/apache/gravitino/authorization/Privileges.java
b/api/src/main/java/org/apache/gravitino/authorization/Privileges.java
index 11697521a0..b499069a7d 100644
--- a/api/src/main/java/org/apache/gravitino/authorization/Privileges.java
+++ b/api/src/main/java/org/apache/gravitino/authorization/Privileges.java
@@ -133,14 +133,29 @@ public class Privileges {
return CreateModelVersion.allow();
case USE_MODEL:
return UseModel.allow();
+
+ // Tag
case CREATE_TAG:
return CreateTag.allow();
case APPLY_TAG:
return ApplyTag.allow();
+
+ // Policy
case APPLY_POLICY:
return ApplyPolicy.allow();
case CREATE_POLICY:
return CreatePolicy.allow();
+
+ // Job template
+ case REGISTER_JOB_TEMPLATE:
+ return RegisterJobTemplate.allow();
+ case USE_JOB_TEMPLATE:
+ return UseJobTemplate.allow();
+
+ // Job
+ case RUN_JOB:
+ return RunJob.allow();
+
default:
throw new IllegalArgumentException("Doesn't support the privilege: " +
name);
}
@@ -222,10 +237,28 @@ public class Privileges {
return CreateModelVersion.deny();
case USE_MODEL:
return UseModel.deny();
+
+ // Tag
case CREATE_TAG:
return CreateTag.deny();
case APPLY_TAG:
return ApplyTag.deny();
+
+ // Policy
+ case APPLY_POLICY:
+ return ApplyPolicy.deny();
+ case CREATE_POLICY:
+ return CreatePolicy.deny();
+
+ // Job template
+ case REGISTER_JOB_TEMPLATE:
+ return RegisterJobTemplate.deny();
+ case USE_JOB_TEMPLATE:
+ return UseJobTemplate.deny();
+
+ // Job
+ case RUN_JOB:
+ return RunJob.deny();
default:
throw new IllegalArgumentException("Doesn't support the privilege: " +
name);
}
@@ -984,7 +1017,6 @@ public class Privileges {
protected CreatePolicy(Condition condition, Name name) {
super(condition, name);
}
-
/**
* @return The instance with allow condition of the privilege.
*/
@@ -1005,6 +1037,35 @@ public class Privileges {
}
}
+ /** The privilege to run a job. */
+ public static class RunJob extends GenericPrivilege<RunJob> {
+ private static final RunJob ALLOW_INSTANCE = new RunJob(Condition.ALLOW,
Name.RUN_JOB);
+ private static final RunJob DENY_INSTANCE = new RunJob(Condition.DENY,
Name.RUN_JOB);
+
+ private RunJob(Condition condition, Name name) {
+ super(condition, name);
+ }
+
+ /**
+ * @return The instance with allow condition of the privilege.
+ */
+ public static RunJob allow() {
+ return ALLOW_INSTANCE;
+ }
+
+ /**
+ * @return The instance with deny condition of the privilege.
+ */
+ public static RunJob deny() {
+ return DENY_INSTANCE;
+ }
+
+ @Override
+ public boolean canBindTo(MetadataObject.Type type) {
+ return type == MetadataObject.Type.METALAKE;
+ }
+ }
+
/** The privilege to apply policy to object. */
public static final class ApplyPolicy extends GenericPrivilege<ApplyPolicy> {
@@ -1042,4 +1103,66 @@ public class Privileges {
return type == MetadataObject.Type.METALAKE || type ==
MetadataObject.Type.POLICY;
}
}
+
+ /** The privilege to register a job template. */
+ public static class RegisterJobTemplate extends
GenericPrivilege<RegisterJobTemplate> {
+ private static final RegisterJobTemplate ALLOW_INSTANCE =
+ new RegisterJobTemplate(Condition.ALLOW, Name.REGISTER_JOB_TEMPLATE);
+ private static final RegisterJobTemplate DENY_INSTANCE =
+ new RegisterJobTemplate(Condition.DENY, Name.REGISTER_JOB_TEMPLATE);
+
+ private RegisterJobTemplate(Condition condition, Name name) {
+ super(condition, name);
+ }
+
+ /**
+ * @return The instance with allow condition of the privilege.
+ */
+ public static RegisterJobTemplate allow() {
+ return ALLOW_INSTANCE;
+ }
+
+ /**
+ * @return The instance with deny condition of the privilege.
+ */
+ public static RegisterJobTemplate deny() {
+ return DENY_INSTANCE;
+ }
+
+ @Override
+ public boolean canBindTo(MetadataObject.Type type) {
+ return type == MetadataObject.Type.METALAKE;
+ }
+ }
+
+ /** The privilege to use a job template. */
+ public static class UseJobTemplate extends GenericPrivilege<UseJobTemplate> {
+ private static final UseJobTemplate ALLOW_INSTANCE =
+ new UseJobTemplate(Condition.ALLOW, Name.USE_JOB_TEMPLATE);
+ private static final UseJobTemplate DENY_INSTANCE =
+ new UseJobTemplate(Condition.DENY, Name.USE_JOB_TEMPLATE);
+
+ private UseJobTemplate(Condition condition, Name name) {
+ super(condition, name);
+ }
+
+ /**
+ * @return The instance with allow condition of the privilege.
+ */
+ public static UseJobTemplate allow() {
+ return ALLOW_INSTANCE;
+ }
+
+ /**
+ * @return The instance with deny condition of the privilege.
+ */
+ public static UseJobTemplate deny() {
+ return DENY_INSTANCE;
+ }
+
+ @Override
+ public boolean canBindTo(MetadataObject.Type type) {
+ return type == MetadataObject.Type.METALAKE || type ==
MetadataObject.Type.JOB_TEMPLATE;
+ }
+ }
}
diff --git
a/api/src/main/java/org/apache/gravitino/authorization/SecurableObjects.java
b/api/src/main/java/org/apache/gravitino/authorization/SecurableObjects.java
index e63e3d0982..e6f444cbb9 100644
--- a/api/src/main/java/org/apache/gravitino/authorization/SecurableObjects.java
+++ b/api/src/main/java/org/apache/gravitino/authorization/SecurableObjects.java
@@ -138,6 +138,40 @@ public class SecurableObjects {
return of(MetadataObject.Type.MODEL, names, privileges);
}
+ /**
+ * Create the tag {@link SecurableObject} with the given tag name and
privileges.
+ *
+ * @param tag The tag name
+ * @param privileges The privileges of the tag
+ * @return The created tag {@link SecurableObject}
+ */
+ public static SecurableObject ofTag(String tag, List<Privilege> privileges) {
+ return of(MetadataObject.Type.TAG, Lists.newArrayList(tag), privileges);
+ }
+
+ /**
+ * Create the policy {@link SecurableObject} with the given policy name and
privileges.
+ *
+ * @param policy The policy name
+ * @param privileges The privileges of the policy
+ * @return The created policy {@link SecurableObject}
+ */
+ public static SecurableObject ofPolicy(String policy, List<Privilege>
privileges) {
+ return of(MetadataObject.Type.POLICY, Lists.newArrayList(policy),
privileges);
+ }
+
+ /**
+ * Create the job template {@link SecurableObject} with the given job
template name and
+ * privileges.
+ *
+ * @param jobTemplate The job template name
+ * @param privileges The privileges of the job template
+ * @return The created job template {@link SecurableObject}
+ */
+ public static SecurableObject ofJobTemplate(String jobTemplate,
List<Privilege> privileges) {
+ return of(MetadataObject.Type.JOB_TEMPLATE,
Lists.newArrayList(jobTemplate), privileges);
+ }
+
private static class SecurableObjectImpl extends MetadataObjectImpl
implements SecurableObject {
private List<Privilege> privileges;
diff --git a/api/src/test/java/org/apache/gravitino/TestMetadataObjects.java
b/api/src/test/java/org/apache/gravitino/TestMetadataObjects.java
index f792220e18..391a150eba 100644
--- a/api/src/test/java/org/apache/gravitino/TestMetadataObjects.java
+++ b/api/src/test/java/org/apache/gravitino/TestMetadataObjects.java
@@ -99,4 +99,32 @@ public class TestMetadataObjects {
MetadataObject roleObject3 = MetadataObjects.parse("role",
MetadataObject.Type.ROLE);
Assertions.assertEquals("role", roleObject3.fullName());
}
+
+ @Test
+ public void testJobObject() {
+ MetadataObject jobObject = MetadataObjects.of(null, "job_12345",
MetadataObject.Type.JOB);
+ Assertions.assertEquals("job_12345", jobObject.fullName());
+
+ MetadataObject jobObject2 = MetadataObjects.parse("job_12345",
MetadataObject.Type.JOB);
+ Assertions.assertEquals("job_12345", jobObject2.fullName());
+
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> MetadataObjects.of("parent", "job_12345",
MetadataObject.Type.JOB));
+ }
+
+ @Test
+ public void testJobTemplateObject() {
+ MetadataObject jobTemplateObject =
+ MetadataObjects.of(null, "template_abc",
MetadataObject.Type.JOB_TEMPLATE);
+ Assertions.assertEquals("template_abc", jobTemplateObject.fullName());
+
+ MetadataObject jobTemplateObject2 =
+ MetadataObjects.parse("template_abc",
MetadataObject.Type.JOB_TEMPLATE);
+ Assertions.assertEquals("template_abc", jobTemplateObject2.fullName());
+
+ Assertions.assertThrows(
+ IllegalArgumentException.class,
+ () -> MetadataObjects.of("parent", "template_abc",
MetadataObject.Type.JOB_TEMPLATE));
+ }
}
diff --git
a/api/src/test/java/org/apache/gravitino/authorization/TestSecurableObjects.java
b/api/src/test/java/org/apache/gravitino/authorization/TestSecurableObjects.java
index 813ac9f4b2..b05b52a326 100644
---
a/api/src/test/java/org/apache/gravitino/authorization/TestSecurableObjects.java
+++
b/api/src/test/java/org/apache/gravitino/authorization/TestSecurableObjects.java
@@ -108,7 +108,7 @@ public class TestSecurableObjects {
MetadataObject.Type.METALAKE,
Lists.newArrayList("metalake", "catalog"),
Lists.newArrayList(Privileges.UseCatalog.allow())));
- Assertions.assertTrue(e.getMessage().contains("length of names is 2"));
+ Assertions.assertTrue(e.getMessage().contains("the length of names must be
1"));
e =
Assertions.assertThrows(
IllegalArgumentException.class,
@@ -117,7 +117,7 @@ public class TestSecurableObjects {
MetadataObject.Type.CATALOG,
Lists.newArrayList("metalake", "catalog"),
Lists.newArrayList(Privileges.UseCatalog.allow())));
- Assertions.assertTrue(e.getMessage().contains("length of names is 2"));
+ Assertions.assertTrue(e.getMessage().contains("the length of names must be
1"));
e =
Assertions.assertThrows(
@@ -127,7 +127,7 @@ public class TestSecurableObjects {
MetadataObject.Type.TABLE,
Lists.newArrayList("metalake"),
Lists.newArrayList(Privileges.SelectTable.allow())));
- Assertions.assertTrue(e.getMessage().contains("the length of names is 1"));
+ Assertions.assertTrue(e.getMessage().contains("the length of names must be
3"));
e =
Assertions.assertThrows(
IllegalArgumentException.class,
@@ -136,7 +136,7 @@ public class TestSecurableObjects {
MetadataObject.Type.TOPIC,
Lists.newArrayList("metalake"),
Lists.newArrayList(Privileges.ConsumeTopic.allow())));
- Assertions.assertTrue(e.getMessage().contains("the length of names is 1"));
+ Assertions.assertTrue(e.getMessage().contains("the length of names must be
3"));
e =
Assertions.assertThrows(
IllegalArgumentException.class,
@@ -145,7 +145,7 @@ public class TestSecurableObjects {
MetadataObject.Type.FILESET,
Lists.newArrayList("metalake"),
Lists.newArrayList(Privileges.ReadFileset.allow())));
- Assertions.assertTrue(e.getMessage().contains("the length of names is 1"));
+ Assertions.assertTrue(e.getMessage().contains("the length of names must be
3"));
e =
Assertions.assertThrows(
@@ -155,7 +155,7 @@ public class TestSecurableObjects {
MetadataObject.Type.SCHEMA,
Lists.newArrayList("catalog", "schema", "table"),
Lists.newArrayList(Privileges.UseSchema.allow())));
- Assertions.assertTrue(e.getMessage().contains("the length of names is 3"));
+ Assertions.assertTrue(e.getMessage().contains("the length of names must be
2"));
}
@Test
@@ -184,6 +184,9 @@ public class TestSecurableObjects {
Privilege applyTag = Privileges.ApplyTag.allow();
Privilege createPolicy = Privileges.CreatePolicy.allow();
Privilege applyPolicy = Privileges.ApplyPolicy.allow();
+ Privilege registerJobTemplate = Privileges.RegisterJobTemplate.allow();
+ Privilege runJob = Privileges.RunJob.allow();
+ Privilege useJobTemplate = Privileges.UseJobTemplate.allow();
// Test create catalog
Assertions.assertTrue(createCatalog.canBindTo(MetadataObject.Type.METALAKE));
@@ -396,6 +399,7 @@ public class TestSecurableObjects {
Assertions.assertFalse(applyTag.canBindTo(MetadataObject.Type.SCHEMA));
Assertions.assertFalse(applyTag.canBindTo(MetadataObject.Type.TABLE));
Assertions.assertFalse(applyTag.canBindTo(MetadataObject.Type.MODEL));
+
Assertions.assertFalse(useModel.canBindTo(MetadataObject.Type.TOPIC));
Assertions.assertFalse(useModel.canBindTo(MetadataObject.Type.FILESET));
Assertions.assertFalse(useModel.canBindTo(MetadataObject.Type.ROLE));
@@ -419,5 +423,38 @@ public class TestSecurableObjects {
Assertions.assertFalse(useModel.canBindTo(MetadataObject.Type.TOPIC));
Assertions.assertFalse(useModel.canBindTo(MetadataObject.Type.FILESET));
Assertions.assertFalse(useModel.canBindTo(MetadataObject.Type.ROLE));
+
+
Assertions.assertTrue(registerJobTemplate.canBindTo(MetadataObject.Type.METALAKE));
+
Assertions.assertFalse(registerJobTemplate.canBindTo(MetadataObject.Type.CATALOG));
+
Assertions.assertFalse(registerJobTemplate.canBindTo(MetadataObject.Type.SCHEMA));
+
Assertions.assertFalse(registerJobTemplate.canBindTo(MetadataObject.Type.TABLE));
+
Assertions.assertFalse(registerJobTemplate.canBindTo(MetadataObject.Type.TOPIC));
+
Assertions.assertFalse(registerJobTemplate.canBindTo(MetadataObject.Type.FILESET));
+
Assertions.assertFalse(registerJobTemplate.canBindTo(MetadataObject.Type.ROLE));
+
Assertions.assertFalse(registerJobTemplate.canBindTo(MetadataObject.Type.COLUMN));
+
Assertions.assertFalse(registerJobTemplate.canBindTo(MetadataObject.Type.JOB_TEMPLATE));
+
Assertions.assertFalse(registerJobTemplate.canBindTo(MetadataObject.Type.JOB));
+
+ Assertions.assertTrue(runJob.canBindTo(MetadataObject.Type.METALAKE));
+ Assertions.assertFalse(runJob.canBindTo(MetadataObject.Type.CATALOG));
+ Assertions.assertFalse(runJob.canBindTo(MetadataObject.Type.SCHEMA));
+ Assertions.assertFalse(runJob.canBindTo(MetadataObject.Type.TABLE));
+ Assertions.assertFalse(runJob.canBindTo(MetadataObject.Type.TOPIC));
+ Assertions.assertFalse(runJob.canBindTo(MetadataObject.Type.FILESET));
+ Assertions.assertFalse(runJob.canBindTo(MetadataObject.Type.ROLE));
+ Assertions.assertFalse(runJob.canBindTo(MetadataObject.Type.COLUMN));
+ Assertions.assertFalse(runJob.canBindTo(MetadataObject.Type.JOB));
+ Assertions.assertFalse(runJob.canBindTo(MetadataObject.Type.JOB_TEMPLATE));
+
+
Assertions.assertTrue(useJobTemplate.canBindTo(MetadataObject.Type.METALAKE));
+
Assertions.assertFalse(useJobTemplate.canBindTo(MetadataObject.Type.CATALOG));
+
Assertions.assertFalse(useJobTemplate.canBindTo(MetadataObject.Type.SCHEMA));
+
Assertions.assertFalse(useJobTemplate.canBindTo(MetadataObject.Type.TABLE));
+
Assertions.assertFalse(useJobTemplate.canBindTo(MetadataObject.Type.TOPIC));
+
Assertions.assertFalse(useJobTemplate.canBindTo(MetadataObject.Type.FILESET));
+ Assertions.assertFalse(useJobTemplate.canBindTo(MetadataObject.Type.ROLE));
+
Assertions.assertFalse(useJobTemplate.canBindTo(MetadataObject.Type.COLUMN));
+
Assertions.assertTrue(useJobTemplate.canBindTo(MetadataObject.Type.JOB_TEMPLATE));
+ Assertions.assertFalse(useJobTemplate.canBindTo(MetadataObject.Type.JOB));
}
}
diff --git a/core/src/main/java/org/apache/gravitino/tag/TagManager.java
b/core/src/main/java/org/apache/gravitino/tag/TagManager.java
index 867e49d1c1..38c3f5bab7 100644
--- a/core/src/main/java/org/apache/gravitino/tag/TagManager.java
+++ b/core/src/main/java/org/apache/gravitino/tag/TagManager.java
@@ -64,6 +64,16 @@ public class TagManager implements TagDispatcher {
private final EntityStore entityStore;
+ private static final Set<MetadataObject.Type>
SUPPORTED_METADATA_OBJECT_TYPES_FOR_TAGS =
+ Sets.newHashSet(
+ MetadataObject.Type.CATALOG,
+ MetadataObject.Type.SCHEMA,
+ MetadataObject.Type.TABLE,
+ MetadataObject.Type.FILESET,
+ MetadataObject.Type.TOPIC,
+ MetadataObject.Type.COLUMN,
+ MetadataObject.Type.MODEL);
+
public TagManager(IdGenerator idGenerator, EntityStore entityStore) {
this.idGenerator = idGenerator;
this.entityStore = entityStore;
@@ -298,8 +308,7 @@ public class TagManager implements TagDispatcher {
String metalake, MetadataObject metadataObject, String[] tagsToAdd,
String[] tagsToRemove)
throws NoSuchMetadataObjectException, TagAlreadyAssociatedException {
Preconditions.checkArgument(
- !metadataObject.type().equals(MetadataObject.Type.METALAKE)
- && !metadataObject.type().equals(MetadataObject.Type.ROLE),
+
SUPPORTED_METADATA_OBJECT_TYPES_FOR_TAGS.contains(metadataObject.type()),
"Cannot associate tags for unsupported metadata object type %s",
metadataObject.type());
diff --git a/docs/security/access-control.md b/docs/security/access-control.md
index 23d1a0c8f1..9c4a5c0328 100644
--- a/docs/security/access-control.md
+++ b/docs/security/access-control.md
@@ -115,7 +115,7 @@ If a securable object needs to be managed by more than one
person at the same ti
The metadata object that supports ownership is as follows:
| Metadata Object Type |
-| -------------------- |
+|----------------------|
| Metalake |
| Catalog |
| Schema |
@@ -125,6 +125,8 @@ The metadata object that supports ownership is as follows:
| Role |
| Model |
| Tag |
+| JobTemplate |
+| Job |
### User
Users are generally granted one or multiple Roles, and users have different
operating privileges depending on their Role.
@@ -279,6 +281,19 @@ DENY `WRITE_FILESET` won‘t deny the `READ_FILESET`
operation if the user has t
| CREATE_POLICY | Metalake | Create a policy
|
| APPLY_POLICY | Metalake, Policy | Associate policies with metadata
objects. |
+### Job template privileges
+
+| Name | Supports Securable Object | Operation
|
+|-----------------------|---------------------------|-----------------------------------------|
+| REGISTER_JOB_TEMPLATE | Metalake | Register a job template
|
+| USE_JOB_TEMPLATE | Metalake, JobTemplate | Use a job template when
running the job |
+
+### Job privileges
+| Name | Supports Securable Object | Operation |
+|---------|---------------------------|-----------|
+| RUN_JOB | Metalake | Run a job |
+
+
## Inheritance Model
Securable objects in Gravitino are hierarchical and privileges are inherited
downward.
@@ -1059,4 +1074,12 @@ The following table lists the required privileges for
each API.
| associate-object-policies | Requires both `APPLY_POLICY` permission
and permission to **load metadata objects**.
|
| list policies for object | Requires both permission to **get the
policy** and permission to **load metadata objects**.
|
| get policy for object | Requires both permission to **get the
policy** and permission to **load metadata objects**.
|
-
+| list job templates | The owner of the metalake can see all
the job templates, others can see the job templates which they can get.
|
+| register a job template | `REGISTER_JOB_TEMPLATE` on the metalake
or the owner of the metalake.
|
+| get a job template | `USE_JOB_TEMPLATE` on the metalake or
job template, the owner of the metalake or the job template.
|
+| delete a job template | The owner of the metalake or the job
template.
|
+| alter a job template | The owner of the metalake or the job
template.
|
+| list jobs | The owner of the metalake can see all
the jobs, others can see the jobs which they can get.
|
+| run a job | The owner of the metalake , or have both
`RUN_JOB` on the metalake and `USE_JOB_TEMPLATE` on the job template
|
+| get a job | The owner of the metalake or the job.
|
+| cancel a job | The owner of the metalake or the job.
|