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 {