This is an automated email from the ASF dual-hosted git repository. yongzao pushed a commit to branch audit-log-patch in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit c1bd88d30d7359475f14cb903e527941e9c11ee1 Author: Yongzao <[email protected]> AuthorDate: Sat Sep 27 15:31:42 2025 +0800 patch 4 table model --- .../iotdb/db/it/audit/IoTDBAuditLogBasicIT.java | 191 +++++++++++++++++++-- .../relational/analyzer/StatementAnalyzer.java | 37 +--- .../relational/security/AccessControlImpl.java | 80 +++++++-- 3 files changed, 252 insertions(+), 56 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/audit/IoTDBAuditLogBasicIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/audit/IoTDBAuditLogBasicIT.java index 0f831ad8a74..86956b36e55 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/audit/IoTDBAuditLogBasicIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/audit/IoTDBAuditLogBasicIT.java @@ -43,7 +43,10 @@ import java.sql.Statement; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Set; import java.util.StringJoiner; +import java.util.stream.Collectors; +import java.util.stream.Stream; @RunWith(IoTDBTestRunner.class) @Category({LocalStandaloneIT.class}) @@ -145,7 +148,16 @@ public class IoTDBAuditLogBasicIT { "SHOW TABLES", "DESC table1", "ALTER TABLE table1 set properties TTL='INF'", + "CREATE USER user1 'IoTDB@2021abc'", + "CREATE role role1", + "GRANT SELECT, ALTER, INSERT, DELETE ON test.table1 TO ROLE role1", + "GRANT ROLE role1 TO user1", + "LIST USER", + "LIST ROLE", + "DROP ROLE role1", + "DROP USER user1", "INSERT INTO table1(time, t1, a1, s1) values(1, 't1', 'a1', 's1')", + "SELECT * FROM table1", "DELETE FROM table1", "DROP TABLE table1", "DROP DATABASE IF EXISTS test"); @@ -297,8 +309,8 @@ public class IoTDBAuditLogBasicIT { "127.0.0.1", "OBJECT_AUTHENTICATION", "DDL", - "[SYSTEM]", - "GLOBAL", + "[CREATE]", + "OBJECT", "true", "test", "CREATE TABLE table1(t1 STRING TAG, a1 STRING ATTRIBUTE, s1 TEXT FIELD)", @@ -331,6 +343,118 @@ public class IoTDBAuditLogBasicIT { "test", "DESC table1", "User root (ID=0) requests authority on object table1 with result true"), + // Create user + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "DDL", + "[SECURITY]", + "GLOBAL", + "true", + "null", + "CREATE USER user1 'IoTDB@2021abc'", + "User root (ID=0) requests authority on object user1 with result true"), + // Create role + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "DDL", + "[SECURITY]", + "GLOBAL", + "true", + "null", + "CREATE role role1", + "User root (ID=0) requests authority on object role1 with result true"), + // Grant privileges to role + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "DDL", + "[SECURITY]", + "GLOBAL", + "true", + "test", + "GRANT SELECT, ALTER, INSERT, DELETE ON test.table1 TO ROLE role1", + "User root (ID=0) requests authority on object role1 with result true"), + // Grant role to user + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "DDL", + "[SECURITY]", + "GLOBAL", + "true", + "null", + "GRANT ROLE role1 TO user1", + "User root (ID=0) requests authority on object user: user1, role: role1 with result true"), + // List user TODO: better audit + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "QUERY", + "[SECURITY]", + "GLOBAL", + "true", + "null", + "LIST USER", + "User root (ID=0) requests authority on object null with result true"), + // List role TODO: better audit + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "QUERY", + "[SECURITY]", + "GLOBAL", + "true", + "null", + "LIST ROLE", + "User root (ID=0) requests authority on object null with result true"), + // Drop role + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "DDL", + "[SECURITY]", + "GLOBAL", + "true", + "null", + "DROP ROLE role1", + "User root (ID=0) requests authority on object role1 with result true"), + // Drop user + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "DDL", + "[SECURITY]", + "GLOBAL", + "true", + "null", + "DROP USER user1", + "User root (ID=0) requests authority on object user1 with result true"), // Insert into table Arrays.asList( "node_1", @@ -345,7 +469,34 @@ public class IoTDBAuditLogBasicIT { "test", "INSERT INTO table1(time, t1, a1, s1) values(1, 't1', 'a1', 's1')", "User root (ID=0) requests authority on object table1 with result true"), - // Delete table TODO: find the delete SQL + // Select from table, including fetch device + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "QUERY", + "[SELECT]", + "OBJECT", + "true", + "test", + "SELECT * FROM table1", + "User root (ID=0) requests authority on object table1 with result true"), + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "QUERY", + "[SELECT]", + "OBJECT", + "true", + "test", + "fetch device for query", + "User root (ID=0) requests authority on object table1 with result true"), + // Delete table Arrays.asList( "node_1", "u_0", @@ -357,7 +508,21 @@ public class IoTDBAuditLogBasicIT { "OBJECT", "true", "test", - "null", + "DELETE FROM table1", + "User root (ID=0) requests authority on object table1 with result true"), + // Drop table + Arrays.asList( + "node_1", + "u_0", + "root", + "127.0.0.1", + "OBJECT_AUTHENTICATION", + "DDL", + "[DROP]", + "OBJECT", + "true", + "test", + "DROP TABLE table1", "User root (ID=0) requests authority on object table1 with result true"), // Drop database Arrays.asList( @@ -373,7 +538,7 @@ public class IoTDBAuditLogBasicIT { "test", "DROP DATABASE IF EXISTS test", "User root (ID=0) requests authority on object test with result true"), - // Select audit log TODO: find out why twice + // Select audit log Arrays.asList( "node_1", "u_0", @@ -385,7 +550,7 @@ public class IoTDBAuditLogBasicIT { "OBJECT", "true", "__audit", - "null", + "SELECT * FROM __audit.audit_log ORDER BY TIME", "User root (ID=0) requests authority on object __audit with result true"), Arrays.asList( "node_1", @@ -398,8 +563,10 @@ public class IoTDBAuditLogBasicIT { "OBJECT", "true", "__audit", - "null", + "fetch device for query", "User root (ID=0) requests authority on object __audit with result true")); + private static final Set<Integer> INDEX_FOR_CONTAIN = + Stream.of(11, 12).collect(Collectors.toSet()); @Test public void basicAuditLogTestForTableModel() throws SQLException { @@ -409,7 +576,7 @@ public class IoTDBAuditLogBasicIT { statement.execute(sql); } int count = 0; - ResultSet resultSet = statement.executeQuery("SELECT * FROM __audit.audit_log"); + ResultSet resultSet = statement.executeQuery("SELECT * FROM __audit.audit_log ORDER BY TIME"); while (resultSet.next()) { LOGGER.info("Expected audit log: {}", TABLE_MODEL_AUDIT_FIELDS.get(count)); List<String> actualFields = new ArrayList<>(); @@ -418,10 +585,14 @@ public class IoTDBAuditLogBasicIT { } LOGGER.info("Actual audit log: {}", actualFields); List<String> expectedFields = TABLE_MODEL_AUDIT_FIELDS.get(count); - for (int i = 1; i <= 11; i++) { + for (int i = 1; i <= 12; i++) { + if (INDEX_FOR_CONTAIN.contains(i)) { + Assert.assertTrue(resultSet.getString(i + 1).contains(expectedFields.get(i - 1))); + continue; + } Assert.assertEquals(expectedFields.get(i - 1), resultSet.getString(i + 1)); } - Assert.assertTrue(resultSet.getString(13).contains(expectedFields.get(11))); + count++; } Assert.assertEquals(TABLE_MODEL_AUDIT_FIELDS.size(), count); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java index 58e3e510418..408c52909e7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java @@ -19,7 +19,6 @@ package org.apache.iotdb.db.queryengine.plan.relational.analyzer; -import org.apache.iotdb.commons.audit.UserEntity; import org.apache.iotdb.commons.schema.table.TsTable; import org.apache.iotdb.commons.schema.table.column.TsTableColumnCategory; import org.apache.iotdb.commons.schema.table.column.TsTableColumnSchema; @@ -503,10 +502,7 @@ public class StatementAnalyzer { accessControl.checkCanInsertIntoTable( sessionContext.getUserName(), new QualifiedObjectName(node.getDatabase(), node.getTableName()), - new UserEntity( - sessionContext.getUserId(), - sessionContext.getUserName(), - sessionContext.getCliHostname())); + queryContext); final TranslationMap translationMap = analyzeTraverseDevice(node, context, true); final TsTable table = DataNodeTableCache.getInstance().getTable(node.getDatabase(), node.getTableName()); @@ -567,10 +563,7 @@ public class StatementAnalyzer { accessControl.checkCanDeleteFromTable( sessionContext.getUserName(), new QualifiedObjectName(node.getDatabase(), node.getTableName()), - new UserEntity( - sessionContext.getUserId(), - sessionContext.getUserName(), - sessionContext.getCliHostname())); + queryContext); final TsTable table = DataNodeTableCache.getInstance().getTable(node.getDatabase(), node.getTableName()); DataNodeTreeViewSchemaUtils.checkTableInWrite(node.getDatabase(), table); @@ -645,12 +638,7 @@ public class StatementAnalyzer { } // verify access privileges accessControl.checkCanInsertIntoTable( - sessionContext.getUserName(), - targetTable, - new UserEntity( - sessionContext.getUserId(), - sessionContext.getUserName(), - sessionContext.getCliHostname())); + sessionContext.getUserName(), targetTable, queryContext); // verify the insert destination columns match the query Optional<TableSchema> tableSchema = metadata.getTableSchema(sessionContext, targetTable); @@ -773,10 +761,7 @@ public class StatementAnalyzer { new QualifiedObjectName( AnalyzeUtils.getDatabaseName(node, queryContext), node.getTable().getName().getSuffix()), - new UserEntity( - sessionContext.getUserId(), - sessionContext.getUserName(), - sessionContext.getCliHostname())); + queryContext); AnalyzeUtils.analyzeDelete(node, queryContext); analysis.setScope(node, ret); @@ -3071,17 +3056,10 @@ public class StatementAnalyzer { return resultScope; } } - QualifiedObjectName name = createQualifiedObjectName(sessionContext, table.getName()); // access control - accessControl.checkCanSelectFromTable( - sessionContext.getUserName(), - name, - new UserEntity( - sessionContext.getUserId(), - sessionContext.getUserName(), - sessionContext.getCliHostname())); + accessControl.checkCanSelectFromTable(sessionContext.getUserName(), name, queryContext); analysis.setRelationName( table, QualifiedName.of(name.getDatabaseName(), name.getObjectName())); @@ -4505,10 +4483,7 @@ public class StatementAnalyzer { accessControl.checkCanSelectFromTable( sessionContext.getUserName(), new QualifiedObjectName(node.getDatabase(), node.getTableName()), - new UserEntity( - sessionContext.getUserId(), - sessionContext.getUserName(), - sessionContext.getCliHostname())); + queryContext); analyzeTraverseDevice(node, context, node.getWhere().isPresent()); final TsTable table = 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 bfffb794335..c3d084463ad 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 @@ -107,7 +107,9 @@ public class AccessControlImpl implements AccessControl { @Override public void checkCanCreateTable( String userName, QualifiedObjectName tableName, IAuditEntity auditEntity) { - auditEntity.setAuditLogOperation(AuditLogOperation.DDL); + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setDatabase(tableName.getDatabaseName()); InformationSchemaUtils.checkDBNameInWrite(tableName.getDatabaseName()); if (userName.equals(AuthorityChecker.INTERNAL_AUDIT_USER) && tableName.getDatabaseName().equals(TABLE_MODEL_AUDIT_DATABASE)) { @@ -117,7 +119,7 @@ public class AccessControlImpl implements AccessControl { checkAuditDatabase(tableName.getDatabaseName()); if (hasGlobalPrivilege(auditEntity, PrivilegeType.SYSTEM)) { ITableAuthCheckerImpl.recordAuditLog( - auditEntity.setPrivilegeType(PrivilegeType.SYSTEM).setResult(true), + auditEntity.setPrivilegeType(PrivilegeType.CREATE).setResult(true), tableName::getObjectName); return; } @@ -127,10 +129,15 @@ public class AccessControlImpl implements AccessControl { @Override public void checkCanDropTable( String userName, QualifiedObjectName tableName, IAuditEntity auditEntity) { + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setDatabase(tableName.getDatabaseName()); InformationSchemaUtils.checkDBNameInWrite(tableName.getDatabaseName()); checkAuditDatabase(tableName.getDatabaseName()); if (hasGlobalPrivilege(auditEntity, PrivilegeType.SYSTEM)) { - ITableAuthCheckerImpl.recordAuditLog(auditEntity, tableName::getObjectName); + ITableAuthCheckerImpl.recordAuditLog( + auditEntity.setPrivilegeType(PrivilegeType.DROP).setResult(true), + tableName::getObjectName); return; } authChecker.checkTablePrivilege(userName, tableName, TableModelPrivilege.DROP, auditEntity); @@ -246,58 +253,82 @@ public class AccessControlImpl implements AccessControl { switch (type) { case CREATE_USER: case DROP_USER: + case UPDATE_USER: + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY); if (AuthorityChecker.SUPER_USER_ID == auditEntity.getUserId()) { - ITableAuthCheckerImpl.recordAuditLog(auditEntity, statement::getUserName); + ITableAuthCheckerImpl.recordAuditLog(auditEntity.setResult(true), statement::getUserName); return; } authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_USER, auditEntity); return; - case UPDATE_USER: case LIST_USER_PRIV: + auditEntity + .setAuditLogOperation(AuditLogOperation.QUERY) + .setPrivilegeType(PrivilegeType.SECURITY); if (AuthorityChecker.SUPER_USER_ID == auditEntity.getUserId() || statement.getUserName().equals(userName)) { - ITableAuthCheckerImpl.recordAuditLog(auditEntity, statement::getUserName); + ITableAuthCheckerImpl.recordAuditLog(auditEntity.setResult(true), statement::getUserName); return; } authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_USER, auditEntity); return; case LIST_USER: + auditEntity.setAuditLogOperation(AuditLogOperation.QUERY); if (!hasGlobalPrivilege(auditEntity, PrivilegeType.MANAGE_USER)) { statement.setUserName(userName); + } else { + auditEntity.setPrivilegeType(PrivilegeType.SECURITY); + ITableAuthCheckerImpl.recordAuditLog(auditEntity.setResult(true), statement::getUserName); } return; case CREATE_ROLE: case DROP_ROLE: + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY); if (AuthorityChecker.SUPER_USER_ID == auditEntity.getUserId()) { - ITableAuthCheckerImpl.recordAuditLog(auditEntity, statement::getRoleName); + ITableAuthCheckerImpl.recordAuditLog(auditEntity.setResult(true), statement::getRoleName); return; } authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE, auditEntity); return; case GRANT_USER_ROLE: case REVOKE_USER_ROLE: + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY); if (AuthorityChecker.SUPER_USER_ID == auditEntity.getUserId()) { ITableAuthCheckerImpl.recordAuditLog( - auditEntity, + auditEntity.setResult(true), () -> "user: " + statement.getUserName() + ", role: " + statement.getRoleName()); return; } authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE, auditEntity); return; case LIST_ROLE: + auditEntity + .setAuditLogOperation(AuditLogOperation.QUERY) + .setPrivilegeType(PrivilegeType.SECURITY); if (statement.getUserName() != null && !statement.getUserName().equals(userName)) { authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE, auditEntity); - ITableAuthCheckerImpl.recordAuditLog(auditEntity, statement::getRoleName); + ITableAuthCheckerImpl.recordAuditLog(auditEntity.setResult(true), statement::getRoleName); return; } if (!hasGlobalPrivilege(auditEntity, PrivilegeType.MANAGE_ROLE)) { statement.setUserName(userName); + } else { + ITableAuthCheckerImpl.recordAuditLog(auditEntity.setResult(true), statement::getRoleName); } return; case LIST_ROLE_PRIV: + auditEntity + .setAuditLogOperation(AuditLogOperation.QUERY) + .setPrivilegeType(PrivilegeType.SECURITY); if (AuthorityChecker.SUPER_USER_ID == auditEntity.getUserId() || AuthorityChecker.checkRole(userName, statement.getRoleName())) { - ITableAuthCheckerImpl.recordAuditLog(auditEntity, statement::getRoleName); + ITableAuthCheckerImpl.recordAuditLog(auditEntity.setResult(true), statement::getRoleName); return; } authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE, auditEntity); @@ -306,9 +337,13 @@ public class AccessControlImpl implements AccessControl { case GRANT_USER_ANY: case REVOKE_ROLE_ANY: case REVOKE_USER_ANY: + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY) + .setDatabase(statement.getDatabase()); if (hasGlobalPrivilege(auditEntity, PrivilegeType.SECURITY)) { ITableAuthCheckerImpl.recordAuditLog( - auditEntity, () -> statement.getUserName() + statement.getRoleName()); + auditEntity.setResult(true), () -> statement.getUserName() + statement.getRoleName()); return; } for (PrivilegeType privilegeType : statement.getPrivilegeTypes()) { @@ -320,9 +355,13 @@ public class AccessControlImpl implements AccessControl { case REVOKE_ROLE_ALL: case GRANT_USER_ALL: case REVOKE_USER_ALL: + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY) + .setDatabase(statement.getDatabase()); if (hasGlobalPrivilege(auditEntity, PrivilegeType.SECURITY)) { ITableAuthCheckerImpl.recordAuditLog( - auditEntity, () -> statement.getUserName() + statement.getRoleName()); + auditEntity.setResult(true), () -> statement.getUserName() + statement.getRoleName()); return; } for (TableModelPrivilege privilege : TableModelPrivilege.values()) { @@ -339,9 +378,13 @@ public class AccessControlImpl implements AccessControl { case GRANT_ROLE_DB: case REVOKE_USER_DB: case REVOKE_ROLE_DB: + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY) + .setDatabase(statement.getDatabase()); if (hasGlobalPrivilege(auditEntity, PrivilegeType.SECURITY)) { ITableAuthCheckerImpl.recordAuditLog( - auditEntity, () -> statement.getUserName() + statement.getRoleName()); + auditEntity.setResult(true), () -> statement.getUserName() + statement.getRoleName()); return; } for (PrivilegeType privilegeType : statement.getPrivilegeTypes()) { @@ -356,9 +399,13 @@ public class AccessControlImpl implements AccessControl { case GRANT_ROLE_TB: case REVOKE_USER_TB: case REVOKE_ROLE_TB: + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY) + .setDatabase(statement.getDatabase()); if (hasGlobalPrivilege(auditEntity, PrivilegeType.SECURITY)) { ITableAuthCheckerImpl.recordAuditLog( - auditEntity, () -> statement.getUserName() + statement.getRoleName()); + auditEntity.setResult(true), () -> statement.getUserName() + statement.getRoleName()); return; } for (PrivilegeType privilegeType : statement.getPrivilegeTypes()) { @@ -374,9 +421,12 @@ public class AccessControlImpl implements AccessControl { case GRANT_ROLE_SYS: case REVOKE_USER_SYS: case REVOKE_ROLE_SYS: + auditEntity + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY); if (hasGlobalPrivilege(auditEntity, PrivilegeType.SECURITY)) { ITableAuthCheckerImpl.recordAuditLog( - auditEntity, () -> statement.getUserName() + statement.getRoleName()); + auditEntity.setResult(true), () -> statement.getUserName() + statement.getRoleName()); return; } for (PrivilegeType privilegeType : statement.getPrivilegeTypes()) {
