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 b03f4229ab [#9747][followup]feat(authz): Add view auth to 
IcebergRESTAuthInterceptionService (#10020)
b03f4229ab is described below

commit b03f4229ab3a6bce1ba7d7df2b9caaf272a7804f
Author: Bharath Krishna <[email protected]>
AuthorDate: Mon Feb 23 11:32:17 2026 +0530

    [#9747][followup]feat(authz): Add view auth to 
IcebergRESTAuthInterceptionService (#10020)
    
    ### What changes were proposed in this pull request?
    
    Add view authz classes to IcebergRESTAuthInterceptionService.
    Also update the ICEBERG_LOAD_VIEW_AUTHORIZATION_EXPRESSION to have
    ANY_SELECT_TABLE / ANY_MODIFY_TABLE / ANY_CREATE_TABLE
    
    ### Why are the changes needed?
    
    This is to enable authorization for views
    
    Fix: #9747
    
    ### Does this PR introduce _any_ user-facing change?
    
    No
    
    ### How was this patch tested?
    
    Update integration tests
---
 .../iceberg/service/rest/IcebergViewOperations.java          | 10 ++--------
 .../IcebergMetadataAuthorizationMethodInterceptor.java       | 10 ++++++++--
 .../web/filter/IcebergRESTAuthInterceptionService.java       |  6 +++++-
 .../iceberg/integration/test/IcebergViewAuthorizationIT.java | 10 +++++-----
 .../expression/AuthorizationExpressionConstants.java         | 12 ++++++++++++
 5 files changed, 32 insertions(+), 16 deletions(-)

diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergViewOperations.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergViewOperations.java
index 5a1adc9f53..86e3d66b5a 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergViewOperations.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/iceberg/service/rest/IcebergViewOperations.java
@@ -162,10 +162,7 @@ public class IcebergViewOperations {
   @Timed(name = "load-view." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
   @ResponseMetered(name = "load-view", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (VIEW::OWNER || 
ANY_SELECT_VIEW || ANY_CREATE_VIEW)",
+      expression = 
AuthorizationExpressionConstants.ICEBERG_LOAD_VIEW_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.VIEW)
   public Response loadView(
       @AuthorizationMetadata(type = Entity.EntityType.CATALOG) 
@PathParam("prefix") String prefix,
@@ -283,10 +280,7 @@ public class IcebergViewOperations {
   @Timed(name = "view-exists." + MetricNames.HTTP_PROCESS_DURATION, absolute = 
true)
   @ResponseMetered(name = "view-exists", absolute = true)
   @AuthorizationExpression(
-      expression =
-          "ANY(OWNER, METALAKE, CATALOG) || "
-              + "SCHEMA_OWNER_WITH_USE_CATALOG || "
-              + "ANY_USE_CATALOG && ANY_USE_SCHEMA && (VIEW::OWNER || 
ANY_SELECT_VIEW || ANY_CREATE_VIEW)",
+      expression = 
AuthorizationExpressionConstants.ICEBERG_LOAD_VIEW_AUTHORIZATION_EXPRESSION,
       accessMetadataType = MetadataObject.Type.VIEW)
   public Response viewExists(
       @AuthorizationMetadata(type = Entity.EntityType.CATALOG) 
@PathParam("prefix") String prefix,
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/IcebergMetadataAuthorizationMethodInterceptor.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/IcebergMetadataAuthorizationMethodInterceptor.java
index 3c751a5065..643e8eb532 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/IcebergMetadataAuthorizationMethodInterceptor.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/IcebergMetadataAuthorizationMethodInterceptor.java
@@ -81,10 +81,16 @@ public class IcebergMetadataAuthorizationMethodInterceptor
                   metalakeName, catalog, schema, 
RESTUtil.decodeString(value)));
           break;
         case VIEW:
+          String decodedViewName = RESTUtil.decodeString(value);
           nameIdentifierMap.put(
               EntityType.VIEW,
-              NameIdentifierUtil.ofView(
-                  metalakeName, catalog, schema, 
RESTUtil.decodeString(value)));
+              NameIdentifierUtil.ofView(metalakeName, catalog, schema, 
decodedViewName));
+          // Also register as TABLE so ANY_SELECT_TABLE in
+          // ICEBERG_LOAD_VIEW_AUTHORIZATION_EXPRESSION
+          // matches when Spark probes viewExists(tableName) during table 
resolution.
+          nameIdentifierMap.put(
+              EntityType.TABLE,
+              NameIdentifierUtil.ofTable(metalakeName, catalog, schema, 
decodedViewName));
           break;
         default:
           break;
diff --git 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/IcebergRESTAuthInterceptionService.java
 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/IcebergRESTAuthInterceptionService.java
index e1f4f6005b..b7ea32d820 100644
--- 
a/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/IcebergRESTAuthInterceptionService.java
+++ 
b/iceberg/iceberg-rest-server/src/main/java/org/apache/gravitino/server/web/filter/IcebergRESTAuthInterceptionService.java
@@ -26,6 +26,8 @@ import org.aopalliance.intercept.MethodInterceptor;
 import org.apache.gravitino.iceberg.service.rest.IcebergNamespaceOperations;
 import org.apache.gravitino.iceberg.service.rest.IcebergTableOperations;
 import org.apache.gravitino.iceberg.service.rest.IcebergTableRenameOperations;
+import org.apache.gravitino.iceberg.service.rest.IcebergViewOperations;
+import org.apache.gravitino.iceberg.service.rest.IcebergViewRenameOperations;
 import org.glassfish.hk2.api.Filter;
 
 /**
@@ -41,7 +43,9 @@ public class IcebergRESTAuthInterceptionService extends 
BaseInterceptionService
         ImmutableSet.of(
             IcebergTableOperations.class.getName(),
             IcebergTableRenameOperations.class.getName(),
-            IcebergNamespaceOperations.class.getName()));
+            IcebergNamespaceOperations.class.getName(),
+            IcebergViewOperations.class.getName(),
+            IcebergViewRenameOperations.class.getName()));
   }
 
   @Override
diff --git 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergViewAuthorizationIT.java
 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergViewAuthorizationIT.java
index 73532b49b3..827647020e 100644
--- 
a/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergViewAuthorizationIT.java
+++ 
b/iceberg/iceberg-rest-server/src/test/java/org/apache/gravitino/iceberg/integration/test/IcebergViewAuthorizationIT.java
@@ -70,8 +70,8 @@ public class IcebergViewAuthorizationIT extends 
IcebergAuthorizationIT {
         MetadataObjects.of(
             Arrays.asList(GRAVITINO_CATALOG_NAME, SCHEMA_NAME), 
MetadataObject.Type.SCHEMA);
     metalakeClientWithAllPrivilege.setOwner(schemaObject, SUPER_USER, 
Owner.Type.USER);
-    clearViews();
     grantUseSchemaRole(SCHEMA_NAME);
+    clearViews();
     sql("USE %s;", SPARK_CATALOG_NAME);
     sql("USE %s;", SCHEMA_NAME);
   }
@@ -309,10 +309,8 @@ public class IcebergViewAuthorizationIT extends 
IcebergAuthorizationIT {
     Assertions.assertTrue(owner.isPresent());
     Assertions.assertEquals(NORMAL_USER, owner.get().name());
 
-    // Clean up
-    revokeRole(createViewRole);
-    // NORMAL_USER still owns the renamed view, so they can drop it
     sql("DROP VIEW IF EXISTS %s.%s", destSchema, viewName + "_renamed");
+    revokeRole(createViewRole);
     catalogClientWithAllPrivilege.asSchemas().dropSchema(destSchema, false);
   }
 
@@ -463,7 +461,9 @@ public class IcebergViewAuthorizationIT extends 
IcebergAuthorizationIT {
     securableObjects.add(catalogObject);
     SecurableObject schemaObject =
         SecurableObjects.ofSchema(
-            catalogObject, schema, 
ImmutableList.of(Privileges.CreateView.allow()));
+            catalogObject,
+            schema,
+            ImmutableList.of(Privileges.UseSchema.allow(), 
Privileges.CreateView.allow()));
     securableObjects.add(schemaObject);
     metalakeClientWithAllPrivilege.createRole(roleName, new HashMap<>(), 
securableObjects);
     
metalakeClientWithAllPrivilege.grantRolesToUser(ImmutableList.of(roleName), 
NORMAL_USER);
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 188b24683c..d145b2c9b8 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
@@ -82,6 +82,18 @@ public class AuthorizationExpressionConstants {
                   ANY_USE_CATALOG && ANY_USE_SCHEMA && (VIEW::OWNER || 
ANY_SELECT_VIEW || ANY_CREATE_VIEW)
                   """;
 
+  //  Adding ANY_SELECT_TABLE / ANY_MODIFY_TABLE / ANY_CREATE_TABLE here 
because Spark probes
+  //  viewExists(tableName) when resolving any relation. Without table 
privileges in the
+  //  expression, users who only hold table grants get a spurious 403 on the 
HEAD /views/{name}
+  //  probe, blocking legitimate table reads.
+  public static final String ICEBERG_LOAD_VIEW_AUTHORIZATION_EXPRESSION =
+      """
+                  ANY(OWNER, METALAKE, CATALOG) ||
+                  SCHEMA_OWNER_WITH_USE_CATALOG ||
+                  ANY_USE_CATALOG && ANY_USE_SCHEMA && (VIEW::OWNER || 
ANY_SELECT_VIEW || ANY_CREATE_VIEW
+                      || ANY_SELECT_TABLE || ANY_MODIFY_TABLE || 
ANY_CREATE_TABLE)
+                  """;
+
   public static final String FILTER_TABLE_AUTHORIZATION_EXPRESSION =
       """
                   ANY(OWNER, METALAKE, CATALOG, SCHEMA, TABLE) ||

Reply via email to