This is an automated email from the ASF dual-hosted git repository. jackietien pushed a commit to branch ty/object_type in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit e53f7e23e8e9d0db55f9ec921e7e205fc1458747 Author: shuwenwei <[email protected]> AuthorDate: Tue Dec 2 13:59:39 2025 +0800 Fix the check of grant option for tree model (#16845) (cherry picked from commit 8cd141895b11ee0bb91df646a0c88b3f4df9afc6) --- .../org/apache/iotdb/db/it/auth/IoTDBAuthIT.java | 14 ++++ .../security/TreeAccessCheckVisitor.java | 15 +++- .../org/apache/iotdb/db/auth/TreeAccessTest.java | 84 ++++++++++++++++++++++ 3 files changed, 111 insertions(+), 2 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java index 24204320f75..441dd1fdb5c 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java @@ -981,6 +981,8 @@ public class IoTDBAuthIT { adminStmt.execute("CREATE USER user1 'password123456'"); adminStmt.execute("CREATE USER user2 'password123456'"); adminStmt.execute("CREATE USER user3 'password123456'"); + adminStmt.execute("CREATE USER user4 'password123456'"); + adminStmt.execute("CREATE USER user5 'password123456'"); adminStmt.execute("CREATE ROLE testRole"); adminStmt.execute("GRANT system ON root.** TO ROLE testRole WITH GRANT OPTION"); adminStmt.execute("GRANT READ_DATA ON root.t1.** TO ROLE testRole"); @@ -1095,6 +1097,18 @@ public class IoTDBAuthIT { } } + try (Connection userCon = EnvFactory.getEnv().getConnection("user4", "password123456"); + Statement userStmt = userCon.createStatement()) { + adminStmt.execute("GRANT SYSTEM ON root.** TO USER user4"); + try { + Assert.assertThrows( + SQLException.class, () -> userStmt.execute("GRANT SYSTEM ON root.** TO USER user5")); + adminStmt.execute("GRANT SYSTEM ON root.** TO USER user5"); + } finally { + userStmt.close(); + } + } + adminStmt.close(); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java index 89daafccfbb..86f4dce2b84 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckVisitor.java @@ -643,7 +643,7 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces for (String s : statement.getPrivilegeList()) { PrivilegeType privilegeType = PrivilegeType.valueOf(s.toUpperCase()); if (privilegeType.isSystemPrivilege()) { - if (!checkHasGlobalAuth(context, privilegeType, auditObject)) { + if (!checkHasGlobalAuth(context, privilegeType, auditObject, true)) { return AuthorityChecker.getTSStatus( false, "Has no permission to execute " @@ -1932,13 +1932,24 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces protected boolean checkHasGlobalAuth( IAuditEntity context, PrivilegeType requiredPrivilege, Supplier<String> auditObject) { + return checkHasGlobalAuth(context, requiredPrivilege, auditObject, false); + } + + protected boolean checkHasGlobalAuth( + IAuditEntity context, + PrivilegeType requiredPrivilege, + Supplier<String> auditObject, + boolean checkGrantOption) { if (AuthorityChecker.SUPER_USER.equals(context.getUsername())) { recordObjectAuthenticationAuditLog( context.setPrivilegeType(requiredPrivilege).setResult(true), auditObject); return true; } boolean result = - AuthorityChecker.checkSystemPermission(context.getUsername(), requiredPrivilege); + checkGrantOption + ? AuthorityChecker.checkSystemPermissionGrantOption( + context.getUsername(), requiredPrivilege) + : AuthorityChecker.checkSystemPermission(context.getUsername(), requiredPrivilege); recordObjectAuthenticationAuditLog( context.setPrivilegeType(requiredPrivilege).setResult(result), auditObject); return result; diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/TreeAccessTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/TreeAccessTest.java new file mode 100644 index 00000000000..af8746fae3e --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/TreeAccessTest.java @@ -0,0 +1,84 @@ +/* + * 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.iotdb.db.auth; + +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.User; +import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckContext; +import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckVisitor; +import org.apache.iotdb.db.queryengine.plan.statement.AuthorType; +import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; +import org.apache.iotdb.rpc.TSStatusCode; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +public class TreeAccessTest { + + @Before + public void setup() { + AuthorityChecker.getAuthorityFetcher().getAuthorCache().invalidAllCache(); + } + + @After + public void teardown() { + AuthorityChecker.getAuthorityFetcher().getAuthorCache().invalidAllCache(); + } + + @Test + public void test1() { + User mockUser = Mockito.mock(User.class); + Mockito.when(mockUser.getName()).thenReturn("user1"); + Mockito.when(mockUser.getUserId()).thenReturn(10000L); + Mockito.when(mockUser.checkSysPriGrantOpt(PrivilegeType.SYSTEM)).thenReturn(false); + AuthorityChecker.getAuthorityFetcher() + .getAuthorCache() + .putUserCache(mockUser.getName(), mockUser); + User mockUser2 = Mockito.mock(User.class); + Mockito.when(mockUser2.getName()).thenReturn("user2"); + Mockito.when(mockUser2.getUserId()).thenReturn(10001L); + AuthorityChecker.getAuthorityFetcher() + .getAuthorCache() + .putUserCache(mockUser.getName(), mockUser); + AuthorityChecker.getAuthorityFetcher() + .getAuthorCache() + .putUserCache(mockUser2.getName(), mockUser2); + TreeAccessCheckVisitor treeAccessCheckVisitor = new TreeAccessCheckVisitor(); + + AuthorStatement authorStatement = new AuthorStatement(AuthorType.GRANT_USER); + authorStatement.setPrivilegeList(new String[] {"SYSTEM"}); + authorStatement.setUserName("user2"); + authorStatement.setGrantOpt(true); + Assert.assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + treeAccessCheckVisitor + .visitAuthor(authorStatement, new TreeAccessCheckContext(10000L, "user1", "")) + .getCode()); + Mockito.when(mockUser.checkSysPriGrantOpt(PrivilegeType.SYSTEM)).thenReturn(true); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + treeAccessCheckVisitor + .visitAuthor(authorStatement, new TreeAccessCheckContext(10000L, "user1", "")) + .getCode()); + } +}
