This is an automated email from the ASF dual-hosted git repository. justinchen pushed a commit to branch fix-audit-logger in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit 69d1381d5cb6aaf59f2454cf66c35fc7c36d25a3 Author: Yongzao <[email protected]> AuthorDate: Sat Sep 27 21:25:10 2025 +0800 Audit log patch for both tree and table models (#16497) --- .../iotdb/db/it/audit/IoTDBAuditLogBasicIT.java | 579 ++++++++++++++++++++- .../org/apache/iotdb/db/audit/DNAuditLogger.java | 20 +- .../org/apache/iotdb/db/auth/AuthorityChecker.java | 24 +- .../protocol/thrift/IoTDBDataNodeReceiver.java | 8 +- .../sink/protocol/writeback/WriteBackSink.java | 7 +- .../PipePlanTreePrivilegeParseVisitor.java | 5 +- .../db/protocol/client/DataNodeInternalClient.java | 7 +- .../iotdb/db/protocol/mqtt/MPPPublishHandler.java | 7 +- .../rest/handler/AuthorizationHandler.java | 12 +- .../protocol/thrift/impl/ClientRPCServiceImpl.java | 219 +++++++- .../analyze/schema/AutoCreateSchemaExecutor.java | 3 +- .../relational/analyzer/StatementAnalyzer.java | 37 +- .../plan/relational/security/AccessControl.java | 3 +- .../relational/security/AccessControlImpl.java | 86 ++- .../relational/security/AllowAllAccessControl.java | 4 +- .../security/TreeAccessCheckContext.java | 21 +- .../security/TreeAccessCheckVisitor.java | 39 +- .../iotdb/commons/audit/AbstractAuditLogger.java | 2 +- 18 files changed, 926 insertions(+), 157 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..4d96a41406c 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 @@ -27,9 +27,9 @@ import org.apache.iotdb.it.framework.IoTDBTestRunner; import org.apache.iotdb.itbase.category.LocalStandaloneIT; import org.apache.iotdb.itbase.env.BaseEnv; -import org.junit.AfterClass; +import org.junit.After; import org.junit.Assert; -import org.junit.BeforeClass; +import org.junit.Before; import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.runner.RunWith; @@ -43,18 +43,20 @@ 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; +/** + * This test class ensures the audit log behave exactly the same as we expected, including the + * number, sequence and content of the audit logs. + */ @RunWith(IoTDBTestRunner.class) @Category({LocalStandaloneIT.class}) public class IoTDBAuditLogBasicIT { private static final Logger LOGGER = LoggerFactory.getLogger(IoTDBAuditLogBasicIT.class); - - /** - * Ensure the Audit log behave exactly the same as we expected, including number, sequence and - * content. - */ private static final List<String> AUDIT_TABLE_COLUMNS = Arrays.asList( AbstractAuditLogger.AUDIT_LOG_NODE_ID, @@ -93,8 +95,8 @@ public class IoTDBAuditLogBasicIT { private static final String AUDITABLE_OPERATION_RESULT = "SUCCESS,FAIL"; - @BeforeClass - public static void setUp() throws SQLException { + @Before + public void setUp() throws SQLException { EnvFactory.getEnv() .getConfig() .getCommonConfig() @@ -130,8 +132,8 @@ public class IoTDBAuditLogBasicIT { } } - @AfterClass - public static void tearDown() { + @After + public void tearDown() { EnvFactory.getEnv().cleanClusterEnvironment(); } @@ -145,13 +147,22 @@ 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"); private static final List<List<String>> TABLE_MODEL_AUDIT_FIELDS = Arrays.asList( - // Start DataNode + // Start audit service Arrays.asList( "node_1", "u_none", @@ -165,7 +176,7 @@ public class IoTDBAuditLogBasicIT { "null", "null", "Successfully start the Audit service with configurations (auditableOperationType [DDL, DML, QUERY, CONTROL], auditableOperationLevel GLOBAL, auditableOperationResult SUCCESS,FAIL)"), - // Show audit database TODO: Fix typo in tree model + // Show audit database Arrays.asList( "node_1", "u_0", @@ -174,10 +185,10 @@ public class IoTDBAuditLogBasicIT { "OBJECT_AUTHENTICATION", "QUERY", "[MANAGE_DATABASE]", - "GLOBAL", + "OBJECT", "true", "[root.__audit]", - "null", + "SHOW DATABASES root.__audit", "User root (ID=0) requests authority on object root.__audit with result true"), // Desc audit table Arrays.asList( @@ -262,7 +273,7 @@ public class IoTDBAuditLogBasicIT { "test", "ALTER DATABASE test SET PROPERTIES TTL='INF'", "User root (ID=0) requests authority on object test with result true"), - // Use database TODO: Find out why twice + // Use database, twice for both read and write connections Arrays.asList( "node_1", "u_0", @@ -297,8 +308,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 +342,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 ...", + "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: whether to include user object? + 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: whether to include role object? + 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", @@ -343,9 +466,36 @@ public class IoTDBAuditLogBasicIT { "OBJECT", "true", "test", - "INSERT INTO table1(time, t1, a1, s1) values(1, 't1', 'a1', 's1')", + "INSERT INTO table1(time, t1, a1, s1) values(...)", "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 +507,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 +537,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 +549,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 +562,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> TABLE_INDEX_FOR_CONTAIN = + Stream.of(11, 12).collect(Collectors.toSet()); @Test public void basicAuditLogTestForTableModel() throws SQLException { @@ -409,7 +575,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,13 +584,372 @@ 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 (TABLE_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); } } + + private static final List<String> TREE_MODEL_AUDIT_SQLS = + Arrays.asList( + "CREATE DATABASE root.test", + "CREATE TIMESERIES root.test.d1.s2 WITH DATATYPE=INT64", + "CREATE ALIGNED TIMESERIES root.test.d2(s1 BOOLEAN, s2 INT64)", + "ALTER TIMESERIES root.test.d2.s1 ADD TAGS tag3=v3, tag4=v4", + "CREATE USER user1 'IoTDB@2021abc'", + "CREATE role role1", + "GRANT READ_DATA, WRITE_DATA ON root.test TO ROLE role1", + "GRANT ROLE role1 TO user1", + "LIST USER", + "LIST ROLE", + "DROP ROLE role1", + "DROP USER user1", + "INSERT INTO root.test.d1(timestamp,s2) VALUES(1,1)", + "INSERT INTO root.test.d2(timestamp,s1,s2) ALIGNED VALUES(1,true,1)", + "SELECT ** FROM root.test", + "DELETE FROM root.test.d2", + "DROP TIMESERIES root.test.d1.s2", + "set ttl to root.test.** 360000", + "DELETE DATABASE root.test"); + private static final List<List<String>> TREE_MODEL_AUDIT_FIELDS = + Arrays.asList( + // Start audit service + Arrays.asList( + "root.__audit.log.node_1.u_none", + "true", + "GLOBAL", + "[AUDIT]", + "null", + "CONTROL", + "Successfully start the Audit service with configurations (auditableOperationType [DDL, DML, QUERY, CONTROL], auditableOperationLevel GLOBAL, auditableOperationResult SUCCESS,FAIL)", + "null", + "CHANGE_AUDIT_OPTION", + "null", + "null"), + // Show audit database + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[MANAGE_DATABASE]", + "[root.__audit]", + "QUERY", + "User root (ID=0) requests authority on object root.__audit with result true", + "SHOW DATABASES root.__audit", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Desc audit table + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[READ_SCHEMA]", + "__audit", + "QUERY", + "User root (ID=0) requests authority on object audit_log with result true", + "DESC __audit.audit_log", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Create database + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[MANAGE_DATABASE]", + "root.test", + "DDL", + "User root (ID=0) requests authority on object root.test with result true", + "CREATE DATABASE root.test", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Create (aligned) timeseries TODO: fill database if necessary, same as follows + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[WRITE_SCHEMA]", + "null", + "DDL", + "User root (ID=0) requests authority on object [root.test.d1.s2] with result true", + "CREATE TIMESERIES root.test.d1.s2 WITH DATATYPE=INT64", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[WRITE_SCHEMA]", + "null", + "DDL", + "User root (ID=0) requests authority on object [root.test.d2.s1, root.test.d2.s2] with result true", + "CREATE ALIGNED TIMESERIES root.test.d2(s1 BOOLEAN, s2 INT64)", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Alter timeseries + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[WRITE_SCHEMA]", + "null", + "DDL", + "User root (ID=0) requests authority on object [root.test.d2.s1] with result true", + "ALTER TIMESERIES root.test.d2.s1 ADD TAGS tag3=v3, tag4=v4", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Create user + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "GLOBAL", + "[MANAGE_USER]", + "null", + "DDL", + "User root (ID=0) requests authority on object user1 with result true", + "CREATE USER user1 ...", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Create role + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "GLOBAL", + "[MANAGE_ROLE]", + "null", + "DDL", + "User root (ID=0) requests authority on object role1 with result true", + "CREATE role role1", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Grant privileges to role + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "GLOBAL", + "[SECURITY]", + "null", + "DDL", + "User root (ID=0) requests authority on object role1 with result true", + "GRANT READ_DATA, WRITE_DATA ON root.test TO ROLE role1", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Grant role to user + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "GLOBAL", + "[MANAGE_ROLE]", + "null", + "DDL", + "User root (ID=0) requests authority on object user: user1, role: role1 with result true", + "GRANT ROLE role1 TO user1", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // List user TODO: whether to include user object? + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "GLOBAL", + "[MANAGE_USER]", + "null", + "QUERY", + "User root (ID=0) requests authority on object null with result true", + "LIST USER", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // List role TODO: whether to include role object? + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "GLOBAL", + "[MANAGE_ROLE]", + "null", + "QUERY", + "User root (ID=0) requests authority on object null with result true", + "LIST ROLE", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Drop role + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "GLOBAL", + "[MANAGE_ROLE]", + "null", + "DDL", + "User root (ID=0) requests authority on object role1 with result true", + "DROP ROLE role1", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Drop user + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "GLOBAL", + "[MANAGE_USER]", + "null", + "DDL", + "User root (ID=0) requests authority on object user1 with result true", + "DROP USER user1", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Insert into (aligned) timeseries + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[WRITE_DATA]", + "null", + "DML", + "User root (ID=0) requests authority on object [root.test.d1.s2] with result true", + "INSERT INTO root.test.d1(timestamp,s2) VALUES(...)", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[WRITE_DATA]", + "null", + "DML", + "User root (ID=0) requests authority on object [root.test.d2.s1, root.test.d2.s2] with result true", + "INSERT INTO root.test.d2(timestamp,s1,s2) ALIGNED VALUES(...)", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Select all timeseries + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[READ_DATA]", + "null", + "QUERY", + "User root (ID=0) requests authority on object [root.test.**] with result true", + "SELECT ** FROM root.test", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Delete timeseries data + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[WRITE_DATA]", + "null", + "DML", + "User root (ID=0) requests authority on object [root.test.d2] with result true", + "DELETE FROM root.test.d2", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Drop timeseries + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[WRITE_SCHEMA]", + "null", + "DDL", + "User root (ID=0) requests authority on object [root.test.d1.s2] with result true", + "DROP TIMESERIES root.test.d1.s2", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Set TTL to devices + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "GLOBAL", + "[SYSTEM]", + "null", + "DDL", + "User root (ID=0) requests authority on object [root.test.**] with result true", + "set ttl to root.test.** 360000", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Delete database + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[MANAGE_DATABASE]", + "[root.test]", + "DDL", + "User root (ID=0) requests authority on object [root.test] with result true", + "DELETE DATABASE root.test", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root"), + // Select audit log + Arrays.asList( + "root.__audit.log.node_1.u_0", + "true", + "OBJECT", + "[READ_DATA]", + "null", + "QUERY", + "User root (ID=0) requests authority on object [root.__audit.log.**.*] with result true", + "SELECT * FROM root.__audit.log.** ORDER BY TIME ALIGN BY DEVICE", + "OBJECT_AUTHENTICATION", + "127.0.0.1", + "root")); + private static final Set<Integer> TREE_INDEX_FOR_CONTAIN = + Stream.of(7).collect(Collectors.toSet()); + + @Test + public void basicAuditLogTestForTreeModel() throws SQLException { + try (Connection connection = EnvFactory.getEnv().getConnection(BaseEnv.TREE_SQL_DIALECT); + Statement statement = connection.createStatement()) { + for (String sql : TREE_MODEL_AUDIT_SQLS) { + statement.execute(sql); + } + int count = 0; + ResultSet resultSet = + statement.executeQuery("SELECT * FROM root.__audit.log.** ORDER BY TIME ALIGN BY DEVICE"); + while (resultSet.next()) { + LOGGER.info("Expected audit log: {}", TREE_MODEL_AUDIT_FIELDS.get(count)); + List<String> actualFields = new ArrayList<>(); + for (int i = 1; i <= 11; i++) { + actualFields.add(resultSet.getString(i + 1)); + } + LOGGER.info("Actual audit log: {}", actualFields); + List<String> expectedFields = TREE_MODEL_AUDIT_FIELDS.get(count); + for (int i = 1; i <= 11; i++) { + if (TREE_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)); + } + + count++; + } + Assert.assertEquals(TREE_MODEL_AUDIT_FIELDS.size(), count); + } + } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/DNAuditLogger.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/DNAuditLogger.java index c26bbb1a38d..1fdb138592f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/DNAuditLogger.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/audit/DNAuditLogger.java @@ -77,6 +77,8 @@ import java.util.List; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; +import java.util.regex.Matcher; +import java.util.regex.Pattern; import static org.apache.iotdb.db.pipe.receiver.protocol.legacy.loader.ILoader.SCHEMA_FETCHER; @@ -159,6 +161,20 @@ public class DNAuditLogger extends AbstractAuditLogger { AUDIT_LOG_LOG }); insertStatement.setAligned(false); + String sqlString = auditLogFields.getSqlString(); + if (sqlString != null) { + if (sqlString.toUpperCase().startsWith("CREATE USER")) { + sqlString = String.join(" ", Arrays.asList(sqlString.split(" ")).subList(0, 3)) + " ..."; + } + Pattern pattern = Pattern.compile("(?i)(values)\\([^)]*\\)"); + Matcher matcher = pattern.matcher(sqlString); + StringBuffer sb = new StringBuffer(); + while (matcher.find()) { + matcher.appendReplacement(sb, matcher.group(1) + "(...)"); + } + matcher.appendTail(sb); + sqlString = sb.toString(); + } insertStatement.setValues( new Object[] { new Binary(username == null ? "null" : username, TSFileConfig.STRING_CHARSET), @@ -178,9 +194,7 @@ public class DNAuditLogger extends AbstractAuditLogger { new Binary( auditLogFields.getDatabase() == null ? "null" : auditLogFields.getDatabase(), TSFileConfig.STRING_CHARSET), - new Binary( - auditLogFields.getSqlString() == null ? "null" : auditLogFields.getSqlString(), - TSFileConfig.STRING_CHARSET), + new Binary(sqlString == null ? "null" : sqlString, TSFileConfig.STRING_CHARSET), new Binary(log == null ? "null" : log, TSFileConfig.STRING_CHARSET) }); insertStatement.setDataTypes( 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 31a7ffe3a11..bbd9ada2b9e 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 @@ -45,6 +45,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControlImpl; import org.apache.iotdb.db.queryengine.plan.relational.security.ITableAuthCheckerImpl; +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.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.statement.Statement; @@ -176,22 +177,21 @@ public class AuthorityChecker { } } - /** Check whether specific Session has the authorization to given plan. */ - public static TSStatus checkAuthority(Statement statement, IClientSession session) { + public static TSStatus checkAuthority(Statement statement, IAuditEntity auditEntity) { long startTime = System.nanoTime(); try { + if (auditEntity instanceof TreeAccessCheckContext) { + return accessControl.checkPermissionBeforeProcess( + statement, (TreeAccessCheckContext) auditEntity); + } return accessControl.checkPermissionBeforeProcess( statement, - new UserEntity(session.getUserId(), session.getUsername(), session.getClientAddress())); - } finally { - PERFORMANCE_OVERVIEW_METRICS.recordAuthCost(System.nanoTime() - startTime); - } - } - - public static TSStatus checkAuthority(Statement statement, UserEntity userEntity) { - long startTime = System.nanoTime(); - try { - return accessControl.checkPermissionBeforeProcess(statement, userEntity); + (TreeAccessCheckContext) + new TreeAccessCheckContext( + auditEntity.getUserId(), + auditEntity.getUsername(), + auditEntity.getCliHostname()) + .setSqlString(auditEntity.getSqlString())); } finally { PERFORMANCE_OVERVIEW_METRICS.recordAuthCost(System.nanoTime() - startTime); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java index 3544e6736f6..1511933ad35 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/receiver/protocol/thrift/IoTDBDataNodeReceiver.java @@ -86,6 +86,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.ClusterCon import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.CreateDBTask; import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.metadata.write.view.AlterLogicalViewNode; +import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckContext; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.PipeEnriched; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; import org.apache.iotdb.db.queryengine.plan.statement.Statement; @@ -880,7 +881,12 @@ public class IoTDBDataNodeReceiver extends IoTDBFileReceiver { // For table model, the authority check is done in inner execution. No need to check here if (!isTableModelStatement) { final TSStatus permissionCheckStatus = - AuthorityChecker.checkAuthority(statement, clientSession); + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (permissionCheckStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { PipeLogger.log( LOGGER::warn, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/writeback/WriteBackSink.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/writeback/WriteBackSink.java index 53af1bed26a..fddb9f2f1a9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/writeback/WriteBackSink.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/sink/protocol/writeback/WriteBackSink.java @@ -45,6 +45,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.executor.ClusterCon import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.CreateDBTask; import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner; import org.apache.iotdb.db.queryengine.plan.planner.plan.node.write.InsertNode; +import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckContext; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; import org.apache.iotdb.db.queryengine.plan.statement.Statement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertBaseStatement; @@ -481,7 +482,11 @@ public class WriteBackSink implements PipeConnector { if (useEventUserName && userName != null) { session.setUsername(userName); } - final TSStatus permissionCheckStatus = AuthorityChecker.checkAuthority(statement, session); + final TSStatus permissionCheckStatus = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + session.getUserId(), session.getUsername(), session.getClientAddress())); if (permissionCheckStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { PipeLogger.log( LOGGER::warn, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/schemaregion/PipePlanTreePrivilegeParseVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/schemaregion/PipePlanTreePrivilegeParseVisitor.java index 260f6bd1d37..5967097418d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/schemaregion/PipePlanTreePrivilegeParseVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/pipe/source/schemaregion/PipePlanTreePrivilegeParseVisitor.java @@ -20,7 +20,6 @@ package org.apache.iotdb.db.pipe.source.schemaregion; import org.apache.iotdb.commons.audit.IAuditEntity; -import org.apache.iotdb.commons.audit.UserEntity; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.exception.auth.AccessDeniedException; import org.apache.iotdb.commons.path.MeasurementPath; @@ -328,7 +327,9 @@ public class PipePlanTreePrivilegeParseVisitor final DeleteDataNode node, final IAuditEntity auditEntity) { final List<MeasurementPath> intersectedPaths = TreeAccessCheckVisitor.getIntersectedPaths4Pipe( - node.getPathList(), new TreeAccessCheckContext((UserEntity) auditEntity)); + node.getPathList(), + new TreeAccessCheckContext( + auditEntity.getUserId(), auditEntity.getUsername(), auditEntity.getCliHostname())); if (!skip && !intersectedPaths.equals(node.getPathList())) { throw new AccessDeniedException("Not has privilege to transfer plan: " + node); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/DataNodeInternalClient.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/DataNodeInternalClient.java index d96151b2e93..227f8731cae 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/DataNodeInternalClient.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/DataNodeInternalClient.java @@ -38,6 +38,7 @@ import org.apache.iotdb.db.queryengine.plan.analyze.schema.ISchemaFetcher; import org.apache.iotdb.db.queryengine.plan.execution.ExecutionResult; import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; +import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckContext; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertMultiTabletsStatement; @@ -98,7 +99,11 @@ public class DataNodeInternalClient { public TSStatus insertTablets(InsertMultiTabletsStatement statement) { try { // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, session); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + session.getUserId(), session.getUsername(), session.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/mqtt/MPPPublishHandler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/mqtt/MPPPublishHandler.java index a6464a56c49..c993da93b73 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/mqtt/MPPPublishHandler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/mqtt/MPPPublishHandler.java @@ -37,6 +37,7 @@ import org.apache.iotdb.db.queryengine.plan.analyze.schema.ISchemaFetcher; import org.apache.iotdb.db.queryengine.plan.execution.ExecutionResult; import org.apache.iotdb.db.queryengine.plan.planner.LocalExecutionPlanner; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; +import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckContext; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertTabletStatement; @@ -279,7 +280,11 @@ public class MPPPublishHandler extends AbstractInterceptHandler { } statement.setAligned(false); - tsStatus = AuthorityChecker.checkAuthority(statement, session); + tsStatus = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + session.getUserId(), session.getUsername(), session.getClientAddress())); if (tsStatus.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { LOG.warn(tsStatus.message); } else { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/rest/handler/AuthorizationHandler.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/rest/handler/AuthorizationHandler.java index 989f5211670..3c30e3e95a9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/rest/handler/AuthorizationHandler.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/rest/handler/AuthorizationHandler.java @@ -19,29 +19,19 @@ package org.apache.iotdb.db.protocol.rest.handler; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.audit.UserEntity; -import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.db.auth.AuthorityChecker; -import org.apache.iotdb.db.auth.BasicAuthorityCache; -import org.apache.iotdb.db.auth.ClusterAuthorityFetcher; -import org.apache.iotdb.db.auth.IAuthorityFetcher; import org.apache.iotdb.db.protocol.rest.model.ExecutionStatus; import org.apache.iotdb.db.queryengine.plan.statement.Statement; import org.apache.iotdb.rpc.TSStatusCode; -import org.apache.ratis.util.MemoizedSupplier; - import javax.ws.rs.core.Response; import javax.ws.rs.core.SecurityContext; public class AuthorizationHandler { - private static final MemoizedSupplier<IAuthorityFetcher> authorityFetcher = - MemoizedSupplier.valueOf(() -> new ClusterAuthorityFetcher(new BasicAuthorityCache())); - public Response checkAuthority(SecurityContext securityContext, Statement statement) { String userName = securityContext.getUserPrincipal().getName(); - User user = authorityFetcher.get().getUser(userName); - long userId = user == null ? -1 : user.getUserId(); + long userId = AuthorityChecker.getUserId(userName).orElse(-1L); TSStatus status = AuthorityChecker.checkAuthority(statement, new UserEntity(userId, userName, "")); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java index fb7a0813002..74c2f6eda6b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/thrift/impl/ClientRPCServiceImpl.java @@ -88,6 +88,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.Ta import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.TableDeviceSchemaCache; import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.TableId; import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.cache.TreeDeviceSchemaCacheManager; +import org.apache.iotdb.db.queryengine.plan.relational.security.TreeAccessCheckContext; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetSqlDialect; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Use; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.ParsingException; @@ -346,7 +347,14 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { false); } else { // permission check - TSStatus status = AuthorityChecker.checkAuthority(s, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + s, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress()) + .setSqlString(statement)); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return RpcUtils.getTSExecuteStatementResp(status); } @@ -521,7 +529,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { Statement s = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(s, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + s, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return RpcUtils.getTSExecuteStatementResp(status); } @@ -610,7 +624,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { try { Statement s = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(s, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + s, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return RpcUtils.getTSExecuteStatementResp(status); } @@ -701,7 +721,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { try { Statement s = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(s, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + s, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return RpcUtils.getTSExecuteStatementResp(status); } @@ -1145,7 +1171,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { // cache miss Statement s = StatementGenerator.createStatement(convert(req)); // permission check - TSStatus status = AuthorityChecker.checkAuthority(s, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + s, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return RpcUtils.getTSExecuteStatementResp(status); } @@ -1540,7 +1572,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { // Step 1: Create SetStorageGroupStatement DatabaseSchemaStatement statement = StatementGenerator.createStatement(storageGroup); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -1578,7 +1616,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { // Step 1: transfer from TSCreateTimeseriesReq to Statement CreateTimeSeriesStatement statement = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -1622,7 +1666,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { // Step 1: transfer from CreateAlignedTimeSeriesReq to Statement CreateAlignedTimeSeriesStatement statement = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -1666,7 +1716,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { // Step 1: transfer from CreateMultiTimeSeriesReq to Statement CreateMultiTimeSeriesStatement statement = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -1706,7 +1762,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { StatementGenerator.createDeleteTimeSeriesStatement(path); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -1745,7 +1807,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { DeleteDatabaseStatement statement = StatementGenerator.createStatement(storageGroups); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -1825,7 +1893,14 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { false); } else { // permission check - TSStatus status = AuthorityChecker.checkAuthority(s, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + s, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress()) + .setSqlString(statement)); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2015,7 +2090,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { } // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2075,7 +2156,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { } // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2137,7 +2224,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { } // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2201,7 +2294,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { } // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2270,7 +2369,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { } // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2333,7 +2438,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { // permission check for tree model, table model does this in the analysis stage if (!req.isWriteToTable()) { - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2404,7 +2515,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { } // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2494,7 +2611,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { DeleteDataStatement statement = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2554,7 +2677,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { CreateSchemaTemplateStatement statement = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2717,7 +2846,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { try { IClientSession clientSession = SESSION_MANAGER.getCurrSessionAndUpdateIdleTime(); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { resp.setStatus(status); return resp; @@ -2796,7 +2931,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { SetSchemaTemplateStatement statement = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2837,7 +2978,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { UnsetSchemaTemplateStatement statement = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2878,7 +3025,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { DropSchemaTemplateStatement statement = StatementGenerator.createStatement(req); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -2917,7 +3070,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { StatementGenerator.createBatchActivateTemplateStatement(req.getDevicePathList()); // permission check - TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } @@ -3007,7 +3166,13 @@ public class ClientRPCServiceImpl implements IClientRPCServiceWithHandler { final InsertRowStatement statement = StatementGenerator.createStatement(req); // Permission check - final TSStatus status = AuthorityChecker.checkAuthority(statement, clientSession); + final TSStatus status = + AuthorityChecker.checkAuthority( + statement, + new TreeAccessCheckContext( + clientSession.getUserId(), + clientSession.getUsername(), + clientSession.getClientAddress())); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; } 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 37be5691ee5..6806b224778 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 @@ -495,8 +495,7 @@ class AutoCreateSchemaExecutor { // Auto create timeseries and return the existing timeseries info private List<MeasurementPath> executeInternalCreateTimeseriesStatement( final Statement statement, final MPPQueryContext context) { - final TSStatus status = - AuthorityChecker.checkAuthority(statement, context.getSession().getUserEntity()); + final 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/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/AccessControl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java index 6a1cf27d98a..35c04bf4c18 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java @@ -21,7 +21,6 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.audit.IAuditEntity; -import org.apache.iotdb.commons.audit.UserEntity; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.exception.auth.AccessDeniedException; import org.apache.iotdb.commons.path.PartialPath; @@ -218,7 +217,7 @@ public interface AccessControl { // ====================================== TREE ============================================= - TSStatus checkPermissionBeforeProcess(Statement statement, UserEntity userEntity); + TSStatus checkPermissionBeforeProcess(Statement statement, TreeAccessCheckContext context); /** called by load */ TSStatus checkFullPathWriteDataPermission( 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..af1dec2f68a 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 @@ -22,7 +22,6 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.audit.AuditLogOperation; import org.apache.iotdb.commons.audit.IAuditEntity; -import org.apache.iotdb.commons.audit.UserEntity; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.exception.auth.AccessDeniedException; @@ -107,7 +106,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 +118,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 +128,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 +252,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 +336,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 +354,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 +377,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 +398,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 +420,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()) { @@ -420,8 +469,9 @@ public class AccessControlImpl implements AccessControl { } @Override - public TSStatus checkPermissionBeforeProcess(Statement statement, UserEntity userEntity) { - return treeAccessCheckVisitor.process(statement, new TreeAccessCheckContext(userEntity)); + public TSStatus checkPermissionBeforeProcess( + Statement statement, TreeAccessCheckContext context) { + return treeAccessCheckVisitor.process(statement, context); } @Override diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java index 3ce14c2fd47..11b20883dbb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java @@ -21,7 +21,6 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.audit.IAuditEntity; -import org.apache.iotdb.commons.audit.UserEntity; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; @@ -116,7 +115,8 @@ public class AllowAllAccessControl implements AccessControl { String username, Collection<PrivilegeType> privilegeTypes, IAuditEntity auditEntity) {} @Override - public TSStatus checkPermissionBeforeProcess(Statement statement, UserEntity userEntity) { + public TSStatus checkPermissionBeforeProcess( + Statement statement, TreeAccessCheckContext context) { return SUCCEED; } 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 dc92249a0bb..5dbb91cdbe6 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 @@ -22,7 +22,6 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; import org.apache.iotdb.commons.audit.AuditEventType; import org.apache.iotdb.commons.audit.AuditLogOperation; import org.apache.iotdb.commons.audit.IAuditEntity; -import org.apache.iotdb.commons.audit.UserEntity; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import java.util.Collections; @@ -30,25 +29,29 @@ import java.util.List; public class TreeAccessCheckContext implements IAuditEntity { - private final UserEntity userEntity; + private final long userId; + private final String username; + private final String cliHostname; - public TreeAccessCheckContext(UserEntity userEntity) { - this.userEntity = userEntity; + public TreeAccessCheckContext(long userId, String username, String cliHostname) { + this.userId = userId; + this.username = username; + this.cliHostname = cliHostname; } @Override public long getUserId() { - return userEntity.getUserId(); + return userId; } @Override public String getUsername() { - return userEntity.getUsername(); + return username; } @Override public String getCliHostname() { - return userEntity.getCliHostname(); + return cliHostname; } private AuditEventType auditEventType; @@ -134,8 +137,4 @@ public class TreeAccessCheckContext implements IAuditEntity { this.sqlString = sqlString; return this; } - - public UserEntity getUserEntity() { - return userEntity; - } } 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 76761aab42f..6d8a63d6a83 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 @@ -530,9 +530,13 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces @Override public TSStatus visitAuthor(AuthorStatement statement, TreeAccessCheckContext context) { AuthorType authorType = statement.getAuthorType(); + Supplier<String> auditObject; switch (authorType) { case CREATE_USER: case DROP_USER: + context + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY); return checkGlobalAuth( context.setAuditLogOperation(AuditLogOperation.DDL), PrivilegeType.MANAGE_USER, @@ -542,22 +546,31 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces if (statement.getUserName().equals(context.getUsername())) { return RpcUtils.SUCCESS_STATUS; } + context + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY); return checkGlobalAuth( context.setAuditLogOperation(AuditLogOperation.DDL), PrivilegeType.MANAGE_USER, statement::getUserName); case LIST_USER: + context + .setAuditLogOperation(AuditLogOperation.QUERY) + .setPrivilegeType(PrivilegeType.SECURITY); if (checkHasGlobalAuth( context.setAuditLogOperation(AuditLogOperation.QUERY), PrivilegeType.MANAGE_USER, - null)) { + statement::getUserName)) { return RpcUtils.SUCCESS_STATUS; } statement.setUserName(context.getUsername()); return RpcUtils.SUCCESS_STATUS; case LIST_USER_PRIVILEGE: + context + .setAuditLogOperation(AuditLogOperation.QUERY) + .setPrivilegeType(PrivilegeType.SECURITY); if (context.getUsername().equals(statement.getUserName())) { return RpcUtils.SUCCESS_STATUS; } @@ -567,6 +580,9 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces statement::getUserName); case LIST_ROLE_PRIVILEGE: + context + .setAuditLogOperation(AuditLogOperation.QUERY) + .setPrivilegeType(PrivilegeType.SECURITY); if (!AuthorityChecker.checkRole(context.getUsername(), statement.getRoleName())) { return checkGlobalAuth( context.setAuditLogOperation(AuditLogOperation.QUERY), @@ -577,10 +593,13 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces } case LIST_ROLE: + context + .setAuditLogOperation(AuditLogOperation.QUERY) + .setPrivilegeType(PrivilegeType.SECURITY); if (checkHasGlobalAuth( context.setAuditLogOperation(AuditLogOperation.QUERY), PrivilegeType.MANAGE_ROLE, - null)) { + statement::getRoleName)) { return SUCCEED; } // list roles of other user is not allowed @@ -595,17 +614,27 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces case DROP_ROLE: case GRANT_USER_ROLE: case REVOKE_USER_ROLE: + context + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY); + auditObject = + authorType == AuthorType.CREATE_ROLE || authorType == AuthorType.DROP_ROLE + ? statement::getRoleName + : () -> "user: " + statement.getUserName() + ", role: " + statement.getRoleName(); return checkGlobalAuth( context.setAuditLogOperation(AuditLogOperation.DDL), PrivilegeType.MANAGE_ROLE, - statement::getRoleName); + auditObject); case REVOKE_USER: case GRANT_USER: case GRANT_ROLE: case REVOKE_ROLE: + context + .setAuditLogOperation(AuditLogOperation.DDL) + .setPrivilegeType(PrivilegeType.SECURITY); context.setAuditLogOperation(AuditLogOperation.DDL); - Supplier<String> auditObject = + auditObject = () -> authorType == AuthorType.REVOKE_USER || authorType == AuthorType.GRANT_USER ? statement.getUserName() @@ -1383,6 +1412,7 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces @Override public TSStatus visitAlterTimeSeries( AlterTimeSeriesStatement statement, TreeAccessCheckContext context) { + context.setAuditLogOperation(AuditLogOperation.DDL); // audit db is read-only if (includeByAuditTreeDB(statement.getPath()) && !context.getUsername().equals(AuthorityChecker.INTERNAL_AUDIT_USER)) { @@ -1398,6 +1428,7 @@ public class TreeAccessCheckVisitor extends StatementVisitor<TSStatus, TreeAcces @Override public TSStatus visitDeleteTimeSeries( DeleteTimeSeriesStatement statement, TreeAccessCheckContext context) { + context.setAuditLogOperation(AuditLogOperation.DDL); // audit db is read-only for (PartialPath path : statement.getPathPatternList()) { if (includeByAuditTreeDB(path) diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AbstractAuditLogger.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AbstractAuditLogger.java index 82eac3606d0..d2d8ce6046c 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AbstractAuditLogger.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/audit/AbstractAuditLogger.java @@ -94,6 +94,7 @@ public abstract class AbstractAuditLogger { case DELETE: case INSERT: case SELECT: + case MANAGE_DATABASE: case WRITE_DATA: case READ_SCHEMA: case WRITE_SCHEMA: @@ -106,7 +107,6 @@ public abstract class AbstractAuditLogger { case MANAGE_ROLE: case MANAGE_USER: case USE_TRIGGER: - case MANAGE_DATABASE: case EXTEND_TEMPLATE: default: return PrivilegeLevel.GLOBAL;
