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

czweng 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 347ae2104 [core] Fix snapshot privilege check logic (#4376)
347ae2104 is described below

commit 347ae2104101c2a3d37832fabac5e5a5b7c20b48
Author: yunfengzhou-hub <[email protected]>
AuthorDate: Mon Oct 28 15:19:45 2024 +0800

    [core] Fix snapshot privilege check logic (#4376)
    
    This closes #4376.
---
 .../paimon/privilege/NoPrivilegeException.java     | 18 +++++
 .../apache/paimon/privilege/PrivilegeChecker.java  | 16 ++++
 .../paimon/privilege/PrivilegedFileStore.java      |  2 +-
 .../paimon/privilege/PrivilegedFileStoreTable.java | 21 +++++
 .../paimon/catalog/FileSystemCatalogTest.java      |  3 +-
 .../paimon/privilege/PrivilegedCatalogTest.java    | 91 ++++++++++++++++++++++
 6 files changed, 149 insertions(+), 2 deletions(-)

diff --git 
a/paimon-core/src/main/java/org/apache/paimon/privilege/NoPrivilegeException.java
 
b/paimon-core/src/main/java/org/apache/paimon/privilege/NoPrivilegeException.java
index c868aebe7..5feb78058 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/privilege/NoPrivilegeException.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/privilege/NoPrivilegeException.java
@@ -23,6 +23,9 @@ import java.util.stream.Collectors;
 
 /** Thrown when tries to perform an operation but the current user does not 
have the privilege. */
 public class NoPrivilegeException extends RuntimeException {
+    private final String user;
+    private final String objectType;
+    private final String identifier;
 
     public NoPrivilegeException(
             String user, String objectType, String identifier, 
PrivilegeType... privilege) {
@@ -35,5 +38,20 @@ public class NoPrivilegeException extends RuntimeException {
                                 .collect(Collectors.joining(" or ")),
                         objectType,
                         identifier));
+        this.user = user;
+        this.objectType = objectType;
+        this.identifier = identifier;
+    }
+
+    String getUser() {
+        return user;
+    }
+
+    String getObjectType() {
+        return objectType;
+    }
+
+    String getIdentifier() {
+        return identifier;
     }
 }
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegeChecker.java 
b/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegeChecker.java
index ebd579bdb..1771d40f4 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegeChecker.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegeChecker.java
@@ -24,6 +24,22 @@ import java.io.Serializable;
 
 /** Check if current user has privilege to perform related operations. */
 public interface PrivilegeChecker extends Serializable {
+    default void assertCanSelectOrInsert(Identifier identifier) {
+        try {
+            assertCanSelect(identifier);
+        } catch (NoPrivilegeException e) {
+            try {
+                assertCanInsert(identifier);
+            } catch (NoPrivilegeException e1) {
+                throw new NoPrivilegeException(
+                        e1.getUser(),
+                        e1.getObjectType(),
+                        e1.getIdentifier(),
+                        PrivilegeType.SELECT,
+                        PrivilegeType.INSERT);
+            }
+        }
+    }
 
     void assertCanSelect(Identifier identifier);
 
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegedFileStore.java
 
b/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegedFileStore.java
index ec068b25a..59243a535 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegedFileStore.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegedFileStore.java
@@ -72,7 +72,7 @@ public class PrivilegedFileStore<T> implements FileStore<T> {
 
     @Override
     public SnapshotManager snapshotManager() {
-        privilegeChecker.assertCanSelect(identifier);
+        privilegeChecker.assertCanSelectOrInsert(identifier);
         return wrapped.snapshotManager();
     }
 
diff --git 
a/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegedFileStoreTable.java
 
b/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegedFileStoreTable.java
index 4eb1a1a90..37990ed5a 100644
--- 
a/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegedFileStoreTable.java
+++ 
b/paimon-core/src/main/java/org/apache/paimon/privilege/PrivilegedFileStoreTable.java
@@ -19,6 +19,7 @@
 package org.apache.paimon.privilege;
 
 import org.apache.paimon.FileStore;
+import org.apache.paimon.Snapshot;
 import org.apache.paimon.catalog.Identifier;
 import org.apache.paimon.manifest.ManifestCacheFilter;
 import org.apache.paimon.schema.TableSchema;
@@ -35,12 +36,14 @@ import org.apache.paimon.table.source.InnerTableRead;
 import org.apache.paimon.table.source.StreamDataTableScan;
 import org.apache.paimon.table.source.snapshot.SnapshotReader;
 import org.apache.paimon.utils.BranchManager;
+import org.apache.paimon.utils.SnapshotManager;
 import org.apache.paimon.utils.TagManager;
 
 import java.time.Duration;
 import java.util.Map;
 import java.util.Objects;
 import java.util.Optional;
+import java.util.OptionalLong;
 
 /** {@link FileStoreTable} with privilege checks. */
 public class PrivilegedFileStoreTable extends DelegatedFileStoreTable {
@@ -55,6 +58,24 @@ public class PrivilegedFileStoreTable extends 
DelegatedFileStoreTable {
         this.identifier = identifier;
     }
 
+    @Override
+    public SnapshotManager snapshotManager() {
+        privilegeChecker.assertCanSelectOrInsert(identifier);
+        return wrapped.snapshotManager();
+    }
+
+    @Override
+    public OptionalLong latestSnapshotId() {
+        privilegeChecker.assertCanSelectOrInsert(identifier);
+        return wrapped.latestSnapshotId();
+    }
+
+    @Override
+    public Snapshot snapshot(long snapshotId) {
+        privilegeChecker.assertCanSelectOrInsert(identifier);
+        return wrapped.snapshot(snapshotId);
+    }
+
     @Override
     public SnapshotReader newSnapshotReader() {
         privilegeChecker.assertCanSelect(identifier);
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/catalog/FileSystemCatalogTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/catalog/FileSystemCatalogTest.java
index 67fbd2718..35a5c06ff 100644
--- 
a/paimon-core/src/test/java/org/apache/paimon/catalog/FileSystemCatalogTest.java
+++ 
b/paimon-core/src/test/java/org/apache/paimon/catalog/FileSystemCatalogTest.java
@@ -22,7 +22,8 @@ import org.apache.paimon.fs.Path;
 
 import org.junit.jupiter.api.BeforeEach;
 
-class FileSystemCatalogTest extends CatalogTestBase {
+/** Tests for {@link FileSystemCatalog}. */
+public class FileSystemCatalogTest extends CatalogTestBase {
 
     @BeforeEach
     public void setUp() throws Exception {
diff --git 
a/paimon-core/src/test/java/org/apache/paimon/privilege/PrivilegedCatalogTest.java
 
b/paimon-core/src/test/java/org/apache/paimon/privilege/PrivilegedCatalogTest.java
new file mode 100644
index 000000000..019707108
--- /dev/null
+++ 
b/paimon-core/src/test/java/org/apache/paimon/privilege/PrivilegedCatalogTest.java
@@ -0,0 +1,91 @@
+/*
+ * 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.privilege;
+
+import org.apache.paimon.catalog.Catalog;
+import org.apache.paimon.catalog.FileSystemCatalogTest;
+import org.apache.paimon.catalog.Identifier;
+import org.apache.paimon.table.FileStoreTable;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.function.Executable;
+
+import java.util.OptionalLong;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+
+/** Tests for {@link PrivilegedCatalog}. */
+public class PrivilegedCatalogTest extends FileSystemCatalogTest {
+    private static final String PASSWORD_ROOT = "123456";
+    private static final String USERNAME_TEST_USER = "test_user";
+    private static final String PASSWORD_TEST_USER = "test_password";
+
+    @BeforeEach
+    public void setUp() throws Exception {
+        super.setUp();
+        getPrivilegeManager("anonymous", 
"anonymous").initializePrivilege(PASSWORD_ROOT);
+        catalog = new PrivilegedCatalog(catalog, getPrivilegeManager("root", 
PASSWORD_ROOT));
+    }
+
+    @Override
+    @Test
+    public void testGetTable() throws Exception {
+        super.testGetTable();
+        Identifier identifier = Identifier.create("test_db", "test_table");
+
+        PrivilegedCatalog rootCatalog = ((PrivilegedCatalog) catalog);
+        rootCatalog.createPrivilegedUser(USERNAME_TEST_USER, 
PASSWORD_TEST_USER);
+        Catalog userCatalog =
+                new PrivilegedCatalog(
+                        rootCatalog.wrapped(),
+                        getPrivilegeManager(USERNAME_TEST_USER, 
PASSWORD_TEST_USER));
+        FileStoreTable dataTable = (FileStoreTable) 
userCatalog.getTable(identifier);
+
+        assertNoPrivilege(dataTable::snapshotManager);
+        assertNoPrivilege(dataTable::latestSnapshotId);
+        assertNoPrivilege(() -> dataTable.snapshot(0));
+
+        rootCatalog.grantPrivilegeOnTable(USERNAME_TEST_USER, identifier, 
PrivilegeType.SELECT);
+        userCatalog =
+                new PrivilegedCatalog(
+                        rootCatalog.wrapped(),
+                        getPrivilegeManager(USERNAME_TEST_USER, 
PASSWORD_TEST_USER));
+        FileStoreTable dataTable2 = (FileStoreTable) 
userCatalog.getTable(identifier);
+
+        assertThat(dataTable2.snapshotManager().latestSnapshotId()).isNull();
+        
assertThat(dataTable2.latestSnapshotId()).isEqualTo(OptionalLong.empty());
+        assertThatThrownBy(() -> dataTable2.snapshot(0)).isNotNull();
+    }
+
+    private FileBasedPrivilegeManager getPrivilegeManager(String user, String 
password) {
+        return new FileBasedPrivilegeManager(warehouse, fileIO, user, 
password);
+    }
+
+    private void assertNoPrivilege(Executable executable) {
+        Exception e = assertThrows(Exception.class, executable);
+        if (e.getCause() != null) {
+            assertThat(e).hasRootCauseInstanceOf(NoPrivilegeException.class);
+        } else {
+            assertThat(e).isInstanceOf(NoPrivilegeException.class);
+        }
+    }
+}

Reply via email to