This is an automated email from the ASF dual-hosted git repository.

lzljs3620320 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/paimon.git


The following commit(s) were added to refs/heads/master by this push:
     new 2439245fde [rest] Adjust auth table for row level access control 
(#5634)
2439245fde is described below

commit 2439245fde3a392d16ced20b5377b40fd90394a1
Author: Jingsong Lee <[email protected]>
AuthorDate: Tue May 20 17:29:32 2025 +0800

    [rest] Adjust auth table for row level access control (#5634)
---
 docs/static/rest-catalog-open-api.yaml             |  9 +++++++-
 .../main/java/org/apache/paimon/rest/RESTApi.java  | 21 ++++++++++-------
 .../rest/requests/AuthTableQueryRequest.java       | 18 +++++----------
 .../AuthTableQueryResponse.java}                   | 24 ++++++--------------
 .../org/apache/paimon/catalog/AbstractCatalog.java |  4 +++-
 .../java/org/apache/paimon/catalog/Catalog.java    |  8 +++----
 .../org/apache/paimon/catalog/DelegateCatalog.java |  4 ++--
 .../java/org/apache/paimon/rest/RESTCatalog.java   |  4 ++--
 .../apache/paimon/table/CatalogEnvironment.java    |  7 +++---
 .../paimon/table/source/AbstractDataTableScan.java | 17 ++------------
 .../apache/paimon/table/source/TableQueryAuth.java |  4 +++-
 .../org/apache/paimon/rest/RESTCatalogServer.java  | 26 +++++++++++++---------
 12 files changed, 69 insertions(+), 77 deletions(-)

diff --git a/docs/static/rest-catalog-open-api.yaml 
b/docs/static/rest-catalog-open-api.yaml
index 3cab0939c6..7e99e46392 100644
--- a/docs/static/rest-catalog-open-api.yaml
+++ b/docs/static/rest-catalog-open-api.yaml
@@ -663,7 +663,11 @@ paths:
               $ref: '#/components/schemas/AuthTableQueryRequest'
       responses:
         "200":
-          description: Success, no content
+          description: OK
+          content:
+            application/json:
+              schema:
+                $ref: '#/components/schemas/AuthTableQueryResponse'
         "401":
           $ref: '#/components/responses/UnauthorizedErrorResponse'
         "403":
@@ -2431,6 +2435,9 @@ components:
           type: array
           items:
             type: string
+    AuthTableQueryResponse:
+      type: object
+      properties:
         filter:
           type: array
           items:
diff --git a/paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java 
b/paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java
index ed6a90113f..48d197d881 100644
--- a/paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java
+++ b/paimon-api/src/main/java/org/apache/paimon/rest/RESTApi.java
@@ -48,6 +48,7 @@ import 
org.apache.paimon.rest.requests.MarkDonePartitionsRequest;
 import org.apache.paimon.rest.requests.RenameTableRequest;
 import org.apache.paimon.rest.requests.RollbackTableRequest;
 import org.apache.paimon.rest.responses.AlterDatabaseResponse;
+import org.apache.paimon.rest.responses.AuthTableQueryResponse;
 import org.apache.paimon.rest.responses.CommitTableResponse;
 import org.apache.paimon.rest.responses.ConfigResponse;
 import org.apache.paimon.rest.responses.GetDatabaseResponse;
@@ -571,18 +572,22 @@ public class RESTApi {
      * Auth table query.
      *
      * @param identifier database name and table name.
-     * @param select select columns
-     * @param filter pushed filter
+     * @param select select columns, null if select all
+     * @return additional filter for row level access control
      * @throws NoSuchResourceException Exception thrown on HTTP 404 means the 
table not exists
      * @throws ForbiddenException Exception thrown on HTTP 403 means don't 
have the permission for
      *     this table
      */
-    public void authTableQuery(Identifier identifier, List<String> select, 
List<String> filter) {
-        AuthTableQueryRequest request = new AuthTableQueryRequest(select, 
filter);
-        client.post(
-                resourcePaths.authTable(identifier.getDatabaseName(), 
identifier.getObjectName()),
-                request,
-                restAuthFunction);
+    public List<String> authTableQuery(Identifier identifier, @Nullable 
List<String> select) {
+        AuthTableQueryRequest request = new AuthTableQueryRequest(select);
+        AuthTableQueryResponse response =
+                client.post(
+                        resourcePaths.authTable(
+                                identifier.getDatabaseName(), 
identifier.getObjectName()),
+                        request,
+                        AuthTableQueryResponse.class,
+                        restAuthFunction);
+        return response.filter();
     }
 
     /**
diff --git 
a/paimon-api/src/main/java/org/apache/paimon/rest/requests/AuthTableQueryRequest.java
 
b/paimon-api/src/main/java/org/apache/paimon/rest/requests/AuthTableQueryRequest.java
index c1869a6b52..2e05821835 100644
--- 
a/paimon-api/src/main/java/org/apache/paimon/rest/requests/AuthTableQueryRequest.java
+++ 
b/paimon-api/src/main/java/org/apache/paimon/rest/requests/AuthTableQueryRequest.java
@@ -25,6 +25,8 @@ import 
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGet
 import 
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonIgnoreProperties;
 import 
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty;
 
+import javax.annotation.Nullable;
+
 import java.util.List;
 
 /** Request for auth table query. */
@@ -32,29 +34,19 @@ import java.util.List;
 public class AuthTableQueryRequest implements RESTRequest {
 
     private static final String FIELD_SELECT = "select";
-    private static final String FIELD_FILTER = "filter";
 
     @JsonProperty(FIELD_SELECT)
+    @Nullable
     private final List<String> select;
 
-    @JsonProperty(FIELD_FILTER)
-    private final List<String> filter;
-
     @JsonCreator
-    public AuthTableQueryRequest(
-            @JsonProperty(FIELD_SELECT) List<String> select,
-            @JsonProperty(FIELD_FILTER) List<String> filter) {
+    public AuthTableQueryRequest(@JsonProperty(FIELD_SELECT) @Nullable 
List<String> select) {
         this.select = select;
-        this.filter = filter;
     }
 
     @JsonGetter(FIELD_SELECT)
+    @Nullable
     public List<String> select() {
         return select;
     }
-
-    @JsonGetter(FIELD_FILTER)
-    public List<String> filter() {
-        return filter;
-    }
 }
diff --git 
a/paimon-api/src/main/java/org/apache/paimon/rest/requests/AuthTableQueryRequest.java
 
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/AuthTableQueryResponse.java
similarity index 71%
copy from 
paimon-api/src/main/java/org/apache/paimon/rest/requests/AuthTableQueryRequest.java
copy to 
paimon-api/src/main/java/org/apache/paimon/rest/responses/AuthTableQueryResponse.java
index c1869a6b52..0f833b0330 100644
--- 
a/paimon-api/src/main/java/org/apache/paimon/rest/requests/AuthTableQueryRequest.java
+++ 
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/AuthTableQueryResponse.java
@@ -16,43 +16,33 @@
  * limitations under the License.
  */
 
-package org.apache.paimon.rest.requests;
+package org.apache.paimon.rest.responses;
 
-import org.apache.paimon.rest.RESTRequest;
+import org.apache.paimon.rest.RESTResponse;
 
 import 
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonCreator;
 import 
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonGetter;
 import 
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import 
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonInclude;
 import 
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty;
 
 import java.util.List;
 
-/** Request for auth table query. */
+/** Response for auth table query. */
 @JsonIgnoreProperties(ignoreUnknown = true)
-public class AuthTableQueryRequest implements RESTRequest {
+public class AuthTableQueryResponse implements RESTResponse {
 
-    private static final String FIELD_SELECT = "select";
     private static final String FIELD_FILTER = "filter";
 
-    @JsonProperty(FIELD_SELECT)
-    private final List<String> select;
-
+    @JsonInclude(JsonInclude.Include.NON_NULL)
     @JsonProperty(FIELD_FILTER)
     private final List<String> filter;
 
     @JsonCreator
-    public AuthTableQueryRequest(
-            @JsonProperty(FIELD_SELECT) List<String> select,
-            @JsonProperty(FIELD_FILTER) List<String> filter) {
-        this.select = select;
+    public AuthTableQueryResponse(@JsonProperty(FIELD_FILTER) List<String> 
filter) {
         this.filter = filter;
     }
 
-    @JsonGetter(FIELD_SELECT)
-    public List<String> select() {
-        return select;
-    }
-
     @JsonGetter(FIELD_FILTER)
     public List<String> filter() {
         return filter;
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java 
b/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
index 956efc0440..c292fd69b8 100644
--- a/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/catalog/AbstractCatalog.java
@@ -503,7 +503,9 @@ public abstract class AbstractCatalog implements Catalog {
     }
 
     @Override
-    public void authTableQuery(Identifier identifier, List<String> select, 
List<String> filter) {}
+    public List<String> authTableQuery(Identifier identifier, List<String> 
select) {
+        throw new UnsupportedOperationException();
+    }
 
     @Override
     public void createPartitions(Identifier identifier, List<Map<String, 
String>> partitions)
diff --git a/paimon-core/src/main/java/org/apache/paimon/catalog/Catalog.java 
b/paimon-core/src/main/java/org/apache/paimon/catalog/Catalog.java
index 792b1a5879..9fc8fc0065 100644
--- a/paimon-core/src/main/java/org/apache/paimon/catalog/Catalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/catalog/Catalog.java
@@ -791,14 +791,14 @@ public interface Catalog extends AutoCloseable {
     // ==================== Table Auth ==========================
 
     /**
-     * Auth table query select and filter.
+     * Auth table query select and get the filter for row level access control.
      *
      * @param identifier path of the table to alter partitions
-     * @param select selected fields
-     * @param filter query filters
+     * @param select selected fields, null if select all
+     * @return additional filter for row level access control
      * @throws TableNotExistException if the table does not exist
      */
-    void authTableQuery(Identifier identifier, List<String> select, 
List<String> filter)
+    List<String> authTableQuery(Identifier identifier, @Nullable List<String> 
select)
             throws TableNotExistException;
 
     // ==================== Catalog Information ==========================
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/catalog/DelegateCatalog.java 
b/paimon-core/src/main/java/org/apache/paimon/catalog/DelegateCatalog.java
index 42dfd400b2..d494dc56ba 100644
--- a/paimon-core/src/main/java/org/apache/paimon/catalog/DelegateCatalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/catalog/DelegateCatalog.java
@@ -337,9 +337,9 @@ public abstract class DelegateCatalog implements Catalog {
     }
 
     @Override
-    public void authTableQuery(Identifier identifier, List<String> select, 
List<String> filter)
+    public List<String> authTableQuery(Identifier identifier, @Nullable 
List<String> select)
             throws TableNotExistException {
-        wrapped.authTableQuery(identifier, select, filter);
+        return wrapped.authTableQuery(identifier, select);
     }
 
     @Override
diff --git a/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java 
b/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java
index ed69638304..e006828045 100644
--- a/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java
+++ b/paimon-core/src/main/java/org/apache/paimon/rest/RESTCatalog.java
@@ -464,11 +464,11 @@ public class RESTCatalog implements Catalog {
     }
 
     @Override
-    public void authTableQuery(Identifier identifier, List<String> select, 
List<String> filter)
+    public List<String> authTableQuery(Identifier identifier, @Nullable 
List<String> select)
             throws TableNotExistException {
         checkNotSystemTable(identifier, "authTable");
         try {
-            api.authTableQuery(identifier, select, filter);
+            return api.authTableQuery(identifier, select);
         } catch (NoSuchResourceException e) {
             throw new TableNotExistException(identifier);
         } catch (ForbiddenException e) {
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/CatalogEnvironment.java 
b/paimon-core/src/main/java/org/apache/paimon/table/CatalogEnvironment.java
index 1ed133bf1f..3ea579e120 100644
--- a/paimon-core/src/main/java/org/apache/paimon/table/CatalogEnvironment.java
+++ b/paimon-core/src/main/java/org/apache/paimon/table/CatalogEnvironment.java
@@ -35,6 +35,7 @@ import org.apache.paimon.utils.SnapshotManager;
 import javax.annotation.Nullable;
 
 import java.io.Serializable;
+import java.util.Collections;
 
 /** Catalog environment in table which contains log factory, metastore client 
factory. */
 public class CatalogEnvironment implements Serializable {
@@ -126,11 +127,11 @@ public class CatalogEnvironment implements Serializable {
 
     public TableQueryAuth tableQueryAuth(CoreOptions options) {
         if (!options.queryAuthEnabled() || catalogLoader == null) {
-            return (select, filter) -> {};
+            return select -> Collections.emptyList();
         }
-        return (select, filter) -> {
+        return select -> {
             try (Catalog catalog = catalogLoader.load()) {
-                catalog.authTableQuery(identifier, select, filter);
+                return catalog.authTableQuery(identifier, select);
             } catch (Exception e) {
                 throw new RuntimeException(e);
             }
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractDataTableScan.java
 
b/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractDataTableScan.java
index 2424878951..42d4562608 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractDataTableScan.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/source/AbstractDataTableScan.java
@@ -27,7 +27,6 @@ import org.apache.paimon.data.BinaryRow;
 import org.apache.paimon.metrics.MetricRegistry;
 import org.apache.paimon.operation.FileStoreScan;
 import org.apache.paimon.options.Options;
-import org.apache.paimon.predicate.LeafPredicate;
 import org.apache.paimon.predicate.Predicate;
 import org.apache.paimon.schema.TableSchema;
 import org.apache.paimon.table.source.snapshot.CompactedStartingScanner;
@@ -62,7 +61,6 @@ import org.slf4j.LoggerFactory;
 
 import javax.annotation.Nullable;
 
-import java.util.ArrayList;
 import java.util.List;
 import java.util.Map;
 import java.util.Optional;
@@ -70,7 +68,6 @@ import java.util.TimeZone;
 
 import static org.apache.paimon.CoreOptions.FULL_COMPACTION_DELTA_COMMITS;
 import static org.apache.paimon.CoreOptions.IncrementalBetweenScanMode.DIFF;
-import static org.apache.paimon.predicate.PredicateBuilder.splitAnd;
 import static org.apache.paimon.utils.Preconditions.checkArgument;
 import static org.apache.paimon.utils.Preconditions.checkNotNull;
 
@@ -155,18 +152,8 @@ abstract class AbstractDataTableScan implements 
DataTableScan {
         if (!options.queryAuthEnabled()) {
             return;
         }
-
-        List<String> projection = readType == null ? schema.fieldNames() : 
readType.getFieldNames();
-        List<String> filter = new ArrayList<>();
-        if (predicate != null) {
-            List<Predicate> predicates = splitAnd(predicate);
-            for (Predicate predicate : predicates) {
-                if (predicate instanceof LeafPredicate) {
-                    filter.add(predicate.toString());
-                }
-            }
-        }
-        queryAuth.auth(projection, filter);
+        queryAuth.auth(readType == null ? null : readType.getFieldNames());
+        // TODO add support for row level access control
     }
 
     @Override
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/table/source/TableQueryAuth.java 
b/paimon-core/src/main/java/org/apache/paimon/table/source/TableQueryAuth.java
index 7f1e259b7e..96a0dfb3a5 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/table/source/TableQueryAuth.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/table/source/TableQueryAuth.java
@@ -18,10 +18,12 @@
 
 package org.apache.paimon.table.source;
 
+import javax.annotation.Nullable;
+
 import java.util.List;
 
 /** Table query auth. */
 public interface TableQueryAuth {
 
-    void auth(List<String> select, List<String> filter);
+    List<String> auth(@Nullable List<String> select);
 }
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogServer.java 
b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogServer.java
index 69a512f624..01ce6c391a 100644
--- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogServer.java
+++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTCatalogServer.java
@@ -58,6 +58,7 @@ import 
org.apache.paimon.rest.requests.MarkDonePartitionsRequest;
 import org.apache.paimon.rest.requests.RenameTableRequest;
 import org.apache.paimon.rest.requests.RollbackTableRequest;
 import org.apache.paimon.rest.responses.AlterDatabaseResponse;
+import org.apache.paimon.rest.responses.AuthTableQueryResponse;
 import org.apache.paimon.rest.responses.CommitTableResponse;
 import org.apache.paimon.rest.responses.ConfigResponse;
 import org.apache.paimon.rest.responses.ErrorResponse;
@@ -677,21 +678,26 @@ public class RESTCatalogServer {
         if (noPermissionTables.contains(identifier.getFullName())) {
             throw new Catalog.TableNoPermissionException(identifier);
         }
-        if (!tableMetadataStore.containsKey(identifier.getFullName())) {
+
+        TableMetadata metadata = 
tableMetadataStore.get(identifier.getFullName());
+        if (metadata == null) {
             throw new Catalog.TableNotExistException(identifier);
         }
         List<String> columnAuth = 
columnAuthHandler.get(identifier.getFullName());
         if (columnAuth != null) {
-            requestBody
-                    .select()
-                    .forEach(
-                            column -> {
-                                if (!columnAuth.contains(column)) {
-                                    throw new 
Catalog.TableNoPermissionException(identifier);
-                                }
-                            });
+            List<String> select = requestBody.select();
+            if (select == null) {
+                select = metadata.schema().fieldNames();
+            }
+            select.forEach(
+                    column -> {
+                        if (!columnAuth.contains(column)) {
+                            throw new 
Catalog.TableNoPermissionException(identifier);
+                        }
+                    });
         }
-        return new MockResponse().setResponseCode(200);
+        AuthTableQueryResponse response = new 
AuthTableQueryResponse(Collections.emptyList());
+        return mockResponse(response, 200);
     }
 
     private MockResponse commitTableHandle(Identifier identifier, String data) 
throws Exception {

Reply via email to