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

shuwenwei pushed a commit to branch separationOfAdminPowers
in repository https://gitbox.apache.org/repos/asf/iotdb.git

commit 48d4912946cb2d0dd2bb1544ee2d5a4eaaab9c83
Author: shuwenwei <[email protected]>
AuthorDate: Wed Sep 10 10:21:07 2025 +0800

    add admin privileges
---
 .../antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4  | 11 ++++++++
 .../org/apache/iotdb/db/auth/AuthorityChecker.java | 30 +++++++++++++++++---
 .../iotdb/db/auth/ClusterAuthorityFetcher.java     | 22 ++++++++++-----
 .../apache/iotdb/db/auth/IAuthorityFetcher.java    |  2 +-
 .../java/org/apache/iotdb/db/conf/IoTDBConfig.java | 10 +++++++
 .../db/queryengine/plan/statement/Statement.java   |  4 +++
 .../conf/iotdb-system.properties.template          | 15 ++++++++++
 .../iotdb/commons/auth/entity/PrivilegeType.java   | 17 +++++++++--
 .../org/apache/iotdb/commons/auth/entity/Role.java |  3 +-
 .../org/apache/iotdb/commons/utils/AuthUtils.java  | 33 ++++++++++++++++++++++
 .../db/relational/grammar/sql/RelationalSql.g4     |  4 +++
 11 files changed, 136 insertions(+), 15 deletions(-)

diff --git 
a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4 
b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
index 787412e0812..0054bf779fd 100644
--- a/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
+++ b/iotdb-core/antlr/src/main/antlr4/org/apache/iotdb/db/qp/sql/SqlLexer.g4
@@ -1072,6 +1072,9 @@ PRIVILEGE_VALUE
     | EXTEND_TEMPLATE
     | MANAGE_DATABASE
     | MAINTAIN
+    | SYSTEM
+    | SECURITY
+    | AUDIT
     ;
 
 READ_DATA
@@ -1130,6 +1133,14 @@ MAINTAIN
     : M A I N T A I N
     ;
 
+SECURITY
+    : S E C U R I T Y
+    ;
+
+AUDIT
+    : A U D I T
+    ;
+
 REPAIR
     : R E P A I R
     ;
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
index 71f064008df..f8693173c8f 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java
@@ -28,12 +28,14 @@ import org.apache.iotdb.commons.path.PathPatternTree;
 import org.apache.iotdb.commons.schema.column.ColumnHeader;
 import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant;
 import org.apache.iotdb.commons.service.metric.PerformanceOverviewMetrics;
+import org.apache.iotdb.commons.utils.AuthUtils;
 import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp;
 import org.apache.iotdb.confignode.rpc.thrift.TDBPrivilege;
 import org.apache.iotdb.confignode.rpc.thrift.TPathPrivilege;
 import org.apache.iotdb.confignode.rpc.thrift.TRoleResp;
 import org.apache.iotdb.confignode.rpc.thrift.TTablePrivilege;
 import org.apache.iotdb.confignode.rpc.thrift.TUserResp;
+import org.apache.iotdb.db.conf.IoTDBDescriptor;
 import 
org.apache.iotdb.db.pipe.source.dataregion.realtime.listener.PipeInsertionDataNodeListener;
 import org.apache.iotdb.db.protocol.session.IClientSession;
 import org.apache.iotdb.db.queryengine.common.header.DatasetHeader;
@@ -55,6 +57,7 @@ import java.util.Collections;
 import java.util.List;
 import java.util.Map;
 import java.util.Set;
+import java.util.StringJoiner;
 import java.util.stream.Collectors;
 
 import static 
org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.LIST_USER_OR_ROLE_PRIVILEGES_COLUMN_HEADERS;
@@ -122,7 +125,11 @@ public class AuthorityChecker {
   public static TSStatus checkAuthority(Statement statement, IClientSession 
session) {
     long startTime = System.nanoTime();
     try {
-      return statement.checkPermissionBeforeProcess(session.getUsername());
+      if 
(IoTDBDescriptor.getInstance().getConfig().isEnableSeparationOfPowers()) {
+        return 
statement.checkSeparatedAdminPermissionBeforeProcess(session.getUsername());
+      } else {
+        return statement.checkPermissionBeforeProcess(session.getUsername());
+      }
     } finally {
       PERFORMANCE_OVERVIEW_METRICS.recordAuthCost(System.nanoTime() - 
startTime);
     }
@@ -131,7 +138,11 @@ public class AuthorityChecker {
   public static TSStatus checkAuthority(Statement statement, String userName) {
     long startTime = System.nanoTime();
     try {
-      return statement.checkPermissionBeforeProcess(userName);
+      if 
(IoTDBDescriptor.getInstance().getConfig().isEnableSeparationOfPowers()) {
+        return statement.checkSeparatedAdminPermissionBeforeProcess(userName);
+      } else {
+        return statement.checkPermissionBeforeProcess(userName);
+      }
     } finally {
       PERFORMANCE_OVERVIEW_METRICS.recordAuthCost(System.nanoTime() - 
startTime);
     }
@@ -154,7 +165,18 @@ public class AuthorityChecker {
     return hasPermission
         ? SUCCEED
         : new TSStatus(TSStatusCode.NO_PERMISSION.getStatusCode())
-            .setMessage(NO_PERMISSION_PROMOTION + neededPrivilege);
+            .setMessage(
+                NO_PERMISSION_PROMOTION
+                    + getSatisfyAnyNeededPrivilegeString(
+                        
AuthUtils.getAllPrivilegesContainingCurrentPrivilege(neededPrivilege)));
+  }
+
+  private static String getSatisfyAnyNeededPrivilegeString(List<PrivilegeType> 
privileges) {
+    StringJoiner sj = new StringJoiner("/");
+    for (PrivilegeType privilege : privileges) {
+      sj.add(privilege.toString());
+    }
+    return sj.toString();
   }
 
   public static TSStatus getGrantOptTSStatus(
@@ -243,7 +265,7 @@ public class AuthorityChecker {
   }
 
   public static boolean checkSystemPermission(String userName, PrivilegeType 
permission) {
-    return authorityFetcher.get().checkUserSysPrivileges(userName, 
permission).getCode()
+    return authorityFetcher.get().checkUserHasAnySysPrivileges(userName, 
permission).getCode()
         == TSStatusCode.SUCCESS_STATUS.getStatusCode();
   }
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java
index 863a3653263..ad57817ce78 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java
@@ -117,14 +117,22 @@ public class ClusterAuthorityFetcher implements 
IAuthorityFetcher {
   }
 
   @Override
-  public TSStatus checkUserSysPrivileges(String username, PrivilegeType 
permission) {
+  public TSStatus checkUserHasAnySysPrivileges(String username, 
PrivilegeType... permissions) {
     checkCacheAvailable();
-    return checkPrivilege(
-        username,
-        new PrivilegeUnion(permission, false),
-        (role, union) -> role.checkSysPrivilege(union.getPrivilegeType()),
-        new TCheckUserPrivilegesReq(
-            username, PrivilegeModelType.SYSTEM.ordinal(), 
permission.ordinal(), false));
+    TSStatus status;
+    for (PrivilegeType permission : permissions) {
+      status =
+          checkPrivilege(
+              username,
+              new PrivilegeUnion(permission, false),
+              (role, union) -> 
role.checkSysPrivilege(union.getPrivilegeType()),
+              new TCheckUserPrivilegesReq(
+                  username, PrivilegeModelType.SYSTEM.ordinal(), 
permission.ordinal(), false));
+      if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+        return status;
+      }
+    }
+    return RpcUtils.getStatus(TSStatusCode.NO_PERMISSION);
   }
 
   @Override
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java
index 6b93c211fd6..6fce706d6d4 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java
@@ -45,7 +45,7 @@ public interface IAuthorityFetcher {
   TSStatus checkUserPathPrivilegesGrantOpt(
       String username, List<? extends PartialPath> allPath, PrivilegeType 
permission);
 
-  TSStatus checkUserSysPrivileges(String username, PrivilegeType permission);
+  TSStatus checkUserHasAnySysPrivileges(String username, PrivilegeType... 
permissions);
 
   TSStatus checkUserDBPrivileges(String username, String database, 
PrivilegeType permission);
 
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
index ecb18b3c575..66c72113913 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/conf/IoTDBConfig.java
@@ -929,6 +929,8 @@ public class IoTDBConfig {
   /** Cache expire time of user and role */
   private int authorCacheExpireTime = 30;
 
+  private boolean enableSeparationOfPowers = false;
+
   /** Number of queues per forwarding trigger */
   private int triggerForwardMaxQueueNumber = 8;
 
@@ -3138,6 +3140,14 @@ public class IoTDBConfig {
     this.authorCacheExpireTime = authorCacheExpireTime;
   }
 
+  public boolean isEnableSeparationOfPowers() {
+    return enableSeparationOfPowers;
+  }
+
+  public void setEnableSeparationOfPowers(boolean enableSeparationOfPowers) {
+    this.enableSeparationOfPowers = enableSeparationOfPowers;
+  }
+
   public int getTriggerForwardMaxQueueNumber() {
     return triggerForwardMaxQueueNumber;
   }
diff --git 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/Statement.java
 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/Statement.java
index fc9aa1486a9..6e2cba7a0cc 100644
--- 
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/Statement.java
+++ 
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/Statement.java
@@ -68,6 +68,10 @@ public abstract class Statement extends StatementNode {
         "Only the admin user can perform this operation");
   }
 
+  public TSStatus checkSeparatedAdminPermissionBeforeProcess(final String 
userName) {
+    throw new RuntimeException("Not implemented yet");
+  }
+
   public org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement 
toRelationalStatement(
       final MPPQueryContext context) {
     throw new UnsupportedOperationException("Method not implemented yet");
diff --git 
a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
 
b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
index c03f49a3ac3..b0a853d5525 100644
--- 
a/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
+++ 
b/iotdb-core/node-commons/src/assembly/resources/conf/iotdb-system.properties.template
@@ -1713,6 +1713,21 @@ author_cache_expire_time=30
 # If you want to allow all URIs, you can specify it as .*
 trusted_uri_pattern=file:.*
 
+# Enable separation of powers
+# effectiveMode: first_start
+# Datatype: Boolean
+enable_separation_of_powers=false
+
+# The user name of default database security admin
+# effectiveMode: first_start
+# Datatype: string
+dsa_name=iotdb_security
+
+# The user name of default database auditor admin
+# effectiveMode: first_start
+# Datatype: string
+daa_name=iotdb_audit
+
 ####################
 ### UDF Configuration
 ####################
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeType.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeType.java
index 773a1be38ba..bb455073d49 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeType.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeType.java
@@ -44,7 +44,11 @@ public enum PrivilegeType {
   ALTER(PrivilegeModelType.RELATIONAL),
   SELECT(PrivilegeModelType.RELATIONAL),
   INSERT(PrivilegeModelType.RELATIONAL),
-  DELETE(PrivilegeModelType.RELATIONAL);
+  DELETE(PrivilegeModelType.RELATIONAL),
+
+  SYSTEM(PrivilegeModelType.SYSTEM),
+  SECURITY(PrivilegeModelType.SYSTEM),
+  AUDIT(PrivilegeModelType.SYSTEM);
 
   private final PrivilegeModelType modelType;
 
@@ -93,7 +97,16 @@ public enum PrivilegeType {
   }
 
   public boolean forRelationalSys() {
-    return this == MANAGE_USER || this == MANAGE_ROLE;
+    switch (this) {
+      case MANAGE_USER:
+      case MANAGE_ROLE:
+      case SYSTEM:
+      case SECURITY:
+      case AUDIT:
+        return true;
+      default:
+        return false;
+    }
   }
 
   public PrivilegeModelType getModelType() {
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java
index 21481de80c4..d798f0085ca 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java
@@ -515,7 +515,8 @@ public class Role {
   }
 
   public boolean checkSysPrivilege(PrivilegeType priv) {
-    return sysPrivilegeSet.contains(priv);
+    return AuthUtils.getAllPrivilegesContainingCurrentPrivilege(priv).stream()
+        .anyMatch(sysPrivilegeSet::contains);
   }
 
   public boolean checkSysPriGrantOpt(PrivilegeType priv) {
diff --git 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
index 09824dfbd69..1ee4c8beb46 100644
--- 
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
+++ 
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java
@@ -41,6 +41,8 @@ import java.io.DataOutputStream;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
@@ -508,6 +510,12 @@ public class AuthUtils {
         return PrivilegeType.MAINTAIN;
       case 9:
         return PrivilegeType.USE_MODEL;
+      case 10:
+        return PrivilegeType.SYSTEM;
+      case 11:
+        return PrivilegeType.SECURITY;
+      case 12:
+        return PrivilegeType.AUDIT;
       default:
         // Not reach here.
         LOGGER.warn("Not support position");
@@ -537,11 +545,36 @@ public class AuthUtils {
         return 8;
       case USE_MODEL:
         return 9;
+      case SYSTEM:
+        return 10;
+      case SECURITY:
+        return 11;
+      case AUDIT:
+        return 12;
       default:
         return -1;
     }
   }
 
+  public static List<PrivilegeType> 
getAllPrivilegesContainingCurrentPrivilege(PrivilegeType priv) {
+    switch (priv) {
+      case MANAGE_USER:
+      case MANAGE_ROLE:
+        return Arrays.asList(priv, PrivilegeType.SECURITY);
+      case MAINTAIN:
+      case USE_UDF:
+      case USE_MODEL:
+      case USE_TRIGGER:
+      case USE_CQ:
+      case USE_PIPE:
+      case MANAGE_DATABASE:
+      case EXTEND_TEMPLATE:
+        return Arrays.asList(priv, PrivilegeType.SYSTEM);
+      default:
+        return Collections.singletonList(priv);
+    }
+  }
+
   public static int pathPosToPri(int pos) {
     switch (pos) {
       case 0:
diff --git 
a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
 
b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
index 80a869d2644..f7f5517dcc2 100644
--- 
a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
+++ 
b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4
@@ -761,6 +761,9 @@ objectScope
 systemPrivilege
     : MANAGE_USER
     | MANAGE_ROLE
+    | SYSTEM
+    | SECURITY
+    | AUDIT
     ;
 
 objectPrivilege
@@ -1773,6 +1776,7 @@ WRAPPER: 'WRAPPER';
 WRITE: 'WRITE';
 YEAR: 'YEAR' | 'Y';
 ZONE: 'ZONE';
+AUDIT: 'AUDIT';
 
 EQ: '=';
 NEQ: '<>' | '!=';

Reply via email to