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 4ccd244519 [rest] add column masking rules in authTableQuery response
(#7024)
4ccd244519 is described below
commit 4ccd24451913da5417d69862a65cef84906ca816
Author: Jiajia Li <[email protected]>
AuthorDate: Tue Jan 13 19:01:15 2026 +0800
[rest] add column masking rules in authTableQuery response (#7024)
---
docs/static/rest-catalog-open-api.yaml | 5 ++
.../main/java/org/apache/paimon/rest/RESTApi.java | 18 ++++---
.../rest/responses/AuthTableQueryResponse.java | 16 ++++++-
.../org/apache/paimon/catalog/AbstractCatalog.java | 2 +-
.../java/org/apache/paimon/catalog/Catalog.java | 7 +--
.../org/apache/paimon/catalog/DelegateCatalog.java | 2 +-
.../paimon/catalog/TableQueryAuthResult.java | 53 ++++++++++++++++++++
.../java/org/apache/paimon/rest/RESTCatalog.java | 56 +++++++++++++++++++++-
.../apache/paimon/table/CatalogEnvironment.java | 4 +-
.../apache/paimon/table/source/TableQueryAuth.java | 4 +-
.../apache/paimon/rest/MockRESTCatalogTest.java | 46 ++++++++++++++++++
.../org/apache/paimon/rest/MockRESTMessage.java | 19 ++++++++
.../org/apache/paimon/rest/RESTApiJsonTest.java | 11 +++++
.../org/apache/paimon/rest/RESTCatalogServer.java | 14 +++++-
14 files changed, 234 insertions(+), 23 deletions(-)
diff --git a/docs/static/rest-catalog-open-api.yaml
b/docs/static/rest-catalog-open-api.yaml
index fcf5c53d11..88e7396514 100644
--- a/docs/static/rest-catalog-open-api.yaml
+++ b/docs/static/rest-catalog-open-api.yaml
@@ -2923,6 +2923,11 @@ components:
type: array
items:
type: string
+ columnMasking:
+ type: object
+ description: Column masking rules as a map from column name to
transform entry JSON string.
+ additionalProperties:
+ type: string
AlterDatabaseRequest:
type: object
properties:
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 a6bf162d55..99266d1dba 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
@@ -672,21 +672,19 @@ public class RESTApi {
*
* @param identifier database name and table name.
* @param select select columns, null if select all
- * @return additional filter for row level access control
+ * @return additional filter for row level access control and column
masking rules
* @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 List<String> authTableQuery(Identifier identifier, @Nullable
List<String> select) {
+ public AuthTableQueryResponse 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();
+ return client.post(
+ resourcePaths.authTable(identifier.getDatabaseName(),
identifier.getObjectName()),
+ request,
+ AuthTableQueryResponse.class,
+ restAuthFunction);
}
/**
diff --git
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/AuthTableQueryResponse.java
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/AuthTableQueryResponse.java
index 0f833b0330..3aab9ea14e 100644
---
a/paimon-api/src/main/java/org/apache/paimon/rest/responses/AuthTableQueryResponse.java
+++
b/paimon-api/src/main/java/org/apache/paimon/rest/responses/AuthTableQueryResponse.java
@@ -27,24 +27,38 @@ import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonInc
import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.annotation.JsonProperty;
import java.util.List;
+import java.util.Map;
/** Response for auth table query. */
@JsonIgnoreProperties(ignoreUnknown = true)
public class AuthTableQueryResponse implements RESTResponse {
private static final String FIELD_FILTER = "filter";
+ private static final String FIELD_COLUMN_MASKING = "columnMasking";
@JsonInclude(JsonInclude.Include.NON_NULL)
@JsonProperty(FIELD_FILTER)
private final List<String> filter;
@JsonCreator
- public AuthTableQueryResponse(@JsonProperty(FIELD_FILTER) List<String>
filter) {
+ public AuthTableQueryResponse(
+ @JsonProperty(FIELD_FILTER) List<String> filter,
+ @JsonProperty(FIELD_COLUMN_MASKING) Map<String, String>
columnMasking) {
this.filter = filter;
+ this.columnMasking = columnMasking;
}
@JsonGetter(FIELD_FILTER)
public List<String> filter() {
return filter;
}
+
+ @JsonInclude(JsonInclude.Include.NON_NULL)
+ @JsonProperty(FIELD_COLUMN_MASKING)
+ private final Map<String, String> columnMasking;
+
+ @JsonGetter(FIELD_COLUMN_MASKING)
+ public Map<String, String> columnMasking() {
+ return columnMasking;
+ }
}
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 045710356b..5d6c102208 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
@@ -577,7 +577,7 @@ public abstract class AbstractCatalog implements Catalog {
}
@Override
- public List<String> authTableQuery(Identifier identifier, List<String>
select) {
+ public TableQueryAuthResult authTableQuery(Identifier identifier,
List<String> select) {
throw new UnsupportedOperationException();
}
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 74e35dde3d..f4e7913c24 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
@@ -1032,14 +1032,15 @@ public interface Catalog extends AutoCloseable {
// ==================== Table Auth ==========================
/**
- * Auth table query select and get the filter for row level access control.
+ * Auth table query select and get the filter for row level access control
and column masking
+ * rules.
*
* @param identifier path of the table to alter partitions
* @param select selected fields, null if select all
- * @return additional filter for row level access control
+ * @return additional filter for row level access control and column
masking rules
* @throws TableNotExistException if the table does not exist
*/
- List<String> authTableQuery(Identifier identifier, @Nullable List<String>
select)
+ TableQueryAuthResult 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 5e286191de..b92cd63d94 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
@@ -409,7 +409,7 @@ public abstract class DelegateCatalog implements Catalog {
}
@Override
- public List<String> authTableQuery(Identifier identifier, @Nullable
List<String> select)
+ public TableQueryAuthResult authTableQuery(Identifier identifier,
@Nullable List<String> select)
throws TableNotExistException {
return wrapped.authTableQuery(identifier, select);
}
diff --git
a/paimon-core/src/main/java/org/apache/paimon/catalog/TableQueryAuthResult.java
b/paimon-core/src/main/java/org/apache/paimon/catalog/TableQueryAuthResult.java
new file mode 100644
index 0000000000..dcc94031a8
--- /dev/null
+++
b/paimon-core/src/main/java/org/apache/paimon/catalog/TableQueryAuthResult.java
@@ -0,0 +1,53 @@
+/*
+ * 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.paimon.catalog;
+
+import org.apache.paimon.predicate.Predicate;
+import org.apache.paimon.predicate.Transform;
+
+import javax.annotation.Nullable;
+
+import java.util.Collections;
+import java.util.Map;
+
+/** Auth result for table query, including row level filter and optional
column masking rules. */
+public class TableQueryAuthResult {
+
+ @Nullable private final Predicate rowFilter;
+ private final Map<String, Transform> columnMasking;
+
+ public TableQueryAuthResult(
+ @Nullable Predicate rowFilter, Map<String, Transform>
columnMasking) {
+ this.rowFilter = rowFilter;
+ this.columnMasking = columnMasking == null ? Collections.emptyMap() :
columnMasking;
+ }
+
+ public static TableQueryAuthResult empty() {
+ return new TableQueryAuthResult(null, Collections.emptyMap());
+ }
+
+ @Nullable
+ public Predicate rowFilter() {
+ return rowFilter;
+ }
+
+ public Map<String, Transform> columnMasking() {
+ return columnMasking;
+ }
+}
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 d8254bb58f..c41810c0fd 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
@@ -30,6 +30,7 @@ import org.apache.paimon.catalog.Database;
import org.apache.paimon.catalog.Identifier;
import org.apache.paimon.catalog.PropertyChange;
import org.apache.paimon.catalog.TableMetadata;
+import org.apache.paimon.catalog.TableQueryAuthResult;
import org.apache.paimon.fs.FileIO;
import org.apache.paimon.fs.Path;
import org.apache.paimon.function.Function;
@@ -37,12 +38,17 @@ import org.apache.paimon.function.FunctionChange;
import org.apache.paimon.options.Options;
import org.apache.paimon.partition.Partition;
import org.apache.paimon.partition.PartitionStatistics;
+import org.apache.paimon.predicate.And;
+import org.apache.paimon.predicate.CompoundPredicate;
+import org.apache.paimon.predicate.Predicate;
+import org.apache.paimon.predicate.Transform;
import org.apache.paimon.rest.exceptions.AlreadyExistsException;
import org.apache.paimon.rest.exceptions.BadRequestException;
import org.apache.paimon.rest.exceptions.ForbiddenException;
import org.apache.paimon.rest.exceptions.NoSuchResourceException;
import org.apache.paimon.rest.exceptions.NotImplementedException;
import org.apache.paimon.rest.exceptions.ServiceFailureException;
+import org.apache.paimon.rest.responses.AuthTableQueryResponse;
import org.apache.paimon.rest.responses.ErrorResponse;
import org.apache.paimon.rest.responses.GetDatabaseResponse;
import org.apache.paimon.rest.responses.GetFunctionResponse;
@@ -58,6 +64,7 @@ import org.apache.paimon.table.Table;
import org.apache.paimon.table.TableSnapshot;
import org.apache.paimon.table.sink.BatchTableCommit;
import org.apache.paimon.table.system.SystemTableLoader;
+import org.apache.paimon.utils.JsonSerdeUtil;
import org.apache.paimon.utils.Pair;
import org.apache.paimon.utils.SnapshotNotExistException;
import org.apache.paimon.view.View;
@@ -67,6 +74,8 @@ import org.apache.paimon.view.ViewSchema;
import org.apache.paimon.shade.org.apache.commons.lang3.StringUtils;
+import org.jetbrains.annotations.NotNull;
+
import javax.annotation.Nullable;
import java.io.IOException;
@@ -79,6 +88,7 @@ import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
+import java.util.TreeMap;
import java.util.stream.Collectors;
import static org.apache.paimon.CoreOptions.BRANCH;
@@ -524,11 +534,12 @@ public class RESTCatalog implements Catalog {
}
@Override
- public List<String> authTableQuery(Identifier identifier, @Nullable
List<String> select)
+ public TableQueryAuthResult authTableQuery(Identifier identifier,
@Nullable List<String> select)
throws TableNotExistException {
checkNotSystemTable(identifier, "authTable");
try {
- return api.authTableQuery(identifier, select);
+ AuthTableQueryResponse response = api.authTableQuery(identifier,
select);
+ return getTableQueryAuthResult(response);
} catch (NoSuchResourceException e) {
throw new TableNotExistException(identifier);
} catch (ForbiddenException e) {
@@ -1154,4 +1165,45 @@ public class RESTCatalog implements Catalog {
}
return schema;
}
+
+ private static @NotNull TableQueryAuthResult getTableQueryAuthResult(
+ AuthTableQueryResponse response) {
+ List<String> predicateJsons = response == null ? null :
response.filter();
+ Predicate rowFilter = null;
+ if (predicateJsons != null && !predicateJsons.isEmpty()) {
+ List<Predicate> predicates = new ArrayList<>();
+ for (String json : predicateJsons) {
+ if (StringUtils.isEmpty(json)) {
+ continue;
+ }
+ Predicate predicate = JsonSerdeUtil.fromJson(json,
Predicate.class);
+ if (predicate != null) {
+ predicates.add(predicate);
+ }
+ }
+ if (predicates.size() == 1) {
+ rowFilter = predicates.get(0);
+ } else if (!predicates.isEmpty()) {
+ rowFilter = new CompoundPredicate(And.INSTANCE, predicates);
+ }
+ }
+
+ Map<String, Transform> columnMasking = new TreeMap<>();
+ Map<String, String> maskingJsons = response == null ? null :
response.columnMasking();
+ if (maskingJsons != null && !maskingJsons.isEmpty()) {
+ for (Map.Entry<String, String> e : maskingJsons.entrySet()) {
+ String column = e.getKey();
+ String json = e.getValue();
+ if (StringUtils.isEmpty(column) || StringUtils.isEmpty(json)) {
+ continue;
+ }
+ Transform transform = JsonSerdeUtil.fromJson(json,
Transform.class);
+ if (transform == null) {
+ continue;
+ }
+ columnMasking.put(column, transform);
+ }
+ }
+ return new TableQueryAuthResult(rowFilter, columnMasking);
+ }
}
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 0f61f8fa09..d07e009214 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
@@ -28,6 +28,7 @@ import org.apache.paimon.catalog.CatalogSnapshotCommit;
import org.apache.paimon.catalog.Identifier;
import org.apache.paimon.catalog.RenamingSnapshotCommit;
import org.apache.paimon.catalog.SnapshotCommit;
+import org.apache.paimon.catalog.TableQueryAuthResult;
import org.apache.paimon.operation.Lock;
import org.apache.paimon.table.source.TableQueryAuth;
import org.apache.paimon.tag.SnapshotLoaderImpl;
@@ -37,7 +38,6 @@ import org.apache.paimon.utils.SnapshotManager;
import javax.annotation.Nullable;
import java.io.Serializable;
-import java.util.Collections;
import java.util.Optional;
/** Catalog environment in table which contains log factory, metastore client
factory. */
@@ -154,7 +154,7 @@ public class CatalogEnvironment implements Serializable {
public TableQueryAuth tableQueryAuth(CoreOptions options) {
if (!options.queryAuthEnabled() || catalogLoader == null) {
- return select -> Collections.emptyList();
+ return select -> TableQueryAuthResult.empty();
}
return select -> {
try (Catalog catalog = catalogLoader.load()) {
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 96a0dfb3a5..3d45ec2f33 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,6 +18,8 @@
package org.apache.paimon.table.source;
+import org.apache.paimon.catalog.TableQueryAuthResult;
+
import javax.annotation.Nullable;
import java.util.List;
@@ -25,5 +27,5 @@ import java.util.List;
/** Table query auth. */
public interface TableQueryAuth {
- List<String> auth(@Nullable List<String> select);
+ TableQueryAuthResult auth(@Nullable List<String> select);
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTCatalogTest.java
b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTCatalogTest.java
index 150a8e28eb..7a52bf1af9 100644
--- a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTCatalogTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTCatalogTest.java
@@ -25,8 +25,14 @@ import org.apache.paimon.TableType;
import org.apache.paimon.catalog.Catalog;
import org.apache.paimon.catalog.CatalogContext;
import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.catalog.TableQueryAuthResult;
import org.apache.paimon.options.CatalogOptions;
import org.apache.paimon.options.Options;
+import org.apache.paimon.predicate.FieldRef;
+import org.apache.paimon.predicate.Predicate;
+import org.apache.paimon.predicate.PredicateBuilder;
+import org.apache.paimon.predicate.Transform;
+import org.apache.paimon.predicate.UpperTransform;
import org.apache.paimon.rest.auth.AuthProvider;
import org.apache.paimon.rest.auth.AuthProviderEnum;
import org.apache.paimon.rest.auth.BearTokenAuthProvider;
@@ -35,9 +41,12 @@ import org.apache.paimon.rest.auth.DLFTokenLoader;
import org.apache.paimon.rest.auth.DLFTokenLoaderFactory;
import org.apache.paimon.rest.auth.RESTAuthParameter;
import org.apache.paimon.rest.exceptions.NotAuthorizedException;
+import org.apache.paimon.rest.responses.AuthTableQueryResponse;
import org.apache.paimon.rest.responses.ConfigResponse;
import org.apache.paimon.schema.Schema;
import org.apache.paimon.types.DataTypes;
+import org.apache.paimon.types.RowType;
+import org.apache.paimon.utils.JsonSerdeUtil;
import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableMap;
@@ -55,6 +64,7 @@ import java.util.UUID;
import static org.apache.paimon.catalog.Catalog.TABLE_DEFAULT_OPTION_PREFIX;
import static org.apache.paimon.rest.RESTApi.HEADER_PREFIX;
+import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.junit.jupiter.api.Assertions.assertEquals;
@@ -246,6 +256,42 @@ class MockRESTCatalogTest extends RESTCatalogTest {
catalog.dropTable(identifier, true);
}
+ @Test
+ void testAuthTableQueryResponseWithColumnMasking() throws Exception {
+ Identifier identifier = Identifier.create("test_db", "auth_table");
+ catalog.createDatabase(identifier.getDatabaseName(), true);
+ catalog.createTable(
+ Identifier.create(identifier.getDatabaseName(),
identifier.getTableName()),
+ DEFAULT_TABLE_SCHEMA,
+ false);
+
+ PredicateBuilder builder =
+ new PredicateBuilder(RowType.of(DataTypes.INT(),
DataTypes.STRING()));
+ Predicate predicate = builder.equal(0, 100);
+ String predicateJson = JsonSerdeUtil.toFlatJson(predicate);
+
+ Transform transform =
+ new UpperTransform(
+ Collections.singletonList(new FieldRef(1, "col2",
DataTypes.STRING())));
+ String transformJson = JsonSerdeUtil.toFlatJson(transform);
+
+ // Set up mock response with filter and columnMasking
+ List<String> filter = Collections.singletonList(predicateJson);
+ Map<String, String> columnMasking = new HashMap<>();
+ columnMasking.put("col2", transformJson);
+ AuthTableQueryResponse response = new AuthTableQueryResponse(filter,
columnMasking);
+ restCatalogServer.setTableQueryAuthResponse(identifier, response);
+
+ TableQueryAuthResult result = catalog.authTableQuery(identifier, null);
+ assertThat(result.rowFilter()).isEqualTo(predicate);
+ assertThat(result.columnMasking()).isNotEmpty();
+ assertThat(result.columnMasking()).containsKey("col2");
+ assertThat(result.columnMasking().get("col2")).isEqualTo(transform);
+
+ catalog.dropTable(identifier, true);
+ catalog.dropDatabase(identifier.getDatabaseName(), true, true);
+ }
+
private void checkHeader(String headerName, String headerValue) {
// Verify that the header were included in the requests
List<Map<String, String>> receivedHeaders =
restCatalogServer.getReceivedHeaders();
diff --git
a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
index f487815e4d..d202d41ea1 100644
--- a/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
+++ b/paimon-core/src/test/java/org/apache/paimon/rest/MockRESTMessage.java
@@ -24,6 +24,10 @@ import org.apache.paimon.function.FunctionChange;
import org.apache.paimon.function.FunctionDefinition;
import org.apache.paimon.function.FunctionImpl;
import org.apache.paimon.partition.Partition;
+import org.apache.paimon.predicate.Equal;
+import org.apache.paimon.predicate.FieldRef;
+import org.apache.paimon.predicate.LeafPredicate;
+import org.apache.paimon.predicate.UpperTransform;
import org.apache.paimon.rest.requests.AlterDatabaseRequest;
import org.apache.paimon.rest.requests.AlterFunctionRequest;
import org.apache.paimon.rest.requests.AlterTableRequest;
@@ -35,6 +39,7 @@ import org.apache.paimon.rest.requests.CreateViewRequest;
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.GetDatabaseResponse;
import org.apache.paimon.rest.responses.GetFunctionResponse;
import org.apache.paimon.rest.responses.GetTableResponse;
@@ -52,6 +57,7 @@ import org.apache.paimon.types.DataType;
import org.apache.paimon.types.DataTypes;
import org.apache.paimon.types.IntType;
import org.apache.paimon.types.RowType;
+import org.apache.paimon.utils.JsonSerdeUtil;
import org.apache.paimon.view.ViewChange;
import org.apache.paimon.view.ViewSchema;
@@ -382,4 +388,17 @@ public class MockRESTMessage {
List<String> primaryKeys = Arrays.asList("f0", "f1");
return new Schema(fields, partitionKeys, primaryKeys, options,
"comment");
}
+
+ public static AuthTableQueryResponse authTableQueryResponse() {
+ LeafPredicate predicate =
+ new LeafPredicate(
+ Equal.INSTANCE, DataTypes.INT(), 0, "id",
Collections.singletonList(1));
+ List<String> filter =
java.util.Arrays.asList(JsonSerdeUtil.toFlatJson(predicate));
+ Map<String, String> columnMasking = new HashMap<>();
+ FieldRef fieldRef = new FieldRef(1, "f1", DataTypes.STRING());
+ UpperTransform upperTransform = new
UpperTransform(Collections.singletonList(fieldRef));
+ columnMasking.put("c1", JsonSerdeUtil.toFlatJson(upperTransform));
+
+ return new AuthTableQueryResponse(filter, columnMasking);
+ }
}
diff --git
a/paimon-core/src/test/java/org/apache/paimon/rest/RESTApiJsonTest.java
b/paimon-core/src/test/java/org/apache/paimon/rest/RESTApiJsonTest.java
index 10aea7f3b4..1b8b1f71d0 100644
--- a/paimon-core/src/test/java/org/apache/paimon/rest/RESTApiJsonTest.java
+++ b/paimon-core/src/test/java/org/apache/paimon/rest/RESTApiJsonTest.java
@@ -29,6 +29,7 @@ import org.apache.paimon.rest.requests.CreateViewRequest;
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.ConfigResponse;
import org.apache.paimon.rest.responses.ErrorResponse;
import org.apache.paimon.rest.responses.GetDatabaseResponse;
@@ -298,4 +299,14 @@ public class RESTApiJsonTest {
AlterFunctionRequest parseData = RESTApi.fromJson(requestStr,
AlterFunctionRequest.class);
assertEquals(parseData.changes().size(), request.changes().size());
}
+
+ @Test
+ public void authTableQueryResponseParseTest() throws Exception {
+ AuthTableQueryResponse response =
MockRESTMessage.authTableQueryResponse();
+ String responseStr = RESTApi.toJson(response);
+ AuthTableQueryResponse parseData =
+ RESTApi.fromJson(responseStr, AuthTableQueryResponse.class);
+ assertEquals(response.filter(), parseData.filter());
+ assertEquals(response.columnMasking(), parseData.columnMasking());
+ }
}
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 544b490385..ff4d932501 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
@@ -107,6 +107,7 @@ import org.apache.paimon.view.ViewChange;
import org.apache.paimon.view.ViewImpl;
import org.apache.paimon.view.ViewSchema;
+import org.apache.paimon.shade.guava30.com.google.common.collect.ImmutableMap;
import
org.apache.paimon.shade.jackson2.com.fasterxml.jackson.core.JsonProcessingException;
import org.apache.paimon.shade.org.apache.commons.lang3.StringUtils;
@@ -116,7 +117,6 @@ import okhttp3.mockwebserver.MockWebServer;
import okhttp3.mockwebserver.RecordedRequest;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import org.testcontainers.shaded.com.google.common.collect.ImmutableMap;
import javax.annotation.Nullable;
@@ -185,6 +185,8 @@ public class RESTCatalogServer {
private final List<String> noPermissionTables = new ArrayList<>();
private final Map<String, Function> functionStore = new HashMap<>();
private final Map<String, List<String>> columnAuthHandler = new
HashMap<>();
+ private final Map<String, AuthTableQueryResponse>
tableQueryAuthResponseHandler =
+ new HashMap<>();
public final ConfigResponse configResponse;
public final String warehouse;
@@ -266,6 +268,10 @@ public class RESTCatalogServer {
columnAuthHandler.put(identifier.getFullName(), select);
}
+ public void setTableQueryAuthResponse(Identifier identifier,
AuthTableQueryResponse response) {
+ tableQueryAuthResponseHandler.put(identifier.getFullName(), response);
+ }
+
public RESTToken getDataToken(Identifier identifier) {
return DataTokenStore.getDataToken(warehouse,
identifier.getFullName());
}
@@ -829,7 +835,11 @@ public class RESTCatalogServer {
}
});
}
- AuthTableQueryResponse response = new
AuthTableQueryResponse(Collections.emptyList());
+ AuthTableQueryResponse response =
+ tableQueryAuthResponseHandler.get(identifier.getFullName());
+ if (response == null) {
+ response = new AuthTableQueryResponse(Collections.emptyList(),
ImmutableMap.of());
+ }
return mockResponse(response, 200);
}