This is an automated email from the ASF dual-hosted git repository.
JackieTien97 pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 6b77a8dc8f0 Isolate writes to the internal system database
root.__system (#19017)
6b77a8dc8f0 is described below
commit 6b77a8dc8f000b9117f2c7b250b5ed97a680e89b
Author: alpass163 <[email protected]>
AuthorDate: Mon Oct 26 17:41:42 2026 +0800
Isolate writes to the internal system database root.__system (#19017)
---
.../iotdb/trigger/StatisticsUpdaterTrigger.java | 2 +-
.../org/apache/iotdb/db/auth/AuthorityChecker.java | 1 +
.../impl/DataNodeInternalRPCServiceImpl.java | 13 +-
.../db/queryengine/common/MPPQueryContext.java | 5 +
.../db/queryengine/plan/analyze/AnalyzeUtils.java | 21 +++-
.../queryengine/plan/analyze/AnalyzeVisitor.java | 20 ++-
.../analyze/schema/AutoCreateSchemaExecutor.java | 6 +-
.../relational/security/AccessControlImpl.java | 20 +++
.../security/TreeAccessCheckContext.java | 11 ++
.../security/TreeAccessCheckVisitor.java | 138 +++++++++++++++++++++
.../metrics/IoTDBInternalLocalReporter.java | 13 +-
.../org/apache/iotdb/db/auth/TreeAccessTest.java | 102 +++++++++++++++
.../conf/iotdb-system.properties.template | 4 +
.../apache/iotdb/commons/audit/IAuditEntity.java | 5 +
.../commons/queryengine/common/SessionInfo.java | 13 ++
.../commons/schema/InternalDatabaseUtils.java | 52 ++++++++
.../apache/iotdb/commons/schema/table/Audit.java | 1 +
.../iotdb/commons/schema/table/AuditTest.java | 1 +
18 files changed, 404 insertions(+), 24 deletions(-)
diff --git
a/example/trigger/src/main/java/org/apache/iotdb/trigger/StatisticsUpdaterTrigger.java
b/example/trigger/src/main/java/org/apache/iotdb/trigger/StatisticsUpdaterTrigger.java
index 4f1217b6f66..c8fed4130c2 100644
---
a/example/trigger/src/main/java/org/apache/iotdb/trigger/StatisticsUpdaterTrigger.java
+++
b/example/trigger/src/main/java/org/apache/iotdb/trigger/StatisticsUpdaterTrigger.java
@@ -45,7 +45,7 @@ public class StatisticsUpdaterTrigger implements Trigger {
private static final Logger LOGGER =
LoggerFactory.getLogger(StatisticsUpdaterTrigger.class);
- private static final String TARGET_DEVICE = "root.__system.statistics";
+ private static final String TARGET_DEVICE = "root.statistics";
private static final String TARGET_SERIES = "total_count";
private String ip;
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 53c5da0ddc4..ccf79c78240 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
@@ -226,6 +226,7 @@ public class AuthorityChecker {
auditEntity.getUserId(),
auditEntity.getUsername(),
auditEntity.getCliHostname())
+ .setInternal(auditEntity.isInternal())
.setSqlString(auditEntity.getSqlString()));
}
return status;
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
index 5d3bce4e4c3..1f5f95b4460 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/DataNodeInternalRPCServiceImpl.java
@@ -3557,12 +3557,13 @@ public class DataNodeInternalRPCServiceImpl implements
IDataNodeRPCService.Iface
InsertRowStatement statement = StatementGenerator.createStatement(req);
SessionInfo sessionInfo =
new SessionInfo(
- 0,
- new UserEntity(
- AuthorityChecker.SUPER_USER_ID,
- AuthorityChecker.SUPER_USER,
-
IoTDBDescriptor.getInstance().getConfig().getInternalAddress()),
- ZoneId.systemDefault());
+ 0,
+ new UserEntity(
+ AuthorityChecker.SUPER_USER_ID,
+ AuthorityChecker.SUPER_USER,
+
IoTDBDescriptor.getInstance().getConfig().getInternalAddress()),
+ ZoneId.systemDefault())
+ .setInternal(true);
long queryId = SESSION_MANAGER.requestQueryId();
ExecutionResult result =
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java
index 311bf04322a..d609c5c8e02 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/common/MPPQueryContext.java
@@ -1086,6 +1086,11 @@ public class MPPQueryContext implements IAuditEntity {
return session.getUserName();
}
+ @Override
+ public boolean isInternal() {
+ return session != null && session.isInternal();
+ }
+
@Override
public String getCliHostname() {
if (session == null || session.getCliHostname() == null) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
index a1d5dc7a27f..95bf1570350 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeUtils.java
@@ -20,7 +20,9 @@
package org.apache.iotdb.db.queryengine.plan.analyze;
import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot;
+import org.apache.iotdb.commons.audit.IAuditEntity;
import org.apache.iotdb.commons.exception.IoTDBException;
import org.apache.iotdb.commons.exception.IoTDBRuntimeException;
import org.apache.iotdb.commons.exception.SemanticException;
@@ -56,6 +58,7 @@ import
org.apache.iotdb.db.queryengine.plan.relational.metadata.DeviceEntry;
import
org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.TableDeviceSchemaFetcher;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionRewriter;
import
org.apache.iotdb.db.queryengine.plan.relational.planner.ir.ExpressionTreeRewriter;
+import
org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckVisitor;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Delete;
import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.TimeRange;
import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertBaseStatement;
@@ -168,7 +171,9 @@ public class AnalyzeUtils {
validateSchema(analysis, insertBaseStatement, schemaValidation);
InsertBaseStatement realStatement =
- removeLogicalView ? removeLogicalView(analysis, insertBaseStatement) :
insertBaseStatement;
+ removeLogicalView
+ ? removeLogicalView(analysis, insertBaseStatement, context)
+ : insertBaseStatement;
if (analysis.isFinishQueryAfterAnalyze()) {
return realStatement;
}
@@ -343,9 +348,19 @@ public class AnalyzeUtils {
}
public static InsertBaseStatement removeLogicalView(
- IAnalysis analysis, InsertBaseStatement insertBaseStatement) {
+ IAnalysis analysis, InsertBaseStatement insertBaseStatement,
IAuditEntity context) {
try {
- return insertBaseStatement.removeLogicalView();
+ final InsertBaseStatement realStatement =
insertBaseStatement.removeLogicalView();
+ // Inserts into a logical view are rewritten to the view's source
series. Check the real
+ // target paths so a view created before this restriction cannot write
root.__system.
+ final TSStatus systemDatabaseStatus =
+ TreeAccessCheckVisitor.checkSystemDatabaseWrite(
+ context, realStatement.getDevicePathsStream().toList());
+ if (systemDatabaseStatus != null) {
+ analysis.setFinishQueryAfterAnalyze(true);
+ analysis.setFailStatus(systemDatabaseStatus);
+ }
+ return realStatement;
} catch (SemanticException e) {
analysis.setFinishQueryAfterAnalyze(true);
if (e.getCause() instanceof IoTDBException) {
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
index c9a422dec98..5d42cde5859 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/AnalyzeVisitor.java
@@ -87,6 +87,7 @@ import
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.GroupByTimePa
import
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.GroupByVariationParameter;
import
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.IntoPathDescriptor;
import
org.apache.iotdb.db.queryengine.plan.planner.plan.parameter.OrderByParameter;
+import
org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckVisitor;
import org.apache.iotdb.db.queryengine.plan.statement.StatementNode;
import org.apache.iotdb.db.queryengine.plan.statement.StatementVisitor;
import org.apache.iotdb.db.queryengine.plan.statement.component.FillComponent;
@@ -2903,7 +2904,8 @@ public class AnalyzeVisitor extends
StatementVisitor<Analysis, MPPQueryContext>
analysis,
insertRowStatement,
() -> SchemaValidator.validate(schemaFetcher, insertRowStatement,
context));
- InsertBaseStatement realInsertStatement = removeLogicalView(analysis,
insertRowStatement);
+ InsertBaseStatement realInsertStatement =
+ removeLogicalView(analysis, insertRowStatement, context);
if (analysis.isFinishQueryAfterAnalyze()) {
return analysis;
}
@@ -2943,7 +2945,7 @@ public class AnalyzeVisitor extends
StatementVisitor<Analysis, MPPQueryContext>
insertRowsStatement,
() -> SchemaValidator.validate(schemaFetcher, insertRowsStatement,
context));
InsertRowsStatement realInsertRowsStatement =
- (InsertRowsStatement) removeLogicalView(analysis, insertRowsStatement);
+ (InsertRowsStatement) removeLogicalView(analysis, insertRowsStatement,
context);
if (analysis.isFinishQueryAfterAnalyze()) {
return analysis;
}
@@ -2968,7 +2970,8 @@ public class AnalyzeVisitor extends
StatementVisitor<Analysis, MPPQueryContext>
insertMultiTabletsStatement,
() -> SchemaValidator.validate(schemaFetcher,
insertMultiTabletsStatement, context));
InsertMultiTabletsStatement realStatement =
- (InsertMultiTabletsStatement) removeLogicalView(analysis,
insertMultiTabletsStatement);
+ (InsertMultiTabletsStatement)
+ removeLogicalView(analysis, insertMultiTabletsStatement, context);
if (analysis.isFinishQueryAfterAnalyze()) {
return analysis;
}
@@ -2993,7 +2996,7 @@ public class AnalyzeVisitor extends
StatementVisitor<Analysis, MPPQueryContext>
insertRowsOfOneDeviceStatement,
() -> SchemaValidator.validate(schemaFetcher,
insertRowsOfOneDeviceStatement, context));
InsertBaseStatement realInsertStatement =
- removeLogicalView(analysis, insertRowsOfOneDeviceStatement);
+ removeLogicalView(analysis, insertRowsOfOneDeviceStatement, context);
if (analysis.isFinishQueryAfterAnalyze()) {
return analysis;
}
@@ -3725,6 +3728,15 @@ public class AnalyzeVisitor extends
StatementVisitor<Analysis, MPPQueryContext>
}
}
deleteDataStatement.setPathList(new ArrayList<>(deletePatternSet));
+ // Deletes through a writable view are rewritten to the view's source
series as well.
+ final TSStatus systemDatabaseStatus =
+ TreeAccessCheckVisitor.checkSystemDatabaseWrite(
+ context, deleteDataStatement.getPathList());
+ if (systemDatabaseStatus != null) {
+ analysis.setFinishQueryAfterAnalyze(true);
+ analysis.setFailStatus(systemDatabaseStatus);
+ return analysis;
+ }
} else {
for (PartialPath devicePattern : patternTree.getAllDevicePaths()) {
schemaTree
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/AutoCreateSchemaExecutor.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/AutoCreateSchemaExecutor.java
index fe8695c859d..2c5836c96c8 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/AutoCreateSchemaExecutor.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/schema/AutoCreateSchemaExecutor.java
@@ -567,8 +567,7 @@ class AutoCreateSchemaExecutor {
private void internalActivateTemplate(PartialPath devicePath,
MPPQueryContext context) {
ActivateTemplateStatement statement = new
ActivateTemplateStatement(devicePath);
- TSStatus status =
- AuthorityChecker.checkAuthority(statement,
context.getSession().getUserEntity());
+ TSStatus status = AuthorityChecker.checkAuthority(statement, context);
if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
throw new IoTDBRuntimeException(status.getMessage(), status.getCode());
}
@@ -585,8 +584,7 @@ class AutoCreateSchemaExecutor {
MPPQueryContext context) {
InternalBatchActivateTemplateStatement statement =
new
InternalBatchActivateTemplateStatement(devicesNeedActivateTemplate);
- TSStatus status =
- AuthorityChecker.checkAuthority(statement,
context.getSession().getUserEntity());
+ TSStatus status = AuthorityChecker.checkAuthority(statement, context);
if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
throw new IoTDBRuntimeException(status.getMessage(), status.getCode());
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
index 0db343b44d9..d0f847878a2 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
@@ -606,6 +606,12 @@ public class AccessControlImpl implements AccessControl {
IAuditEntity auditEntity, IDeviceID device, String measurementId) {
try {
PartialPath path = new MeasurementPath(device, measurementId);
+ final TSStatus systemDatabaseStatus =
+ TreeAccessCheckVisitor.checkSystemDatabaseWrite(
+ auditEntity, Collections.singletonList(path));
+ if (systemDatabaseStatus != null) {
+ return systemDatabaseStatus;
+ }
// Apache IoTDB does not support external writes to the audit database.
if (includeByAuditTreeDB(path)
&&
!auditEntity.getUsername().equals(AuthorityChecker.INTERNAL_AUDIT_USER)) {
@@ -633,6 +639,20 @@ public class AccessControlImpl implements AccessControl {
@Override
public TSStatus checkCanAlterView(
IAuditEntity entity, List<PartialPath> sourcePaths, List<PartialPath>
targetPaths) {
+ if (sourcePaths != null) {
+ final TSStatus sourceSystemDatabaseStatus =
+ TreeAccessCheckVisitor.checkSystemDatabaseWrite(entity, sourcePaths);
+ if (sourceSystemDatabaseStatus != null) {
+ return sourceSystemDatabaseStatus;
+ }
+ }
+ if (targetPaths != null) {
+ final TSStatus targetSystemDatabaseStatus =
+ TreeAccessCheckVisitor.checkSystemDatabaseWrite(entity, targetPaths);
+ if (targetSystemDatabaseStatus != null) {
+ return targetSystemDatabaseStatus;
+ }
+ }
if (AuthorityChecker.SUPER_USER_ID == entity.getUserId()) {
DNAuditLogger.getInstance()
.recordObjectAuthenticationAuditLog(
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckContext.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckContext.java
index 5dbb91cdbe6..ac09431fc2c 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckContext.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TreeAccessCheckContext.java
@@ -32,6 +32,7 @@ public class TreeAccessCheckContext implements IAuditEntity {
private final long userId;
private final String username;
private final String cliHostname;
+ private boolean internal;
public TreeAccessCheckContext(long userId, String username, String
cliHostname) {
this.userId = userId;
@@ -54,6 +55,16 @@ public class TreeAccessCheckContext implements IAuditEntity {
return cliHostname;
}
+ @Override
+ public boolean isInternal() {
+ return internal;
+ }
+
+ public TreeAccessCheckContext setInternal(final boolean internal) {
+ this.internal = internal;
+ return this;
+ }
+
private AuditEventType auditEventType;
private AuditLogOperation auditLogOperation;
private List<PrivilegeType> privilegeTypeList;
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 ae1ba598379..c1945f9d728 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
@@ -30,6 +30,7 @@ import org.apache.iotdb.commons.path.MeasurementPath;
import org.apache.iotdb.commons.path.PartialPath;
import org.apache.iotdb.commons.path.PathPatternTree;
import org.apache.iotdb.commons.path.PathPatternTreeUtils;
+import org.apache.iotdb.commons.schema.InternalDatabaseUtils;
import org.apache.iotdb.commons.schema.SchemaConstant;
import org.apache.iotdb.commons.schema.table.Audit;
import org.apache.iotdb.commons.utils.StatusUtils;
@@ -180,6 +181,7 @@ import com.google.common.collect.ImmutableList;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
@@ -278,6 +280,12 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
public TSStatus visitSetSchemaTemplate(
SetSchemaTemplateStatement setSchemaTemplateStatement,
TreeAccessCheckContext context) {
context.setAuditLogOperation(AuditLogOperation.DDL);
+ final TSStatus systemDatabaseStatus =
+ checkSystemDatabaseWrite(
+ context,
Collections.singletonList(setSchemaTemplateStatement.getPath()));
+ if (systemDatabaseStatus != null) {
+ return systemDatabaseStatus;
+ }
// root.__audit can never be set template
TSStatus status =
checkUnsupportedAuditDatabaseWrite(
@@ -379,6 +387,11 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
@Override
public TSStatus visitUnsetSchemaTemplate(
UnsetSchemaTemplateStatement unsetSchemaTemplateStatement,
TreeAccessCheckContext context) {
+ final TSStatus systemDatabaseStatus =
+ checkSystemDatabaseWrite(context,
unsetSchemaTemplateStatement.getPaths());
+ if (systemDatabaseStatus != null) {
+ return systemDatabaseStatus;
+ }
return checkSystemAuth(
context.setAuditLogOperation(AuditLogOperation.DDL),
() -> unsetSchemaTemplateStatement.getPaths().toString());
@@ -437,6 +450,34 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
.setMessage(getUnsupportedAuditDatabaseOperationMessage(TREE_MODEL_AUDIT_DATABASE));
}
}
+ final TSStatus systemDatabaseStatus = checkSystemDatabaseWrite(context,
paths);
+ if (systemDatabaseStatus != null) {
+ return systemDatabaseStatus;
+ }
+ // A logical view redirects inserts and deletes to its source series
during analysis, so the
+ // source paths must be checked here as well.
+ if (statement.getSourcePaths().fullPathList != null) {
+ final TSStatus sourceSystemDatabaseStatus =
+ checkSystemDatabaseWrite(context,
statement.getSourcePaths().fullPathList);
+ if (sourceSystemDatabaseStatus != null) {
+ return sourceSystemDatabaseStatus;
+ }
+ }
+ if (statement.getSourcePaths().prefixOfPathsGroup != null) {
+ final TSStatus sourceSystemDatabaseStatus =
+ checkSystemDatabaseWrite(
+ context,
Collections.singletonList(statement.getSourcePaths().prefixOfPathsGroup));
+ if (sourceSystemDatabaseStatus != null) {
+ return sourceSystemDatabaseStatus;
+ }
+ }
+ if (statement.getQueryStatement() != null) {
+ final TSStatus sourceSystemDatabaseStatus =
+ checkSystemDatabaseWrite(context,
statement.getQueryStatement().getPaths());
+ if (sourceSystemDatabaseStatus != null) {
+ return sourceSystemDatabaseStatus;
+ }
+ }
if (AuthorityChecker.SUPER_USER.equals(context.getUsername())) {
statement.setCanSeeAuditDB(true);
@@ -496,6 +537,35 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
public TSStatus visitAlterLogicalView(
AlterLogicalViewStatement statement, TreeAccessCheckContext context) {
context.setAuditLogOperation(AuditLogOperation.DDL);
+ final List<PartialPath> targetPathList = statement.getTargetPathList();
+ if (targetPathList != null) {
+ final TSStatus systemDatabaseStatus = checkSystemDatabaseWrite(context,
targetPathList);
+ if (systemDatabaseStatus != null) {
+ return systemDatabaseStatus;
+ }
+ }
+ if (statement.getSourcePaths().fullPathList != null) {
+ final TSStatus sourceSystemDatabaseStatus =
+ checkSystemDatabaseWrite(context,
statement.getSourcePaths().fullPathList);
+ if (sourceSystemDatabaseStatus != null) {
+ return sourceSystemDatabaseStatus;
+ }
+ }
+ if (statement.getSourcePaths().prefixOfPathsGroup != null) {
+ final TSStatus sourceSystemDatabaseStatus =
+ checkSystemDatabaseWrite(
+ context,
Collections.singletonList(statement.getSourcePaths().prefixOfPathsGroup));
+ if (sourceSystemDatabaseStatus != null) {
+ return sourceSystemDatabaseStatus;
+ }
+ }
+ if (statement.getQueryStatement() != null) {
+ final TSStatus sourceSystemDatabaseStatus =
+ checkSystemDatabaseWrite(context,
statement.getQueryStatement().getPaths());
+ if (sourceSystemDatabaseStatus != null) {
+ return sourceSystemDatabaseStatus;
+ }
+ }
if (AuthorityChecker.SUPER_USER.equals(context.getUsername())) {
statement.setCanSeeAuditDB(true);
if (statement.getQueryStatement() != null) {
@@ -1060,6 +1130,18 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
return new TSStatus(TSStatusCode.NO_PERMISSION.getStatusCode())
.setMessage(getReservedDatabaseNameErrorMsg(TREE_MODEL_AUDIT_DATABASE));
}
+ // The system database is created by the database itself, not through SQL.
+ if (!context.isInternal()
+ &&
InternalDatabaseUtils.isSystemDatabasePath(statement.getDatabasePath())) {
+ context
+ .setDatabase(statement.getDatabasePath().getFullPath())
+ .setPrivilegeType(PrivilegeType.MANAGE_DATABASE)
+ .setAuditLogOperation(AuditLogOperation.DDL);
+ AUDIT_LOGGER.recordObjectAuthenticationAuditLog(
+ context.setResult(false), statement.getDatabasePath()::getFullPath);
+ return new TSStatus(TSStatusCode.NO_PERMISSION.getStatusCode())
+
.setMessage(getReservedDatabaseNameErrorMsg(SchemaConstant.SYSTEM_DATABASE));
+ }
return checkCreateOrAlterDatabasePermission(
context.setAuditLogOperation(AuditLogOperation.DDL),
statement.getDatabasePath());
}
@@ -1067,6 +1149,12 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
@Override
public TSStatus visitAlterDatabase(
DatabaseSchemaStatement databaseSchemaStatement, TreeAccessCheckContext
context) {
+ final TSStatus systemDatabaseStatus =
+ checkSystemDatabaseWrite(
+ context,
Collections.singletonList(databaseSchemaStatement.getDatabasePath()));
+ if (systemDatabaseStatus != null) {
+ return systemDatabaseStatus;
+ }
return checkCreateOrAlterDatabasePermission(
context.setAuditLogOperation(AuditLogOperation.DDL),
databaseSchemaStatement.getDatabasePath());
@@ -1116,6 +1204,14 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
context
.setAuditLogOperation(AuditLogOperation.DDL)
.setDatabase(statement.getPrefixPath().toString());
+ if (!context.isInternal()
+ && SchemaConstant.SYSTEM_DATABASE.equals(String.join(".",
statement.getPrefixPath()))) {
+ AUDIT_LOGGER.recordObjectAuthenticationAuditLog(
+
context.setPrivilegeType(PrivilegeType.MANAGE_DATABASE).setResult(false),
+ () -> String.join(".", statement.getPrefixPath()));
+ return new TSStatus(TSStatusCode.NO_PERMISSION.getStatusCode())
+
.setMessage(getUnsupportedAuditDatabaseOperationMessage(SchemaConstant.SYSTEM_DATABASE));
+ }
for (String prefixPath : statement.getPrefixPath()) {
// root.__audit can never be deleted
if (TREE_MODEL_AUDIT_DATABASE.equals(prefixPath)) {
@@ -1183,6 +1279,27 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
}
// ==================================== data related
========================================
+ /**
+ * Rejects writes to {@code root.__system} from sessions that are not
dedicated internal writers.
+ * Callers must pass the paths that will actually be written, including the
source paths of a
+ * writable view, because analysis rewrites inserts and deletes to those
source series.
+ */
+ public static TSStatus checkSystemDatabaseWrite(
+ final IAuditEntity context, final Collection<? extends PartialPath>
checkedPaths) {
+ if (context.isInternal()) {
+ return null;
+ }
+ for (PartialPath path : checkedPaths) {
+ if (InternalDatabaseUtils.isSystemDatabasePath(path)) {
+
AUDIT_LOGGER.recordObjectAuthenticationAuditLog(context.setResult(false),
path::toString);
+ return new TSStatus(TSStatusCode.NO_PERMISSION.getStatusCode())
+ .setMessage(
+
getUnsupportedAuditDatabaseOperationMessage(SchemaConstant.SYSTEM_DATABASE));
+ }
+ }
+ return null;
+ }
+
@Override
public TSStatus visitInsertBase(InsertBaseStatement statement,
TreeAccessCheckContext context) {
context.setAuditLogOperation(AuditLogOperation.DML).setPrivilegeType(PrivilegeType.WRITE_DATA);
@@ -1201,6 +1318,11 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
return new TSStatus(TSStatusCode.NO_PERMISSION.getStatusCode())
.setMessage(getUnsupportedAuditDatabaseOperationMessage(TREE_MODEL_AUDIT_DATABASE));
}
+ final TSStatus unsupportedSystemStatus =
+ checkSystemDatabaseWrite(context,
statement.getDevicePathsStream().toList());
+ if (unsupportedSystemStatus != null) {
+ return unsupportedSystemStatus;
+ }
if (AuthorityChecker.SUPER_USER.equals(context.getUsername())) {
AUDIT_LOGGER.recordObjectAuthenticationAuditLog(
@@ -1301,6 +1423,13 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
PrivilegeType permission,
Function<List<? extends PartialPath>, String> auditObjectFormatter) {
context.setPrivilegeType(permission);
+ if (permission == PrivilegeType.WRITE_SCHEMA || permission ==
PrivilegeType.WRITE_DATA) {
+ final TSStatus systemDatabaseStatus =
+ checkSystemDatabaseWrite(context, checkedPathsSupplier.get());
+ if (systemDatabaseStatus != null) {
+ return systemDatabaseStatus;
+ }
+ }
if (AuthorityChecker.SUPER_USER.equals(context.getUsername())) {
AUDIT_LOGGER.recordObjectAuthenticationAuditLog(
context.setResult(true), () ->
auditObjectFormatter.apply(checkedPathsSupplier.get()));
@@ -1760,6 +1889,11 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
.setMessage(getUnsupportedAuditDatabaseOperationMessage(TREE_MODEL_AUDIT_DATABASE));
}
}
+ final TSStatus systemDatabaseStatus =
+ checkSystemDatabaseWrite(context, statement.getPathPatternList());
+ if (systemDatabaseStatus != null) {
+ return systemDatabaseStatus;
+ }
return checkTimeSeriesPermission(context, statement::getPaths,
PrivilegeType.WRITE_SCHEMA);
}
@@ -2068,6 +2202,10 @@ public class TreeAccessCheckVisitor extends
StatementVisitor<TSStatus, TreeAcces
public TSStatus visitSetTTL(SetTTLStatement statement,
TreeAccessCheckContext context) {
context.setPrivilegeType(PrivilegeType.SYSTEM).setAuditLogOperation(AuditLogOperation.DDL);
List<PartialPath> checkedPaths = statement.getPaths();
+ final TSStatus systemDatabaseStatus = checkSystemDatabaseWrite(context,
checkedPaths);
+ if (systemDatabaseStatus != null) {
+ return systemDatabaseStatus;
+ }
boolean[] pathsNotEndWithMultiLevelWildcard = null;
for (int i = 0; i < checkedPaths.size(); i++) {
PartialPath checkedPath = checkedPaths.get(i);
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/IoTDBInternalLocalReporter.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/IoTDBInternalLocalReporter.java
index ad13233dbac..5749d99b66f 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/IoTDBInternalLocalReporter.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/service/metrics/IoTDBInternalLocalReporter.java
@@ -97,12 +97,13 @@ public class IoTDBInternalLocalReporter extends
IoTDBInternalReporter {
Executors.newSingleThreadScheduledExecutor(),
Coordinator.getInstance(),
new SessionInfo(
- 0,
- new UserEntity(
- AuthorityChecker.SUPER_USER_ID,
- AuthorityChecker.SUPER_USER,
-
IoTDBDescriptor.getInstance().getConfig().getInternalAddress()),
- ZoneId.systemDefault()),
+ 0,
+ new UserEntity(
+ AuthorityChecker.SUPER_USER_ID,
+ AuthorityChecker.SUPER_USER,
+
IoTDBDescriptor.getInstance().getConfig().getInternalAddress()),
+ ZoneId.systemDefault())
+ .setInternal(true),
ClusterPartitionFetcher.getInstance(),
ClusterSchemaFetcher.getInstance());
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
index 04fdde70613..1bd9ffb2f7a 100644
---
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
@@ -23,12 +23,21 @@ import org.apache.iotdb.commons.auth.entity.PrivilegeType;
import org.apache.iotdb.commons.auth.entity.User;
import org.apache.iotdb.commons.conf.CommonConfig;
import org.apache.iotdb.commons.conf.CommonDescriptor;
+import org.apache.iotdb.commons.path.MeasurementPath;
import org.apache.iotdb.commons.path.PartialPath;
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.crud.InsertStatement;
+import
org.apache.iotdb.db.queryengine.plan.statement.metadata.AlterTimeSeriesStatement;
import
org.apache.iotdb.db.queryengine.plan.statement.metadata.CountLevelTimeSeriesStatement;
import
org.apache.iotdb.db.queryengine.plan.statement.metadata.CountTimeSeriesStatement;
+import
org.apache.iotdb.db.queryengine.plan.statement.metadata.CreateTimeSeriesStatement;
+import
org.apache.iotdb.db.queryengine.plan.statement.metadata.DatabaseSchemaStatement;
+import
org.apache.iotdb.db.queryengine.plan.statement.metadata.DeleteTimeSeriesStatement;
+import
org.apache.iotdb.db.queryengine.plan.statement.metadata.template.ActivateTemplateStatement;
+import
org.apache.iotdb.db.queryengine.plan.statement.metadata.template.SetSchemaTemplateStatement;
+import
org.apache.iotdb.db.queryengine.plan.statement.metadata.view.CreateLogicalViewStatement;
import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement;
import org.apache.iotdb.rpc.TSStatusCode;
@@ -238,6 +247,99 @@ public class TreeAccessTest {
new TreeAccessCheckContext(10000L, "user1", ""), new
PartialPath("root.sg")));
}
+ @Test
+ public void testSystemDatabaseWriteRequiresInternalCaller() throws Exception
{
+ final TreeAccessCheckVisitor visitor = new TreeAccessCheckVisitor();
+ final TreeAccessCheckContext externalCaller =
+ new TreeAccessCheckContext(AuthorityChecker.SUPER_USER_ID,
AuthorityChecker.SUPER_USER, "");
+ final TreeAccessCheckContext internalCaller =
+ new TreeAccessCheckContext(AuthorityChecker.SUPER_USER_ID,
AuthorityChecker.SUPER_USER, "")
+ .setInternal(true);
+
+ final InsertStatement systemInsert = new InsertStatement();
+ systemInsert.setDevice(new PartialPath("root.__system.d"));
+ systemInsert.setMeasurementList(new String[] {"s"});
+ Assert.assertEquals(
+ TSStatusCode.NO_PERMISSION.getStatusCode(),
+ visitor.visitInsert(systemInsert, externalCaller).getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ visitor.visitInsert(systemInsert, internalCaller).getCode());
+
+ final CreateTimeSeriesStatement systemCreate = new
CreateTimeSeriesStatement();
+ systemCreate.setPath(new MeasurementPath("root.__system.d.s"));
+ Assert.assertEquals(
+ TSStatusCode.NO_PERMISSION.getStatusCode(),
+ visitor.visitCreateTimeseries(systemCreate, externalCaller).getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ visitor.visitCreateTimeseries(systemCreate, internalCaller).getCode());
+
+ final DeleteTimeSeriesStatement deleteTimeSeries =
+ new DeleteTimeSeriesStatement(
+ Collections.singletonList(new PartialPath("root.__system.**")));
+ Assert.assertEquals(
+ TSStatusCode.NO_PERMISSION.getStatusCode(),
+ visitor.visitDeleteTimeSeries(deleteTimeSeries,
externalCaller).getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ visitor.visitDeleteTimeSeries(deleteTimeSeries,
internalCaller).getCode());
+
+ final AlterTimeSeriesStatement alterTimeSeries = new
AlterTimeSeriesStatement();
+ alterTimeSeries.setPath(new MeasurementPath("root.__system.d.s"));
+ Assert.assertEquals(
+ TSStatusCode.NO_PERMISSION.getStatusCode(),
+ visitor.visitAlterTimeSeries(alterTimeSeries,
externalCaller).getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ visitor.visitAlterTimeSeries(alterTimeSeries,
internalCaller).getCode());
+
+ final ActivateTemplateStatement activateTemplate =
+ new ActivateTemplateStatement(new PartialPath("root.__system.d"));
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ visitor.visitActivateTemplate(activateTemplate,
internalCaller).getCode());
+
+ final SetSchemaTemplateStatement setSchemaTemplate =
+ new SetSchemaTemplateStatement("t", new PartialPath("root.__system"));
+ Assert.assertEquals(
+ TSStatusCode.NO_PERMISSION.getStatusCode(),
+ visitor.visitSetSchemaTemplate(setSchemaTemplate,
externalCaller).getCode());
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ visitor.visitSetSchemaTemplate(setSchemaTemplate,
internalCaller).getCode());
+
+ final CreateLogicalViewStatement systemView = new
CreateLogicalViewStatement();
+ systemView.setTargetFullPaths(Collections.singletonList(new
PartialPath("root.sg.v.s")));
+ systemView.setSourceFullPaths(Collections.singletonList(new
PartialPath("root.__system.d.s")));
+ Assert.assertEquals(
+ TSStatusCode.NO_PERMISSION.getStatusCode(),
+ visitor.visitCreateLogicalView(systemView, externalCaller).getCode());
+
+ // The system database cannot be created through SQL.
+ final DatabaseSchemaStatement systemDatabaseCreate =
+ new
DatabaseSchemaStatement(DatabaseSchemaStatement.DatabaseSchemaStatementType.CREATE);
+ systemDatabaseCreate.setDatabasePath(new PartialPath("root.__system"));
+ Assert.assertEquals(
+ TSStatusCode.NO_PERMISSION.getStatusCode(),
+ visitor.visitSetDatabase(systemDatabaseCreate,
externalCaller).getCode());
+
+ final DatabaseSchemaStatement systemCaseVariantCreate =
+ new
DatabaseSchemaStatement(DatabaseSchemaStatement.DatabaseSchemaStatementType.CREATE);
+ systemCaseVariantCreate.setDatabasePath(new PartialPath("root.__System"));
+ Assert.assertEquals(
+ TSStatusCode.NO_PERMISSION.getStatusCode(),
+ visitor.visitSetDatabase(systemCaseVariantCreate,
externalCaller).getCode());
+
+ // Writes to a normal database are unaffected.
+ final InsertStatement normalInsert = new InsertStatement();
+ normalInsert.setDevice(new PartialPath("root.sg.d"));
+ normalInsert.setMeasurementList(new String[] {"s"});
+ Assert.assertEquals(
+ TSStatusCode.SUCCESS_STATUS.getStatusCode(),
+ visitor.visitInsert(normalInsert, externalCaller).getCode());
+ }
+
@Test
public void testPathLogLimitDoesNotLimitPermissionCheck() throws Exception {
final CommonConfig config = CommonDescriptor.getInstance().getConfig();
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 d952bce23ca..4db46c0cdbf 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
@@ -391,6 +391,8 @@ prometheus_reporter_async_update=true
# The reporters of metric module to report metrics
# If there are more than one reporter, please separate them by commas ",".
# Options: [JMX, PROMETHEUS]
+# The IOTDB session reporter is not supported: it writes to root.__system,
which rejects writes
+# from external client sessions.
# effectiveMode: restart
# Datatype: String
cn_metric_reporter_list=
@@ -414,6 +416,8 @@ cn_metric_prometheus_reporter_port=9091
# The reporters of metric module to report metrics
# If there are more than one reporter, please separate them by commas ",".
# Options: [JMX, PROMETHEUS]
+# The IOTDB session reporter is not supported: it writes to root.__system,
which rejects writes
+# from external client sessions.
# effectiveMode: restart
# Datatype: String
dn_metric_reporter_list=
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/IAuditEntity.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/IAuditEntity.java
index 0b46b0fd286..ad4c85f18d3 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/IAuditEntity.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/IAuditEntity.java
@@ -58,4 +58,9 @@ public interface IAuditEntity {
String getSqlString();
IAuditEntity setSqlString(String sqlString);
+
+ /** Whether the caller is a dedicated internal writer of the database
itself. */
+ default boolean isInternal() {
+ return false;
+ }
}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/common/SessionInfo.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/common/SessionInfo.java
index de681067c93..d6a0f4120ab 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/common/SessionInfo.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/queryengine/common/SessionInfo.java
@@ -50,6 +50,10 @@ public class SessionInfo {
// SessionInfo is sent to execution nodes, so this field does not need
serialization.
private final transient boolean enableAutoCreateSchema;
+ // Whether the session is opened by the database itself. Like
enableAutoCreateSchema, this is
+ // only consulted during analysis and therefore does not need serialization.
+ private transient boolean internal;
+
public SessionInfo(long sessionId, UserEntity userEntity, ZoneId zoneId) {
this.sessionId = sessionId;
this.userEntity = userEntity;
@@ -165,6 +169,15 @@ public class SessionInfo {
return enableAutoCreateSchema;
}
+ public boolean isInternal() {
+ return internal;
+ }
+
+ public SessionInfo setInternal(final boolean internal) {
+ this.internal = internal;
+ return this;
+ }
+
public static SessionInfo deserializeFrom(final ByteBuffer buffer) {
final long sessionId = ReadWriteIOUtils.readLong(buffer);
final long userId = ReadWriteIOUtils.readLong(buffer);
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/InternalDatabaseUtils.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/InternalDatabaseUtils.java
new file mode 100644
index 00000000000..3ed5ba24fd0
--- /dev/null
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/InternalDatabaseUtils.java
@@ -0,0 +1,52 @@
+/*
+ * 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.commons.schema;
+
+import org.apache.iotdb.commons.path.PartialPath;
+import org.apache.iotdb.commons.schema.table.Audit;
+
+public final class InternalDatabaseUtils {
+
+ private InternalDatabaseUtils() {}
+
+ /** Whether the given database (or one of its descendants) is an internal
database. */
+ public static boolean isInternalDatabase(final String databaseName) {
+ return isSystemDatabase(databaseName) ||
Audit.isAuditDatabase(databaseName);
+ }
+
+ /** Whether the given database (or one of its descendants) is {@code
root.__system}. */
+ public static boolean isSystemDatabase(final String databaseName) {
+ return isDatabaseOrDescendant(databaseName,
SchemaConstant.SYSTEM_DATABASE);
+ }
+
+ /** Whether the device or measurement path belongs to {@code root.__system}.
*/
+ public static boolean isSystemDatabasePath(final PartialPath path) {
+ return path != null && isSystemDatabase(path.getFullPath());
+ }
+
+ // Internal database names are matched case-insensitively: root.__System is
also internal.
+ private static boolean isDatabaseOrDescendant(
+ final String databaseName, final String internalDatabase) {
+ return databaseName != null
+ && databaseName.regionMatches(true, 0, internalDatabase, 0,
internalDatabase.length())
+ && (databaseName.length() == internalDatabase.length()
+ || databaseName.charAt(internalDatabase.length()) == '.');
+ }
+}
diff --git
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/Audit.java
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/Audit.java
index a33cda1b5dc..fa672344081 100644
---
a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/Audit.java
+++
b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/table/Audit.java
@@ -57,6 +57,7 @@ public class Audit {
|| isDatabaseOrDescendant(databaseName, TREE_MODEL_AUDIT_DATABASE);
}
+ // Audit database names are matched case-insensitively: root.__Audit is also
an audit database.
private static boolean isDatabaseOrDescendant(
final String databaseName, final String auditDatabaseName) {
return databaseName != null
diff --git
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/schema/table/AuditTest.java
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/schema/table/AuditTest.java
index ad66cd0f284..ddb01615798 100644
---
a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/schema/table/AuditTest.java
+++
b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/schema/table/AuditTest.java
@@ -34,6 +34,7 @@ public class AuditTest {
assertTrue(Audit.isAuditDatabase("ROOT.__AUDIT"));
assertTrue(Audit.isAuditDatabase("__audit.data"));
assertTrue(Audit.isAuditDatabase("root.__audit.data"));
+ assertTrue(Audit.isAuditDatabase("root.__Audit.data"));
assertFalse(Audit.isAuditDatabase("__audit_data"));
assertFalse(Audit.isAuditDatabase("root.__audit_data"));
assertFalse(Audit.isAuditDatabase(null));