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 0ab370555e [#7889] feat(authz): support partition operation
authorization (#8967)
0ab370555e is described below
commit 0ab370555ed759af290a893a36f70411a02577fe
Author: yangyang zhong <[email protected]>
AuthorDate: Fri Oct 31 14:40:57 2025 +0800
[#7889] feat(authz): support partition operation authorization (#8967)
### What changes were proposed in this pull request?
support partition operation authorization
### Why are the changes needed?
Fix: #7889
### Does this PR introduce _any_ user-facing change?
None
### How was this patch tested?
org.apache.gravitino.client.integration.test.authorization.PartitionAuthorizationIT
---
.../authorization/PartitionAuthorizationIT.java | 253 +++++++++++++++++++++
.../AuthorizationExpressionConstants.java | 7 +
.../web/filter/GravitinoInterceptionService.java | 4 +-
.../server/web/rest/PartitionOperations.java | 53 +++--
.../gravitino/server/web/rest/TableOperations.java | 12 +-
5 files changed, 302 insertions(+), 27 deletions(-)
diff --git
a/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/PartitionAuthorizationIT.java
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/PartitionAuthorizationIT.java
new file mode 100644
index 0000000000..b25fc1f0f4
--- /dev/null
+++
b/clients/client-java/src/test/java/org/apache/gravitino/client/integration/test/authorization/PartitionAuthorizationIT.java
@@ -0,0 +1,253 @@
+/*
+ * 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.integration.test.authorization;
+
+import static
org.apache.gravitino.client.integration.test.StatisticIT.TABLE_COMMENT;
+import static org.junit.Assert.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import org.apache.gravitino.Catalog;
+import org.apache.gravitino.MetadataObject;
+import org.apache.gravitino.MetadataObjects;
+import org.apache.gravitino.NameIdentifier;
+import org.apache.gravitino.authorization.Privileges;
+import org.apache.gravitino.authorization.SecurableObject;
+import org.apache.gravitino.authorization.SecurableObjects;
+import org.apache.gravitino.client.GravitinoMetalake;
+import org.apache.gravitino.exceptions.ForbiddenException;
+import org.apache.gravitino.integration.test.container.ContainerSuite;
+import org.apache.gravitino.integration.test.container.HiveContainer;
+import org.apache.gravitino.rel.Column;
+import org.apache.gravitino.rel.SupportsPartitions;
+import org.apache.gravitino.rel.Table;
+import org.apache.gravitino.rel.TableCatalog;
+import org.apache.gravitino.rel.expressions.literals.Literal;
+import org.apache.gravitino.rel.expressions.literals.Literals;
+import org.apache.gravitino.rel.expressions.transforms.Transform;
+import org.apache.gravitino.rel.expressions.transforms.Transforms;
+import org.apache.gravitino.rel.partitions.IdentityPartition;
+import org.apache.gravitino.rel.partitions.Partitions;
+import org.apache.gravitino.rel.types.Types;
+import org.junit.jupiter.api.AfterAll;
+import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.MethodOrderer;
+import org.junit.jupiter.api.Order;
+import org.junit.jupiter.api.Tag;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.TestMethodOrder;
+
+@Tag("gravitino-docker-test")
+@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
+public class PartitionAuthorizationIT extends BaseRestApiAuthorizationIT {
+
+ private static final String CATALOG = "catalog";
+ private static final String SCHEMA = "partitionSchema";
+ private static final ContainerSuite containerSuite =
ContainerSuite.getInstance();
+ private static String hmsUri;
+ private static String role = "role";
+
+ @BeforeAll
+ public void startIntegrationTest() throws Exception {
+ containerSuite.startHiveContainer();
+ super.startIntegrationTest();
+ hmsUri =
+ String.format(
+ "thrift://%s:%d",
+ containerSuite.getHiveContainer().getContainerIpAddress(),
+ HiveContainer.HIVE_METASTORE_PORT);
+ Map<String, String> properties = Maps.newHashMap();
+ properties.put("metastore.uris", hmsUri);
+ client
+ .loadMetalake(METALAKE)
+ .createCatalog(CATALOG, Catalog.Type.RELATIONAL, "hive", "comment",
properties)
+ .asSchemas()
+ .createSchema(SCHEMA, "test", new HashMap<>());
+ // try to load the schema as normal user, expect failure
+ assertThrows(
+ "Can not access metadata {" + CATALOG + "." + SCHEMA + "}.",
+ RuntimeException.class,
+ () -> {
+ normalUserClient
+ .loadMetalake(METALAKE)
+ .loadCatalog(CATALOG)
+ .asSchemas()
+ .loadSchema(SCHEMA);
+ });
+ // grant tester privilege
+ List<SecurableObject> securableObjects = new ArrayList<>();
+ GravitinoMetalake gravitinoMetalake = client.loadMetalake(METALAKE);
+ SecurableObject catalogObject =
+ SecurableObjects.ofCatalog(CATALOG,
ImmutableList.of(Privileges.UseCatalog.allow()));
+ securableObjects.add(catalogObject);
+ gravitinoMetalake.createRole(role, new HashMap<>(), securableObjects);
+ gravitinoMetalake.grantRolesToUser(ImmutableList.of(role), NORMAL_USER);
+ gravitinoMetalake.grantPrivilegesToRole(
+ role,
+ catalogObject,
+ ImmutableSet.of(
+ Privileges.SelectTable.allow(),
+ Privileges.UseSchema.allow(),
+ Privileges.UseCatalog.allow()));
+ // normal user can load the catalog but not the schema
+ Catalog catalogLoadByNormalUser =
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG);
+ assertEquals(CATALOG, catalogLoadByNormalUser.name());
+ TableCatalog tableCatalog =
client.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ Column[] columns = createColumns();
+
+ NameIdentifier nameIdentifier = NameIdentifier.of(SCHEMA, "table1");
+
+ tableCatalog.createTable(
+ nameIdentifier,
+ columns,
+ TABLE_COMMENT,
+ new HashMap<>(),
+ new Transform[] {Transforms.identity(columns[0].name())});
+ // normal user cannot create table
+ TableCatalog tableCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ assertThrows(
+ "Can not access metadata {" + CATALOG + "." + SCHEMA + "}.",
+ ForbiddenException.class,
+ () -> {
+ tableCatalogNormalUser.createTable(
+ NameIdentifier.of(SCHEMA, "table2"), createColumns(), "test2",
new HashMap<>());
+ });
+ }
+
+ @AfterAll
+ public void stopIntegrationTest() throws IOException, InterruptedException {
+
client.loadMetalake(METALAKE).loadCatalog(CATALOG).asSchemas().dropSchema(SCHEMA,
true);
+ super.stopIntegrationTest();
+ }
+
+ private Column[] createColumns() {
+ return new Column[] {Column.of("col1", Types.StringType.get())};
+ }
+
+ @Test
+ @Order(1)
+ public void testCreatePartition() {
+ TableCatalog tableCatalog =
client.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ Table table1 = tableCatalog.loadTable(NameIdentifier.of(SCHEMA, "table1"));
+
+ TableCatalog tableCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ SupportsPartitions supportsPartitions =
getTable1(tableCatalogNormalUser).supportPartitions();
+ assertThrows(
+ "Can not access metadata {" + CATALOG + "." + SCHEMA + "}.",
+ ForbiddenException.class,
+ () -> {
+ supportsPartitions.addPartition(genPartition());
+ });
+ SupportsPartitions supportsPartitionsByAdmin = table1.supportPartitions();
+ supportsPartitionsByAdmin.addPartition(genPartition());
+ }
+
+ private static Table getTable1(TableCatalog tableCatalogNormalUser) {
+ return tableCatalogNormalUser.loadTable(NameIdentifier.of(SCHEMA,
"table1"));
+ }
+
+ private static IdentityPartition genPartition() {
+ return Partitions.identity(
+ new String[][] {{"col1"}}, new Literal[]
{Literals.stringLiteral("1")});
+ }
+
+ @Test
+ @Order(2)
+ public void testListPartition() {
+ TableCatalog tableCatalog =
client.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ Table table1 = tableCatalog.loadTable(NameIdentifier.of(SCHEMA, "table1"));
+
+ TableCatalog tableCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ SupportsPartitions supportsPartitions =
+ tableCatalogNormalUser.loadTable(NameIdentifier.of(SCHEMA,
"table1")).supportPartitions();
+ MetadataObject metadataObject = MetadataObjects.of(null, CATALOG,
MetadataObject.Type.CATALOG);
+ client
+ .loadMetalake(METALAKE)
+ .revokePrivilegesFromRole(
+ role, metadataObject,
ImmutableSet.of(Privileges.SelectTable.allow()));
+ assertThrows(
+ "Can not access metadata {" + CATALOG + "." + SCHEMA + "}.",
+ ForbiddenException.class,
+ supportsPartitions::listPartitions);
+ SupportsPartitions supportsPartitionsByAdmin = table1.supportPartitions();
+ supportsPartitionsByAdmin.listPartitions();
+ client
+ .loadMetalake(METALAKE)
+ .grantPrivilegesToRole(
+ role, metadataObject,
ImmutableSet.of(Privileges.SelectTable.allow()));
+ }
+
+ @Test
+ @Order(3)
+ public void testLoadPartition() {
+ TableCatalog tableCatalog =
client.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ Table table1 = tableCatalog.loadTable(NameIdentifier.of(SCHEMA, "table1"));
+
+ TableCatalog tableCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ SupportsPartitions supportsPartitions =
+ tableCatalogNormalUser.loadTable(NameIdentifier.of(SCHEMA,
"table1")).supportPartitions();
+ MetadataObject metadataObject = MetadataObjects.of(null, CATALOG,
MetadataObject.Type.CATALOG);
+ client
+ .loadMetalake(METALAKE)
+ .revokePrivilegesFromRole(
+ role, metadataObject,
ImmutableSet.of(Privileges.SelectTable.allow()));
+ assertThrows(
+ "Can not access metadata {" + CATALOG + "." + SCHEMA + "}.",
+ ForbiddenException.class,
+ () -> {
+ supportsPartitions.getPartition("col1=1");
+ });
+ SupportsPartitions supportsPartitionsByAdmin = table1.supportPartitions();
+ supportsPartitionsByAdmin.getPartition("col1=1");
+ client
+ .loadMetalake(METALAKE)
+ .grantPrivilegesToRole(
+ role, metadataObject,
ImmutableSet.of(Privileges.SelectTable.allow()));
+ }
+
+ @Test
+ @Order(4)
+ public void testDropPartition() {
+ TableCatalog tableCatalog =
client.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ Table table1 = tableCatalog.loadTable(NameIdentifier.of(SCHEMA, "table1"));
+
+ TableCatalog tableCatalogNormalUser =
+
normalUserClient.loadMetalake(METALAKE).loadCatalog(CATALOG).asTableCatalog();
+ SupportsPartitions supportsPartitions =
+ tableCatalogNormalUser.loadTable(NameIdentifier.of(SCHEMA,
"table1")).supportPartitions();
+ assertThrows(
+ "Can not access metadata {" + CATALOG + "." + SCHEMA + "}.",
+ ForbiddenException.class,
+ () -> {
+ supportsPartitions.dropPartition("col1=1");
+ });
+ SupportsPartitions supportsPartitionsByAdmin = table1.supportPartitions();
+ supportsPartitionsByAdmin.dropPartition("col1=1");
+ }
+}
diff --git
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java
index 5d5017d7f1..47dd6cb654 100644
---
a/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java
+++
b/server-common/src/main/java/org/apache/gravitino/server/authorization/expression/AuthorizationExpressionConstants.java
@@ -40,6 +40,13 @@ public class AuthorizationExpressionConstants {
ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER ||
ANY_SELECT_TABLE || ANY_MODIFY_TABLE)
""";
+ public static final String alterTableAuthorizationExpression =
+ """
+ ANY(OWNER, METALAKE, CATALOG) ||
+ SCHEMA_OWNER_WITH_USE_CATALOG ||
+ ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER ||
ANY_MODIFY_TABLE)
+ """;
+
public static final String loadTopicsAuthorizationExpression =
"""
ANY(OWNER, METALAKE, CATALOG) ||
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/filter/GravitinoInterceptionService.java
b/server/src/main/java/org/apache/gravitino/server/web/filter/GravitinoInterceptionService.java
index dec93f882f..a43d4d4236 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/filter/GravitinoInterceptionService.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/filter/GravitinoInterceptionService.java
@@ -54,6 +54,7 @@ import org.apache.gravitino.server.web.rest.GroupOperations;
import org.apache.gravitino.server.web.rest.MetalakeOperations;
import org.apache.gravitino.server.web.rest.ModelOperations;
import org.apache.gravitino.server.web.rest.OwnerOperations;
+import org.apache.gravitino.server.web.rest.PartitionOperations;
import org.apache.gravitino.server.web.rest.PermissionOperations;
import org.apache.gravitino.server.web.rest.RoleOperations;
import org.apache.gravitino.server.web.rest.SchemaOperations;
@@ -93,7 +94,8 @@ public class GravitinoInterceptionService implements
InterceptionService {
PermissionOperations.class.getName(),
RoleOperations.class.getName(),
OwnerOperations.class.getName(),
- StatisticOperations.class.getName()));
+ StatisticOperations.class.getName(),
+ PartitionOperations.class.getName()));
}
@Override
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/PartitionOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/PartitionOperations.java
index ec7cfc3c40..b4bf0eeed4 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/PartitionOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/PartitionOperations.java
@@ -36,6 +36,8 @@ import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
+import org.apache.gravitino.Entity;
+import org.apache.gravitino.MetadataObject;
import org.apache.gravitino.NameIdentifier;
import org.apache.gravitino.catalog.PartitionDispatcher;
import org.apache.gravitino.dto.rel.partitions.PartitionDTO;
@@ -47,6 +49,9 @@ import org.apache.gravitino.dto.responses.PartitionResponse;
import org.apache.gravitino.dto.util.DTOConverters;
import org.apache.gravitino.metrics.MetricNames;
import org.apache.gravitino.rel.partitions.Partition;
+import
org.apache.gravitino.server.authorization.annotations.AuthorizationExpression;
+import
org.apache.gravitino.server.authorization.annotations.AuthorizationMetadata;
+import
org.apache.gravitino.server.authorization.expression.AuthorizationExpressionConstants;
import org.apache.gravitino.server.web.Utils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -67,11 +72,15 @@ public class PartitionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "list-partition-name." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "list-partition-name", absolute = true)
+ @AuthorizationExpression(
+ expression =
AuthorizationExpressionConstants.loadTableAuthorizationExpression,
+ accessMetadataType = MetadataObject.Type.TABLE)
public Response listPartitionNames(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("table") String table,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("table") @AuthorizationMetadata(type =
Entity.EntityType.TABLE) String table,
@QueryParam("details") @DefaultValue("false") boolean verbose) {
LOG.info(
"Received list partition {} request for table: {}.{}.{}.{}",
@@ -119,11 +128,15 @@ public class PartitionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "get-partition." + MetricNames.HTTP_PROCESS_DURATION, absolute
= true)
@ResponseMetered(name = "get-partition", absolute = true)
+ @AuthorizationExpression(
+ expression =
AuthorizationExpressionConstants.loadTableAuthorizationExpression,
+ accessMetadataType = MetadataObject.Type.TABLE)
public Response getPartition(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("table") String table,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("table") @AuthorizationMetadata(type =
Entity.EntityType.TABLE) String table,
@PathParam("partition") String partition) {
LOG.info(
"Received get partition request for partition[{}] of
table[{}.{}.{}.{}]",
@@ -157,11 +170,15 @@ public class PartitionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "add-partitions." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "add-partitions", absolute = true)
+ @AuthorizationExpression(
+ expression =
AuthorizationExpressionConstants.alterTableAuthorizationExpression,
+ accessMetadataType = MetadataObject.Type.TABLE)
public Response addPartitions(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("table") String table,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("table") @AuthorizationMetadata(type =
Entity.EntityType.TABLE) String table,
AddPartitionsRequest request) {
LOG.info(
"Received add {} partition(s) request for table {}.{}.{}.{} ",
@@ -197,11 +214,15 @@ public class PartitionOperations {
@Produces("application/vnd.gravitino.v1+json")
@Timed(name = "drop-partition." + MetricNames.HTTP_PROCESS_DURATION,
absolute = true)
@ResponseMetered(name = "drop-partition", absolute = true)
+ @AuthorizationExpression(
+ expression =
AuthorizationExpressionConstants.alterTableAuthorizationExpression,
+ accessMetadataType = MetadataObject.Type.TABLE)
public Response dropPartition(
- @PathParam("metalake") String metalake,
- @PathParam("catalog") String catalog,
- @PathParam("schema") String schema,
- @PathParam("table") String table,
+ @PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
+ String metalake,
+ @PathParam("catalog") @AuthorizationMetadata(type =
Entity.EntityType.CATALOG) String catalog,
+ @PathParam("schema") @AuthorizationMetadata(type =
Entity.EntityType.SCHEMA) String schema,
+ @PathParam("table") @AuthorizationMetadata(type =
Entity.EntityType.TABLE) String table,
@PathParam("partition") String partition,
@QueryParam("purge") @DefaultValue("false") boolean purge) {
LOG.info(
diff --git
a/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
b/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
index ec47cf8e64..31ef89544e 100644
---
a/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
+++
b/server/src/main/java/org/apache/gravitino/server/web/rest/TableOperations.java
@@ -164,10 +164,7 @@ public class TableOperations {
@Timed(name = "load-table." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "load-table", absolute = true)
@AuthorizationExpression(
- expression =
- "ANY(OWNER, METALAKE, CATALOG) || "
- + "SCHEMA_OWNER_WITH_USE_CATALOG || "
- + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER ||
ANY_SELECT_TABLE|| ANY_MODIFY_TABLE)",
+ expression =
AuthorizationExpressionConstants.loadTableAuthorizationExpression,
accessMetadataType = MetadataObject.Type.TABLE)
public Response loadTable(
@PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)
@@ -198,12 +195,7 @@ public class TableOperations {
@Timed(name = "alter-table." + MetricNames.HTTP_PROCESS_DURATION, absolute =
true)
@ResponseMetered(name = "alter-table", absolute = true)
@AuthorizationExpression(
- expression =
- """
- ANY(OWNER, METALAKE, CATALOG) ||
- SCHEMA_OWNER_WITH_USE_CATALOG ||
- ANY_USE_CATALOG && ANY_USE_SCHEMA && (TABLE::OWNER ||
ANY_MODIFY_TABLE)
- """,
+ expression =
AuthorizationExpressionConstants.alterTableAuthorizationExpression,
accessMetadataType = MetadataObject.Type.TABLE)
public Response alterTable(
@PathParam("metalake") @AuthorizationMetadata(type =
Entity.EntityType.METALAKE)