This is an automated email from the ASF dual-hosted git repository.
jshao pushed a commit to branch branch-0.6
in repository https://gitbox.apache.org/repos/asf/gravitino.git
The following commit(s) were added to refs/heads/branch-0.6 by this push:
new 174f74d53 [#4886] feat(server,core): Supports to list roles by object
(#5032)
174f74d53 is described below
commit 174f74d537e787bcd03d720d4c8c05160f0aeafe
Author: github-actions[bot]
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Fri Sep 27 17:19:24 2024 +0800
[#4886] feat(server,core): Supports to list roles by object (#5032)
### What changes were proposed in this pull request?
Supports to list roles by object
### Why are the changes needed?
Fix: #4886
### Does this PR introduce _any_ user-facing change?
I will add the document later.
### How was this patch tested?
Add new UT.
Co-authored-by: roryqi <[email protected]>
---
.../main/java/org/apache/gravitino/Catalog.java | 9 +
.../main/java/org/apache/gravitino/Metalake.java | 9 +
api/src/main/java/org/apache/gravitino/Schema.java | 9 +
.../SupportsRoles.java} | 31 +--
.../java/org/apache/gravitino/file/Fileset.java | 9 +
.../java/org/apache/gravitino/messaging/Topic.java | 9 +
.../main/java/org/apache/gravitino/rel/Table.java | 9 +
.../apache/gravitino/client/BaseSchemaCatalog.java | 16 +-
.../apache/gravitino/client/GenericFileset.java | 16 +-
.../org/apache/gravitino/client/GenericSchema.java | 16 +-
.../org/apache/gravitino/client/GenericTopic.java | 16 +-
.../apache/gravitino/client/GravitinoMetalake.java | 19 +-
.../client/MetadataObjectRoleOperations.java | 56 +++++
.../apache/gravitino/client/RelationalTable.java | 16 +-
.../apache/gravitino/client/TestSupportRoles.java | 256 +++++++++++++++++++++
.../gravitino/SupportsRelationOperations.java | 23 +-
.../authorization/AccessControlDispatcher.java | 14 ++
.../authorization/AccessControlManager.java | 10 +-
.../authorization/FutureGrantManager.java | 2 +-
.../gravitino/authorization/RoleManager.java | 33 +++
.../hook/AccessControlHookDispatcher.java | 8 +
.../gravitino/storage/relational/JDBCBackend.java | 6 +-
.../storage/relational/RelationalEntityStore.java | 6 +-
.../relational/service/RoleMetaService.java | 96 ++++----
.../authorization/TestAccessControlManager.java | 29 +++
.../relational/service/TestRoleMetaService.java | 2 +-
.../test/authorization/AccessControlIT.java | 53 +++++
.../web/rest/MetadataObjectRoleOperations.java | 89 +++++++
.../web/rest/TestMetadataObjectRoleOperations.java | 146 ++++++++++++
.../server/web/rest/TestRoleOperations.java | 34 +--
30 files changed, 939 insertions(+), 108 deletions(-)
diff --git a/api/src/main/java/org/apache/gravitino/Catalog.java
b/api/src/main/java/org/apache/gravitino/Catalog.java
index 052a04d94..431a798d5 100644
--- a/api/src/main/java/org/apache/gravitino/Catalog.java
+++ b/api/src/main/java/org/apache/gravitino/Catalog.java
@@ -21,6 +21,7 @@ package org.apache.gravitino;
import java.util.Locale;
import java.util.Map;
import org.apache.gravitino.annotation.Evolving;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.file.FilesetCatalog;
import org.apache.gravitino.messaging.TopicCatalog;
import org.apache.gravitino.rel.TableCatalog;
@@ -181,4 +182,12 @@ public interface Catalog extends Auditable {
default SupportsTags supportsTags() throws UnsupportedOperationException {
throw new UnsupportedOperationException("Catalog does not support tag
operations");
}
+
+ /**
+ * @return the {@link SupportsRoles} if the catalog supports role operations.
+ * @throws UnsupportedOperationException if the catalog does not support
role operations.
+ */
+ default SupportsRoles supportsRoles() throws UnsupportedOperationException {
+ throw new UnsupportedOperationException("Catalog does not support role
operations");
+ }
}
diff --git a/api/src/main/java/org/apache/gravitino/Metalake.java
b/api/src/main/java/org/apache/gravitino/Metalake.java
index 6b4ac76ba..fb6fdbee0 100644
--- a/api/src/main/java/org/apache/gravitino/Metalake.java
+++ b/api/src/main/java/org/apache/gravitino/Metalake.java
@@ -20,6 +20,7 @@ package org.apache.gravitino;
import java.util.Map;
import org.apache.gravitino.annotation.Evolving;
+import org.apache.gravitino.authorization.SupportsRoles;
/**
* The interface of a metalake. The metalake is the top level entity in the
Apache Gravitino system,
@@ -50,4 +51,12 @@ public interface Metalake extends Auditable {
* @return The properties of the metalake.
*/
Map<String, String> properties();
+
+ /**
+ * @return the {@link SupportsRoles} if the metalake supports role
operations.
+ * @throws UnsupportedOperationException if the metalake does not support
role operations.
+ */
+ default SupportsRoles supportsRoles() {
+ throw new UnsupportedOperationException("Metalake does not support role
operations.");
+ }
}
diff --git a/api/src/main/java/org/apache/gravitino/Schema.java
b/api/src/main/java/org/apache/gravitino/Schema.java
index 872b0a25e..7cedf94f6 100644
--- a/api/src/main/java/org/apache/gravitino/Schema.java
+++ b/api/src/main/java/org/apache/gravitino/Schema.java
@@ -22,6 +22,7 @@ import java.util.Collections;
import java.util.Map;
import javax.annotation.Nullable;
import org.apache.gravitino.annotation.Evolving;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.tag.SupportsTags;
/**
@@ -56,4 +57,12 @@ public interface Schema extends Auditable {
default SupportsTags supportsTags() {
throw new UnsupportedOperationException("Schema does not support tag
operations.");
}
+
+ /**
+ * @return the {@link SupportsRoles} if the schema supports role operations.
+ * @throws UnsupportedOperationException if the schema does not support role
operations.
+ */
+ default SupportsRoles supportsRoles() {
+ throw new UnsupportedOperationException("Schema does not support role
operations.");
+ }
}
diff --git a/api/src/main/java/org/apache/gravitino/Metalake.java
b/api/src/main/java/org/apache/gravitino/authorization/SupportsRoles.java
similarity index 55%
copy from api/src/main/java/org/apache/gravitino/Metalake.java
copy to api/src/main/java/org/apache/gravitino/authorization/SupportsRoles.java
index 6b4ac76ba..e83a7e20e 100644
--- a/api/src/main/java/org/apache/gravitino/Metalake.java
+++ b/api/src/main/java/org/apache/gravitino/authorization/SupportsRoles.java
@@ -16,38 +16,21 @@
* specific language governing permissions and limitations
* under the License.
*/
-package org.apache.gravitino;
+package org.apache.gravitino.authorization;
-import java.util.Map;
import org.apache.gravitino.annotation.Evolving;
/**
- * The interface of a metalake. The metalake is the top level entity in the
Apache Gravitino system,
- * containing a set of catalogs.
+ * Interface for supporting list role names for objects. This interface will
be mixed with metadata
+ * objects to provide listing role operations.
*/
@Evolving
-public interface Metalake extends Auditable {
+public interface SupportsRoles {
/**
- * The name of the metalake.
+ * List all the role names associated with this metadata object.
*
- * @return The name of the metalake.
+ * @return The role name list associated with this metadata object.
*/
- String name();
-
- /**
- * The comment of the metalake. Note. this method will return null if the
comment is not set for
- * this metalake.
- *
- * @return The comment of the metalake.
- */
- String comment();
-
- /**
- * The properties of the metalake. Note, this method will return null if the
properties are not
- * set.
- *
- * @return The properties of the metalake.
- */
- Map<String, String> properties();
+ String[] listBindingRoleNames();
}
diff --git a/api/src/main/java/org/apache/gravitino/file/Fileset.java
b/api/src/main/java/org/apache/gravitino/file/Fileset.java
index ccff039da..97afcc650 100644
--- a/api/src/main/java/org/apache/gravitino/file/Fileset.java
+++ b/api/src/main/java/org/apache/gravitino/file/Fileset.java
@@ -24,6 +24,7 @@ import javax.annotation.Nullable;
import org.apache.gravitino.Auditable;
import org.apache.gravitino.Namespace;
import org.apache.gravitino.annotation.Evolving;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.tag.SupportsTags;
/**
@@ -114,4 +115,12 @@ public interface Fileset extends Auditable {
default SupportsTags supportsTags() {
throw new UnsupportedOperationException("Fileset does not support tag
operations.");
}
+
+ /**
+ * @return The {@link SupportsRoles} if the fileset supports role operations.
+ * @throws UnsupportedOperationException If the fileset does not support
role operations.
+ */
+ default SupportsRoles supportsRoles() {
+ throw new UnsupportedOperationException("Fileset does not support role
operations.");
+ }
}
diff --git a/api/src/main/java/org/apache/gravitino/messaging/Topic.java
b/api/src/main/java/org/apache/gravitino/messaging/Topic.java
index 78607f486..7162c45d2 100644
--- a/api/src/main/java/org/apache/gravitino/messaging/Topic.java
+++ b/api/src/main/java/org/apache/gravitino/messaging/Topic.java
@@ -24,6 +24,7 @@ import javax.annotation.Nullable;
import org.apache.gravitino.Auditable;
import org.apache.gravitino.Namespace;
import org.apache.gravitino.annotation.Evolving;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.tag.SupportsTags;
/**
@@ -58,4 +59,12 @@ public interface Topic extends Auditable {
default SupportsTags supportsTags() {
throw new UnsupportedOperationException("Topic does not support tag
operations.");
}
+
+ /**
+ * @return the {@link SupportsRoles} if the topic supports role operations.
+ * @throws UnsupportedOperationException if the topic does not support role
operations.
+ */
+ default SupportsRoles supportsRoles() {
+ throw new UnsupportedOperationException("Topic does not support role
operations.");
+ }
}
diff --git a/api/src/main/java/org/apache/gravitino/rel/Table.java
b/api/src/main/java/org/apache/gravitino/rel/Table.java
index c6bafb97a..8bb9e3c12 100644
--- a/api/src/main/java/org/apache/gravitino/rel/Table.java
+++ b/api/src/main/java/org/apache/gravitino/rel/Table.java
@@ -24,6 +24,7 @@ import javax.annotation.Nullable;
import org.apache.gravitino.Auditable;
import org.apache.gravitino.Namespace;
import org.apache.gravitino.annotation.Evolving;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.rel.expressions.distributions.Distribution;
import org.apache.gravitino.rel.expressions.distributions.Distributions;
import org.apache.gravitino.rel.expressions.sorts.SortOrder;
@@ -103,4 +104,12 @@ public interface Table extends Auditable {
default SupportsTags supportsTags() {
throw new UnsupportedOperationException("Table does not support tag
operations.");
}
+
+ /**
+ * @return The {@link SupportsRoles} if the table supports role operations.
+ * @throws UnsupportedOperationException If the table does not support role
operations.
+ */
+ default SupportsRoles supportsRoles() {
+ throw new UnsupportedOperationException("Table does not support role
operations.");
+ }
}
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/BaseSchemaCatalog.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/BaseSchemaCatalog.java
index 7d46af3a5..9359ea439 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/BaseSchemaCatalog.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/BaseSchemaCatalog.java
@@ -31,6 +31,7 @@ import org.apache.gravitino.Namespace;
import org.apache.gravitino.Schema;
import org.apache.gravitino.SchemaChange;
import org.apache.gravitino.SupportsSchemas;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.dto.AuditDTO;
import org.apache.gravitino.dto.CatalogDTO;
import org.apache.gravitino.dto.requests.SchemaCreateRequest;
@@ -53,7 +54,7 @@ import org.apache.gravitino.tag.Tag;
* create, load, alter and drop a schema with specified identifier.
*/
abstract class BaseSchemaCatalog extends CatalogDTO
- implements Catalog, SupportsSchemas, SupportsTags {
+ implements Catalog, SupportsSchemas, SupportsTags, SupportsRoles {
/** The REST client to send the requests. */
protected final RESTClient restClient;
@@ -61,6 +62,7 @@ abstract class BaseSchemaCatalog extends CatalogDTO
private final Namespace catalogNamespace;
private final MetadataObjectTagOperations objectTagOperations;
+ private final MetadataObjectRoleOperations objectRoleOperations;
BaseSchemaCatalog(
Namespace catalogNamespace,
@@ -84,6 +86,8 @@ abstract class BaseSchemaCatalog extends CatalogDTO
MetadataObjects.of(null, this.name(), MetadataObject.Type.CATALOG);
this.objectTagOperations =
new MetadataObjectTagOperations(catalogNamespace.level(0),
metadataObject, restClient);
+ this.objectRoleOperations =
+ new MetadataObjectRoleOperations(catalogNamespace.level(0),
metadataObject, restClient);
}
@Override
@@ -96,6 +100,11 @@ abstract class BaseSchemaCatalog extends CatalogDTO
return this;
}
+ @Override
+ public SupportsRoles supportsRoles() throws UnsupportedOperationException {
+ return this;
+ }
+
/**
* List all the schemas under the given catalog namespace.
*
@@ -239,6 +248,11 @@ abstract class BaseSchemaCatalog extends CatalogDTO
return objectTagOperations.associateTags(tagsToAdd, tagsToRemove);
}
+ @Override
+ public String[] listBindingRoleNames() {
+ return objectRoleOperations.listBindingRoleNames();
+ }
+
/**
* Get the namespace of the current catalog, which is "metalake".
*
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericFileset.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericFileset.java
index 32e1d7392..68eda6985 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericFileset.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericFileset.java
@@ -26,6 +26,7 @@ import org.apache.gravitino.Audit;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.MetadataObjects;
import org.apache.gravitino.Namespace;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.dto.file.FilesetDTO;
import org.apache.gravitino.exceptions.NoSuchTagException;
import org.apache.gravitino.file.Fileset;
@@ -33,11 +34,12 @@ import org.apache.gravitino.tag.SupportsTags;
import org.apache.gravitino.tag.Tag;
/** Represents a generic fileset. */
-class GenericFileset implements Fileset, SupportsTags {
+class GenericFileset implements Fileset, SupportsTags, SupportsRoles {
private final FilesetDTO filesetDTO;
private final MetadataObjectTagOperations objectTagOperations;
+ private final MetadataObjectRoleOperations objectRoleOperations;
GenericFileset(FilesetDTO filesetDTO, RESTClient restClient, Namespace
filesetNs) {
this.filesetDTO = filesetDTO;
@@ -46,6 +48,8 @@ class GenericFileset implements Fileset, SupportsTags {
MetadataObject filesetObject = MetadataObjects.of(filesetFullName,
MetadataObject.Type.FILESET);
this.objectTagOperations =
new MetadataObjectTagOperations(filesetNs.level(0), filesetObject,
restClient);
+ this.objectRoleOperations =
+ new MetadataObjectRoleOperations(filesetNs.level(0), filesetObject,
restClient);
}
@Override
@@ -84,6 +88,11 @@ class GenericFileset implements Fileset, SupportsTags {
return this;
}
+ @Override
+ public SupportsRoles supportsRoles() {
+ return this;
+ }
+
@Override
public String[] listTags() {
return objectTagOperations.listTags();
@@ -104,6 +113,11 @@ class GenericFileset implements Fileset, SupportsTags {
return objectTagOperations.associateTags(tagsToAdd, tagsToRemove);
}
+ @Override
+ public String[] listBindingRoleNames() {
+ return objectRoleOperations.listBindingRoleNames();
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericSchema.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericSchema.java
index e595a53ab..22af2e3a2 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericSchema.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericSchema.java
@@ -23,23 +23,27 @@ import org.apache.gravitino.Audit;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.MetadataObjects;
import org.apache.gravitino.Schema;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.dto.SchemaDTO;
import org.apache.gravitino.exceptions.NoSuchTagException;
import org.apache.gravitino.tag.SupportsTags;
import org.apache.gravitino.tag.Tag;
/** Represents a generic schema. */
-class GenericSchema implements Schema, SupportsTags {
+class GenericSchema implements Schema, SupportsTags, SupportsRoles {
private final SchemaDTO schemaDTO;
private final MetadataObjectTagOperations objectTagOperations;
+ private final MetadataObjectRoleOperations objectRoleOperations;
GenericSchema(SchemaDTO schemaDTO, RESTClient restClient, String metalake,
String catalog) {
this.schemaDTO = schemaDTO;
MetadataObject schemaObject =
MetadataObjects.of(catalog, schemaDTO.name(),
MetadataObject.Type.SCHEMA);
this.objectTagOperations = new MetadataObjectTagOperations(metalake,
schemaObject, restClient);
+ this.objectRoleOperations =
+ new MetadataObjectRoleOperations(metalake, schemaObject, restClient);
}
@Override
@@ -47,6 +51,11 @@ class GenericSchema implements Schema, SupportsTags {
return this;
}
+ @Override
+ public SupportsRoles supportsRoles() {
+ return this;
+ }
+
@Override
public String name() {
return schemaDTO.name();
@@ -87,6 +96,11 @@ class GenericSchema implements Schema, SupportsTags {
return objectTagOperations.associateTags(tagsToAdd, tagsToRemove);
}
+ @Override
+ public String[] listBindingRoleNames() {
+ return objectRoleOperations.listBindingRoleNames();
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericTopic.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericTopic.java
index e317df069..9debbc8c2 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/GenericTopic.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/GenericTopic.java
@@ -25,6 +25,7 @@ import org.apache.gravitino.Audit;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.MetadataObjects;
import org.apache.gravitino.Namespace;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.dto.messaging.TopicDTO;
import org.apache.gravitino.exceptions.NoSuchTagException;
import org.apache.gravitino.messaging.Topic;
@@ -32,11 +33,12 @@ import org.apache.gravitino.tag.SupportsTags;
import org.apache.gravitino.tag.Tag;
/** Represents a generic topic. */
-class GenericTopic implements Topic, SupportsTags {
+class GenericTopic implements Topic, SupportsTags, SupportsRoles {
private final TopicDTO topicDTO;
private final MetadataObjectTagOperations objectTagOperations;
+ private final MetadataObjectRoleOperations objectRoleOperations;
GenericTopic(TopicDTO topicDTO, RESTClient restClient, Namespace topicNs) {
this.topicDTO = topicDTO;
@@ -45,6 +47,8 @@ class GenericTopic implements Topic, SupportsTags {
MetadataObject topicObject = MetadataObjects.of(topicFullName,
MetadataObject.Type.TOPIC);
this.objectTagOperations =
new MetadataObjectTagOperations(topicNs.level(0), topicObject,
restClient);
+ this.objectRoleOperations =
+ new MetadataObjectRoleOperations(topicNs.level(0), topicObject,
restClient);
}
@Override
@@ -72,6 +76,11 @@ class GenericTopic implements Topic, SupportsTags {
return this;
}
+ @Override
+ public SupportsRoles supportsRoles() {
+ return this;
+ }
+
@Override
public String[] listTags() {
return objectTagOperations.listTags();
@@ -92,6 +101,11 @@ class GenericTopic implements Topic, SupportsTags {
return objectTagOperations.associateTags(tagsToAdd, tagsToRemove);
}
+ @Override
+ public String[] listBindingRoleNames() {
+ return objectRoleOperations.listBindingRoleNames();
+ }
+
@Override
public boolean equals(Object obj) {
if (this == obj) {
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoMetalake.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoMetalake.java
index 58973b4cf..8f98b6fd3 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoMetalake.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/GravitinoMetalake.java
@@ -32,12 +32,14 @@ import org.apache.commons.lang3.StringUtils;
import org.apache.gravitino.Catalog;
import org.apache.gravitino.CatalogChange;
import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.SupportsCatalogs;
import org.apache.gravitino.authorization.Group;
import org.apache.gravitino.authorization.Owner;
import org.apache.gravitino.authorization.Role;
import org.apache.gravitino.authorization.SecurableObject;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.authorization.User;
import org.apache.gravitino.dto.AuditDTO;
import org.apache.gravitino.dto.MetalakeDTO;
@@ -93,7 +95,8 @@ import org.apache.gravitino.tag.TagOperations;
* catalogs as sub-level metadata collections. With {@link GravitinoMetalake},
users can list,
* create, load, alter and drop a catalog with specified identifier.
*/
-public class GravitinoMetalake extends MetalakeDTO implements
SupportsCatalogs, TagOperations {
+public class GravitinoMetalake extends MetalakeDTO
+ implements SupportsCatalogs, TagOperations, SupportsRoles {
private static final String API_METALAKES_CATALOGS_PATH =
"api/metalakes/%s/catalogs/%s";
private static final String API_PERMISSION_PATH =
"api/metalakes/%s/permissions/%s";
private static final String API_METALAKES_USERS_PATH =
"api/metalakes/%s/users/%s";
@@ -105,6 +108,7 @@ public class GravitinoMetalake extends MetalakeDTO
implements SupportsCatalogs,
private static final String BLANK_PLACEHOLDER = "";
private final RESTClient restClient;
+ private final MetadataObjectRoleOperations metadataObjectRoleOperations;
GravitinoMetalake(
String name,
@@ -114,6 +118,9 @@ public class GravitinoMetalake extends MetalakeDTO
implements SupportsCatalogs,
RESTClient restClient) {
super(name, comment, properties, auditDTO);
this.restClient = restClient;
+ this.metadataObjectRoleOperations =
+ new MetadataObjectRoleOperations(
+ name, MetadataObjects.of(null, name,
MetadataObject.Type.METALAKE), restClient);
}
/**
@@ -308,6 +315,11 @@ public class GravitinoMetalake extends MetalakeDTO
implements SupportsCatalogs,
ErrorHandlers.catalogErrorHandler().accept(resp);
}
+ @Override
+ public SupportsRoles supportsRoles() {
+ return this;
+ }
+
/*
* List all the tag names under a metalake.
*
@@ -896,6 +908,11 @@ public class GravitinoMetalake extends MetalakeDTO
implements SupportsCatalogs,
resp.validate();
}
+ @Override
+ public String[] listBindingRoleNames() {
+ return metadataObjectRoleOperations.listBindingRoleNames();
+ }
+
static class Builder extends MetalakeDTO.Builder<Builder> {
private RESTClient restClient;
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectRoleOperations.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectRoleOperations.java
new file mode 100644
index 000000000..54a663435
--- /dev/null
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/MetadataObjectRoleOperations.java
@@ -0,0 +1,56 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.client;
+
+import java.util.Collections;
+import java.util.Locale;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.authorization.SupportsRoles;
+import org.apache.gravitino.dto.responses.NameListResponse;
+
+class MetadataObjectRoleOperations implements SupportsRoles {
+
+ private final RESTClient restClient;
+
+ private final String roleRequestPath;
+
+ MetadataObjectRoleOperations(
+ String metalakeName, MetadataObject metadataObject, RESTClient
restClient) {
+ this.restClient = restClient;
+ this.roleRequestPath =
+ String.format(
+ "api/metalakes/%s/objects/%s/%s/roles",
+ metalakeName,
+ metadataObject.type().name().toLowerCase(Locale.ROOT),
+ metadataObject.fullName());
+ }
+
+ @Override
+ public String[] listBindingRoleNames() {
+ NameListResponse resp =
+ restClient.get(
+ roleRequestPath,
+ NameListResponse.class,
+ Collections.emptyMap(),
+ ErrorHandlers.roleErrorHandler());
+ resp.validate();
+
+ return resp.getNames();
+ }
+}
diff --git
a/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalTable.java
b/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalTable.java
index af7e094b1..83634295f 100644
---
a/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalTable.java
+++
b/clients/client-java/src/main/java/org/apache/gravitino/client/RelationalTable.java
@@ -32,6 +32,7 @@ import org.apache.gravitino.Audit;
import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.MetadataObjects;
import org.apache.gravitino.Namespace;
+import org.apache.gravitino.authorization.SupportsRoles;
import org.apache.gravitino.dto.rel.TableDTO;
import org.apache.gravitino.dto.rel.partitions.PartitionDTO;
import org.apache.gravitino.dto.requests.AddPartitionsRequest;
@@ -55,7 +56,7 @@ import org.apache.gravitino.tag.SupportsTags;
import org.apache.gravitino.tag.Tag;
/** Represents a relational table. */
-class RelationalTable implements Table, SupportsPartitions, SupportsTags {
+class RelationalTable implements Table, SupportsPartitions, SupportsTags,
SupportsRoles {
private static final Joiner DOT_JOINER = Joiner.on(".");
@@ -66,6 +67,7 @@ class RelationalTable implements Table, SupportsPartitions,
SupportsTags {
private final Namespace namespace;
private final MetadataObjectTagOperations objectTagOperations;
+ private final MetadataObjectRoleOperations objectRoleOperations;
/**
* Creates a new RelationalTable.
@@ -94,6 +96,8 @@ class RelationalTable implements Table, SupportsPartitions,
SupportsTags {
MetadataObjects.parse(tableFullName(namespace, tableDTO.name()),
MetadataObject.Type.TABLE);
this.objectTagOperations =
new MetadataObjectTagOperations(namespace.level(0), tableObject,
restClient);
+ this.objectRoleOperations =
+ new MetadataObjectRoleOperations(namespace.level(0), tableObject,
restClient);
}
/**
@@ -284,6 +288,11 @@ class RelationalTable implements Table,
SupportsPartitions, SupportsTags {
return this;
}
+ @Override
+ public SupportsRoles supportsRoles() {
+ return this;
+ }
+
private static String tableFullName(Namespace tableNS, String tableName) {
return DOT_JOINER.join(tableNS.level(1), tableNS.level(2), tableName);
}
@@ -307,4 +316,9 @@ class RelationalTable implements Table, SupportsPartitions,
SupportsTags {
public String[] associateTags(String[] tagsToAdd, String[] tagsToRemove) {
return objectTagOperations.associateTags(tagsToAdd, tagsToRemove);
}
+
+ @Override
+ public String[] listBindingRoleNames() {
+ return objectRoleOperations.listBindingRoleNames();
+ }
}
diff --git
a/clients/client-java/src/test/java/org/apache/gravitino/client/TestSupportRoles.java
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestSupportRoles.java
new file mode 100644
index 000000000..b22d5b21b
--- /dev/null
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/TestSupportRoles.java
@@ -0,0 +1,256 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.client;
+
+import static org.apache.hc.core5.http.HttpStatus.SC_INTERNAL_SERVER_ERROR;
+import static org.apache.hc.core5.http.HttpStatus.SC_NOT_FOUND;
+import static org.apache.hc.core5.http.HttpStatus.SC_OK;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.util.Collections;
+import java.util.Locale;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.Metalake;
+import org.apache.gravitino.Namespace;
+import org.apache.gravitino.Schema;
+import org.apache.gravitino.authorization.SupportsRoles;
+import org.apache.gravitino.dto.AuditDTO;
+import org.apache.gravitino.dto.SchemaDTO;
+import org.apache.gravitino.dto.file.FilesetDTO;
+import org.apache.gravitino.dto.messaging.TopicDTO;
+import org.apache.gravitino.dto.rel.ColumnDTO;
+import org.apache.gravitino.dto.rel.TableDTO;
+import org.apache.gravitino.dto.responses.ErrorResponse;
+import org.apache.gravitino.dto.responses.NameListResponse;
+import org.apache.gravitino.exceptions.NotFoundException;
+import org.apache.gravitino.file.Fileset;
+import org.apache.gravitino.messaging.Topic;
+import org.apache.gravitino.rel.Table;
+import org.apache.gravitino.rel.types.Types;
+import org.apache.hc.core5.http.Method;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+
+public class TestSupportRoles extends TestBase {
+ private static final String METALAKE_NAME = "metalake";
+
+ private static Catalog relationalCatalog;
+
+ private static Catalog filesetCatalog;
+
+ private static Catalog messagingCatalog;
+
+ private static Schema genericSchema;
+
+ private static Table relationalTable;
+
+ private static Fileset genericFileset;
+
+ private static Topic genericTopic;
+ private static Metalake metalake;
+
+ @BeforeAll
+ public static void setUp() throws Exception {
+ TestBase.setUp();
+ metalake = TestGravitinoMetalake.createMetalake(client, METALAKE_NAME);
+
+ relationalCatalog =
+ new RelationalCatalog(
+ Namespace.of(METALAKE_NAME),
+ "catalog1",
+ Catalog.Type.RELATIONAL,
+ "test",
+ "comment",
+ Collections.emptyMap(),
+ AuditDTO.builder().build(),
+ client.restClient());
+
+ filesetCatalog =
+ new FilesetCatalog(
+ Namespace.of(METALAKE_NAME),
+ "catalog2",
+ Catalog.Type.FILESET,
+ "test",
+ "comment",
+ Collections.emptyMap(),
+ AuditDTO.builder().build(),
+ client.restClient());
+
+ messagingCatalog =
+ new MessagingCatalog(
+ Namespace.of(METALAKE_NAME),
+ "catalog3",
+ Catalog.Type.MESSAGING,
+ "test",
+ "comment",
+ Collections.emptyMap(),
+ AuditDTO.builder().build(),
+ client.restClient());
+
+ genericSchema =
+ new GenericSchema(
+ SchemaDTO.builder()
+ .withName("schema1")
+ .withComment("comment1")
+ .withProperties(Collections.emptyMap())
+ .withAudit(AuditDTO.builder().withCreator("test").build())
+ .build(),
+ client.restClient(),
+ METALAKE_NAME,
+ "catalog1");
+
+ relationalTable =
+ RelationalTable.from(
+ Namespace.of(METALAKE_NAME, "catalog1", "schema1"),
+ TableDTO.builder()
+ .withName("table1")
+ .withComment("comment1")
+ .withColumns(
+ new ColumnDTO[] {
+ ColumnDTO.builder()
+ .withName("col1")
+ .withDataType(Types.IntegerType.get())
+ .build()
+ })
+ .withProperties(Collections.emptyMap())
+ .withAudit(AuditDTO.builder().withCreator("test").build())
+ .build(),
+ client.restClient());
+
+ genericFileset =
+ new GenericFileset(
+ FilesetDTO.builder()
+ .name("fileset1")
+ .comment("comment1")
+ .type(Fileset.Type.EXTERNAL)
+ .storageLocation("s3://bucket/path")
+ .properties(Collections.emptyMap())
+ .audit(AuditDTO.builder().withCreator("test").build())
+ .build(),
+ client.restClient(),
+ Namespace.of(METALAKE_NAME, "catalog1", "schema1"));
+
+ genericTopic =
+ new GenericTopic(
+ TopicDTO.builder()
+ .withName("topic1")
+ .withComment("comment1")
+ .withProperties(Collections.emptyMap())
+ .withAudit(AuditDTO.builder().withCreator("test").build())
+ .build(),
+ client.restClient(),
+ Namespace.of(METALAKE_NAME, "catalog1", "schema1"));
+ }
+
+ @Test
+ public void testListRolesForMetalake() throws JsonProcessingException {
+ testListRoles(
+ metalake.supportsRoles(),
+ MetadataObjects.of(null, metalake.name(),
MetadataObject.Type.METALAKE));
+ }
+
+ @Test
+ public void testListRolesForCatalog() throws JsonProcessingException {
+ testListRoles(
+ relationalCatalog.supportsRoles(),
+ MetadataObjects.of(null, relationalCatalog.name(),
MetadataObject.Type.CATALOG));
+
+ testListRoles(
+ filesetCatalog.supportsRoles(),
+ MetadataObjects.of(null, filesetCatalog.name(),
MetadataObject.Type.CATALOG));
+
+ testListRoles(
+ messagingCatalog.supportsRoles(),
+ MetadataObjects.of(null, messagingCatalog.name(),
MetadataObject.Type.CATALOG));
+ }
+
+ @Test
+ public void testListRolesForSchema() throws JsonProcessingException {
+ testListRoles(
+ genericSchema.supportsRoles(),
+ MetadataObjects.of("catalog1", genericSchema.name(),
MetadataObject.Type.SCHEMA));
+ }
+
+ @Test
+ public void testListRolesForTable() throws JsonProcessingException {
+ testListRoles(
+ relationalTable.supportsRoles(),
+ MetadataObjects.of("catalog1.schema1", relationalTable.name(),
MetadataObject.Type.TABLE));
+ }
+
+ @Test
+ public void testListRolesForFileset() throws JsonProcessingException {
+ testListRoles(
+ genericFileset.supportsRoles(),
+ MetadataObjects.of("catalog1.schema1", genericFileset.name(),
MetadataObject.Type.FILESET));
+ }
+
+ @Test
+ public void testListRolesForTopic() throws JsonProcessingException {
+ testListRoles(
+ genericTopic.supportsRoles(),
+ MetadataObjects.of("catalog1.schema1", genericTopic.name(),
MetadataObject.Type.TOPIC));
+ }
+
+ private void testListRoles(SupportsRoles supportsRoles, MetadataObject
metadataObject)
+ throws JsonProcessingException {
+ String path =
+ "/api/metalakes/"
+ + METALAKE_NAME
+ + "/objects/"
+ + metadataObject.type().name().toLowerCase(Locale.ROOT)
+ + "/"
+ + metadataObject.fullName()
+ + "/roles";
+
+ String[] roles = new String[] {"role1", "role2"};
+ NameListResponse resp = new NameListResponse(roles);
+ buildMockResource(Method.GET, path, null, resp, SC_OK);
+
+ String[] actualTags = supportsRoles.listBindingRoleNames();
+ Assertions.assertArrayEquals(roles, actualTags);
+
+ // Return empty list
+ NameListResponse resp1 = new NameListResponse(new String[0]);
+ buildMockResource(Method.GET, path, null, resp1, SC_OK);
+
+ String[] actualRoles1 = supportsRoles.listBindingRoleNames();
+ Assertions.assertArrayEquals(new String[0], actualRoles1);
+
+ // Test throw NotFoundException
+ ErrorResponse errorResp =
+ ErrorResponse.notFound(NotFoundException.class.getSimpleName(), "mock
error");
+ buildMockResource(Method.GET, path, null, errorResp, SC_NOT_FOUND);
+
+ Throwable ex =
+ Assertions.assertThrows(NotFoundException.class,
supportsRoles::listBindingRoleNames);
+ Assertions.assertTrue(ex.getMessage().contains("mock error"));
+
+ // Test throw internal error
+ ErrorResponse errorResp1 = ErrorResponse.internalError("mock error");
+ buildMockResource(Method.GET, path, null, errorResp1,
SC_INTERNAL_SERVER_ERROR);
+
+ Throwable ex1 =
+ Assertions.assertThrows(RuntimeException.class,
supportsRoles::listBindingRoleNames);
+ Assertions.assertTrue(ex1.getMessage().contains("mock error"));
+ }
+}
diff --git
a/core/src/main/java/org/apache/gravitino/SupportsRelationOperations.java
b/core/src/main/java/org/apache/gravitino/SupportsRelationOperations.java
index 617f72ab9..d203b94de 100644
--- a/core/src/main/java/org/apache/gravitino/SupportsRelationOperations.java
+++ b/core/src/main/java/org/apache/gravitino/SupportsRelationOperations.java
@@ -40,7 +40,7 @@ public interface SupportsRelationOperations {
}
/**
- * List the entities according to a give entity in a specific relation.
+ * List the entities according to a given entity in a specific relation.
*
* @param relType The type of relation.
* @param nameIdentifier The given entity identifier
@@ -48,8 +48,27 @@ public interface SupportsRelationOperations {
* @return The list of entities
* @throws IOException When occurs storage issues, it will throw IOException.
*/
+ default <E extends Entity & HasIdentifier> List<E> listEntitiesByRelation(
+ Type relType, NameIdentifier nameIdentifier, Entity.EntityType
identType) throws IOException {
+ return listEntitiesByRelation(relType, nameIdentifier, identType, true /*
allFields*/);
+ }
+
+ /**
+ * List the entities according to a given entity in a specific relation.
+ *
+ * @param relType The type of relation.
+ * @param nameIdentifier The given entity identifier
+ * @param identType The given entity type.
+ * @param allFields Some fields may have a relatively high acquisition cost,
EntityStore provide
+ * an optional setting to avoid fetching these high-cost fields to
improve the performance. If
+ * true, the method will fetch all the fields, Otherwise, the method
will fetch all the fields
+ * except for high-cost fields.
+ * @return The list of entities
+ * @throws IOException When occurs storage issues, it will throw IOException.
+ */
<E extends Entity & HasIdentifier> List<E> listEntitiesByRelation(
- Type relType, NameIdentifier nameIdentifier, Entity.EntityType
identType) throws IOException;
+ Type relType, NameIdentifier nameIdentifier, Entity.EntityType
identType, boolean allFields)
+ throws IOException;
/**
* insert a relation between two entities
diff --git
a/core/src/main/java/org/apache/gravitino/authorization/AccessControlDispatcher.java
b/core/src/main/java/org/apache/gravitino/authorization/AccessControlDispatcher.java
index 95cb304de..3214c187f 100644
---
a/core/src/main/java/org/apache/gravitino/authorization/AccessControlDispatcher.java
+++
b/core/src/main/java/org/apache/gravitino/authorization/AccessControlDispatcher.java
@@ -20,8 +20,10 @@ package org.apache.gravitino.authorization;
import java.util.List;
import java.util.Map;
+import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
import org.apache.gravitino.exceptions.NoSuchGroupException;
+import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchRoleException;
import org.apache.gravitino.exceptions.NoSuchUserException;
@@ -246,4 +248,16 @@ public interface AccessControlDispatcher {
* @throws NoSuchMetalakeException If the Metalake with the given name does
not exist.
*/
String[] listRoleNames(String metalake) throws NoSuchMetalakeException;
+
+ /**
+ * Lists the role names associated the metadata object.
+ *
+ * @param metalake The Metalake of the Role.
+ * @return The role list.
+ * @throws NoSuchMetalakeException If the Metalake with the given name does
not exist.
+ * @throws NoSuchMetadataObjectException If the Metadata object with the
given name does not
+ * exist.
+ */
+ String[] listRoleNamesByObject(String metalake, MetadataObject object)
+ throws NoSuchMetalakeException, NoSuchMetadataObjectException;
}
diff --git
a/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java
b/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java
index 8872afade..c2f2976aa 100644
---
a/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java
+++
b/core/src/main/java/org/apache/gravitino/authorization/AccessControlManager.java
@@ -18,14 +18,15 @@
*/
package org.apache.gravitino.authorization;
-import com.google.common.annotations.VisibleForTesting;
import java.util.List;
import java.util.Map;
import org.apache.gravitino.Config;
import org.apache.gravitino.Configs;
import org.apache.gravitino.EntityStore;
+import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
import org.apache.gravitino.exceptions.NoSuchGroupException;
+import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchRoleException;
import org.apache.gravitino.exceptions.NoSuchUserException;
@@ -148,8 +149,9 @@ public class AccessControlManager implements
AccessControlDispatcher {
return roleManager.listRoleNames(metalake);
}
- @VisibleForTesting
- RoleManager getRoleManager() {
- return roleManager;
+ @Override
+ public String[] listRoleNamesByObject(String metalake, MetadataObject object)
+ throws NoSuchMetalakeException, NoSuchMetadataObjectException {
+ return roleManager.listRoleNamesByObject(metalake, object);
}
}
diff --git
a/core/src/main/java/org/apache/gravitino/authorization/FutureGrantManager.java
b/core/src/main/java/org/apache/gravitino/authorization/FutureGrantManager.java
index c24817ea5..b838e1956 100644
---
a/core/src/main/java/org/apache/gravitino/authorization/FutureGrantManager.java
+++
b/core/src/main/java/org/apache/gravitino/authorization/FutureGrantManager.java
@@ -20,6 +20,7 @@ package org.apache.gravitino.authorization;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import java.io.IOException;
import java.util.List;
import java.util.Map;
@@ -37,7 +38,6 @@ import
org.apache.gravitino.connector.authorization.AuthorizationPlugin;
import org.apache.gravitino.meta.GroupEntity;
import org.apache.gravitino.meta.RoleEntity;
import org.apache.gravitino.meta.UserEntity;
-import org.glassfish.jersey.internal.guava.Sets;
/**
* FutureGrantManager is responsible for granting privileges to future object.
When you grant a
diff --git
a/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
b/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
index 8b195894f..dc675fdce 100644
--- a/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
+++ b/core/src/main/java/org/apache/gravitino/authorization/RoleManager.java
@@ -27,15 +27,19 @@ import java.util.Map;
import org.apache.gravitino.Entity;
import org.apache.gravitino.EntityAlreadyExistsException;
import org.apache.gravitino.EntityStore;
+import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.Namespace;
+import org.apache.gravitino.SupportsRelationOperations;
import org.apache.gravitino.exceptions.NoSuchEntityException;
+import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchRoleException;
import org.apache.gravitino.exceptions.RoleAlreadyExistsException;
import org.apache.gravitino.meta.AuditInfo;
import org.apache.gravitino.meta.RoleEntity;
import org.apache.gravitino.storage.IdGenerator;
+import org.apache.gravitino.utils.MetadataObjectUtil;
import org.apache.gravitino.utils.PrincipalUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -148,6 +152,35 @@ class RoleManager {
}
}
+ String[] listRoleNamesByObject(String metalake, MetadataObject object) {
+ try {
+ AuthorizationUtils.checkMetalakeExists(metalake);
+
+ return store.relationOperations()
+ .listEntitiesByRelation(
+ SupportsRelationOperations.Type.METADATA_OBJECT_ROLE_REL,
+ MetadataObjectUtil.toEntityIdent(metalake, object),
+ MetadataObjectUtil.toEntityType(object),
+ false /* allFields */)
+ .stream()
+ .map(entity -> ((RoleEntity) entity).name())
+ .toArray(String[]::new);
+
+ } catch (NoSuchEntityException nse) {
+ LOG.error("Metadata object {} (type {}) doesn't exist",
object.fullName(), object.type());
+ throw new NoSuchMetadataObjectException(
+ "Metadata object %s (type %s) doesn't exist", object.fullName(),
object.type());
+ } catch (IOException ioe) {
+ LOG.error(
+ "Listing roles under metalake {} by object full name {} and type {}
failed due to storage issues",
+ metalake,
+ object.fullName(),
+ object.type(),
+ ioe);
+ throw new RuntimeException(ioe);
+ }
+ }
+
private RoleEntity getRoleEntity(NameIdentifier identifier) {
try {
return store.get(identifier, Entity.EntityType.ROLE, RoleEntity.class);
diff --git
a/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
b/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
index 7882e9c8a..65ed2c9da 100644
---
a/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
+++
b/core/src/main/java/org/apache/gravitino/hook/AccessControlHookDispatcher.java
@@ -22,6 +22,7 @@ import java.util.List;
import java.util.Map;
import org.apache.gravitino.Entity;
import org.apache.gravitino.GravitinoEnv;
+import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.authorization.AccessControlDispatcher;
import org.apache.gravitino.authorization.AuthorizationUtils;
import org.apache.gravitino.authorization.Group;
@@ -32,6 +33,7 @@ import org.apache.gravitino.authorization.SecurableObject;
import org.apache.gravitino.authorization.User;
import org.apache.gravitino.exceptions.GroupAlreadyExistsException;
import org.apache.gravitino.exceptions.NoSuchGroupException;
+import org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.exceptions.NoSuchMetalakeException;
import org.apache.gravitino.exceptions.NoSuchRoleException;
import org.apache.gravitino.exceptions.NoSuchUserException;
@@ -162,4 +164,10 @@ public class AccessControlHookDispatcher implements
AccessControlDispatcher {
public String[] listRoleNames(String metalake) throws
NoSuchMetalakeException {
return dispatcher.listRoleNames(metalake);
}
+
+ @Override
+ public String[] listRoleNamesByObject(String metalake, MetadataObject object)
+ throws NoSuchMetalakeException, NoSuchMetadataObjectException {
+ return dispatcher.listRoleNamesByObject(metalake, object);
+ }
}
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
b/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
index 2b9a6d0e4..42b079234 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/JDBCBackend.java
@@ -369,9 +369,7 @@ public class JDBCBackend implements RelationalBackend {
@Override
public <E extends Entity & HasIdentifier> List<E> listEntitiesByRelation(
- SupportsRelationOperations.Type relType,
- NameIdentifier nameIdentifier,
- Entity.EntityType identType) {
+ Type relType, NameIdentifier nameIdentifier, Entity.EntityType
identType, boolean allFields) {
switch (relType) {
case OWNER_REL:
List<E> list = Lists.newArrayList();
@@ -382,7 +380,7 @@ public class JDBCBackend implements RelationalBackend {
case METADATA_OBJECT_ROLE_REL:
return (List<E>)
RoleMetaService.getInstance()
- .listRolesByMetadataObjectIdentAndType(nameIdentifier,
identType);
+ .listRolesByMetadataObjectIdentAndType(nameIdentifier,
identType, allFields);
case ROLE_GROUP_REL:
if (identType == Entity.EntityType.ROLE) {
return (List<E>)
GroupMetaService.getInstance().listGroupsByRoleIdent(nameIdentifier);
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
index c95db1a07..a337e7a78 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/RelationalEntityStore.java
@@ -188,11 +188,9 @@ public class RelationalEntityStore
@Override
public <E extends Entity & HasIdentifier> List<E> listEntitiesByRelation(
- SupportsRelationOperations.Type relType,
- NameIdentifier nameIdentifier,
- Entity.EntityType identType)
+ Type relType, NameIdentifier nameIdentifier, Entity.EntityType
identType, boolean allFields)
throws IOException {
- return backend.listEntitiesByRelation(relType, nameIdentifier, identType);
+ return backend.listEntitiesByRelation(relType, nameIdentifier, identType,
allFields);
}
@Override
diff --git
a/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
b/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
index 1e914f59a..915a14950 100644
---
a/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
+++
b/core/src/main/java/org/apache/gravitino/storage/relational/service/RoleMetaService.java
@@ -57,21 +57,6 @@ public class RoleMetaService {
private RoleMetaService() {}
- private RolePO getRolePOByMetalakeIdAndName(Long metalakeId, String
roleName) {
- RolePO rolePO =
- SessionUtils.getWithoutCommit(
- RoleMetaMapper.class,
- mapper -> mapper.selectRoleMetaByMetalakeIdAndName(metalakeId,
roleName));
-
- if (rolePO == null) {
- throw new NoSuchEntityException(
- NoSuchEntityException.NO_SUCH_ENTITY_MESSAGE,
- Entity.EntityType.ROLE.name().toLowerCase(),
- roleName);
- }
- return rolePO;
- }
-
public Long getRoleIdByMetalakeIdAndName(Long metalakeId, String roleName) {
Long roleId =
SessionUtils.getWithoutCommit(
@@ -93,7 +78,7 @@ public class RoleMetaService {
}
public List<RoleEntity> listRolesByMetadataObjectIdentAndType(
- NameIdentifier metadataObjectIdent, Entity.EntityType
metadataObjectType) {
+ NameIdentifier metadataObjectIdent, Entity.EntityType
metadataObjectType, boolean allFields) {
String metalake = NameIdentifierUtil.getMetalake(metadataObjectIdent);
long metalakeId =
MetalakeMetaService.getInstance().getMetalakeIdByName(metalake);
MetadataObject metadataObject =
@@ -109,35 +94,18 @@ public class RoleMetaService {
metadataObjectId, metadataObject.type().name()));
return rolePOs.stream()
.map(
- po ->
- POConverters.fromRolePO(
- po, listSecurableObjects(po),
AuthorizationUtils.ofRoleNamespace(metalake)))
+ po -> {
+ if (allFields) {
+ return POConverters.fromRolePO(
+ po, listSecurableObjects(po),
AuthorizationUtils.ofRoleNamespace(metalake));
+ } else {
+ return POConverters.fromRolePO(
+ po, Collections.emptyList(),
AuthorizationUtils.ofRoleNamespace(metalake));
+ }
+ })
.collect(Collectors.toList());
}
- private List<SecurableObject> listSecurableObjects(RolePO po) {
- List<SecurableObjectPO> securableObjectPOs =
listSecurableObjectsByRoleId(po.getRoleId());
- List<SecurableObject> securableObjects = Lists.newArrayList();
-
- for (SecurableObjectPO securableObjectPO : securableObjectPOs) {
- String fullName =
- MetadataObjectService.getMetadataObjectFullName(
- securableObjectPO.getType(),
securableObjectPO.getMetadataObjectId());
- if (fullName != null) {
- securableObjects.add(
- POConverters.fromSecurableObjectPO(
- fullName, securableObjectPO,
getType(securableObjectPO.getType())));
- } else {
- LOG.info(
- "The securable object {} {} may be deleted",
- securableObjectPO.getMetadataObjectId(),
- securableObjectPO.getType());
- }
- }
-
- return securableObjects;
- }
-
public List<RolePO> listRolesByGroupId(Long groupId) {
return SessionUtils.getWithoutCommit(
RoleMetaMapper.class, mapper -> mapper.listRolesByGroupId(groupId));
@@ -234,7 +202,7 @@ public class RoleMetaService {
return true;
}
- private List<SecurableObjectPO> listSecurableObjectsByRoleId(Long roleId) {
+ private static List<SecurableObjectPO> listSecurableObjectsByRoleId(Long
roleId) {
return SessionUtils.getWithoutCommit(
SecurableObjectMapper.class, mapper ->
mapper.listSecurableObjectsByRoleId(roleId));
}
@@ -291,11 +259,49 @@ public class RoleMetaService {
+ securableObjectsCount[0];
}
- private MetadataObject.Type getType(String type) {
+ private static List<SecurableObject> listSecurableObjects(RolePO po) {
+ List<SecurableObjectPO> securableObjectPOs =
listSecurableObjectsByRoleId(po.getRoleId());
+ List<SecurableObject> securableObjects = Lists.newArrayList();
+
+ for (SecurableObjectPO securableObjectPO : securableObjectPOs) {
+ String fullName =
+ MetadataObjectService.getMetadataObjectFullName(
+ securableObjectPO.getType(),
securableObjectPO.getMetadataObjectId());
+ if (fullName != null) {
+ securableObjects.add(
+ POConverters.fromSecurableObjectPO(
+ fullName, securableObjectPO,
getType(securableObjectPO.getType())));
+ } else {
+ LOG.warn(
+ "The securable object {} {} may be deleted",
+ securableObjectPO.getMetadataObjectId(),
+ securableObjectPO.getType());
+ }
+ }
+
+ return securableObjects;
+ }
+
+ private static RolePO getRolePOByMetalakeIdAndName(Long metalakeId, String
roleName) {
+ RolePO rolePO =
+ SessionUtils.getWithoutCommit(
+ RoleMetaMapper.class,
+ mapper -> mapper.selectRoleMetaByMetalakeIdAndName(metalakeId,
roleName));
+
+ if (rolePO == null) {
+ throw new NoSuchEntityException(
+ NoSuchEntityException.NO_SUCH_ENTITY_MESSAGE,
+ Entity.EntityType.ROLE.name().toLowerCase(),
+ roleName);
+ }
+ return rolePO;
+ }
+
+ private static MetadataObject.Type getType(String type) {
return MetadataObject.Type.valueOf(type);
}
- private String getEntityType(SecurableObject securableObject) {
+ private static String getEntityType(SecurableObject securableObject) {
return securableObject.type().name();
}
}
diff --git
a/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
b/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
index 6dfaf54fe..b299c15ef 100644
---
a/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
+++
b/core/src/test/java/org/apache/gravitino/authorization/TestAccessControlManager.java
@@ -115,6 +115,7 @@ public class TestAccessControlManager {
public static void setUp() throws Exception {
File dbDir = new File(DB_DIR);
dbDir.mkdirs();
+
Mockito.when(config.get(SERVICE_ADMINS)).thenReturn(Lists.newArrayList("admin1",
"admin2"));
Mockito.when(config.get(ENTITY_STORE)).thenReturn(RELATIONAL_ENTITY_STORE);
Mockito.when(config.get(ENTITY_RELATIONAL_STORE)).thenReturn(DEFAULT_ENTITY_RELATIONAL_STORE);
@@ -125,10 +126,12 @@ public class TestAccessControlManager {
Mockito.when(config.get(STORE_DELETE_AFTER_TIME)).thenReturn(20 * 60 *
1000L);
Mockito.when(config.get(VERSION_RETENTION_COUNT)).thenReturn(1L);
Mockito.when(config.get(CATALOG_CACHE_EVICTION_INTERVAL_MS)).thenReturn(1000L);
+
Mockito.doReturn(100000L).when(config).get(TREE_LOCK_MAX_NODE_IN_MEMORY);
Mockito.doReturn(1000L).when(config).get(TREE_LOCK_MIN_NODE_IN_MEMORY);
Mockito.doReturn(36000L).when(config).get(TREE_LOCK_CLEAN_INTERVAL);
FieldUtils.writeField(GravitinoEnv.getInstance(), "lockManager", new
LockManager(config), true);
+
entityStore = EntityStoreFactory.createEntityStore(config);
entityStore.initialize(config);
@@ -146,6 +149,7 @@ public class TestAccessControlManager {
AuditInfo.builder().withCreator("test").withCreateTime(Instant.now()).build())
.build();
entityStore.put(catalogEntity, true);
+
CatalogEntity anotherCatalogEntity =
CatalogEntity.builder()
.withId(4L)
@@ -421,6 +425,31 @@ public class TestAccessControlManager {
String[] actualRoles = accessControlManager.listRoleNames("metalake_list");
Arrays.sort(actualRoles);
Assertions.assertArrayEquals(new String[] {"testList1", "testList2"},
actualRoles);
+
+ accessControlManager.deleteRole("metalake_list", "testList1");
+ accessControlManager.deleteRole("metalake_list", "testList2");
+ }
+
+ @Test
+ public void testListRolesByObject() {
+ Map<String, String> props = ImmutableMap.of("k1", "v1");
+ SecurableObject catalogObject =
+ SecurableObjects.ofCatalog("catalog",
Lists.newArrayList(Privileges.UseCatalog.allow()));
+
+ accessControlManager.createRole(
+ "metalake_list", "testList1", props,
Lists.newArrayList(catalogObject));
+
+ accessControlManager.createRole(
+ "metalake_list", "testList2", props,
Lists.newArrayList(catalogObject));
+
+ // Test to list roles
+ String[] listedRoles =
+ accessControlManager.listRoleNamesByObject("metalake_list",
catalogObject);
+ Arrays.sort(listedRoles);
+ Assertions.assertArrayEquals(new String[] {"testList1", "testList2"},
listedRoles);
+
+ accessControlManager.deleteRole("metalake_list", "testList1");
+ accessControlManager.deleteRole("metalake_list", "testList2");
}
private void testProperties(Map<String, String> expectedProps, Map<String,
String> testProps) {
diff --git
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestRoleMetaService.java
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestRoleMetaService.java
index 4a781f018..1f818b112 100644
---
a/core/src/test/java/org/apache/gravitino/storage/relational/service/TestRoleMetaService.java
+++
b/core/src/test/java/org/apache/gravitino/storage/relational/service/TestRoleMetaService.java
@@ -441,7 +441,7 @@ class TestRoleMetaService extends TestJDBCBackend {
List<RoleEntity> roleEntities =
roleMetaService.listRolesByMetadataObjectIdentAndType(
- catalog.nameIdentifier(), catalog.type());
+ catalog.nameIdentifier(), catalog.type(), true);
roleEntities.sort(Comparator.comparing(RoleEntity::name));
Assertions.assertEquals(Lists.newArrayList(role1, role2), roleEntities);
}
diff --git
a/integration-test/src/test/java/org/apache/gravitino/integration/test/authorization/AccessControlIT.java
b/integration-test/src/test/java/org/apache/gravitino/integration/test/authorization/AccessControlIT.java
index a4167f1af..0501ffb46 100644
---
a/integration-test/src/test/java/org/apache/gravitino/integration/test/authorization/AccessControlIT.java
+++
b/integration-test/src/test/java/org/apache/gravitino/integration/test/authorization/AccessControlIT.java
@@ -26,7 +26,10 @@ import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
+import org.apache.gravitino.Catalog;
import org.apache.gravitino.Configs;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.Schema;
import org.apache.gravitino.auth.AuthConstants;
import org.apache.gravitino.authorization.Group;
import org.apache.gravitino.authorization.Privilege;
@@ -42,6 +45,7 @@ import
org.apache.gravitino.exceptions.NoSuchMetadataObjectException;
import org.apache.gravitino.exceptions.NoSuchRoleException;
import org.apache.gravitino.exceptions.NoSuchUserException;
import org.apache.gravitino.exceptions.UserAlreadyExistsException;
+import org.apache.gravitino.file.Fileset;
import org.apache.gravitino.integration.test.util.AbstractIT;
import org.apache.gravitino.utils.RandomNameUtils;
import org.junit.jupiter.api.Assertions;
@@ -61,6 +65,15 @@ public class AccessControlIT extends AbstractIT {
registerCustomConfigs(configs);
AbstractIT.startIntegrationTest();
metalake = client.createMetalake(metalakeName, "metalake comment",
Collections.emptyMap());
+
+ Catalog filesetCatalog =
+ metalake.createCatalog(
+ "fileset_catalog", Catalog.Type.FILESET, "hadoop", "comment",
Collections.emptyMap());
+ NameIdentifier fileIdent = NameIdentifier.of("fileset_schema", "fileset");
+ filesetCatalog.asSchemas().createSchema("fileset_schema", "comment",
Collections.emptyMap());
+ filesetCatalog
+ .asFilesetCatalog()
+ .createFileset(fileIdent, "comment", Fileset.Type.EXTERNAL, "tmp",
Collections.emptyMap());
}
@Test
@@ -187,6 +200,46 @@ public class AccessControlIT extends AbstractIT {
Assertions.assertEquals(
Lists.newArrayList(anotherRoleName, roleName),
Arrays.asList(roleNames));
+ // List roles by the object (metalake)
+ roleNames = metalake.listBindingRoleNames();
+ Arrays.sort(roleNames);
+ Assertions.assertEquals(
+ Lists.newArrayList(anotherRoleName, roleName),
Arrays.asList(roleNames));
+
+ String testObjectRole = "testObjectRole";
+ SecurableObject anotherCatalogObject =
+ SecurableObjects.ofCatalog(
+ "fileset_catalog",
Lists.newArrayList(Privileges.UseCatalog.allow()));
+ SecurableObject schemaObject =
+ SecurableObjects.ofSchema(
+ anotherCatalogObject,
+ "fileset_schema",
+ Lists.newArrayList(Privileges.UseSchema.allow()));
+ SecurableObject filesetObject =
+ SecurableObjects.ofFileset(
+ schemaObject, "fileset",
Lists.newArrayList(Privileges.ReadFileset.allow()));
+
+ metalake.createRole(
+ testObjectRole,
+ properties,
+ Lists.newArrayList(anotherCatalogObject, schemaObject, filesetObject));
+
+ // List roles by the object (catalog)
+ Catalog catalog = metalake.loadCatalog("fileset_catalog");
+ roleNames = catalog.supportsRoles().listBindingRoleNames();
+ Assertions.assertEquals(Lists.newArrayList(testObjectRole),
Arrays.asList(roleNames));
+
+ // List roles by the object (schema)
+ Schema schema = catalog.asSchemas().loadSchema("fileset_schema");
+ roleNames = schema.supportsRoles().listBindingRoleNames();
+ Assertions.assertEquals(Lists.newArrayList(testObjectRole),
Arrays.asList(roleNames));
+
+ // List roles by the object (fileset)
+ Fileset fileset =
+
catalog.asFilesetCatalog().loadFileset(NameIdentifier.of("fileset_schema",
"fileset"));
+ roleNames = fileset.supportsRoles().listBindingRoleNames();
+ Assertions.assertEquals(Lists.newArrayList(testObjectRole),
Arrays.asList(roleNames));
+
// Verify the object
Assertions.assertEquals(1, role.securableObjects().size());
createdObject = role.securableObjects().get(0);
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectRoleOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectRoleOperations.java
new file mode 100644
index 000000000..ad27b22a3
--- /dev/null
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/MetadataObjectRoleOperations.java
@@ -0,0 +1,89 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.server.web.rest;
+
+import com.codahale.metrics.annotation.ResponseMetered;
+import com.codahale.metrics.annotation.Timed;
+import java.util.Locale;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.GET;
+import javax.ws.rs.Path;
+import javax.ws.rs.PathParam;
+import javax.ws.rs.Produces;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.Response;
+import org.apache.gravitino.GravitinoEnv;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.authorization.AccessControlDispatcher;
+import org.apache.gravitino.dto.responses.NameListResponse;
+import org.apache.gravitino.lock.LockType;
+import org.apache.gravitino.lock.TreeLockUtils;
+import org.apache.gravitino.metrics.MetricNames;
+import org.apache.gravitino.server.authorization.NameBindings;
+import org.apache.gravitino.server.web.Utils;
+import org.apache.gravitino.utils.MetadataObjectUtil;
+
[email protected]
+@Path("/metalakes/{metalake}/objects/{type}/{fullName}/roles")
+public class MetadataObjectRoleOperations {
+
+ private final AccessControlDispatcher accessControlDispatcher;
+
+ @Context private HttpServletRequest httpRequest;
+
+ public MetadataObjectRoleOperations() {
+ // Because accessControlManager may be null when Gravitino doesn't enable
authorization,
+ // and Jersey injection doesn't support null value. So
MedataObjectRoleOperations chooses to
+ // retrieve
+ // accessControlDispatcher from GravitinoEnv instead of injection here.
+ this.accessControlDispatcher =
GravitinoEnv.getInstance().accessControlDispatcher();
+ }
+
+ @GET
+ @Produces("application/vnd.gravitino.v1+json")
+ @Timed(name = "list-role-by-object." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
+ @ResponseMetered(name = "list-role-by-object", absolute = true)
+ public Response listRoles(
+ @PathParam("metalake") String metalake,
+ @PathParam("type") String type,
+ @PathParam("fullName") String fullName) {
+ try {
+ MetadataObject object =
+ MetadataObjects.parse(
+ fullName,
MetadataObject.Type.valueOf(type.toUpperCase(Locale.ROOT)));
+
+ NameIdentifier identifier = MetadataObjectUtil.toEntityIdent(metalake,
object);
+ return Utils.doAs(
+ httpRequest,
+ () ->
+ TreeLockUtils.doWithTreeLock(
+ identifier,
+ LockType.READ,
+ () -> {
+ String[] names =
+
accessControlDispatcher.listRoleNamesByObject(metalake, object);
+ return Utils.ok(new NameListResponse(names));
+ }));
+ } catch (Exception e) {
+ return ExceptionHandlers.handleRoleException(OperationType.LIST, "",
metalake, e);
+ }
+ }
+}
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectRoleOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectRoleOperations.java
new file mode 100644
index 000000000..19c545a08
--- /dev/null
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestMetadataObjectRoleOperations.java
@@ -0,0 +1,146 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.gravitino.server.web.rest;
+
+import static org.apache.gravitino.Configs.TREE_LOCK_CLEAN_INTERVAL;
+import static org.apache.gravitino.Configs.TREE_LOCK_MAX_NODE_IN_MEMORY;
+import static org.apache.gravitino.Configs.TREE_LOCK_MIN_NODE_IN_MEMORY;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.doThrow;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.io.IOException;
+import javax.servlet.http.HttpServletRequest;
+import javax.ws.rs.core.Application;
+import javax.ws.rs.core.MediaType;
+import javax.ws.rs.core.Response;
+import org.apache.commons.lang3.reflect.FieldUtils;
+import org.apache.gravitino.Config;
+import org.apache.gravitino.GravitinoEnv;
+import org.apache.gravitino.authorization.AccessControlManager;
+import org.apache.gravitino.dto.responses.ErrorConstants;
+import org.apache.gravitino.dto.responses.ErrorResponse;
+import org.apache.gravitino.dto.responses.NameListResponse;
+import org.apache.gravitino.exceptions.NoSuchMetalakeException;
+import org.apache.gravitino.lock.LockManager;
+import org.apache.gravitino.rest.RESTUtils;
+import org.glassfish.hk2.utilities.binding.AbstractBinder;
+import org.glassfish.jersey.server.ResourceConfig;
+import org.glassfish.jersey.test.JerseyTest;
+import org.glassfish.jersey.test.TestProperties;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
+
+public class TestMetadataObjectRoleOperations extends JerseyTest {
+
+ private static final AccessControlManager manager =
mock(AccessControlManager.class);
+
+ private static class MockServletRequestFactory extends
ServletRequestFactoryBase {
+ @Override
+ public HttpServletRequest get() {
+ HttpServletRequest request = mock(HttpServletRequest.class);
+ when(request.getRemoteUser()).thenReturn(null);
+ return request;
+ }
+ }
+
+ @BeforeAll
+ public static void setup() throws IllegalAccessException {
+ Config config = mock(Config.class);
+ Mockito.doReturn(100000L).when(config).get(TREE_LOCK_MAX_NODE_IN_MEMORY);
+ Mockito.doReturn(1000L).when(config).get(TREE_LOCK_MIN_NODE_IN_MEMORY);
+ Mockito.doReturn(36000L).when(config).get(TREE_LOCK_CLEAN_INTERVAL);
+ FieldUtils.writeField(GravitinoEnv.getInstance(), "lockManager", new
LockManager(config), true);
+ FieldUtils.writeField(GravitinoEnv.getInstance(),
"accessControlDispatcher", manager, true);
+ }
+
+ @Override
+ protected Application configure() {
+ try {
+ forceSet(
+ TestProperties.CONTAINER_PORT,
String.valueOf(RESTUtils.findAvailablePort(2000, 3000)));
+ } catch (IOException e) {
+ throw new RuntimeException(e);
+ }
+
+ ResourceConfig resourceConfig = new ResourceConfig();
+ resourceConfig.register(MetadataObjectRoleOperations.class);
+ resourceConfig.register(
+ new AbstractBinder() {
+ @Override
+ protected void configure() {
+
bindFactory(MockServletRequestFactory.class).to(HttpServletRequest.class);
+ }
+ });
+
+ return resourceConfig;
+ }
+
+ @Test
+ public void testListRoleNames() {
+ when(manager.listRoleNamesByObject(any(), any())).thenReturn(new String[]
{"role"});
+
+ Response resp =
+ target("/metalakes/metalake1/objects/metalake/metalake1/roles/")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .get();
+ Assertions.assertEquals(Response.Status.OK.getStatusCode(),
resp.getStatus());
+
+ NameListResponse listResponse = resp.readEntity(NameListResponse.class);
+ Assertions.assertEquals(0, listResponse.getCode());
+
+ Assertions.assertEquals(1, listResponse.getNames().length);
+ Assertions.assertEquals("role", listResponse.getNames()[0]);
+
+ // Test to throw NoSuchMetalakeException
+ doThrow(new NoSuchMetalakeException("mock error"))
+ .when(manager)
+ .listRoleNamesByObject(any(), any());
+ Response resp1 =
+ target("/metalakes/metalake1/objects/metalake/metalake1/roles/")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .get();
+
+ Assertions.assertEquals(Response.Status.NOT_FOUND.getStatusCode(),
resp1.getStatus());
+
+ ErrorResponse errorResponse = resp1.readEntity(ErrorResponse.class);
+ Assertions.assertEquals(ErrorConstants.NOT_FOUND_CODE,
errorResponse.getCode());
+ Assertions.assertEquals(NoSuchMetalakeException.class.getSimpleName(),
errorResponse.getType());
+
+ // Test to throw internal RuntimeException
+ doThrow(new RuntimeException("mock
error")).when(manager).listRoleNamesByObject(any(), any());
+ Response resp3 =
+ target("/metalakes/metalake1/objects/metalake/metalake1/roles")
+ .request(MediaType.APPLICATION_JSON_TYPE)
+ .accept("application/vnd.gravitino.v1+json")
+ .get();
+
+ Assertions.assertEquals(
+ Response.Status.INTERNAL_SERVER_ERROR.getStatusCode(),
resp3.getStatus());
+
+ ErrorResponse errorResponse2 = resp3.readEntity(ErrorResponse.class);
+ Assertions.assertEquals(ErrorConstants.INTERNAL_ERROR_CODE,
errorResponse2.getCode());
+ Assertions.assertEquals(RuntimeException.class.getSimpleName(),
errorResponse2.getType());
+ }
+}
diff --git
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestRoleOperations.java
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestRoleOperations.java
index eb365d1ac..a2f0c4847 100644
---
a/server/src/test/java/org/apache/gravitino/server/web/rest/TestRoleOperations.java
+++
b/server/src/test/java/org/apache/gravitino/server/web/rest/TestRoleOperations.java
@@ -334,23 +334,6 @@ public class TestRoleOperations extends JerseyTest {
Assertions.assertEquals(RuntimeException.class.getSimpleName(),
errorResponse2.getType());
}
- private Role buildRole(String role) {
- SecurableObject catalog =
- SecurableObjects.ofCatalog("catalog",
Lists.newArrayList(Privileges.UseCatalog.allow()));
- SecurableObject anotherSecurableObject =
- SecurableObjects.ofCatalog(
- "another_catalog",
Lists.newArrayList(Privileges.CreateSchema.deny()));
-
- return RoleEntity.builder()
- .withId(1L)
- .withName(role)
- .withProperties(Collections.emptyMap())
- .withSecurableObjects(Lists.newArrayList(catalog,
anotherSecurableObject))
- .withAuditInfo(
-
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build())
- .build();
- }
-
@Test
public void testDeleteRole() {
when(manager.deleteRole(any(), any())).thenReturn(true);
@@ -502,4 +485,21 @@ public class TestRoleOperations extends JerseyTest {
Assertions.assertEquals(ErrorConstants.INTERNAL_ERROR_CODE,
errorResponse2.getCode());
Assertions.assertEquals(RuntimeException.class.getSimpleName(),
errorResponse2.getType());
}
+
+ private Role buildRole(String role) {
+ SecurableObject catalog =
+ SecurableObjects.ofCatalog("catalog",
Lists.newArrayList(Privileges.UseCatalog.allow()));
+ SecurableObject anotherSecurableObject =
+ SecurableObjects.ofCatalog(
+ "another_catalog",
Lists.newArrayList(Privileges.CreateSchema.deny()));
+
+ return RoleEntity.builder()
+ .withId(1L)
+ .withName(role)
+ .withProperties(Collections.emptyMap())
+ .withSecurableObjects(Lists.newArrayList(catalog,
anotherSecurableObject))
+ .withAuditInfo(
+
AuditInfo.builder().withCreator("creator").withCreateTime(Instant.now()).build())
+ .build();
+ }
}