From 1b0af7dfd25186e196eb2453ba50ccec42b3d0a4 Mon Sep 17 00:00:00 2001 From: Colin Lee <105656576+ColinLeeo@users.noreply.github.com> Date: Fri, 24 Jan 2025 20:07:00 +0800 Subject: [PATCH 1/3] Permission for Relation model --- .../database/IoTDBDatabaseSetAndDeleteIT.java | 4 +- .../apache/iotdb/db/it/auth/IoTDBAuthIT.java | 137 +-- .../db/it/auth/IoTDBClusterAuthorityIT.java | 71 +- .../IoTDBClusterAuthorityRelationalIT.java | 513 ++++++++++ .../db/it/auth/IoTDBRelationalAuthIT.java | 350 +++++++ .../it/manual/IoTDBPipeMetaHistoricalIT.java | 16 +- .../consensus/request/ConfigPhysicalPlan.java | 34 +- .../request/ConfigPhysicalPlanType.java | 33 + .../request/read/auth/AuthorReadPlan.java | 143 --- .../request/write/auth/AuthorPlan.java | 168 +--- .../write/auth/AuthorRelationalPlan.java | 167 ++++ .../request/write/auth/AuthorTreePlan.java | 176 ++++ .../ConfigRegionStateMachine.java | 1 - .../confignode/manager/ConfigManager.java | 18 +- .../iotdb/confignode/manager/IManager.java | 6 +- .../confignode/manager/PermissionManager.java | 23 +- ...igPhysicalPlanTreePatternParseVisitor.java | 34 +- .../protocol/IoTDBConfigNodeReceiver.java | 66 +- .../confignode/persistence/AuthorInfo.java | 551 ++++++----- .../executor/ConfigPlanExecutor.java | 40 +- .../schema/CNPhysicalPlanGenerator.java | 148 ++- .../impl/sync/AuthOperationProcedure.java | 6 +- .../thrift/ConfigNodeRPCServiceProcessor.java | 98 +- .../request/ConfigPhysicalPlanSerDeTest.java | 45 +- ...ysicalPlanTreePatternParseVisitorTest.java | 18 +- .../persistence/AuthorInfoTest.java | 917 ++++++++---------- .../CNPhysicalPlanGeneratorTest.java | 23 +- .../ConfigRegionListeningQueueTest.java | 4 +- .../receiver/PipeEnrichedProcedureTest.java | 4 +- .../impl/sync/AuthOperationProcedureTest.java | 39 +- .../iotdb/db/auth/AuthorityChecker.java | 215 +++- .../iotdb/db/auth/BasicAuthorityCache.java | 8 +- .../db/auth/ClusterAuthorityFetcher.java | 475 +++++---- .../iotdb/db/auth/IAuthorityFetcher.java | 37 +- .../db/protocol/client/ConfigNodeClient.java | 20 +- ...formationSchemaContentSupplierFactory.java | 9 +- .../db/queryengine/plan/Coordinator.java | 9 +- .../cache/partition/PartitionCache.java | 5 +- .../TreeSchemaAutoCreatorAndVerifier.java | 2 +- .../schema/AutoCreateSchemaExecutor.java | 6 +- .../config/TableConfigTaskVisitor.java | 17 +- .../relational/RelationalAuthorizerTask.java | 45 + .../queryengine/plan/parser/ASTVisitor.java | 2 +- .../write/view/AlterLogicalViewNode.java | 2 +- .../relational/security/AccessControl.java | 10 + .../security/AccessControlImpl.java | 217 +++++ .../security/AllowAllAccessControl.java | 7 + .../security/ITableAuthChecker.java | 17 + .../security/ITableAuthCheckerImpl.java | 169 ++++ .../security/TableModelPrivilege.java | 54 +- .../plan/relational/sql/ast/AstVisitor.java | 4 + .../sql/ast/RelationalAuthorStatement.java | 247 +++++ .../relational/sql/parser/AstBuilder.java | 260 +++++ .../relational/sql/util/SqlFormatter.java | 223 +++++ .../plan/relational/type/AuthorRType.java | 53 + .../AuthorityInformationStatement.java | 2 +- .../statement/crud/DeleteDataStatement.java | 3 +- .../statement/crud/InsertBaseStatement.java | 2 +- .../plan/statement/crud/InsertStatement.java | 2 +- .../plan/statement/crud/QueryStatement.java | 2 +- ...nternalBatchActivateTemplateStatement.java | 3 +- ...nternalCreateMultiTimeSeriesStatement.java | 2 +- .../InternalCreateTimeSeriesStatement.java | 2 +- .../metadata/AlterTimeSeriesStatement.java | 3 +- .../metadata/CountDevicesStatement.java | 6 +- .../metadata/CountTimeSeriesStatement.java | 6 +- .../CreateAlignedTimeSeriesStatement.java | 2 +- .../CreateContinuousQueryStatement.java | 2 +- .../metadata/CreateFunctionStatement.java | 2 +- .../CreateMultiTimeSeriesStatement.java | 2 +- .../metadata/CreateTimeSeriesStatement.java | 3 +- .../metadata/CreateTriggerStatement.java | 2 +- .../metadata/DatabaseSchemaStatement.java | 2 +- .../metadata/DeleteDatabaseStatement.java | 2 +- .../metadata/DeleteTimeSeriesStatement.java | 3 +- .../DropContinuousQueryStatement.java | 2 +- .../metadata/DropFunctionStatement.java | 2 +- .../metadata/DropTriggerStatement.java | 2 +- .../metadata/RemoveDataNodeStatement.java | 2 +- .../statement/metadata/SetTTLStatement.java | 3 +- .../metadata/ShowClusterStatement.java | 2 +- .../ShowContinuousQueriesStatement.java | 2 +- .../metadata/ShowDevicesStatement.java | 6 +- .../metadata/ShowFunctionsStatement.java | 2 +- .../metadata/ShowTimeSeriesStatement.java | 6 +- .../metadata/ShowTriggersStatement.java | 2 +- .../metadata/model/CreateModelStatement.java | 2 +- .../metadata/model/DropModelStatement.java | 2 +- .../metadata/model/ShowModelsStatement.java | 2 +- .../metadata/pipe/AlterPipeStatement.java | 2 +- .../pipe/CreatePipePluginStatement.java | 2 +- .../metadata/pipe/CreatePipeStatement.java | 2 +- .../pipe/DropPipePluginStatement.java | 2 +- .../metadata/pipe/DropPipeStatement.java | 2 +- .../pipe/ShowPipePluginsStatement.java | 2 +- .../metadata/pipe/ShowPipesStatement.java | 2 +- .../metadata/pipe/StartPipeStatement.java | 2 +- .../metadata/pipe/StopPipeStatement.java | 2 +- .../region/ExtendRegionStatement.java | 2 +- .../region/MigrateRegionStatement.java | 2 +- .../region/ReconstructRegionStatement.java | 2 +- .../region/RemoveRegionStatement.java | 2 +- .../subscription/CreateTopicStatement.java | 2 +- .../subscription/DropTopicStatement.java | 2 +- .../ShowSubscriptionsStatement.java | 2 +- .../subscription/ShowTopicsStatement.java | 2 +- .../template/ActivateTemplateStatement.java | 3 +- .../BatchActivateTemplateStatement.java | 3 +- .../template/DeactivateTemplateStatement.java | 3 +- .../view/AlterLogicalViewStatement.java | 6 +- .../view/CreateLogicalViewStatement.java | 6 +- .../view/DeleteLogicalViewStatement.java | 3 +- .../view/RenameLogicalViewStatement.java | 2 +- .../plan/statement/sys/AuthorStatement.java | 56 +- .../statement/sys/KillQueryStatement.java | 2 +- .../statement/sys/ShowQueriesStatement.java | 2 +- .../iotdb/db/auth/AuthorizerManagerTest.java | 318 +++--- .../authorizer/LocalFileAuthorizerTest.java | 226 ++--- .../db/auth/entity/DataBasePrivilegeTest.java | 75 ++ .../db/auth/entity/PathPrivilegeTest.java | 42 +- .../apache/iotdb/db/auth/entity/RoleTest.java | 57 +- .../db/auth/entity/TablePrivilegeTest.java | 67 ++ .../apache/iotdb/db/auth/entity/UserTest.java | 14 +- .../auth/role/LocalFileRoleAccessorTest.java | 87 +- .../auth/role/LocalFileRoleManagerTest.java | 162 +--- .../auth/user/LocalFileUserAccessorTest.java | 119 +-- .../auth/user/LocalFileUserManagerTest.java | 117 +-- .../plan/parser/StatementGeneratorTest.java | 13 +- .../sql/parser/AuthorStatementTest.java | 351 +++++++ .../encrypt/MessageDigestEncryptTest.java | 8 +- .../auth/authorizer/BasicAuthorizer.java | 271 ++---- .../commons/auth/authorizer/IAuthorizer.java | 142 +-- .../auth/authorizer/OpenIdAuthorizer.java | 9 +- .../auth/entity/DatabasePrivilege.java | 250 +++++ .../IEntityAccessor.java} | 42 +- .../iotdb/commons/auth/entity/ModelType.java | 26 + .../commons/auth/entity/PathPrivilege.java | 151 +-- .../commons/auth/entity/PriPrivilegeType.java | 153 --- .../auth/entity/PrivilegeModelType.java | 26 + .../commons/auth/entity/PrivilegeType.java | 90 +- .../commons/auth/entity/PrivilegeUnion.java | 151 +++ .../iotdb/commons/auth/entity/Role.java | 558 +++++++++-- .../commons/auth/entity/TablePrivilege.java | 166 ++++ .../iotdb/commons/auth/entity/User.java | 81 +- .../commons/auth/role/BasicRoleManager.java | 267 ++--- .../commons/auth/role/IEntityManager.java | 77 ++ .../commons/auth/role/IRoleAccessor.java | 70 -- .../iotdb/commons/auth/role/IRoleManager.java | 109 --- .../auth/role/LocalFileRoleAccessor.java | 288 +++--- .../auth/role/LocalFileRoleManager.java | 6 +- .../commons/auth/user/BasicUserManager.java | 260 +---- .../iotdb/commons/auth/user/IUserManager.java | 162 ---- .../auth/user/LocalFileUserAccessor.java | 414 +++----- .../auth/user/LocalFileUserManager.java | 8 +- .../schema/column/ColumnHeaderConstant.java | 5 +- .../apache/iotdb/commons/utils/AuthUtils.java | 299 +++--- .../apache/iotdb/commons/utils/IOUtils.java | 140 +-- .../iotdb/commons/utils/SerializeUtils.java | 21 + .../iotdb/commons/utils/AuthUtilsTest.java | 72 +- .../relational/grammar/sql/RelationalSql.g4 | 124 +++ .../src/main/thrift/confignode.thrift | 59 +- 161 files changed, 7728 insertions(+), 4601 deletions(-) create mode 100644 integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBClusterAuthorityRelationalIT.java create mode 100644 integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java delete mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/auth/AuthorReadPlan.java create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorRelationalPlan.java create mode 100644 iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorTreePlan.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/RelationalAuthorizerTask.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/type/AuthorRType.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/DataBasePrivilegeTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/TablePrivilegeTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AuthorStatementTest.java create mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/DatabasePrivilege.java rename iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/{user/IUserAccessor.java => entity/IEntityAccessor.java} (60%) create mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/ModelType.java delete mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PriPrivilegeType.java create mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeModelType.java create mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeUnion.java create mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/TablePrivilege.java create mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IEntityManager.java delete mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleAccessor.java delete mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleManager.java delete mode 100644 iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserManager.java diff --git a/integration-test/src/test/java/org/apache/iotdb/confignode/it/database/IoTDBDatabaseSetAndDeleteIT.java b/integration-test/src/test/java/org/apache/iotdb/confignode/it/database/IoTDBDatabaseSetAndDeleteIT.java index cf29f649c3cf..7a81810a1f53 100644 --- a/integration-test/src/test/java/org/apache/iotdb/confignode/it/database/IoTDBDatabaseSetAndDeleteIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/confignode/it/database/IoTDBDatabaseSetAndDeleteIT.java @@ -56,8 +56,8 @@ public class IoTDBDatabaseSetAndDeleteIT { @Before public void setUp() throws Exception { - // Init 1C0D environment - EnvFactory.getEnv().initClusterEnvironment(1, 0); + // Init 1C1D environment + EnvFactory.getEnv().initClusterEnvironment(1, 1); } @After diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java index 6b719b357d2b..277dda1fa10d 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBAuthIT.java @@ -114,10 +114,11 @@ public void allPrivilegesTest() throws SQLException { // tempuser revoke his write_schema privilege userStmt.execute("REVOKE WRITE_SCHEMA ON root.** FROM USER tempuser"); - // 6. REVOKE ALL will get an error. Assert.assertThrows( - SQLException.class, - () -> adminStmt.execute("REVOKE ALL on root.** FROM USER tempuser")); + SQLException.class, () -> userStmt.execute("GRANT READ_DATA root.t1 to USER tempuser")); + + // 6. REVOKE ALL will be ok. + adminStmt.execute("REVOKE ALL on root.** FROM USER tempuser"); adminStmt.execute("GRANT ALL ON root.** TO USER tempuser"); adminStmt.execute("REVOKE ALL ON root.** FROM USER tempuser"); @@ -254,9 +255,7 @@ public void illegalGrantRevokeUserTest() throws SQLException { () -> userStmt.execute("GRANT WRITE_SCHEMA on root.a.b TO USER tempuser")); // revoke a non-existing privilege adminStmt.execute("REVOKE MANAGE_USER on root.** FROM USER tempuser"); - Assert.assertThrows( - SQLException.class, - () -> adminStmt.execute("REVOKE MANAGE_USER on root.** FROM USER tempuser")); + // revoke a non-existing user Assert.assertThrows( SQLException.class, @@ -312,9 +311,7 @@ public void createDeleteTimeSeriesTest() throws SQLException { () -> userStmt.execute("CREATE TIMESERIES root.b.a WITH DATATYPE=INT32,ENCODING=PLAIN")); - Assert.assertThrows( - SQLException.class, - () -> adminStmt.execute("REVOKE WRITE_SCHEMA ON root.a.b FROM USER tempuser")); + adminStmt.execute("REVOKE WRITE_SCHEMA ON root.a.b FROM USER tempuser"); // no privilege to create this one anymore Assert.assertThrows( SQLException.class, @@ -450,7 +447,7 @@ public void testListUser() throws SQLException { try { ResultSet resultSet = adminStmt.executeQuery("LIST USER"); - String ans = String.format("root,\n"); + String ans = "root,\n"; try { validateResultSet(resultSet, ans); @@ -551,7 +548,7 @@ public void testListUserRole() throws SQLException { // user1 : role1; MANAGE_ROLE,MANAGE_USER // user2 : role1, role2; ResultSet resultSet; - String ans = ""; + String ans; Connection userCon = EnvFactory.getEnv().getConnection("user1", "password"); Statement userStmt = userCon.createStatement(); try { @@ -618,16 +615,16 @@ public void testListUserPrivileges() throws SQLException { validateResultSet(resultSet, ans); resultSet = adminStmt.executeQuery("LIST PRIVILEGES OF USER root"); ans = - ",root.**,MANAGE_USER,true,\n" - + ",root.**,MANAGE_ROLE,true,\n" - + ",root.**,USE_TRIGGER,true,\n" - + ",root.**,USE_UDF,true,\n" - + ",root.**,USE_CQ,true,\n" - + ",root.**,USE_PIPE,true,\n" - + ",root.**,USE_MODEL,true,\n" - + ",root.**,EXTEND_TEMPLATE,true,\n" - + ",root.**,MANAGE_DATABASE,true,\n" - + ",root.**,MAINTAIN,true,\n" + ",,MANAGE_USER,true,\n" + + ",,MANAGE_ROLE,true,\n" + + ",,USE_TRIGGER,true,\n" + + ",,USE_UDF,true,\n" + + ",,USE_CQ,true,\n" + + ",,USE_PIPE,true,\n" + + ",,USE_MODEL,true,\n" + + ",,EXTEND_TEMPLATE,true,\n" + + ",,MANAGE_DATABASE,true,\n" + + ",,MAINTAIN,true,\n" + ",root.**,READ_DATA,true,\n" + ",root.**,WRITE_DATA,true,\n" + ",root.**,READ_SCHEMA,true,\n" @@ -964,22 +961,25 @@ public void testGrantAndGrantOpt() throws SQLException { // 2. USER1 has all privileges on root.** for (PrivilegeType item : PrivilegeType.values()) { + if (item.isRelationalPrivilege()) { + continue; + } String sql = "GRANT %s on root.** to USER user1"; - adminStmt.execute(String.format(sql, item.toString())); + adminStmt.execute(String.format(sql, item)); } // 3.admin lists privileges of user1 ResultSet resultSet = adminStmt.executeQuery("LIST PRIVILEGES OF USER user1"); String ans = - ",root.**,MANAGE_USER,false,\n" - + ",root.**,MANAGE_ROLE,false,\n" - + ",root.**,USE_TRIGGER,false,\n" - + ",root.**,USE_UDF,false,\n" - + ",root.**,USE_CQ,false,\n" - + ",root.**,USE_PIPE,false,\n" - + ",root.**,USE_MODEL,false,\n" - + ",root.**,EXTEND_TEMPLATE,false,\n" - + ",root.**,MANAGE_DATABASE,false,\n" - + ",root.**,MAINTAIN,false,\n" + ",,MANAGE_USER,false,\n" + + ",,MANAGE_ROLE,false,\n" + + ",,USE_TRIGGER,false,\n" + + ",,USE_UDF,false,\n" + + ",,USE_CQ,false,\n" + + ",,USE_PIPE,false,\n" + + ",,USE_MODEL,false,\n" + + ",,EXTEND_TEMPLATE,false,\n" + + ",,MANAGE_DATABASE,false,\n" + + ",,MAINTAIN,false,\n" + ",root.**,READ_DATA,false,\n" + ",root.**,WRITE_DATA,false,\n" + ",root.**,READ_SCHEMA,false,\n" @@ -988,21 +988,24 @@ public void testGrantAndGrantOpt() throws SQLException { // 4. USER2 has all privilegs on root.** with grant option; for (PrivilegeType item : PrivilegeType.values()) { + if (item.isRelationalPrivilege()) { + continue; + } String sql = "GRANT %s on root.** to USER user2 with grant option"; - adminStmt.execute(String.format(sql, item.toString())); + adminStmt.execute(String.format(sql, item)); } resultSet = adminStmt.executeQuery("LIST PRIVILEGES OF USER user2"); ans = - ",root.**,MANAGE_USER,true,\n" - + ",root.**,MANAGE_ROLE,true,\n" - + ",root.**,USE_TRIGGER,true,\n" - + ",root.**,USE_UDF,true,\n" - + ",root.**,USE_CQ,true,\n" - + ",root.**,USE_PIPE,true,\n" - + ",root.**,USE_MODEL,true,\n" - + ",root.**,EXTEND_TEMPLATE,true,\n" - + ",root.**,MANAGE_DATABASE,true,\n" - + ",root.**,MAINTAIN,true,\n" + ",,MANAGE_USER,true,\n" + + ",,MANAGE_ROLE,true,\n" + + ",,USE_TRIGGER,true,\n" + + ",,USE_UDF,true,\n" + + ",,USE_CQ,true,\n" + + ",,USE_PIPE,true,\n" + + ",,USE_MODEL,true,\n" + + ",,EXTEND_TEMPLATE,true,\n" + + ",,MANAGE_DATABASE,true,\n" + + ",,MAINTAIN,true,\n" + ",root.**,READ_DATA,true,\n" + ",root.**,WRITE_DATA,true,\n" + ",root.**,READ_SCHEMA,true,\n" @@ -1019,16 +1022,16 @@ public void testGrantAndGrantOpt() throws SQLException { try { resultSet = userStmt.executeQuery("LIST PRIVILEGES OF USER user1"); ans = - ",root.**,MANAGE_USER,false,\n" - + ",root.**,MANAGE_ROLE,false,\n" - + ",root.**,USE_TRIGGER,false,\n" - + ",root.**,USE_UDF,false,\n" - + ",root.**,USE_CQ,false,\n" - + ",root.**,USE_PIPE,false,\n" - + ",root.**,USE_MODEL,false,\n" - + ",root.**,EXTEND_TEMPLATE,false,\n" - + ",root.**,MANAGE_DATABASE,false,\n" - + ",root.**,MAINTAIN,false,\n" + ",,MANAGE_USER,false,\n" + + ",,MANAGE_ROLE,false,\n" + + ",,USE_TRIGGER,false,\n" + + ",,USE_UDF,false,\n" + + ",,USE_CQ,false,\n" + + ",,USE_PIPE,false,\n" + + ",,USE_MODEL,false,\n" + + ",,EXTEND_TEMPLATE,false,\n" + + ",,MANAGE_DATABASE,false,\n" + + ",,MAINTAIN,false,\n" + ",root.**,READ_DATA,false,\n" + ",root.**,WRITE_DATA,false,\n" + ",root.**,READ_SCHEMA,false,\n" @@ -1052,21 +1055,21 @@ public void testGrantAndGrantOpt() throws SQLException { validateResultSet(resultSet, ans); userStmt.execute("GRANT MANAGE_ROLE ON root.** TO USER user3"); resultSet = userStmt.executeQuery("LIST PRIVILEGES OF USER user3"); - ans = ",root.**,MANAGE_ROLE,false,\n"; + ans = ",,MANAGE_ROLE,false,\n"; validateResultSet(resultSet, ans); userStmt.execute("REVOKE MANAGE_ROLE ON root.** FROM USER user1"); resultSet = userStmt.executeQuery("LIST PRIVILEGES OF USER user1"); ans = - ",root.**,MANAGE_USER,false,\n" - + ",root.**,USE_TRIGGER,false,\n" - + ",root.**,USE_UDF,false,\n" - + ",root.**,USE_CQ,false,\n" - + ",root.**,USE_PIPE,false,\n" - + ",root.**,USE_MODEL,false,\n" - + ",root.**,EXTEND_TEMPLATE,false,\n" - + ",root.**,MANAGE_DATABASE,false,\n" - + ",root.**,MAINTAIN,false,\n" + ",,MANAGE_USER,false,\n" + + ",,USE_TRIGGER,false,\n" + + ",,USE_UDF,false,\n" + + ",,USE_CQ,false,\n" + + ",,USE_PIPE,false,\n" + + ",,USE_MODEL,false,\n" + + ",,EXTEND_TEMPLATE,false,\n" + + ",,MANAGE_DATABASE,false,\n" + + ",,MAINTAIN,false,\n" + ",root.**,READ_DATA,false,\n" + ",root.**,WRITE_DATA,false,\n" + ",root.**,READ_SCHEMA,false,\n" @@ -1115,9 +1118,12 @@ public void testRevokeAndGrantOpt() throws SQLException { // user2 has all privileges without grant option on root.** // user2 has all privileges without grant option on root.t1.** for (PrivilegeType item : PrivilegeType.values()) { + if (item.isRelationalPrivilege()) { + continue; + } String sql = "GRANT %s on root.** to USER user1 WITH GRANT OPTION"; adminStmt.execute(String.format(sql, item)); - if (item.isPathRelevant()) { + if (item.isPathPrivilege()) { adminStmt.execute(String.format("GRANT %s on root.t1.** TO USER user2", item)); } sql = "GRANT %s on root.** to USER user2"; @@ -1130,6 +1136,9 @@ public void testRevokeAndGrantOpt() throws SQLException { try { // revoke privileges on root.** and root.t1.** for (PrivilegeType item : PrivilegeType.values()) { + if (item.isRelationalPrivilege()) { + continue; + } user1Stmt.execute(String.format("REVOKE %s ON root.** FROM USER user2", item)); } diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBClusterAuthorityIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBClusterAuthorityIT.java index d38ba6dac4e0..6c13036b1e94 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBClusterAuthorityIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBClusterAuthorityIT.java @@ -20,6 +20,7 @@ package org.apache.iotdb.db.it.auth; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.auth.entity.PrivilegeModelType; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient; import org.apache.iotdb.commons.exception.IllegalPathException; @@ -60,8 +61,8 @@ public class IoTDBClusterAuthorityIT { @Before public void setUp() throws Exception { - // Init 1C0D environment - EnvFactory.getEnv().initClusterEnvironment(1, 0); + // Init 1C1D environment + EnvFactory.getEnv().initClusterEnvironment(1, 1); } @After @@ -159,9 +160,11 @@ public void permissionTest() throws IllegalPathException { // check user privileges checkUserPrivilegesReq = new TCheckUserPrivilegesReq( - "tempuser0", - AuthUtils.serializePartialPathList(paths), - PrivilegeType.MANAGE_USER.ordinal()); + "tempuser0", + PrivilegeModelType.TREE.ordinal(), + PrivilegeType.MANAGE_USER.ordinal(), + false) + .setPaths(AuthUtils.serializePartialPathList(paths)); status = client.checkUserPrivileges(checkUserPrivilegesReq).getStatus(); assertEquals(TSStatusCode.NO_PERMISSION.getStatusCode(), status.getCode()); @@ -282,9 +285,11 @@ public void permissionTest() throws IllegalPathException { // check user privileges checkUserPrivilegesReq = new TCheckUserPrivilegesReq( - "tempuser0", - AuthUtils.serializePartialPathList(paths), - PrivilegeType.READ_DATA.ordinal()); + "tempuser0", + PrivilegeModelType.TREE.ordinal(), + PrivilegeType.READ_DATA.ordinal(), + false) + .setPaths(AuthUtils.serializePartialPathList(nodeNameList)); status = client.checkUserPrivileges(checkUserPrivilegesReq).getStatus(); assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); @@ -367,10 +372,13 @@ public void permissionTest() throws IllegalPathException { status = authorizerResp.getStatus(); assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); assertEquals(ColumnHeaderConstant.PRIVILEGES, authorizerResp.getTag()); - assertEquals("tempuser0", authorizerResp.getPermissionInfo().getUserInfo().getUsername()); assertEquals( - new ArrayList<>(), authorizerResp.getPermissionInfo().getUserInfo().getPrivilegeList()); - assertEquals(1, authorizerResp.getPermissionInfo().getUserInfo().getRoleListSize()); + "tempuser0", + authorizerResp.getPermissionInfo().getUserInfo().getPermissionInfo().getName()); + assertEquals( + new ArrayList<>(), + authorizerResp.getPermissionInfo().getUserInfo().getPermissionInfo().getPrivilegeList()); + assertEquals(1, authorizerResp.getPermissionInfo().getUserInfo().getRoleSet().size()); // list privileges role authorizerReq = @@ -443,17 +451,32 @@ public void permissionTest() throws IllegalPathException { assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); assertNull(authorizerResp.getMemberInfo()); assertEquals(new HashMap<>(), authorizerResp.getPermissionInfo().getRoleInfo()); + assertEquals(new HashSet<>(), authorizerResp.getPermissionInfo().getUserInfo().getRoleSet()); assertEquals( - new ArrayList<>(), authorizerResp.getPermissionInfo().getUserInfo().getRoleList()); - assertEquals( - PrivilegeType.getPathPriCount(), - authorizerResp.getPermissionInfo().getUserInfo().getPrivilegeList().get(0).priSet.size()); + PrivilegeType.getPrivilegeCount(PrivilegeModelType.TREE), + authorizerResp + .getPermissionInfo() + .getUserInfo() + .getPermissionInfo() + .getPrivilegeList() + .get(0) + .priSet + .size()); assertEquals( - PrivilegeType.getSysPriCount(), - authorizerResp.getPermissionInfo().getUserInfo().getSysPriSet().size()); + PrivilegeType.getPrivilegeCount(PrivilegeModelType.SYSTEM), + authorizerResp + .getPermissionInfo() + .getUserInfo() + .getPermissionInfo() + .getSysPriSet() + .size()); assertEquals( - PrivilegeType.getSysPriCount(), - authorizerResp.getPermissionInfo().getUserInfo().getSysPriSetGrantOptSize()); + PrivilegeType.getPrivilegeCount(PrivilegeModelType.SYSTEM), + authorizerResp + .getPermissionInfo() + .getUserInfo() + .getPermissionInfo() + .getSysPriSetGrantOptSize()); authorizerReq = new TAuthorizerReq( @@ -473,8 +496,9 @@ public void permissionTest() throws IllegalPathException { checkUserPrivilegesReq = new TCheckUserPrivilegesReq( "tempuser0", - AuthUtils.serializePartialPathList(new ArrayList<>()), - PrivilegeType.MANAGE_USER.ordinal()); + PrivilegeModelType.SYSTEM.ordinal(), + PrivilegeType.MANAGE_USER.ordinal(), + false); status = client.checkUserPrivileges(checkUserPrivilegesReq).getStatus(); assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); @@ -496,8 +520,9 @@ public void permissionTest() throws IllegalPathException { checkUserPrivilegesReq = new TCheckUserPrivilegesReq( "tempuser0", - AuthUtils.serializePartialPathList(new ArrayList<>()), - PrivilegeType.MANAGE_DATABASE.ordinal()); + PrivilegeModelType.SYSTEM.ordinal(), + PrivilegeType.MANAGE_DATABASE.ordinal(), + false); status = client.checkUserPrivileges(checkUserPrivilegesReq).getStatus(); assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBClusterAuthorityRelationalIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBClusterAuthorityRelationalIT.java new file mode 100644 index 000000000000..e18e43211be6 --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBClusterAuthorityRelationalIT.java @@ -0,0 +1,513 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.it.auth; + +import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.auth.entity.PrivilegeModelType; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; +import org.apache.iotdb.commons.client.exception.ClientManagerException; +import org.apache.iotdb.commons.client.sync.SyncConfigNodeIServiceClient; +import org.apache.iotdb.commons.utils.AuthUtils; +import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerRelationalReq; +import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; +import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; +import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; +import org.apache.iotdb.confignode.rpc.thrift.TLoginReq; +import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp; +import org.apache.iotdb.confignode.rpc.thrift.TRoleResp; +import org.apache.iotdb.confignode.rpc.thrift.TUserResp; +import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType; +import org.apache.iotdb.it.env.EnvFactory; +import org.apache.iotdb.it.framework.IoTDBTestRunner; +import org.apache.iotdb.itbase.category.ClusterIT; +import org.apache.iotdb.itbase.category.TableClusterIT; +import org.apache.iotdb.rpc.TSStatusCode; + +import org.apache.thrift.TException; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.io.IOException; +import java.util.Collections; +import java.util.List; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +@RunWith(IoTDBTestRunner.class) +@Category({ClusterIT.class, TableClusterIT.class}) +public class IoTDBClusterAuthorityRelationalIT { + @Before + public void setUp() throws Exception { + // Init 1C1D environment + EnvFactory.getEnv().initClusterEnvironment(1, 1); + } + + @After + public void tearDown() { + EnvFactory.getEnv().cleanClusterEnvironment(); + } + + private void runAndCheck(SyncConfigNodeIServiceClient client, TAuthorizerRelationalReq req) + throws TException { + TSStatus status; + status = client.operateRPermission(req); + assertEquals(status.getCode(), TSStatusCode.SUCCESS_STATUS.getStatusCode()); + } + + private void cleanUserAndRole(SyncConfigNodeIServiceClient client) throws TException { + TSStatus status; + + // clean user + TAuthorizerRelationalReq authorizerReq = + new TAuthorizerRelationalReq( + AuthorRType.LIST_USER.ordinal(), "", "", "", "", "", Collections.emptySet(), false); + + TAuthorizerResp authorizerResp = client.queryRPermission(authorizerReq); + status = authorizerResp.getStatus(); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + + List allUsers = authorizerResp.getMemberInfo(); + for (String user : allUsers) { + if (!user.equals("root")) { + authorizerReq = + new TAuthorizerRelationalReq( + AuthorRType.DROP_USER.ordinal(), + user, + "", + "", + "", + "", + Collections.emptySet(), + false); + status = client.operateRPermission(authorizerReq); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + } + } + + // clean role + authorizerReq = + new TAuthorizerRelationalReq( + AuthorRType.LIST_ROLE.ordinal(), "", "", "", "", "", Collections.emptySet(), false); + + authorizerResp = client.queryRPermission(authorizerReq); + status = authorizerResp.getStatus(); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + + List allRoles = authorizerResp.getMemberInfo(); + for (String role : allRoles) { + authorizerReq = + new TAuthorizerRelationalReq( + AuthorRType.DROP_ROLE.ordinal(), role, "", "", "", "", Collections.emptySet(), false); + status = client.operateRPermission(authorizerReq); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + } + } + + private void createUserORRoleAndCheck( + SyncConfigNodeIServiceClient client, String name, boolean isUser, String password) + throws TException { + TSStatus status; + TAuthorizerRelationalReq authorizerReq = + new TAuthorizerRelationalReq( + isUser ? AuthorRType.CREATE_USER.ordinal() : AuthorRType.CREATE_ROLE.ordinal(), + isUser ? name : "", + isUser ? "" : name, + password, + "", + "", + Collections.emptySet(), + false); + status = client.operateRPermission(authorizerReq); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + authorizerReq = + new TAuthorizerRelationalReq( + isUser ? AuthorRType.LIST_USER.ordinal() : AuthorRType.LIST_ROLE.ordinal(), + "", + "", + "", + "", + "", + Collections.emptySet(), + false); + TAuthorizerResp resp = client.queryRPermission(authorizerReq); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), resp.getStatus().getCode()); + assertTrue(resp.getMemberInfo().contains(name)); + } + + private void grantSysPrivilegeAndCheck( + SyncConfigNodeIServiceClient client, + String userName, + String roleName, + boolean toUser, + PrivilegeType sysPriv, + boolean grantOpt) + throws TException { + TSStatus status; + TAuthorizerRelationalReq authorizerRelationalReq = + new TAuthorizerRelationalReq( + toUser ? AuthorRType.GRANT_USER_SYS.ordinal() : AuthorRType.GRANT_ROLE_SYS.ordinal(), + userName, + roleName, + "", + "", + "", + Collections.singleton(sysPriv.ordinal()), + grantOpt); + + status = client.operateRPermission(authorizerRelationalReq); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + TCheckUserPrivilegesReq checkUserPrivilegesReq = + new TCheckUserPrivilegesReq( + userName, PrivilegeModelType.SYSTEM.ordinal(), sysPriv.ordinal(), grantOpt); + TPermissionInfoResp resp = client.checkUserPrivileges(checkUserPrivilegesReq); + assertEquals(resp.status.getCode(), TSStatusCode.SUCCESS_STATUS.getStatusCode()); + assertTrue( + toUser + ? resp.getUserInfo().getPermissionInfo().getSysPriSet().contains(sysPriv.ordinal()) + : resp.getRoleInfo().containsKey(roleName) + && resp.getRoleInfo().get(roleName).getSysPriSet().contains(sysPriv.ordinal())); + if (grantOpt) { + assertTrue( + toUser + ? resp.getUserInfo() + .getPermissionInfo() + .getSysPriSetGrantOpt() + .contains(sysPriv.ordinal()) + : resp.getRoleInfo().containsKey(roleName) + && resp.getRoleInfo() + .get(roleName) + .getSysPriSetGrantOpt() + .contains(sysPriv.ordinal())); + } + } + + private void grantPrivilegeAndCheck( + SyncConfigNodeIServiceClient client, + String username, + String rolename, + boolean toUser, + PrivilegeUnion union) + throws TException { + TSStatus status; + AuthorRType type; + if (union.isForAny()) { + type = toUser ? AuthorRType.GRANT_USER_ANY : AuthorRType.GRANT_ROLE_ANY; + } else if (union.getTbName() == null) { + type = toUser ? AuthorRType.GRANT_USER_DB : AuthorRType.GRANT_ROLE_DB; + } else { + type = toUser ? AuthorRType.GRANT_USER_TB : AuthorRType.GRANT_ROLE_TB; + } + TAuthorizerRelationalReq authorizerRelationalReq = + new TAuthorizerRelationalReq( + type.ordinal(), + toUser ? username : "", + toUser ? "" : rolename, + "", + union.getDBName() == null ? "" : union.getDBName(), + union.getTbName() == null ? "" : union.getTbName(), + Collections.singleton(union.getPrivilegeType().ordinal()), + union.isGrantOption()); + status = client.operateRPermission(authorizerRelationalReq); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + int reqtype = -1; + if (union.getPrivilegeType().isRelationalPrivilege()) { + reqtype = PrivilegeModelType.RELATIONAL.ordinal(); + } else if (union.getPrivilegeType().isSystemPrivilege()) { + reqtype = PrivilegeModelType.SYSTEM.ordinal(); + } + TCheckUserPrivilegesReq checkUserPrivilegesReq = + new TCheckUserPrivilegesReq( + username, reqtype, union.getPrivilegeType().ordinal(), union.isGrantOption()); + if (union.getDBName() != null) { + checkUserPrivilegesReq.setDatabase(union.getDBName()); + } + if (union.getTbName() != null) { + checkUserPrivilegesReq.setTable(union.getTbName()); + } + + TPermissionInfoResp resp = client.checkUserPrivileges(checkUserPrivilegesReq); + assertEquals(resp.status.getCode(), TSStatusCode.SUCCESS_STATUS.getStatusCode()); + if (toUser) { + TUserResp userInfo = resp.getUserInfo(); + if (union.isForAny()) { + assertTrue( + userInfo + .getPermissionInfo() + .getAnyScopeGrantSet() + .contains(union.getPrivilegeType().ordinal())); + } else if (union.getTbName() == null) { + assertTrue(userInfo.getPermissionInfo().getDbPrivilegeMap().containsKey(union.getDBName())); + assertTrue( + userInfo + .getPermissionInfo() + .getDbPrivilegeMap() + .get(union.getDBName()) + .getPrivileges() + .contains(union.getPrivilegeType().ordinal())); + } else { + assertTrue(userInfo.getPermissionInfo().getDbPrivilegeMap().containsKey(union.getDBName())); + assertTrue( + userInfo + .getPermissionInfo() + .getDbPrivilegeMap() + .get(union.getDBName()) + .getTablePrivilegeMap() + .containsKey(union.getTbName())); + assertTrue( + userInfo + .getPermissionInfo() + .getDbPrivilegeMap() + .get(union.getDBName()) + .getTablePrivilegeMap() + .get(union.getTbName()) + .getPrivileges() + .contains(union.getPrivilegeType().ordinal())); + } + } else { + assertTrue(resp.getRoleInfo().containsKey(rolename)); + TRoleResp roleResp = resp.getRoleInfo().get(rolename); + if (union.isForAny()) { + assertTrue(roleResp.getAnyScopeGrantSet().contains(union.getPrivilegeType().ordinal())); + } + if (union.getTbName() == null) { + assertTrue(roleResp.getDbPrivilegeMap().containsKey(union.getDBName())); + assertTrue( + roleResp + .getDbPrivilegeMap() + .get(union.getDBName()) + .getPrivileges() + .contains(union.getPrivilegeType().ordinal())); + } else { + assertTrue(roleResp.getDbPrivilegeMap().containsKey(union.getDBName())); + assertTrue( + roleResp + .getDbPrivilegeMap() + .get(union.getDBName()) + .getTablePrivilegeMap() + .containsKey(union.getTbName())); + assertTrue( + roleResp + .getDbPrivilegeMap() + .get(union.getDBName()) + .getTablePrivilegeMap() + .get(union.getTbName()) + .getPrivileges() + .contains(union.getPrivilegeType().ordinal())); + } + } + } + + private void expectSuccess(TPermissionInfoResp resp) { + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), resp.getStatus().getCode()); + } + + private void expectFailed(TPermissionInfoResp resp) { + assertEquals(TSStatusCode.NO_PERMISSION.getStatusCode(), resp.getStatus().getCode()); + } + + @Test + public void permissionTest() + throws TException, ClientManagerException, IOException, InterruptedException { + try (SyncConfigNodeIServiceClient client = + (SyncConfigNodeIServiceClient) EnvFactory.getEnv().getLeaderConfigNodeConnection()) { + cleanUserAndRole(client); + createUserORRoleAndCheck(client, "user1", true, "password"); + createUserORRoleAndCheck(client, "role1", false, ""); + runAndCheck( + client, + new TAuthorizerRelationalReq( + AuthorRType.GRANT_USER_ROLE.ordinal(), + "user1", + "role1", + "", + "", + "", + Collections.emptySet(), + false)); + grantSysPrivilegeAndCheck(client, "user1", "role1", true, PrivilegeType.MANAGE_USER, false); + grantSysPrivilegeAndCheck(client, "user1", "role1", true, PrivilegeType.MANAGE_ROLE, true); + grantSysPrivilegeAndCheck(client, "user1", "role1", false, PrivilegeType.MAINTAIN, true); + grantPrivilegeAndCheck( + client, "user1", "", true, new PrivilegeUnion("database", "table", PrivilegeType.SELECT)); + grantPrivilegeAndCheck( + client, "user1", "", true, new PrivilegeUnion("database2", PrivilegeType.ALTER)); + grantPrivilegeAndCheck( + client, "user1", "", true, new PrivilegeUnion(PrivilegeType.INSERT, true, true)); + grantPrivilegeAndCheck( + client, + "user1", + "role1", + false, + new PrivilegeUnion("database", "table2", PrivilegeType.DELETE)); + grantPrivilegeAndCheck( + client, + "user1", + "role1", + false, + new PrivilegeUnion("database2", PrivilegeType.CREATE, true)); + + // privileges status + // user1 <-- role1 + // user1 : MANAGE_USER, MANAGE_ROLE(with grant option) + // "database"."table" select; + // "database2".* alter; + // any insert(with grant option); + // role1: MAINTAIN(with grant option) + // "database"."table2" delete, + // "database2".* create(with grant option); + + // check login + TLoginReq req = new TLoginReq("user1", "password"); + expectSuccess(client.login(req)); + + // check user has role. + TAuthorizerReq user_role_req = + new TAuthorizerReq( + 0, + "user1", + "role1", + "", + "", + Collections.emptySet(), + false, + AuthUtils.serializePartialPathList(Collections.emptyList())); + expectSuccess(client.checkRoleOfUser(user_role_req)); + + // check db is visible + TCheckUserPrivilegesReq check_req = + new TCheckUserPrivilegesReq("user1", PrivilegeModelType.RELATIONAL.ordinal(), -1, false) + .setDatabase("database"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check db is visible, because any privileges, so database3 is visible. + check_req = + new TCheckUserPrivilegesReq("user1", PrivilegeModelType.RELATIONAL.ordinal(), -1, false) + .setDatabase("database3"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check table is visible. + check_req = + new TCheckUserPrivilegesReq("user1", PrivilegeModelType.RELATIONAL.ordinal(), -1, false) + .setDatabase("database") + .setTable("table"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check db privileges + check_req = + new TCheckUserPrivilegesReq( + "user1", + PrivilegeModelType.RELATIONAL.ordinal(), + PrivilegeType.ALTER.ordinal(), + false) + .setDatabase("database2"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check db privileges, success for any + check_req = + new TCheckUserPrivilegesReq( + "user1", + PrivilegeModelType.RELATIONAL.ordinal(), + PrivilegeType.INSERT.ordinal(), + false) + .setDatabase("database10"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check db privileges, success for role + check_req = + new TCheckUserPrivilegesReq( + "user1", + PrivilegeModelType.RELATIONAL.ordinal(), + PrivilegeType.CREATE.ordinal(), + false) + .setDatabase("database2"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check db privileges grant option, + check_req = + new TCheckUserPrivilegesReq( + "user1", + PrivilegeModelType.RELATIONAL.ordinal(), + PrivilegeType.CREATE.ordinal(), + true) + .setDatabase("database2"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check tb privilege + check_req = + new TCheckUserPrivilegesReq( + "user1", + PrivilegeModelType.RELATIONAL.ordinal(), + PrivilegeType.SELECT.ordinal(), + false) + .setDatabase("database") + .setTable("table"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check tb privilege + check_req = + new TCheckUserPrivilegesReq( + "user1", + PrivilegeModelType.RELATIONAL.ordinal(), + PrivilegeType.ALTER.ordinal(), + false) + .setDatabase("database2") + .setTable("table"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check tb privilege + check_req = + new TCheckUserPrivilegesReq( + "user1", + PrivilegeModelType.RELATIONAL.ordinal(), + PrivilegeType.CREATE.ordinal(), + false) + .setDatabase("database2") + .setTable("table"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check tb privilege + check_req = + new TCheckUserPrivilegesReq( + "user1", + PrivilegeModelType.RELATIONAL.ordinal(), + PrivilegeType.DELETE.ordinal(), + false) + .setDatabase("database") + .setTable("table2"); + expectSuccess(client.checkUserPrivileges(check_req)); + + // check tb privilege + check_req = + new TCheckUserPrivilegesReq( + "user1", + PrivilegeModelType.RELATIONAL.ordinal(), + PrivilegeType.SELECT.ordinal(), + false) + .setDatabase("database") + .setTable("table2"); + expectFailed(client.checkUserPrivileges(check_req)); + } + } +} diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java new file mode 100644 index 000000000000..dfad35cb6963 --- /dev/null +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/auth/IoTDBRelationalAuthIT.java @@ -0,0 +1,350 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.it.auth; + +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.db.it.utils.TestUtils; +import org.apache.iotdb.it.env.EnvFactory; +import org.apache.iotdb.it.framework.IoTDBTestRunner; +import org.apache.iotdb.itbase.category.TableClusterIT; +import org.apache.iotdb.itbase.category.TableLocalStandaloneIT; +import org.apache.iotdb.itbase.env.BaseEnv; + +import org.junit.After; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Test; +import org.junit.experimental.categories.Category; +import org.junit.runner.RunWith; + +import java.sql.Connection; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.Set; + +@RunWith(IoTDBTestRunner.class) +@Category({TableLocalStandaloneIT.class, TableClusterIT.class}) +public class IoTDBRelationalAuthIT { + @Before + public void setUp() throws Exception { + EnvFactory.getEnv().initClusterEnvironment(); + } + + @After + public void tearDown() throws Exception { + EnvFactory.getEnv().cleanClusterEnvironment(); + } + + @Test + public void listUserPrivileges() throws SQLException { + try (Connection adminCon = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement adminStmt = adminCon.createStatement()) { + + adminStmt.execute("create user testuser 'password'"); + adminStmt.execute("create database testdb"); + adminStmt.execute("GRANT MANAGE_USER to user testuser"); + Assert.assertThrows( + SQLException.class, + () -> { + adminStmt.execute("GRANT MANAGE_DATABASE to user testuser"); + }); + Assert.assertThrows( + SQLException.class, + () -> { + adminStmt.execute("GRANT read_data on database db1 to user testuser"); + }); + + adminStmt.execute("GRANT MANAGE_ROLE TO USER testuser"); + adminStmt.execute("GRANT SELECT ON ANY TO USER testuser"); + adminStmt.execute("GRANT INSERT ON ANY TO USER testuser"); + adminStmt.execute("GRANT DELETE ON ANY TO USER testuser"); + Assert.assertThrows( + SQLException.class, + () -> { + adminStmt.execute("GRANT SELECT ON TABLE TB to user testuser"); + }); + adminStmt.execute("GRANT SELECT ON DATABASE TESTDB TO USER testuser"); + adminStmt.execute("GRANT SELECT ON DATABASE TESTDB TO USER testuser WITH GRANT OPTION"); + + adminStmt.execute("use testdb"); + adminStmt.execute("GRANT SELECT ON TABLE TB to user testuser"); + adminStmt.execute("GRANT INSERT ON TABLE TB to user testuser with grant option"); + adminStmt.execute("GRANT DROP ON testdb.tb to user testuser with grant option"); + + ResultSet rs = adminStmt.executeQuery("LIST PRIVILEGES OF USER testuser"); + Set ans = + new HashSet<>( + Arrays.asList( + ",,MANAGE_USER,false,", + ",,MANAGE_ROLE,false,", + ",*.*,SELECT,false,", + ",*.*,INSERT,false,", + ",*.*,DELETE,false,", + ",testdb.*,SELECT,true,", + ",testdb.tb,SELECT,false,", + ",testdb.tb,INSERT,true,", + ",testdb.tb,DROP,true,")); + TestUtils.assertResultSetEqual(rs, "Role,Scope,Privileges,GrantOption,", ans); + } + } + + @Test + public void checkAuthorStatementPrivilegeCheck() throws SQLException { + try (Connection adminCon = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement adminStmt = adminCon.createStatement()) { + adminStmt.execute("create user testuser 'password'"); + adminStmt.execute("create user testuser2 'password'"); + adminStmt.execute("create role testrole"); + adminStmt.execute("create database testdb"); + + // cannot create admin user + Assert.assertThrows( + SQLException.class, + () -> { + adminStmt.execute("CREATE USER root 'password'"); + }); + // cannot create admin role + Assert.assertThrows( + SQLException.class, + () -> { + adminStmt.execute("CREATE role root"); + }); + + // cannot grant role to admin user + Assert.assertThrows( + SQLException.class, + () -> { + adminStmt.execute("GRANT ROLE testrole to root"); + }); + + // cannot grant privilege to admin user + Assert.assertThrows( + SQLException.class, + () -> { + adminStmt.execute("GRANT MANAGE_USER to root"); + }); + + // admin can do all things below. + adminStmt.execute("GRANT MANAGE_USER to user testuser2 with grant option"); + adminStmt.execute("GRANT MANAGE_ROLE to user testuser"); + adminStmt.execute("GRANT MAINTAIN to ROLE testrole with grant option"); + + adminStmt.execute("use testdb"); + adminStmt.execute("GRANT SELECT ON TABLE TB to user testuser"); + adminStmt.execute("GRANT INSERT ON TABLE TB to user testuser"); + adminStmt.execute("GRANT INSERT ON DATABASE testdb to user testuser"); + adminStmt.execute("GRANT ALTER ON ANY to user testuser"); + + adminStmt.execute("GRANT DROP ON TABLE TB to user testuser2 with grant option"); + adminStmt.execute("GRANT CREATE ON TABLE TB to user testuser2 with grant option"); + adminStmt.execute("GRANT DROP ON DATABASE testdb to user testuser2 with grant option"); + adminStmt.execute("GRANT SELECT ON ANY to user testuser2 with grant option"); + + adminStmt.execute("GRANT ROLE testrole to testuser"); + } + + try (Connection userCon1 = + EnvFactory.getEnv().getConnection("testuser", "password", BaseEnv.TABLE_SQL_DIALECT); + Statement userStmt = userCon1.createStatement()) { + // 1. user1's privileges + // testdb.TB select + // testdb.TB insert + // testdb.* insert + // any alter + // manage_role + // MAINTAIN with grant option + + // cannot create user + Assert.assertThrows( + SQLException.class, + () -> { + userStmt.execute("CREATE USER testuser3 'password'"); + }); + // can create role + userStmt.execute("CREATE ROLE testrole2"); + // can grant role to user + userStmt.execute("GRANT ROLE testrole2 to testuser"); + // cannot grant privileges to other + Assert.assertThrows( + SQLException.class, + () -> { + userStmt.execute("GRANT SELECT ON testdb.TB to role testrole2"); + }); + + Assert.assertThrows( + SQLException.class, + () -> { + userStmt.execute("GRANT ALTER ON ANY to role testrole2"); + }); + + // cannot grant manage_role to other + Assert.assertThrows( + SQLException.class, + () -> { + userStmt.execute("GRANT manage_role to role testrole2"); + }); + userStmt.execute("GRANT MAINTAIN to ROLE testrole2"); + + // can list itself privileges and the all roles privileges + ResultSet rs = userStmt.executeQuery("List privileges of user testuser"); + Set ans = + new HashSet<>( + Arrays.asList( + ",,MANAGE_ROLE,false,", + ",*.*,ALTER,false,", + ",testdb.*,INSERT,false,", + ",testdb.tb,SELECT,false,", + ",testdb.tb,INSERT,false,", + "testrole2,,MAINTAIN,false,", + "testrole,,MAINTAIN,true,")); + TestUtils.assertResultSetEqual(rs, "Role,Scope,Privileges,GrantOption,", ans); + rs = userStmt.executeQuery("List privileges of role testrole"); + ans = new HashSet<>(Collections.singletonList("testrole,,MAINTAIN,true,")); + TestUtils.assertResultSetEqual(rs, "Role,Scope,Privileges,GrantOption,", ans); + rs = userStmt.executeQuery("List privileges of role testrole2"); + ans = new HashSet<>(Collections.singletonList("testrole2,,MAINTAIN,false,")); + TestUtils.assertResultSetEqual(rs, "Role,Scope,Privileges,GrantOption,", ans); + // testdb.TB's privilege is not grant option. + Assert.assertThrows( + SQLException.class, + () -> { + userStmt.execute("GRANT insert on testdb.TB to role testrole2"); + }); + + Assert.assertThrows( + SQLException.class, + () -> { + userStmt.execute("GRANT ALTER on any to role testrole2"); + }); + } + + try (Connection userCon1 = + EnvFactory.getEnv().getConnection("testuser2", "password", BaseEnv.TABLE_SQL_DIALECT); + Statement userStmt = userCon1.createStatement()) { + // 2. user2's privileges + // MANAGE_USER with grant option + // testdb.tb drop with grant option + // testdb.tb create with grant option + // testdb.* drop with grant option + // any select with grant option + + // can create user. + userStmt.execute("CREATE USER testuser3 'password'"); + + // can not create role + Assert.assertThrows( + SQLException.class, + () -> { + userStmt.execute("CREATE ROLE testrole3"); + }); + + // cannot list role's privileges + Assert.assertThrows( + SQLException.class, + () -> { + userStmt.executeQuery("List privileges of role testrole"); + }); + + userStmt.execute("GRANT drop on database testdb to user testuser3"); + userStmt.execute("GRANT SELECT ON database testdb to user testuser3"); + ResultSet rs = userStmt.executeQuery("List privileges of user testuser3"); + Set ans = + new HashSet<>(Arrays.asList(",testdb.*,SELECT,false,", ",testdb.*,DROP,false,")); + TestUtils.assertResultSetEqual(rs, "Role,Scope,Privileges,GrantOption,", ans); + userStmt.execute("REVOKE SELECT ON DATABASE testdb from user testuser3"); + Assert.assertThrows( + SQLException.class, + () -> { + userStmt.execute("GRANT CREATE ON DATABASE testdb to user testuser3"); + }); + + rs = userStmt.executeQuery("List privileges of user testuser3"); + TestUtils.assertResultSetEqual( + rs, "Role,Scope,Privileges,GrantOption,", Collections.singleton(",testdb.*,DROP,false,")); + } + } + + @Test + public void checkGrantRevokeAllPrivileges() throws SQLException { + try (Connection adminCon = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement adminStmt = adminCon.createStatement()) { + adminStmt.execute("create user test 'password'"); + adminStmt.execute("grant all to user test"); + adminStmt.execute("revoke SELECT ON ANY from user test"); + adminStmt.execute("create role role1"); + adminStmt.execute("grant all to role role1 with grant option"); + } + + Set listUserPrivilegeResult = new HashSet<>(); + for (PrivilegeType privilegeType : PrivilegeType.values()) { + if (privilegeType == PrivilegeType.SELECT) { + continue; + } + if (privilegeType.isRelationalPrivilege()) { + listUserPrivilegeResult.add(",*.*," + privilegeType + ",false,"); + } + if (privilegeType.forRelationalSys()) { + listUserPrivilegeResult.add(",," + privilegeType + ",false,"); + } + } + + Set listRolePrivilegeResult = new HashSet<>(); + for (PrivilegeType privilegeType : PrivilegeType.values()) { + if (privilegeType.isRelationalPrivilege()) { + listRolePrivilegeResult.add("role1,*.*," + privilegeType + ",true,"); + } + if (privilegeType.forRelationalSys()) { + listRolePrivilegeResult.add("role1,," + privilegeType + ",true,"); + } + } + + try (Connection userCon = + EnvFactory.getEnv().getConnection("test", "password", BaseEnv.TABLE_SQL_DIALECT); + Statement userConStatement = userCon.createStatement()) { + ResultSet resultSet = userConStatement.executeQuery("List privileges of user test"); + TestUtils.assertResultSetEqual( + resultSet, "Role,Scope,Privileges,GrantOption,", listUserPrivilegeResult); + + // Have manage_role privilege + resultSet = userConStatement.executeQuery("List privileges of role role1"); + TestUtils.assertResultSetEqual( + resultSet, "Role,Scope,Privileges,GrantOption,", listRolePrivilegeResult); + + // Do not have grant option + Assert.assertThrows( + SQLException.class, + () -> { + userConStatement.execute("GRANT SELECT ON DATABASE TEST to role role1"); + }); + } + + try (Connection adminCon = EnvFactory.getEnv().getConnection(BaseEnv.TABLE_SQL_DIALECT); + Statement adminStmt = adminCon.createStatement()) { + adminStmt.execute("REVOKE ALL FROM USER test"); + ResultSet resultSet = adminStmt.executeQuery("List privileges of user test"); + TestUtils.assertResultSetEqual( + resultSet, "Role,Scope,Privileges,GrantOption,", Collections.emptySet()); + } + } +} diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/manual/IoTDBPipeMetaHistoricalIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/manual/IoTDBPipeMetaHistoricalIT.java index cbb82c2cd899..6c36de7b6966 100644 --- a/integration-test/src/test/java/org/apache/iotdb/pipe/it/manual/IoTDBPipeMetaHistoricalIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/manual/IoTDBPipeMetaHistoricalIT.java @@ -235,7 +235,7 @@ public void testAuthInclusion() throws Exception { "list privileges of role `admin`", ColumnHeaderConstant.ROLE + "," - + ColumnHeaderConstant.PATH + + ColumnHeaderConstant.SCOPE + "," + ColumnHeaderConstant.PRIVILEGES + "," @@ -243,13 +243,13 @@ public void testAuthInclusion() throws Exception { + ",", new HashSet<>( Arrays.asList( - "admin,root.**,MANAGE_USER,false,", - "admin,root.**,MANAGE_ROLE,false,", - "admin,root.**,USE_TRIGGER,false,", - "admin,root.**,USE_UDF,false,", - "admin,root.**,USE_CQ,false,", - "admin,root.**,USE_PIPE,false,", - "admin,root.**,MANAGE_DATABASE,false,", + "admin,,MANAGE_USER,false,", + "admin,,MANAGE_ROLE,false,", + "admin,,USE_TRIGGER,false,", + "admin,,USE_UDF,false,", + "admin,,USE_CQ,false,", + "admin,,USE_PIPE,false,", + "admin,,MANAGE_DATABASE,false,", "admin,root.**,READ_DATA,false,", "admin,root.**,READ_SCHEMA,false,"))); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java index 787f196ff946..183ff23a8b7d 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlan.java @@ -27,7 +27,8 @@ import org.apache.iotdb.confignode.consensus.request.write.ainode.RegisterAINodePlan; import org.apache.iotdb.confignode.consensus.request.write.ainode.RemoveAINodePlan; import org.apache.iotdb.confignode.consensus.request.write.ainode.UpdateAINodePlan; -import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorRelationalPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.confignode.ApplyConfigNodePlan; import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan; import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateClusterIdPlan; @@ -285,7 +286,36 @@ public static ConfigPhysicalPlan create(final ByteBuffer buffer) throws IOExcept case RevokeRoleFromUser: case UpdateUser: case CreateUserWithRawPassword: - plan = new AuthorPlan(configPhysicalPlanType); + plan = new AuthorTreePlan(configPhysicalPlanType); + break; + case RCreateUser: + case RCreateRole: + case RUpdateUser: + case RDropRole: + case RDropUser: + case RGrantUserRole: + case RRevokeUserRole: + case RGrantRoleAny: + case RGrantUserAny: + case RGrantUserAll: + case RGrantRoleAll: + case RGrantUserDBPriv: + case RGrantUserTBPriv: + case RGrantRoleDBPriv: + case RGrantRoleTBPriv: + case RRevokeRoleAny: + case RRevokeUserAny: + case RRevokeUserAll: + case RRevokeRoleAll: + case RRevokeUserDBPriv: + case RRevokeUserTBPriv: + case RRevokeRoleDBPriv: + case RRevokeRoleTBPriv: + case RGrantUserSysPri: + case RGrantRoleSysPri: + case RRevokeUserSysPri: + case RRevokeRoleSysPri: + plan = new AuthorRelationalPlan(configPhysicalPlanType); break; case ApplyConfigNode: plan = new ApplyConfigNodePlan(); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java index 6d19699fdf43..1e938bc82e22 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanType.java @@ -132,6 +132,39 @@ public enum ConfigPhysicalPlanType { ListRoleUsers((short) 637), CreateUserWithRawPassword((short) 638), + /** Table Author */ + RCreateUser((short) 641), + RCreateRole((short) 642), + RUpdateUser((short) 643), + RDropUser((short) 644), + RDropRole((short) 645), + RGrantUserRole((short) 646), + RRevokeUserRole((short) 647), + RGrantUserAny((short) 648), + RGrantRoleAny((short) 649), + RGrantUserAll((short) 650), + RGrantRoleAll((short) 652), + RGrantUserDBPriv((short) 653), + RGrantUserTBPriv((short) 654), + RGrantRoleDBPriv((short) 655), + RGrantRoleTBPriv((short) 656), + RRevokeUserAny((short) 657), + RRevokeRoleAny((short) 658), + RRevokeUserAll((short) 659), + RRevokeRoleAll((short) 660), + RRevokeUserDBPriv((short) 661), + RRevokeUserTBPriv((short) 662), + RRevokeRoleDBPriv((short) 663), + RRevokeRoleTBPriv((short) 664), + RGrantUserSysPri((short) 665), + RGrantRoleSysPri((short) 666), + RRevokeUserSysPri((short) 667), + RRevokeRoleSysPri((short) 668), + RListUser((short) 669), + RListRole((short) 670), + RListUserPrivilege((short) 671), + RListRolePrivilege((short) 672), + /** Function. */ CreateFunction((short) 700), DropTreeModelFunction((short) 701), diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/auth/AuthorReadPlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/auth/AuthorReadPlan.java deleted file mode 100644 index d418aa4d0835..000000000000 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/read/auth/AuthorReadPlan.java +++ /dev/null @@ -1,143 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.iotdb.confignode.consensus.request.read.auth; - -import org.apache.iotdb.commons.auth.entity.PrivilegeType; -import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.read.ConfigPhysicalReadPlan; - -import java.util.List; -import java.util.Objects; -import java.util.Set; - -public class AuthorReadPlan extends ConfigPhysicalReadPlan { - - private final ConfigPhysicalPlanType authorType; - private final String roleName; - private String password; - private final String newPassword; - private Set permissions; - private final List nodeNameList; - private String userName; - private final boolean grantOpt; - - /** - * {@link AuthorReadPlan} Constructor. - * - * @param authorType author type - * @param userName user name - * @param roleName role name - * @param password password - * @param newPassword new password - * @param permissions permissions - * @param grantOpt with grant option, only grant statement can set grantOpt = true - * @param nodeNameList node name in Path structure - */ - public AuthorReadPlan( - final ConfigPhysicalPlanType authorType, - final String userName, - final String roleName, - final String password, - final String newPassword, - final Set permissions, - final boolean grantOpt, - final List nodeNameList) { - super(authorType); - this.authorType = authorType; - this.userName = userName; - this.roleName = roleName; - this.password = password; - this.newPassword = newPassword; - this.permissions = permissions; - this.grantOpt = grantOpt; - this.nodeNameList = nodeNameList; - } - - public String getRoleName() { - return roleName; - } - - public String getPassword() { - return password; - } - - public void setPassword(final String password) { - this.password = password; - } - - public Set getPermissions() { - return permissions; - } - - public void setPermissions(final Set permissions) { - this.permissions = permissions; - } - - public String getUserName() { - return userName; - } - - public void setUserName(final String userName) { - this.userName = userName; - } - - @Override - public boolean equals(final Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - final AuthorReadPlan that = (AuthorReadPlan) o; - return Objects.equals(authorType, that.authorType) - && Objects.equals(userName, that.userName) - && Objects.equals(roleName, that.roleName) - && Objects.equals(password, that.password) - && Objects.equals(newPassword, that.newPassword) - && Objects.equals(permissions, that.permissions) - && grantOpt == that.grantOpt - && Objects.equals(nodeNameList, that.nodeNameList); - } - - @Override - public int hashCode() { - return Objects.hash( - authorType, userName, roleName, password, newPassword, permissions, nodeNameList, grantOpt); - } - - @Override - public String toString() { - return "[type:" - + authorType - + ", username:" - + userName - + ", rolename:" - + roleName - + ", permissions:" - + PrivilegeType.toPriType(permissions) - + ", grant option:" - + grantOpt - + ", paths:" - + nodeNameList - + "]"; - } -} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorPlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorPlan.java index 8711cd86587a..5c78c49813c4 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorPlan.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorPlan.java @@ -19,80 +19,42 @@ package org.apache.iotdb.confignode.consensus.request.write.auth; -import org.apache.iotdb.commons.auth.entity.PrivilegeType; -import org.apache.iotdb.commons.exception.MetadataException; -import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.commons.utils.BasicStructureSerDeUtil; -import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; +import org.apache.iotdb.confignode.consensus.request.read.ConfigPhysicalReadPlan; -import org.apache.tsfile.utils.ReadWriteIOUtils; -import org.slf4j.Logger; - -import java.io.DataOutputStream; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.HashSet; -import java.util.List; import java.util.Objects; -import java.util.Set; -public class AuthorPlan extends ConfigPhysicalPlan { - private static final Logger logger = org.slf4j.LoggerFactory.getLogger(AuthorPlan.class); +public abstract class AuthorPlan extends ConfigPhysicalReadPlan { - private ConfigPhysicalPlanType authorType; - private String roleName; - private String password; - private String newPassword; - private Set permissions; - private List nodeNameList; - private String userName; - private boolean grantOpt; + protected String roleName; + protected String password; + protected String newPassword; + protected String userName; + protected boolean grantOpt; public AuthorPlan(final ConfigPhysicalPlanType type) { super(type); - authorType = type; - } - - /** - * {@link AuthorPlan} Constructor. - * - * @param authorType author type - * @param userName user name - * @param roleName role name - * @param password password - * @param newPassword new password - * @param permissions permissions - * @param grantOpt with grant option, only grant statement can set grantOpt = true - * @param nodeNameList node name in Path structure - */ + } + + // To maintain compatibility between TreeAuthorPlan and RelationalAuthorPlan, + // the newPassword field is retained here. public AuthorPlan( - final ConfigPhysicalPlanType authorType, - final String userName, - final String roleName, - final String password, - final String newPassword, - final Set permissions, - final boolean grantOpt, - final List nodeNameList) { - this(authorType); - this.authorType = authorType; + ConfigPhysicalPlanType type, + String userName, + String roleName, + String password, + String newPassword, + boolean grantOpt) { + super(type); this.userName = userName; this.roleName = roleName; this.password = password; this.newPassword = newPassword; - this.permissions = permissions; this.grantOpt = grantOpt; - this.nodeNameList = nodeNameList; } public ConfigPhysicalPlanType getAuthorType() { - return authorType; - } - - public void setAuthorType(final ConfigPhysicalPlanType authorType) { - this.authorType = authorType; + return super.getType(); } public String getRoleName() { @@ -115,12 +77,8 @@ public String getNewPassword() { return newPassword; } - public Set getPermissions() { - return permissions; - } - - public void setPermissions(final Set permissions) { - this.permissions = permissions; + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; } public boolean getGrantOpt() { @@ -131,14 +89,6 @@ public void setGrantOpt(final boolean grantOpt) { this.grantOpt = grantOpt; } - public List getNodeNameList() { - return nodeNameList; - } - - public void setNodeNameList(final List nodeNameList) { - this.nodeNameList = nodeNameList; - } - public String getUserName() { return userName; } @@ -148,99 +98,37 @@ public void setUserName(final String userName) { } @Override - protected void serializeImpl(final DataOutputStream stream) throws IOException { - ReadWriteIOUtils.write(authorType.getPlanType(), stream); - BasicStructureSerDeUtil.write(userName, stream); - BasicStructureSerDeUtil.write(roleName, stream); - BasicStructureSerDeUtil.write(password, stream); - BasicStructureSerDeUtil.write(newPassword, stream); - if (permissions == null) { - stream.write((byte) 0); - } else { - stream.write((byte) 1); - stream.writeInt(permissions.size()); - for (final int permission : permissions) { - stream.writeInt(permission); - } - } - BasicStructureSerDeUtil.write(nodeNameList.size(), stream); - for (final PartialPath partialPath : nodeNameList) { - BasicStructureSerDeUtil.write(partialPath.getFullPath(), stream); - } - BasicStructureSerDeUtil.write(grantOpt ? 1 : 0, stream); - } - - @Override - protected void deserializeImpl(ByteBuffer buffer) { - userName = BasicStructureSerDeUtil.readString(buffer); - roleName = BasicStructureSerDeUtil.readString(buffer); - password = BasicStructureSerDeUtil.readString(buffer); - newPassword = BasicStructureSerDeUtil.readString(buffer); - final byte hasPermissions = buffer.get(); - if (hasPermissions == (byte) 0) { - this.permissions = null; - } else { - final int permissionsSize = buffer.getInt(); - this.permissions = new HashSet<>(); - for (int i = 0; i < permissionsSize; i++) { - permissions.add(buffer.getInt()); - } - } - - final int nodeNameListSize = BasicStructureSerDeUtil.readInt(buffer); - nodeNameList = new ArrayList<>(nodeNameListSize); - try { - for (int i = 0; i < nodeNameListSize; i++) { - nodeNameList.add(new PartialPath(BasicStructureSerDeUtil.readString(buffer))); - } - } catch (MetadataException e) { - logger.error("Invalid path when deserialize authPlan: {}", nodeNameList, e); - } - grantOpt = false; - if (this.authorType.ordinal() >= ConfigPhysicalPlanType.CreateUser.ordinal()) { - grantOpt = BasicStructureSerDeUtil.readInt(buffer) > 0; - } - } - - @Override - public boolean equals(final Object o) { + public boolean equals(Object o) { if (this == o) { return true; } - if (o == null || getClass() != o.getClass()) { + if (!(o instanceof AuthorPlan)) { return false; } - final AuthorPlan that = (AuthorPlan) o; - return Objects.equals(authorType, that.authorType) + AuthorPlan that = (AuthorPlan) o; + return Objects.equals(super.getType(), that.getAuthorType()) && Objects.equals(userName, that.userName) && Objects.equals(roleName, that.roleName) && Objects.equals(password, that.password) && Objects.equals(newPassword, that.newPassword) - && Objects.equals(permissions, that.permissions) - && grantOpt == that.grantOpt - && Objects.equals(nodeNameList, that.nodeNameList); + && grantOpt == that.grantOpt; } @Override public int hashCode() { - return Objects.hash( - authorType, userName, roleName, password, newPassword, permissions, nodeNameList, grantOpt); + return Objects.hash(super.getType(), userName, roleName, password, newPassword, grantOpt); } @Override public String toString() { return "[type:" - + authorType + + super.getType() + ", username:" + userName + ", rolename:" + roleName - + ", permissions:" - + PrivilegeType.toPriType(permissions) + ", grant option:" + grantOpt - + ", paths:" - + nodeNameList + "]"; } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorRelationalPlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorRelationalPlan.java new file mode 100644 index 000000000000..87c38d781ddd --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorRelationalPlan.java @@ -0,0 +1,167 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.confignode.consensus.request.write.auth; + +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.utils.BasicStructureSerDeUtil; +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; + +import org.apache.tsfile.utils.ReadWriteIOUtils; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.Collections; +import java.util.HashSet; +import java.util.Objects; +import java.util.Set; + +public class AuthorRelationalPlan extends AuthorPlan { + protected Set permissions; + protected String databaseName; + protected String tableName; + + public AuthorRelationalPlan(final ConfigPhysicalPlanType authorType) { + super(authorType); + } + + public AuthorRelationalPlan( + final ConfigPhysicalPlanType authorType, + final String userName, + final String roleName, + final String databaseName, + final String tableName, + final Set permissions, + final boolean grantOpt, + final String password) { + super(authorType, userName, roleName, password, "", grantOpt); + this.databaseName = databaseName; + this.tableName = tableName; + this.permissions = permissions; + } + + public AuthorRelationalPlan( + final ConfigPhysicalPlanType authorType, + final String userName, + final String roleName, + final String databaseName, + final String tableName, + final int permission, + final boolean grantOpt) { + super(authorType, userName, roleName, "", "", grantOpt); + this.databaseName = databaseName; + this.tableName = tableName; + this.permissions = Collections.singleton(permission); + } + + public String getDatabaseName() { + return databaseName; + } + + public void setDatabaseName(String databaseName) { + this.databaseName = databaseName; + } + + public String getTableName() { + return tableName; + } + + public void setTableName(String tableName) { + this.tableName = tableName; + } + + public Set getPermissions() { + return permissions; + } + + public void setPermissions(Set permissions) { + this.permissions = permissions; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof AuthorRelationalPlan) { + AuthorRelationalPlan that = (AuthorRelationalPlan) o; + return super.equals(o) + && Objects.equals(this.databaseName, that.databaseName) + && Objects.equals(this.tableName, that.tableName) + && Objects.equals(this.permissions, that.permissions); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), databaseName, tableName, permissions); + } + + @Override + public String toString() { + return "[type:" + + super.getType() + + ", name:" + + userName + + ", role:" + + roleName + + ", permissions:" + + PrivilegeType.toPriType(permissions) + + ", grant option:" + + grantOpt + + ", DB:" + + databaseName + + ", TABLE:" + + tableName + + "]"; + } + + @Override + protected void serializeImpl(DataOutputStream stream) throws IOException { + ReadWriteIOUtils.write(getType().getPlanType(), stream); + BasicStructureSerDeUtil.write(userName, stream); + BasicStructureSerDeUtil.write(roleName, stream); + BasicStructureSerDeUtil.write(password, stream); + BasicStructureSerDeUtil.write(databaseName, stream); + BasicStructureSerDeUtil.write(tableName, stream); + stream.writeInt(permissions.size()); + for (Integer permission : permissions) { + stream.writeInt(permission); + } + stream.write(grantOpt ? (byte) 1 : (byte) 0); + } + + @Override + protected void deserializeImpl(ByteBuffer buffer) { + newPassword = ""; + userName = BasicStructureSerDeUtil.readString(buffer); + roleName = BasicStructureSerDeUtil.readString(buffer); + password = BasicStructureSerDeUtil.readString(buffer); + databaseName = BasicStructureSerDeUtil.readString(buffer); + tableName = BasicStructureSerDeUtil.readString(buffer); + permissions = new HashSet<>(); + int size = buffer.getInt(); + for (int i = 0; i < size; i++) { + permissions.add(buffer.getInt()); + } + grantOpt = buffer.get() == (byte) 1; + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorTreePlan.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorTreePlan.java new file mode 100644 index 000000000000..25c7ec92cd6e --- /dev/null +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/request/write/auth/AuthorTreePlan.java @@ -0,0 +1,176 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.confignode.consensus.request.write.auth; + +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.exception.MetadataException; +import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.utils.BasicStructureSerDeUtil; +import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; + +import org.apache.tsfile.utils.ReadWriteIOUtils; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +public class AuthorTreePlan extends AuthorPlan { + protected Set permissions; + protected List nodeNameList; + + public AuthorTreePlan(final ConfigPhysicalPlanType type) { + super(type); + } + + /** + * {@link AuthorTreePlan} Constructor. + * + * @param authorType author type + * @param userName user name + * @param roleName role name + * @param password password + * @param permissions permissions + * @param grantOpt with grant option, only grant statement can set grantOpt = true + * @param nodeNameList node name in Path structure + */ + public AuthorTreePlan( + final ConfigPhysicalPlanType authorType, + final String userName, + final String roleName, + final String password, + final String newPassword, + final Set permissions, + final boolean grantOpt, + final List nodeNameList) { + super(authorType, userName, roleName, password, newPassword, grantOpt); + this.permissions = permissions; + this.nodeNameList = nodeNameList; + } + + public Set getPermissions() { + return permissions; + } + + public void setPermissions(Set permissions) { + this.permissions = permissions; + } + + public List getNodeNameList() { + return nodeNameList; + } + + public void setNodeNameList(List nodeNameList) { + this.nodeNameList = nodeNameList; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o instanceof AuthorTreePlan) { + AuthorTreePlan that = (AuthorTreePlan) o; + return super.equals(that) + && Objects.equals(permissions, that.permissions) + && Objects.equals(nodeNameList, that.nodeNameList); + } else { + return false; + } + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), permissions, nodeNameList); + } + + @Override + public String toString() { + return "[type:" + + super.getType() + + ", username:" + + super.getUserName() + + ", rolename:" + + super.getRoleName() + + ", permissions:" + + PrivilegeType.toPriType(permissions) + + ", grant option:" + + super.getGrantOpt() + + ", paths:" + + nodeNameList + + "]"; + } + + @Override + protected void serializeImpl(DataOutputStream stream) throws IOException { + ReadWriteIOUtils.write(getType().getPlanType(), stream); + BasicStructureSerDeUtil.write(userName, stream); + BasicStructureSerDeUtil.write(roleName, stream); + BasicStructureSerDeUtil.write(password, stream); + BasicStructureSerDeUtil.write(newPassword, stream); + if (permissions == null) { + stream.write((byte) 0); + } else { + stream.write((byte) 1); + stream.writeInt(permissions.size()); + for (int permission : permissions) { + stream.writeInt(permission); + } + } + BasicStructureSerDeUtil.write(nodeNameList.size(), stream); + for (PartialPath partialPath : nodeNameList) { + BasicStructureSerDeUtil.write(partialPath.getFullPath(), stream); + } + BasicStructureSerDeUtil.write(super.getGrantOpt() ? 1 : 0, stream); + } + + @Override + protected void deserializeImpl(ByteBuffer buffer) { + userName = BasicStructureSerDeUtil.readString(buffer); + roleName = BasicStructureSerDeUtil.readString(buffer); + password = BasicStructureSerDeUtil.readString(buffer); + newPassword = BasicStructureSerDeUtil.readString(buffer); + if (buffer.get() == (byte) 0) { + this.permissions = null; + } else { + int permissionsSize = buffer.getInt(); + this.permissions = new HashSet<>(); + for (int i = 0; i < permissionsSize; i++) { + permissions.add(buffer.getInt()); + } + } + + int nodeNameListSize = BasicStructureSerDeUtil.readInt(buffer); + nodeNameList = new ArrayList<>(nodeNameListSize); + try { + for (int i = 0; i < nodeNameListSize; i++) { + nodeNameList.add(new PartialPath(BasicStructureSerDeUtil.readString(buffer))); + } + } catch (MetadataException e) { + // do nothing + } + if (super.getAuthorType().ordinal() >= ConfigPhysicalPlanType.CreateUser.ordinal()) { + super.setGrantOpt(BasicStructureSerDeUtil.readInt(buffer) > 0); + } + } +} diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/statemachine/ConfigRegionStateMachine.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/statemachine/ConfigRegionStateMachine.java index 8322a55ba3a6..c9389ee40e3f 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/statemachine/ConfigRegionStateMachine.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/statemachine/ConfigRegionStateMachine.java @@ -294,7 +294,6 @@ public void notifyLeaderReady() { () -> configManager.getProcedureManager().getStore().getProcedureInfo().upgrade()); configManager.getRetryFailedTasksThread().startRetryFailedTasksService(); configManager.getPartitionManager().startRegionCleaner(); - configManager.checkUserPathPrivilege(); // Add Metric after leader ready configManager.addMetrics(); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java index 61ea1836f39e..370210da5ea8 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java @@ -36,6 +36,7 @@ import org.apache.iotdb.common.rpc.thrift.TShowConfigurationResp; import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot; import org.apache.iotdb.commons.auth.AuthException; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.cluster.NodeStatus; import org.apache.iotdb.commons.cluster.NodeType; import org.apache.iotdb.commons.conf.CommonConfig; @@ -63,7 +64,6 @@ import org.apache.iotdb.confignode.conf.SystemPropertiesUtils; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; import org.apache.iotdb.confignode.consensus.request.read.ainode.GetAINodeConfigurationPlan; -import org.apache.iotdb.confignode.consensus.request.read.auth.AuthorReadPlan; import org.apache.iotdb.confignode.consensus.request.read.database.CountDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.database.GetDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.datanode.GetDataNodeConfigurationPlan; @@ -1261,7 +1261,7 @@ public TSStatus operatePermission(final AuthorPlan authorPlan) { } @Override - public DataSet queryPermission(final AuthorReadPlan authorPlan) { + public DataSet queryPermission(final AuthorPlan authorPlan) { final TSStatus status = confirmLeader(); if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return permissionManager.queryPermission(authorPlan); @@ -1285,11 +1285,10 @@ public TPermissionInfoResp login(String username, String password) { } @Override - public TPermissionInfoResp checkUserPrivileges( - String username, List paths, int permission) { + public TPermissionInfoResp checkUserPrivileges(String username, PrivilegeUnion union) { TSStatus status = confirmLeader(); if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { - return permissionManager.checkUserPrivileges(username, paths, permission); + return permissionManager.checkUserPrivileges(username, union); } else { TPermissionInfoResp resp = AuthUtils.generateEmptyPermissionInfoResp(); resp.setStatus(status); @@ -1315,17 +1314,12 @@ public TAuthizedPatternTreeResp fetchAuthizedPatternTree(String username, int pe } } - public void checkUserPathPrivilege() { - permissionManager.checkUserPathPrivilege(); - } - - public TPermissionInfoResp checkUserPrivilegeGrantOpt( - String username, List paths, int permission) { + public TPermissionInfoResp checkUserPrivilegeGrantOpt(String username, PrivilegeUnion union) { TSStatus status = confirmLeader(); TPermissionInfoResp resp = new TPermissionInfoResp(); if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { try { - resp = permissionManager.checkUserPrivilegeGrantOpt(username, paths, permission); + resp = permissionManager.checkUserPrivilegeGrantOpt(username, union); } catch (AuthException e) { status.setCode(e.getCode().getStatusCode()).setMessage(e.getMessage()); resp.setStatus(status); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java index a7ed4f7968a2..498079237aee 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/IManager.java @@ -27,11 +27,11 @@ import org.apache.iotdb.common.rpc.thrift.TSetConfigurationReq; import org.apache.iotdb.common.rpc.thrift.TSetSpaceQuotaReq; import org.apache.iotdb.common.rpc.thrift.TShowConfigurationResp; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.cluster.NodeStatus; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.confignode.consensus.request.read.ainode.GetAINodeConfigurationPlan; -import org.apache.iotdb.confignode.consensus.request.read.auth.AuthorReadPlan; import org.apache.iotdb.confignode.consensus.request.read.database.CountDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.database.GetDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.datanode.GetDataNodeConfigurationPlan; @@ -488,13 +488,13 @@ TDataPartitionTableResp getOrCreateDataPartition( * * @return PermissionInfoDataSet */ - DataSet queryPermission(final AuthorReadPlan authorPlan); + DataSet queryPermission(final AuthorPlan authorPlan); /** login. */ TPermissionInfoResp login(String username, String password); /** Check User Privileges. */ - TPermissionInfoResp checkUserPrivileges(String username, List paths, int permission); + TPermissionInfoResp checkUserPrivileges(String username, PrivilegeUnion union); /** * Register ConfigNode when it is first startup. diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java index 9592692d1bc4..4d31ae8961e8 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java @@ -22,9 +22,8 @@ import org.apache.iotdb.common.rpc.thrift.TDataNodeConfiguration; import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; -import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.read.auth.AuthorReadPlan; import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; import org.apache.iotdb.confignode.consensus.response.auth.PermissionInfoResp; import org.apache.iotdb.confignode.manager.consensus.ConsensusManager; @@ -91,7 +90,7 @@ public TSStatus operatePermission(AuthorPlan authorPlan, boolean isGeneratedByPi * @param authorPlan AuthorReq * @return PermissionInfoResp */ - public PermissionInfoResp queryPermission(final AuthorReadPlan authorPlan) { + public PermissionInfoResp queryPermission(final AuthorPlan authorPlan) { try { return (PermissionInfoResp) getConsensusManager().read(authorPlan); } catch (final ConsensusException e) { @@ -110,27 +109,23 @@ public TPermissionInfoResp login(String username, String password) { return authorInfo.login(username, password); } - public TPermissionInfoResp checkUserPrivileges( - String username, List paths, int permission) { - return authorInfo.checkUserPrivileges(username, paths, permission); + public TPermissionInfoResp checkUserPrivileges(String username, PrivilegeUnion union) { + return authorInfo.checkUserPrivileges(username, union); } public TAuthizedPatternTreeResp fetchAuthizedPTree(String username, int permission) throws AuthException { - return authorInfo.generateAuthizedPTree(username, permission); + return authorInfo.generateAuthorizedPTree(username, permission); } - public TPermissionInfoResp checkUserPrivilegeGrantOpt( - String username, List paths, int permission) throws AuthException { - return authorInfo.checkUserPrivilegeGrantOpt(username, paths, permission); + public TPermissionInfoResp checkUserPrivilegeGrantOpt(String username, PrivilegeUnion union) + throws AuthException { + union.setGrantOption(true); + return authorInfo.checkUserPrivileges(username, union); } public TPermissionInfoResp checkRoleOfUser(String username, String rolename) throws AuthException { return authorInfo.checkRoleOfUser(username, rolename); } - - public void checkUserPathPrivilege() { - authorInfo.checkUserPathPrivilege(); - } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/extractor/PipeConfigPhysicalPlanTreePatternParseVisitor.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/extractor/PipeConfigPhysicalPlanTreePatternParseVisitor.java index 5f1d2f3447a1..f5e5888ed7d5 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/extractor/PipeConfigPhysicalPlanTreePatternParseVisitor.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/extractor/PipeConfigPhysicalPlanTreePatternParseVisitor.java @@ -26,6 +26,7 @@ import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanVisitor; import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.database.DatabaseSchemaPlan; import org.apache.iotdb.confignode.consensus.request.write.database.DeleteDatabasePlan; import org.apache.iotdb.confignode.consensus.request.write.database.SetTTLPlan; @@ -166,50 +167,51 @@ public Optional visitExtendSchemaTemplate( @Override public Optional visitGrantUser( final AuthorPlan grantUserPlan, final IoTDBTreePattern pattern) { - return visitPathRelatedAuthorPlan(grantUserPlan, pattern); + return visitTreeAuthorPlan(grantUserPlan, pattern); } @Override public Optional visitRevokeUser( final AuthorPlan revokeUserPlan, final IoTDBTreePattern pattern) { - return visitPathRelatedAuthorPlan(revokeUserPlan, pattern); + return visitTreeAuthorPlan(revokeUserPlan, pattern); } @Override public Optional visitGrantRole( final AuthorPlan revokeUserPlan, final IoTDBTreePattern pattern) { - return visitPathRelatedAuthorPlan(revokeUserPlan, pattern); + return visitTreeAuthorPlan(revokeUserPlan, pattern); } @Override public Optional visitRevokeRole( final AuthorPlan revokeUserPlan, final IoTDBTreePattern pattern) { - return visitPathRelatedAuthorPlan(revokeUserPlan, pattern); + return visitTreeAuthorPlan(revokeUserPlan, pattern); } - private Optional visitPathRelatedAuthorPlan( + private Optional visitTreeAuthorPlan( final AuthorPlan pathRelatedAuthorPlan, final IoTDBTreePattern pattern) { + AuthorTreePlan plan = (AuthorTreePlan) pathRelatedAuthorPlan; final List intersectedPaths = - pathRelatedAuthorPlan.getNodeNameList().stream() + plan.getNodeNameList().stream() .map(pattern::getIntersection) .flatMap(Collection::stream) .collect(Collectors.toList()); final Set permissions = !intersectedPaths.isEmpty() - ? pathRelatedAuthorPlan.getPermissions() - : pathRelatedAuthorPlan.getPermissions().stream() - .filter(permission -> !PrivilegeType.values()[permission].isPathRelevant()) + ? plan.getPermissions() + : plan.getPermissions().stream() + .filter(permission -> !PrivilegeType.values()[permission].isPathPrivilege()) .collect(Collectors.toSet()); return !permissions.isEmpty() ? Optional.of( - new AuthorPlan( - pathRelatedAuthorPlan.getAuthorType(), - pathRelatedAuthorPlan.getUserName(), - pathRelatedAuthorPlan.getRoleName(), - pathRelatedAuthorPlan.getPassword(), - pathRelatedAuthorPlan.getNewPassword(), + new AuthorTreePlan( + plan.getAuthorType(), + plan.getUserName(), + plan.getRoleName(), + plan.getPassword(), + plan.getNewPassword(), permissions, - pathRelatedAuthorPlan.getGrantOpt(), + plan.getGrantOpt(), intersectedPaths)) : Optional.empty(); } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java index 2a94e1a3ac1f..48d9a6f2f167 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/receiver/protocol/IoTDBConfigNodeReceiver.java @@ -21,6 +21,7 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathPatternTree; @@ -40,6 +41,7 @@ import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.database.DatabaseSchemaPlan; import org.apache.iotdb.confignode.consensus.request.write.database.DeleteDatabasePlan; import org.apache.iotdb.confignode.consensus.request.write.database.SetTTLPlan; @@ -257,13 +259,11 @@ private TSStatus checkPermission(final ConfigPhysicalPlan plan) { case AlterDatabase: case DeleteDatabase: return configManager - .checkUserPrivileges( - username, Collections.emptyList(), PrivilegeType.MANAGE_DATABASE.ordinal()) + .checkUserPrivileges(username, new PrivilegeUnion(PrivilegeType.MANAGE_DATABASE)) .getStatus(); case ExtendSchemaTemplate: return configManager - .checkUserPrivileges( - username, Collections.emptyList(), PrivilegeType.EXTEND_TEMPLATE.ordinal()) + .checkUserPrivileges(username, new PrivilegeUnion(PrivilegeType.EXTEND_TEMPLATE)) .getStatus(); case CreateSchemaTemplate: case CommitSetSchemaTemplate: @@ -276,55 +276,62 @@ private TSStatus checkPermission(final ConfigPhysicalPlan plan) { return configManager .checkUserPrivileges( username, - new ArrayList<>( - PathPatternTree.deserialize( - ((PipeDeleteTimeSeriesPlan) plan).getPatternTreeBytes()) - .getAllPathPatterns()), - PrivilegeType.WRITE_SCHEMA.ordinal()) + new PrivilegeUnion( + new ArrayList<>( + PathPatternTree.deserialize( + ((PipeDeleteTimeSeriesPlan) plan).getPatternTreeBytes()) + .getAllPathPatterns()), + PrivilegeType.WRITE_SCHEMA)) .getStatus(); case PipeDeleteLogicalView: return configManager .checkUserPrivileges( username, - new ArrayList<>( - PathPatternTree.deserialize( - ((PipeDeleteLogicalViewPlan) plan).getPatternTreeBytes()) - .getAllPathPatterns()), - PrivilegeType.WRITE_SCHEMA.ordinal()) + new PrivilegeUnion( + new ArrayList<>( + PathPatternTree.deserialize( + ((PipeDeleteLogicalViewPlan) plan).getPatternTreeBytes()) + .getAllPathPatterns()), + PrivilegeType.WRITE_SCHEMA)) .getStatus(); case PipeDeactivateTemplate: return configManager .checkUserPrivileges( username, - new ArrayList<>(((PipeDeactivateTemplatePlan) plan).getTemplateSetInfo().keySet()), - PrivilegeType.WRITE_SCHEMA.ordinal()) + new PrivilegeUnion( + new ArrayList<>( + ((PipeDeactivateTemplatePlan) plan).getTemplateSetInfo().keySet()), + PrivilegeType.WRITE_SCHEMA)) .getStatus(); case SetTTL: return configManager .checkUserPrivileges( username, - Collections.singletonList(new PartialPath(((SetTTLPlan) plan).getPathPattern())), - PrivilegeType.WRITE_SCHEMA.ordinal()) + new PrivilegeUnion( + Collections.singletonList( + new PartialPath(((SetTTLPlan) plan).getPathPattern())), + PrivilegeType.WRITE_SCHEMA)) .getStatus(); case UpdateTriggerStateInTable: case DeleteTriggerInTable: return configManager - .checkUserPrivileges( - username, Collections.emptyList(), PrivilegeType.USE_TRIGGER.ordinal()) + .checkUserPrivileges(username, new PrivilegeUnion(PrivilegeType.USE_TRIGGER)) .getStatus(); case GrantRole: case GrantUser: case RevokeUser: case RevokeRole: - for (final int permission : ((AuthorPlan) plan).getPermissions()) { + for (final int permission : ((AuthorTreePlan) plan).getPermissions()) { final TSStatus status = configManager .checkUserPrivilegeGrantOpt( username, - PrivilegeType.isPathRelevant(permission) - ? ((AuthorPlan) plan).getNodeNameList() - : Collections.emptyList(), - permission) + PrivilegeType.values()[permission].isPathPrivilege() + ? new PrivilegeUnion( + ((AuthorTreePlan) plan).getNodeNameList(), + PrivilegeType.values()[permission], + true) + : new PrivilegeUnion(PrivilegeType.values()[permission], true)) .getStatus(); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return status; @@ -335,23 +342,20 @@ private TSStatus checkPermission(final ConfigPhysicalPlan plan) { return ((AuthorPlan) plan).getUserName().equals(username) ? StatusUtils.OK : configManager - .checkUserPrivileges( - username, Collections.emptyList(), PrivilegeType.MANAGE_USER.ordinal()) + .checkUserPrivileges(username, new PrivilegeUnion(PrivilegeType.MANAGE_USER)) .getStatus(); case CreateUser: case CreateUserWithRawPassword: case DropUser: return configManager - .checkUserPrivileges( - username, Collections.emptyList(), PrivilegeType.MANAGE_USER.ordinal()) + .checkUserPrivileges(username, new PrivilegeUnion(PrivilegeType.MANAGE_USER)) .getStatus(); case CreateRole: case DropRole: case GrantRoleToUser: case RevokeRoleFromUser: return configManager - .checkUserPrivileges( - username, Collections.emptyList(), PrivilegeType.MANAGE_ROLE.ordinal()) + .checkUserPrivileges(username, new PrivilegeUnion(PrivilegeType.MANAGE_ROLE)) .getStatus(); default: return StatusUtils.OK; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java index 3f3b8cffc881..62abe8af8198 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfo.java @@ -24,8 +24,11 @@ import org.apache.iotdb.commons.auth.authorizer.BasicAuthorizer; import org.apache.iotdb.commons.auth.authorizer.IAuthorizer; import org.apache.iotdb.commons.auth.authorizer.OpenIdAuthorizer; +import org.apache.iotdb.commons.auth.entity.ModelType; import org.apache.iotdb.commons.auth.entity.PathPrivilege; -import org.apache.iotdb.commons.auth.entity.PriPrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeModelType; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.conf.CommonConfig; @@ -38,11 +41,11 @@ import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.read.auth.AuthorReadPlan; import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorRelationalPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.response.auth.PermissionInfoResp; import org.apache.iotdb.confignode.rpc.thrift.TAuthizedPatternTreeResp; -import org.apache.iotdb.confignode.rpc.thrift.TPathPrivilege; import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp; import org.apache.iotdb.confignode.rpc.thrift.TRoleResp; import org.apache.iotdb.confignode.rpc.thrift.TUserResp; @@ -60,13 +63,12 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; -import static org.apache.iotdb.commons.auth.entity.PrivilegeType.isPathRelevant; - public class AuthorInfo implements SnapshotProcessor { // Works at config node. @@ -76,8 +78,6 @@ public class AuthorInfo implements SnapshotProcessor { private IAuthorizer authorizer; - private boolean hasPrePriv = true; - public AuthorInfo() { try { authorizer = BasicAuthorizer.getInstance(); @@ -98,10 +98,10 @@ public TPermissionInfoResp login(String username, String password) { // Bring this user's permission information back to the datanode for caching if (authorizer instanceof OpenIdAuthorizer) { username = ((OpenIdAuthorizer) authorizer).getIoTDBUserName(username); - result = getUserPermissionInfo(username); + result = getUserPermissionInfo(username, ModelType.ALL); result.getUserInfo().setIsOpenIdUser(true); } else { - result = getUserPermissionInfo(username); + result = getUserPermissionInfo(username, ModelType.ALL); } result.setStatus(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS, "Login successfully")); @@ -121,63 +121,58 @@ public TPermissionInfoResp login(String username, String password) { return result; } - // if All paths fail, return No permission - // if some paths fail, return SUCCESS and failed index list - // if all path success, return success and empty index list - public TPermissionInfoResp checkUserPrivileges( - String username, List paths, int permission) { - boolean status = true; + public TPermissionInfoResp checkUserPrivileges(String username, PrivilegeUnion union) { + boolean status; TPermissionInfoResp result = new TPermissionInfoResp(); List failedList = new ArrayList<>(); try { - if (paths.isEmpty()) { - status = authorizer.checkUserPrivileges(username, null, permission); - } else { + if (union.getModelType() == PrivilegeModelType.TREE) { + List list = union.getPaths(); int pos = 0; - for (PartialPath path : paths) { - if (!checkOnePath(username, path, permission)) { + for (PartialPath path : list) { + if (!authorizer.checkUserPrivileges( + username, + new PrivilegeUnion(path, union.getPrivilegeType(), union.isGrantOption()))) { failedList.add(pos); } pos++; } - if (failedList.size() == paths.size()) { - status = false; + if (union.isGrantOption()) { + // all path should have grant option. + status = failedList.isEmpty(); + } else { + status = failedList.size() != list.size(); } + } else { + status = authorizer.checkUserPrivileges(username, union); } } catch (AuthException e) { status = false; } - if (status) { - try { - // Bring this user's permission information back to the datanode for caching - result = getUserPermissionInfo(username); - result.setFailPos(failedList); + + try { + result = getUserPermissionInfo(username, ModelType.ALL); + result.setFailPos(failedList); + if (status) { result.setStatus(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS)); - } catch (AuthException e) { - result.setStatus(RpcUtils.getStatus(e.getCode(), e.getMessage())); + } else { + result.setStatus(RpcUtils.getStatus(TSStatusCode.NO_PERMISSION)); } - } else { - result = AuthUtils.generateEmptyPermissionInfoResp(); - result.setFailPos(failedList); - result.setStatus(RpcUtils.getStatus(TSStatusCode.NO_PERMISSION)); + } catch (AuthException e) { + result.setStatus(RpcUtils.getStatus(e.getCode(), e.getMessage())); } return result; } - private boolean checkOnePath(String username, PartialPath path, int permission) - throws AuthException { - try { - if (authorizer.checkUserPrivileges(username, path, permission)) { - return true; - } - } catch (AuthException e) { - LOGGER.error("Error occurs when checking the seriesPath {} for user {}", path, username, e); - throw new AuthException(e.getCode(), e); + public TSStatus authorNonQuery(AuthorPlan authorPlan) { + if (authorPlan instanceof AuthorTreePlan) { + return authorNonQuery((AuthorTreePlan) authorPlan); + } else { + return authorNonQuery((AuthorRelationalPlan) authorPlan); } - return false; } - public TSStatus authorNonQuery(AuthorPlan authorPlan) { + public TSStatus authorNonQuery(AuthorTreePlan authorPlan) { ConfigPhysicalPlanType authorType = authorPlan.getAuthorType(); String userName = authorPlan.getUserName(); String roleName = authorPlan.getRoleName(); @@ -186,122 +181,79 @@ public TSStatus authorNonQuery(AuthorPlan authorPlan) { Set permissions = authorPlan.getPermissions(); boolean grantOpt = authorPlan.getGrantOpt(); List nodeNameList = authorPlan.getNodeNameList(); - // We will process the new version permissions after handling all the old version permissions. - // We assume that: - // 1. the permission logs generated by new version will always come after the old permissions. - // 2. two types of permission logs will not be mixed. - // When we begin to handle the new version's permissions, we need to check whether the old - // permissions have - // been processed before. The encoding and meaning of these old permissions have changed - // significantly. - if (authorType.ordinal() >= ConfigPhysicalPlanType.CreateUserDep.ordinal() - && authorType.ordinal() <= ConfigPhysicalPlanType.UpdateUserDep.ordinal()) { - // if meet old version's permissions, we will set pre version tag. - authorizer.setUserForPreVersion(true); - authorizer.setRoleForPreVersion(true); - } else { - if (hasPrePriv) { - // when we refresh our preversion's information? - // 1. before raftlog redoing finish.(ALL author plans in raftlog are pre version) - // 2. refresh during raftlog. (pre version mixed with new version) - authorizer.checkUserPathPrivilege(); - hasPrePriv = false; - } - } try { switch (authorType) { - case UpdateUserDep: case UpdateUser: authorizer.updateUserPassword(userName, newPassword); break; - case CreateUserDep: - AuthUtils.validatePasswordPre(password); - AuthUtils.validateUsernamePre(userName); - authorizer.createUserWithoutCheck(userName, password); - break; case CreateUser: authorizer.createUser(userName, password); break; case CreateUserWithRawPassword: authorizer.createUserWithRawPassword(userName, password); break; - case CreateRoleDep: - AuthUtils.validateRolenamePre(roleName); - authorizer.createRole(roleName); - break; case CreateRole: - AuthUtils.validateRolename(roleName); authorizer.createRole(roleName); break; - case DropUserDep: case DropUser: authorizer.deleteUser(userName); break; - case DropRoleDep: case DropRole: authorizer.deleteRole(roleName); break; - case GrantRoleDep: - grantPrivilegeForPreVersion(false, roleName, permissions, nodeNameList); - break; case GrantRole: for (int permission : permissions) { - if (!isPathRelevant(permission)) { - authorizer.grantPrivilegeToRole(roleName, null, permission, grantOpt); + PrivilegeType priv = PrivilegeType.values()[permission]; + if (priv.isSystemPrivilege()) { + authorizer.grantPrivilegeToRole(roleName, new PrivilegeUnion(priv, grantOpt)); continue; } for (PartialPath path : nodeNameList) { - authorizer.grantPrivilegeToRole(roleName, path, permission, grantOpt); + authorizer.grantPrivilegeToRole(roleName, new PrivilegeUnion(path, priv, grantOpt)); } } break; - case GrantUserDep: - grantPrivilegeForPreVersion(true, userName, permissions, nodeNameList); - break; case GrantUser: for (int permission : permissions) { - if (!isPathRelevant(permission)) { - authorizer.grantPrivilegeToUser(userName, null, permission, grantOpt); + PrivilegeType priv = PrivilegeType.values()[permission]; + if (priv.isSystemPrivilege()) { + authorizer.grantPrivilegeToUser(userName, new PrivilegeUnion(priv, grantOpt)); continue; } for (PartialPath path : nodeNameList) { - authorizer.grantPrivilegeToUser(userName, path, permission, grantOpt); + authorizer.grantPrivilegeToUser(userName, new PrivilegeUnion(path, priv, grantOpt)); } } break; - case GrantRoleToUserDep: case GrantRoleToUser: authorizer.grantRoleToUser(roleName, userName); break; - case RevokeUserDep: - revokePrivilegeForPreVersion(true, userName, permissions, nodeNameList); - break; case RevokeUser: for (int permission : permissions) { - if (!isPathRelevant(permission)) { - authorizer.revokePrivilegeFromUser(userName, null, permission); + PrivilegeType priv = PrivilegeType.values()[permission]; + if (priv.isSystemPrivilege()) { + authorizer.revokePrivilegeFromUser(userName, new PrivilegeUnion(priv, grantOpt)); continue; } for (PartialPath path : nodeNameList) { - authorizer.revokePrivilegeFromUser(userName, path, permission); + authorizer.revokePrivilegeFromUser( + userName, new PrivilegeUnion(path, priv, grantOpt)); } } break; - case RevokeRoleDep: - revokePrivilegeForPreVersion(false, roleName, permissions, nodeNameList); - break; case RevokeRole: for (int permission : permissions) { - if (!isPathRelevant(permission)) { - authorizer.revokePrivilegeFromRole(roleName, null, permission); + PrivilegeType priv = PrivilegeType.values()[permission]; + if (priv.isSystemPrivilege()) { + authorizer.revokePrivilegeFromRole(roleName, new PrivilegeUnion(priv, grantOpt)); continue; } for (PartialPath path : nodeNameList) { - authorizer.revokePrivilegeFromRole(roleName, path, permission); + authorizer.revokePrivilegeFromRole( + roleName, new PrivilegeUnion(path, priv, grantOpt)); } } break; - case RevokeRoleFromUserDep: case RevokeRoleFromUser: authorizer.revokeRoleFromUser(roleName, userName); break; @@ -312,14 +264,195 @@ public TSStatus authorNonQuery(AuthorPlan authorPlan) { } } catch (AuthException e) { return RpcUtils.getStatus(e.getCode(), e.getMessage()); - } finally { - authorizer.setUserForPreVersion(false); - authorizer.setRoleForPreVersion(false); } return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS); } - public PermissionInfoResp executeListUsers(final AuthorReadPlan plan) throws AuthException { + public TSStatus authorNonQuery(AuthorRelationalPlan authorPlan) { + ConfigPhysicalPlanType authorType = authorPlan.getAuthorType(); + String userName = authorPlan.getUserName(); + String roleName = authorPlan.getRoleName(); + String database = authorPlan.getDatabaseName(); + String table = authorPlan.getTableName(); + boolean grantOpt = authorPlan.getGrantOpt(); + Set permissions = authorPlan.getPermissions(); + Set privileges = new HashSet<>(); + if (authorType.ordinal() >= ConfigPhysicalPlanType.RGrantUserAny.ordinal() + && authorType.ordinal() <= ConfigPhysicalPlanType.RRevokeRoleSysPri.ordinal()) { + for (int permission : permissions) { + privileges.add(PrivilegeType.values()[permission]); + } + } + + try { + switch (authorType) { + case RCreateUser: + authorizer.createUser(userName, authorPlan.getPassword()); + break; + case RCreateRole: + authorizer.createRole(roleName); + break; + case RUpdateUser: + authorizer.updateUserPassword(userName, authorPlan.getPassword()); + break; + case RDropRole: + authorizer.deleteRole(roleName); + break; + case RDropUser: + authorizer.deleteUser(userName); + break; + case RGrantUserRole: + authorizer.grantRoleToUser(roleName, userName); + break; + case RRevokeUserRole: + authorizer.revokeRoleFromUser(roleName, userName); + break; + case RGrantUserAny: + for (PrivilegeType privilege : privileges) { + authorizer.grantPrivilegeToUser( + userName, new PrivilegeUnion(privilege, grantOpt, true)); + } + break; + case RGrantRoleAny: + for (PrivilegeType privilege : privileges) { + authorizer.grantPrivilegeToRole( + roleName, new PrivilegeUnion(privilege, grantOpt, true)); + } + break; + case RGrantUserAll: + for (PrivilegeType privilege : PrivilegeType.values()) { + if (privilege.forRelationalSys()) { + authorizer.grantPrivilegeToUser(userName, new PrivilegeUnion(privilege, grantOpt)); + } + if (privilege.isRelationalPrivilege()) { + authorizer.grantPrivilegeToUser( + userName, new PrivilegeUnion(privilege, grantOpt, true)); + } + } + break; + case RGrantRoleAll: + for (PrivilegeType privilege : PrivilegeType.values()) { + if (privilege.forRelationalSys()) { + authorizer.grantPrivilegeToRole(roleName, new PrivilegeUnion(privilege, grantOpt)); + } + if (privilege.isRelationalPrivilege()) { + authorizer.grantPrivilegeToRole( + roleName, new PrivilegeUnion(privilege, grantOpt, true)); + } + } + break; + case RGrantUserDBPriv: + for (PrivilegeType privilege : privileges) { + authorizer.grantPrivilegeToUser( + userName, new PrivilegeUnion(database, privilege, grantOpt)); + } + break; + case RGrantUserTBPriv: + for (PrivilegeType privilege : privileges) { + authorizer.grantPrivilegeToUser( + userName, new PrivilegeUnion(database, table, privilege, grantOpt)); + } + break; + case RGrantRoleDBPriv: + for (PrivilegeType privilege : privileges) { + authorizer.grantPrivilegeToRole( + roleName, new PrivilegeUnion(database, privilege, grantOpt)); + } + break; + case RGrantRoleTBPriv: + for (PrivilegeType privilege : privileges) { + authorizer.grantPrivilegeToRole( + roleName, new PrivilegeUnion(database, table, privilege, grantOpt)); + } + break; + case RRevokeUserAny: + for (PrivilegeType privilege : privileges) { + authorizer.revokePrivilegeFromUser( + userName, new PrivilegeUnion(privilege, grantOpt, true)); + } + break; + case RRevokeRoleAny: + for (PrivilegeType privilege : privileges) { + authorizer.revokePrivilegeFromRole( + roleName, new PrivilegeUnion(privilege, grantOpt, true)); + } + break; + case RRevokeUserAll: + for (PrivilegeType privilege : PrivilegeType.values()) { + if (privilege.forRelationalSys()) { + authorizer.revokePrivilegeFromUser(userName, new PrivilegeUnion(privilege, grantOpt)); + } + if (privilege.isRelationalPrivilege()) { + authorizer.revokePrivilegeFromUser( + userName, new PrivilegeUnion(privilege, grantOpt, true)); + } + } + break; + case RRevokeRoleAll: + for (PrivilegeType privilege : PrivilegeType.values()) { + if (privilege.forRelationalSys()) { + authorizer.revokePrivilegeFromRole(roleName, new PrivilegeUnion(privilege, grantOpt)); + } + if (privilege.isRelationalPrivilege()) { + authorizer.revokePrivilegeFromRole( + roleName, new PrivilegeUnion(privilege, grantOpt, true)); + } + } + break; + case RRevokeUserDBPriv: + for (PrivilegeType privilege : privileges) { + authorizer.revokePrivilegeFromUser( + userName, new PrivilegeUnion(database, privilege, grantOpt)); + } + break; + case RRevokeUserTBPriv: + for (PrivilegeType privilege : privileges) { + authorizer.revokePrivilegeFromUser( + userName, new PrivilegeUnion(database, table, privilege, grantOpt)); + } + break; + case RRevokeRoleDBPriv: + for (PrivilegeType privilege : privileges) { + authorizer.revokePrivilegeFromRole( + roleName, new PrivilegeUnion(database, privilege, grantOpt)); + } + break; + case RRevokeRoleTBPriv: + for (PrivilegeType privilege : privileges) { + authorizer.revokePrivilegeFromRole( + roleName, new PrivilegeUnion(database, table, privilege, grantOpt)); + } + break; + case RGrantUserSysPri: + for (PrivilegeType privilege : privileges) { + authorizer.grantPrivilegeToUser(userName, new PrivilegeUnion(privilege, grantOpt)); + } + break; + case RGrantRoleSysPri: + for (PrivilegeType privilege : privileges) { + authorizer.grantPrivilegeToRole(roleName, new PrivilegeUnion(privilege, grantOpt)); + } + break; + case RRevokeUserSysPri: + for (PrivilegeType privilege : privileges) { + authorizer.revokePrivilegeFromUser(userName, new PrivilegeUnion(privilege, grantOpt)); + } + break; + case RRevokeRoleSysPri: + for (PrivilegeType privilege : privileges) { + authorizer.revokePrivilegeFromRole(roleName, new PrivilegeUnion(privilege, grantOpt)); + } + break; + default: + throw new AuthException(TSStatusCode.ILLEGAL_PARAMETER, "not support"); + } + } catch (AuthException e) { + return RpcUtils.getStatus(e.getCode(), e.getMessage()); + } + return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS); + } + + public PermissionInfoResp executeListUsers(final AuthorPlan plan) throws AuthException { final PermissionInfoResp result = new PermissionInfoResp(); final List userList = authorizer.listAllUsers(); if (!plan.getRoleName().isEmpty()) { @@ -344,7 +477,7 @@ public PermissionInfoResp executeListUsers(final AuthorReadPlan plan) throws Aut return result; } - public PermissionInfoResp executeListRoles(final AuthorReadPlan plan) throws AuthException { + public PermissionInfoResp executeListRoles(final AuthorPlan plan) throws AuthException { final PermissionInfoResp result = new PermissionInfoResp(); final List permissionInfo = new ArrayList<>(); final List roleList = new ArrayList<>(); @@ -358,7 +491,7 @@ public PermissionInfoResp executeListRoles(final AuthorReadPlan plan) throws Aut result.setMemberInfo(permissionInfo); return result; } - roleList.addAll(user.getRoleList()); + roleList.addAll(user.getRoleSet()); } result.setTag(ColumnHeaderConstant.ROLE); result.setMemberInfo(roleList); @@ -366,8 +499,8 @@ public PermissionInfoResp executeListRoles(final AuthorReadPlan plan) throws Aut return result; } - public PermissionInfoResp executeListRolePrivileges(final AuthorReadPlan plan) - throws AuthException { + public PermissionInfoResp executeListRolePrivileges(final AuthorPlan plan) throws AuthException { + boolean isTreePlan = plan instanceof AuthorTreePlan; final PermissionInfoResp result = new PermissionInfoResp(); final List permissionInfo = new ArrayList<>(); final Role role = authorizer.getRole(plan.getRoleName()); @@ -378,20 +511,8 @@ public PermissionInfoResp executeListRolePrivileges(final AuthorReadPlan plan) return result; } final TPermissionInfoResp resp = new TPermissionInfoResp(); - final TRoleResp roleResp = new TRoleResp(); - roleResp.setRoleName(role.getName()); - final List pathList = new ArrayList<>(); - for (final PathPrivilege path : role.getPathPrivilegeList()) { - final TPathPrivilege pathPri = new TPathPrivilege(); - pathPri.setPriGrantOpt(path.getGrantOpt()); - pathPri.setPriSet(path.getPrivileges()); - pathPri.setPath(path.getPath().toString()); - pathList.add(pathPri); - } - roleResp.setPrivilegeList(pathList); - roleResp.setSysPriSet(role.getSysPrivilege()); - roleResp.setSysPriSetGrantOpt(role.getSysPriGrantOpt()); - final Map roleInfo = new HashMap<>(); + final TRoleResp roleResp = role.getRoleInfo(isTreePlan ? ModelType.TREE : ModelType.RELATIONAL); + Map roleInfo = new HashMap<>(); roleInfo.put(role.getName(), roleResp); resp.setRoleInfo(roleInfo); resp.setStatus(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS)); @@ -402,16 +523,18 @@ public PermissionInfoResp executeListRolePrivileges(final AuthorReadPlan plan) return result; } - public PermissionInfoResp executeListUserPrivileges(final AuthorReadPlan plan) - throws AuthException { + public PermissionInfoResp executeListUserPrivileges(final AuthorPlan plan) throws AuthException { final PermissionInfoResp result = new PermissionInfoResp(); + boolean isTreePlan = plan instanceof AuthorTreePlan; final User user = authorizer.getUser(plan.getUserName()); if (user == null) { result.setStatus( RpcUtils.getStatus(TSStatusCode.USER_NOT_EXIST, NO_USER_MSG + plan.getUserName())); return result; } - final TPermissionInfoResp resp = getUserPermissionInfo(plan.getUserName()); + final TPermissionInfoResp resp = + getUserPermissionInfo( + plan.getUserName(), isTreePlan ? ModelType.TREE : ModelType.RELATIONAL); resp.setStatus(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS)); result.setTag(ColumnHeaderConstant.PRIVILEGES); result.setPermissionInfoResp(resp); @@ -419,10 +542,11 @@ public PermissionInfoResp executeListUserPrivileges(final AuthorReadPlan plan) return result; } - public TAuthizedPatternTreeResp generateAuthizedPTree(String username, int permission) + public TAuthizedPatternTreeResp generateAuthorizedPTree(String username, int permission) throws AuthException { TAuthizedPatternTreeResp resp = new TAuthizedPatternTreeResp(); User user = authorizer.getUser(username); + PrivilegeType type = PrivilegeType.values()[permission]; PathPatternTree pPtree = new PathPatternTree(); if (user == null) { resp.setStatus(RpcUtils.getStatus(TSStatusCode.USER_NOT_EXIST, NO_USER_MSG + username)); @@ -431,15 +555,15 @@ public TAuthizedPatternTreeResp generateAuthizedPTree(String username, int permi return resp; } for (PathPrivilege path : user.getPathPrivilegeList()) { - if (path.checkPrivilege(permission)) { + if (path.checkPrivilege(type)) { pPtree.appendPathPattern(path.getPath()); } } - for (String rolename : user.getRoleList()) { + for (String rolename : user.getRoleSet()) { Role role = authorizer.getRole(rolename); if (role != null) { for (PathPrivilege path : role.getPathPrivilegeList()) { - if (path.checkPrivilege(permission)) { + if (path.checkPrivilege(type)) { pPtree.appendPathPattern(path.getPath()); } } @@ -460,69 +584,7 @@ public TAuthizedPatternTreeResp generateAuthizedPTree(String username, int permi return resp; } resp.setPathPatternTree(ByteBuffer.wrap(byteArrayOutputStream.toByteArray())); - resp.setPermissionInfo(getUserPermissionInfo(username)); - return resp; - } - - public TPermissionInfoResp checkUserPrivilegeGrantOpt( - String username, List paths, int permission) throws AuthException { - User user = authorizer.getUser(username); - TPermissionInfoResp resp = new TPermissionInfoResp(); - boolean status = false; - if (user == null) { - resp.setStatus(RpcUtils.getStatus(TSStatusCode.USER_NOT_EXIST, NO_USER_MSG + username)); - return resp; - } - try { - if (isPathRelevant(permission)) { - for (PartialPath path : paths) { - if (user.checkPathPrivilegeGrantOpt(path, permission)) { - status = true; - continue; - } - if (!status) { - for (String roleName : user.getRoleList()) { - Role role = authorizer.getRole(roleName); - if (role.checkPathPrivilegeGrantOpt(path, permission)) { - status = true; - break; - } - } - } - if (!status) { - break; - } - } - } else { - if (user.checkSysPriGrantOpt(permission)) { - status = true; - } - if (!status) { - for (String roleName : user.getRoleList()) { - Role role = authorizer.getRole(roleName); - if (role.checkSysPriGrantOpt(permission)) { - status = true; - break; - } - } - } - } - } catch (AuthException e) { - status = false; - } - if (status) { - try { - // Bring this user's permission information back to the datanode for caching - resp = getUserPermissionInfo(username); - resp.setStatus(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS)); - } catch (AuthException e) { - resp.setStatus(RpcUtils.getStatus(e.getCode(), e.getMessage())); - } - } else { - resp = AuthUtils.generateEmptyPermissionInfoResp(); - resp.setStatus(RpcUtils.getStatus(TSStatusCode.NO_PERMISSION)); - } - + resp.setPermissionInfo(getUserPermissionInfo(username, ModelType.ALL)); return resp; } @@ -534,8 +596,8 @@ public TPermissionInfoResp checkRoleOfUser(String username, String rolename) throw new AuthException( TSStatusCode.USER_NOT_EXIST, String.format("No such user : %s", username)); } - result = getUserPermissionInfo(username); - if (user.getRoleList().contains(rolename)) { + result = getUserPermissionInfo(username, ModelType.ALL); + if (user.getRoleSet().contains(rolename)) { result.setStatus(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS)); } else { result.setStatus(RpcUtils.getStatus(TSStatusCode.USER_NOT_HAS_ROLE)); @@ -571,95 +633,26 @@ public void clear() throws AuthException { * * @param username The username of the user that needs to be cached */ - public TPermissionInfoResp getUserPermissionInfo(String username) throws AuthException { + public TPermissionInfoResp getUserPermissionInfo(String username, ModelType type) + throws AuthException { TPermissionInfoResp result = new TPermissionInfoResp(); - TUserResp tUserResp = new TUserResp(); - Map tRoleRespMap = new HashMap(); - List userPrivilegeList = new ArrayList<>(); - - // User permission information User user = authorizer.getUser(username); - if (user.getPathPrivilegeList() != null) { - for (PathPrivilege pathPrivilege : user.getPathPrivilegeList()) { - TPathPrivilege path = new TPathPrivilege(); - path.setPath(pathPrivilege.getPath().getFullPath()); - path.setPriSet(pathPrivilege.getPrivileges()); - path.setPriGrantOpt(pathPrivilege.getGrantOpt()); - userPrivilegeList.add(path); - } + if (user == null) { + return AuthUtils.generateEmptyPermissionInfoResp(); } - tUserResp.setUsername(user.getName()); - tUserResp.setPassword(user.getPassword()); - tUserResp.setPrivilegeList(userPrivilegeList); - tUserResp.setRoleList(user.getRoleList()); - tUserResp.setSysPriSet(user.getSysPrivilege()); - tUserResp.setSysPriSetGrantOpt(user.getSysPriGrantOpt()); - + TUserResp tUserResp = user.getUserInfo(type); // Permission information for roles owned by users - if (user.getRoleList() != null) { - for (String roleName : user.getRoleList()) { + if (!user.getRoleSet().isEmpty()) { + for (String roleName : user.getRoleSet()) { Role role = authorizer.getRole(roleName); - List rolePrivilegeList = new ArrayList<>(); - for (PathPrivilege pathPrivilege : role.getPathPrivilegeList()) { - TPathPrivilege path = new TPathPrivilege(); - path.setPath(pathPrivilege.getPath().getFullPath()); - path.setPriSet(pathPrivilege.getPrivileges()); - path.setPriGrantOpt(pathPrivilege.getGrantOpt()); - rolePrivilegeList.add(path); - } - tRoleRespMap.put( - roleName, - new TRoleResp( - roleName, rolePrivilegeList, role.getSysPrivilege(), role.getSysPriGrantOpt())); + TRoleResp roleResp = role.getRoleInfo(type); + result.putToRoleInfo(roleName, roleResp); } + } else { + result.setRoleInfo(new HashMap<>()); } result.setUserInfo(tUserResp); - result.setRoleInfo(tRoleRespMap); result.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode())); return result; } - - public void checkUserPathPrivilege() { - authorizer.checkUserPathPrivilege(); - } - - private void grantPrivilegeForPreVersion( - boolean isUser, String name, Set permissions, List nodeNameList) - throws AuthException { - for (int permission : permissions) { - PriPrivilegeType type = PriPrivilegeType.values()[permission]; - if (type.isAccept()) { - if (isUser) { - for (PartialPath path : nodeNameList) { - authorizer.grantPrivilegeToUser(name, path, permission, false); - } - } else { - for (PartialPath path : nodeNameList) { - authorizer.grantPrivilegeToRole(name, path, permission, false); - } - } - } - } - } - - private void revokePrivilegeForPreVersion( - boolean isUser, String name, Set permissions, List nodeNameList) - throws AuthException { - for (int permission : permissions) { - PriPrivilegeType type = PriPrivilegeType.values()[permission]; - if (type.isAccept()) { - if (!type.isAccept()) { - if (isUser) { - for (PartialPath path : nodeNameList) { - authorizer.revokePrivilegeFromUser(name, path, permission); - } - } else { - for (PartialPath path : nodeNameList) { - authorizer.revokePrivilegeFromRole(name, path, permission); - } - } - } - } - } - } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java index fb3a820d9414..9c5919ac3756 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/executor/ConfigPlanExecutor.java @@ -30,7 +30,6 @@ import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; import org.apache.iotdb.confignode.consensus.request.read.ConfigPhysicalReadPlan; import org.apache.iotdb.confignode.consensus.request.read.ainode.GetAINodeConfigurationPlan; -import org.apache.iotdb.confignode.consensus.request.read.auth.AuthorReadPlan; import org.apache.iotdb.confignode.consensus.request.read.database.CountDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.database.GetDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.datanode.GetDataNodeConfigurationPlan; @@ -298,13 +297,17 @@ public DataSet executeQueryPlan(final ConfigPhysicalReadPlan req) case GetOrCreateSchemaPartition: return partitionInfo.getSchemaPartition((GetSchemaPartitionPlan) req); case ListUser: - return authorInfo.executeListUsers((AuthorReadPlan) req); + case RListUser: + return authorInfo.executeListUsers((AuthorPlan) req); case ListRole: - return authorInfo.executeListRoles((AuthorReadPlan) req); + case RListRole: + return authorInfo.executeListRoles((AuthorPlan) req); case ListUserPrivilege: - return authorInfo.executeListUserPrivileges((AuthorReadPlan) req); + case RListUserPrivilege: + return authorInfo.executeListUserPrivileges((AuthorPlan) req); case ListRolePrivilege: - return authorInfo.executeListRolePrivileges((AuthorReadPlan) req); + case RListRolePrivilege: + return authorInfo.executeListRolePrivileges((AuthorPlan) req); case GetNodePathsPartition: return getSchemaNodeManagementPartition(req); case GetRegionInfoList: @@ -472,6 +475,33 @@ public TSStatus executeNonQueryPlan(ConfigPhysicalPlan physicalPlan) case RevokeRoleDep: case RevokeRoleFromUserDep: case UpdateUserDep: + case RCreateRole: + case RCreateUser: + case RDropUser: + case RDropRole: + case RUpdateUser: + case RGrantUserRole: + case RGrantRoleAny: + case RGrantUserAny: + case RGrantUserAll: + case RGrantRoleAll: + case RGrantUserDBPriv: + case RGrantUserSysPri: + case RGrantUserTBPriv: + case RGrantRoleDBPriv: + case RGrantRoleSysPri: + case RGrantRoleTBPriv: + case RRevokeRoleAny: + case RRevokeUserAny: + case RRevokeUserAll: + case RRevokeRoleAll: + case RRevokeUserDBPriv: + case RRevokeUserSysPri: + case RRevokeUserTBPriv: + case RRevokeRoleDBPriv: + case RRevokeRoleSysPri: + case RRevokeRoleTBPriv: + case RRevokeUserRole: return authorInfo.authorNonQuery((AuthorPlan) physicalPlan); case ApplyConfigNode: return nodeInfo.applyConfigNode((ApplyConfigNodePlan) physicalPlan); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java index 7a721bbcc62a..388b8ba53891 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/CNPhysicalPlanGenerator.java @@ -19,6 +19,7 @@ package org.apache.iotdb.confignode.persistence.schema; +import org.apache.iotdb.commons.auth.entity.PrivilegeModelType; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; @@ -30,7 +31,8 @@ import org.apache.iotdb.commons.utils.ThriftConfigNodeSerDeUtils; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorRelationalPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.database.DatabaseSchemaPlan; import org.apache.iotdb.confignode.consensus.request.write.database.SetTTLPlan; import org.apache.iotdb.confignode.consensus.request.write.pipe.payload.PipeCreateTablePlan; @@ -70,7 +72,6 @@ import static org.apache.iotdb.commons.schema.SchemaConstant.INTERNAL_MNODE_TYPE; import static org.apache.iotdb.commons.schema.SchemaConstant.STORAGE_GROUP_MNODE_TYPE; import static org.apache.iotdb.commons.schema.SchemaConstant.TABLE_MNODE_TYPE; -import static org.apache.iotdb.commons.utils.IOUtils.readAuthString; import static org.apache.iotdb.commons.utils.IOUtils.readString; public class CNPhysicalPlanGenerator @@ -187,23 +188,26 @@ public void checkException() throws Exception { private void generateUserRolePhysicalPlan(final boolean isUser) { try (final DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(inputStream))) { - final Pair versionAndName = - readAuthString(dataInputStream, STRING_ENCODING, strBufferLocal); - if (versionAndName == null) { - return; + int tag = dataInputStream.readInt(); + boolean fromOldVersion = tag < 0; + String user; + if (fromOldVersion) { + user = readString(dataInputStream, STRING_ENCODING, strBufferLocal, -1 * tag); + } else { + user = readString(dataInputStream, STRING_ENCODING, strBufferLocal); } - final String user = versionAndName.left; + if (isUser) { final String rawPassword = readString(dataInputStream, STRING_ENCODING, strBufferLocal); - final AuthorPlan createUser = - new AuthorPlan(ConfigPhysicalPlanType.CreateUserWithRawPassword); + final AuthorTreePlan createUser = + new AuthorTreePlan(ConfigPhysicalPlanType.CreateUserWithRawPassword); createUser.setUserName(user); createUser.setPassword(rawPassword); createUser.setPermissions(new HashSet<>()); createUser.setNodeNameList(new ArrayList<>()); planDeque.add(createUser); } else { - final AuthorPlan createRole = new AuthorPlan(ConfigPhysicalPlanType.CreateRole); + final AuthorTreePlan createRole = new AuthorTreePlan(ConfigPhysicalPlanType.CreateRole); createRole.setRoleName(user); createRole.setPermissions(new HashSet<>()); createRole.setNodeNameList(new ArrayList<>()); @@ -212,17 +216,50 @@ private void generateUserRolePhysicalPlan(final boolean isUser) { final int privilegeMask = dataInputStream.readInt(); generateGrantSysPlan(user, isUser, privilegeMask); - while (dataInputStream.available() != 0) { - final String path = readString(dataInputStream, STRING_ENCODING, strBufferLocal); - final PartialPath priPath; - try { - priPath = new PartialPath(path); - } catch (IllegalPathException exception) { - latestException = exception; - return; + + if (fromOldVersion) { + while (dataInputStream.available() != 0) { + final String path = readString(dataInputStream, STRING_ENCODING, strBufferLocal); + final PartialPath priPath; + try { + priPath = new PartialPath(path); + } catch (IllegalPathException exception) { + latestException = exception; + return; + } + int privileges = dataInputStream.readInt(); + generateGrantAuthorTreePlan(user, isUser, priPath, privileges); + } + } else { + int num = dataInputStream.readInt(); + for (int i = 0; i < num; i++) { + final String path = readString(dataInputStream, STRING_ENCODING, strBufferLocal); + final PartialPath priPath; + try { + priPath = new PartialPath(path); + } catch (IllegalPathException exception) { + latestException = exception; + return; + } + int privileges = dataInputStream.readInt(); + generateGrantAuthorTreePlan(user, isUser, priPath, privileges); + } + int anyScopePriv = dataInputStream.readInt(); + generateGrantAuthorRelationalPlan(user, isUser, null, null, anyScopePriv); + + num = dataInputStream.readInt(); + for (int i = 0; i < num; i++) { + final String databaseName = readString(dataInputStream, STRING_ENCODING, strBufferLocal); + int databasePrivilege = dataInputStream.readInt(); + generateGrantAuthorRelationalPlan(user, isUser, databaseName, null, databasePrivilege); + int tableNum = dataInputStream.readInt(); + for (int tableid = 0; tableid < tableNum; tableid++) { + final String tableName = readString(dataInputStream, STRING_ENCODING, strBufferLocal); + int tablePrivilege = dataInputStream.readInt(); + generateGrantAuthorRelationalPlan( + user, isUser, databaseName, tableName, tablePrivilege); + } } - int privileges = dataInputStream.readInt(); - generateGrantPathPlan(user, isUser, priPath, privileges); } } catch (IOException ioException) { logger.error( @@ -236,9 +273,9 @@ private void generateUserRolePhysicalPlan(final boolean isUser) { private void generateGrantRolePhysicalPlan() { try (final DataInputStream roleInputStream = new DataInputStream(new BufferedInputStream((inputStream)))) { - while (roleInputStream.available() != 0) { + for (int i = 0; roleInputStream.available() != 0; i++) { final String roleName = readString(roleInputStream, STRING_ENCODING, strBufferLocal); - final AuthorPlan plan = new AuthorPlan(ConfigPhysicalPlanType.GrantRoleToUser); + final AuthorTreePlan plan = new AuthorTreePlan(ConfigPhysicalPlanType.GrantRoleToUser); plan.setUserName(userName); plan.setRoleName(roleName); plan.setNodeNameList(new ArrayList<>()); @@ -253,12 +290,11 @@ private void generateGrantRolePhysicalPlan() { } } - private void generateGrantSysPlan( - final String userName, final boolean isUser, final int sysMask) { - for (int i = 0; i < PrivilegeType.getSysPriCount(); i++) { + private void generateGrantSysPlan(String userName, boolean isUser, int sysMask) { + for (int i = 0; i < PrivilegeType.getPrivilegeCount(PrivilegeModelType.SYSTEM); i++) { if ((sysMask & (1 << i)) != 0) { - final AuthorPlan plan = - new AuthorPlan( + final AuthorTreePlan plan = + new AuthorTreePlan( isUser ? ConfigPhysicalPlanType.GrantUser : ConfigPhysicalPlanType.GrantRole); if (isUser) { plan.setUserName(userName); @@ -267,7 +303,7 @@ private void generateGrantSysPlan( plan.setRoleName(userName); plan.setUserName(""); } - plan.setPermissions(Collections.singleton(AuthUtils.posToSysPri(i))); + plan.setPermissions(Collections.singleton(AuthUtils.posToSysPri(i).ordinal())); if ((sysMask & (1 << (i + 16))) != 0) { plan.setGrantOpt(true); } @@ -293,18 +329,18 @@ private void generateSetTTLPlan() { } } - private void generateGrantPathPlan( - final String userName, final boolean isUser, final PartialPath path, final int priMask) { - for (int pos = 0; pos < PrivilegeType.getPathPriCount(); pos++) { + private void generateGrantAuthorTreePlan( + String name, boolean isUser, PartialPath path, int priMask) { + for (int pos = 0; pos < PrivilegeType.getPrivilegeCount(PrivilegeModelType.TREE); pos++) { if (((1 << pos) & priMask) != 0) { - final AuthorPlan plan = - new AuthorPlan( + final AuthorTreePlan plan = + new AuthorTreePlan( isUser ? ConfigPhysicalPlanType.GrantUser : ConfigPhysicalPlanType.GrantRole); if (isUser) { - plan.setUserName(userName); + plan.setUserName(name); plan.setRoleName(""); } else { - plan.setRoleName(userName); + plan.setRoleName(name); plan.setUserName(""); } plan.setPermissions(Collections.singleton(AuthUtils.pathPosToPri(pos))); @@ -317,6 +353,48 @@ private void generateGrantPathPlan( } } + private void generateGrantAuthorRelationalPlan( + String name, boolean isUser, String database, String table, int priMask) { + for (int pos = 0; pos < PrivilegeType.getPrivilegeCount(PrivilegeModelType.RELATIONAL); pos++) { + if (((1 << pos) & priMask) != 0) { + final AuthorRelationalPlan plan; + if (database == null && table == null) { + plan = + new AuthorRelationalPlan( + isUser + ? ConfigPhysicalPlanType.RGrantUserAny + : ConfigPhysicalPlanType.RGrantRoleAny); + } else if (database != null && table == null) { + plan = + new AuthorRelationalPlan( + isUser + ? ConfigPhysicalPlanType.RGrantUserDBPriv + : ConfigPhysicalPlanType.RGrantRoleDBPriv); + } else { + plan = + new AuthorRelationalPlan( + isUser + ? ConfigPhysicalPlanType.RGrantUserTBPriv + : ConfigPhysicalPlanType.RGrantRoleTBPriv); + } + if (isUser) { + plan.setUserName(name); + plan.setRoleName(""); + } else { + plan.setRoleName(name); + plan.setUserName(""); + } + plan.setPermissions(Collections.singleton(AuthUtils.posToObjPri(pos).ordinal())); + if ((1 << (pos + 16) & priMask) != 0) { + plan.setGrantOpt(true); + } + plan.setDatabaseName(database == null ? "" : database); + plan.setTableName(table == null ? "" : table); + planDeque.add(plan); + } + } + } + // NOTE: The stack is reserved for mTree creation, to unify the logic and to get full path // info for schema template set. Do not touch this private void generateDatabasePhysicalPlan() { diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedure.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedure.java index 20cc759a74cb..9d4fb61f85de 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedure.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedure.java @@ -221,8 +221,12 @@ public void deserialize(ByteBuffer byteBuffer) { } this.timeoutMS = ReadWriteIOUtils.readLong(byteBuffer); try { - ReadWriteIOUtils.readInt(byteBuffer); + int length = byteBuffer.getInt(); + int pos = byteBuffer.position(); this.plan = (AuthorPlan) ConfigPhysicalPlan.Factory.create(byteBuffer); + byteBuffer.position(pos + length); + this.user = plan.getUserName(); + this.role = plan.getRoleName(); } catch (IOException e) { LOGGER.error("IO error when deserialize authplan.", e); } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java index dfaf72267ed7..986559545857 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/ConfigNodeRPCServiceProcessor.java @@ -32,6 +32,9 @@ import org.apache.iotdb.common.rpc.thrift.TShowConfigurationResp; import org.apache.iotdb.common.rpc.thrift.TShowTTLReq; import org.apache.iotdb.common.rpc.thrift.TTestConnectionResp; +import org.apache.iotdb.commons.auth.entity.PrivilegeModelType; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.conf.CommonConfig; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.consensus.ConsensusGroupId; @@ -47,7 +50,6 @@ import org.apache.iotdb.confignode.conf.SystemPropertiesUtils; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; import org.apache.iotdb.confignode.consensus.request.read.ainode.GetAINodeConfigurationPlan; -import org.apache.iotdb.confignode.consensus.request.read.auth.AuthorReadPlan; import org.apache.iotdb.confignode.consensus.request.read.database.CountDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.database.GetDatabasePlan; import org.apache.iotdb.confignode.consensus.request.read.datanode.GetDataNodeConfigurationPlan; @@ -56,7 +58,8 @@ import org.apache.iotdb.confignode.consensus.request.read.region.GetRegionInfoListPlan; import org.apache.iotdb.confignode.consensus.request.read.ttl.ShowTTLPlan; import org.apache.iotdb.confignode.consensus.request.write.ainode.RemoveAINodePlan; -import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorRelationalPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan; import org.apache.iotdb.confignode.consensus.request.write.database.DatabaseSchemaPlan; import org.apache.iotdb.confignode.consensus.request.write.database.SetDataReplicationFactorPlan; @@ -90,6 +93,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TAlterPipeReq; import org.apache.iotdb.confignode.rpc.thrift.TAlterSchemaTemplateReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthizedPatternTreeResp; +import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerRelationalReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; @@ -214,6 +218,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TUnsubscribeReq; import org.apache.iotdb.confignode.service.ConfigNode; import org.apache.iotdb.consensus.exception.ConsensusException; +import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType; import org.apache.iotdb.db.queryengine.plan.statement.AuthorType; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; @@ -683,7 +688,7 @@ public TSStatus operatePermission(final TAuthorizerReq req) { throw new IndexOutOfBoundsException("Invalid Author Type ordinal"); } return configManager.operatePermission( - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.values()[ req.getAuthorType() + ConfigPhysicalPlanType.CreateUser.ordinal()], req.getUserName(), @@ -703,7 +708,7 @@ public TAuthorizerResp queryPermission(final TAuthorizerReq req) { final PermissionInfoResp dataSet = (PermissionInfoResp) configManager.queryPermission( - new AuthorReadPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.values()[ req.getAuthorType() + ConfigPhysicalPlanType.CreateUser.ordinal()], req.getUserName(), @@ -720,6 +725,49 @@ public TAuthorizerResp queryPermission(final TAuthorizerReq req) { return resp; } + @Override + public TSStatus operateRPermission(final TAuthorizerRelationalReq req) { + if (req.getAuthorType() < 0 || req.getAuthorType() >= AuthorRType.values().length) { + throw new IndexOutOfBoundsException("Invalid Author Type ordinal"); + } + return configManager.operatePermission( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.values()[ + req.getAuthorType() + ConfigPhysicalPlanType.RCreateUser.ordinal()], + req.getUserName(), + req.getRoleName(), + req.getDatabase(), + req.getTable(), + req.getPermissions(), + req.isGrantOpt(), + req.getPassword())); + } + + @Override + public TAuthorizerResp queryRPermission(final TAuthorizerRelationalReq req) { + if (req.getAuthorType() < 0 || req.getAuthorType() >= AuthorRType.values().length) { + throw new IndexOutOfBoundsException("Invalid Author Type ordinal"); + } + final PermissionInfoResp dataSet = + (PermissionInfoResp) + configManager.queryPermission( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.values()[ + req.getAuthorType() + ConfigPhysicalPlanType.RCreateUser.ordinal()], + req.getUserName(), + req.getRoleName(), + req.getDatabase(), + req.getTable(), + req.getPermissions(), + req.isGrantOpt(), + req.getPassword())); + final TAuthorizerResp resp = new TAuthorizerResp(dataSet.getStatus()); + resp.setMemberInfo(dataSet.getMemberList()); + resp.setPermissionInfo(dataSet.getPermissionInfoResp()); + resp.setTag(dataSet.getTag()); + return resp; + } + @Override public TPermissionInfoResp login(TLoginReq req) { return configManager.login(req.getUserrname(), req.getPassword()); @@ -727,9 +775,37 @@ public TPermissionInfoResp login(TLoginReq req) { @Override public TPermissionInfoResp checkUserPrivileges(TCheckUserPrivilegesReq req) { - List partialPaths = - AuthUtils.deserializePartialPathList(ByteBuffer.wrap(req.getPaths())); - return configManager.checkUserPrivileges(req.getUsername(), partialPaths, req.getPermission()); + PrivilegeModelType reqType = PrivilegeModelType.values()[req.getReqtype()]; + PrivilegeType permission; + // permission = -1. check object's visible. + if (req.getPermission() == -1) { + permission = null; + } else { + permission = PrivilegeType.values()[req.getPermission()]; + } + switch (reqType) { + case TREE: + List partialPaths = + AuthUtils.deserializePartialPathList(ByteBuffer.wrap(req.getPaths())); + return configManager.checkUserPrivileges( + req.getUsername(), new PrivilegeUnion(partialPaths, permission, req.isGrantOpt())); + case SYSTEM: + return configManager.checkUserPrivileges( + req.getUsername(), new PrivilegeUnion(permission, req.isGrantOpt())); + case RELATIONAL: + PrivilegeUnion union; + if (!req.isSetDatabase() && !req.isSetTable()) { + union = new PrivilegeUnion(permission, req.isGrantOpt(), true); + } else if (req.isSetTable()) { + union = + new PrivilegeUnion(req.getDatabase(), req.getTable(), permission, req.isGrantOpt()); + } else { + union = new PrivilegeUnion(req.getDatabase(), permission, req.isGrantOpt()); + } + return configManager.checkUserPrivileges(req.getUsername(), union); + default: + return AuthUtils.generateEmptyPermissionInfoResp(); + } } @Override @@ -737,14 +813,6 @@ public TAuthizedPatternTreeResp fetchAuthizedPatternTree(TCheckUserPrivilegesReq return configManager.fetchAuthizedPatternTree(req.getUsername(), req.getPermission()); } - @Override - public TPermissionInfoResp checkUserPrivilegeGrantOpt(TCheckUserPrivilegesReq req) { - List partialPath = - AuthUtils.deserializePartialPathList(ByteBuffer.wrap(req.getPaths())); - return configManager.checkUserPrivilegeGrantOpt( - req.getUsername(), partialPath, req.getPermission()); - } - @Override public TPermissionInfoResp checkRoleOfUser(TAuthorizerReq req) { return configManager.checkRoleOfUser(req.getUserName(), req.getRoleName()); diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java index 9561c7ccf8a0..2bd8aa34207e 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/consensus/request/ConfigPhysicalPlanSerDeTest.java @@ -64,6 +64,7 @@ import org.apache.iotdb.commons.udf.UDFType; import org.apache.iotdb.commons.utils.TimePartitionUtils; import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.confignode.ApplyConfigNodePlan; import org.apache.iotdb.confignode.consensus.request.write.confignode.RemoveConfigNodePlan; import org.apache.iotdb.confignode.consensus.request.write.confignode.UpdateClusterIdPlan; @@ -475,7 +476,7 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { // create user req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.CreateUser, "thulab", "", @@ -484,12 +485,12 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { new HashSet<>(), false, new ArrayList<>()); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // create role req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.CreateRole, "", "admin", @@ -498,12 +499,12 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { new HashSet<>(), false, new ArrayList<>()); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // alter user req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.UpdateUser, "tempuser", "", @@ -512,7 +513,7 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { new HashSet<>(), false, new ArrayList<>()); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // grant user @@ -520,7 +521,7 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { nodeNameList.add(new PartialPath("root.ln.**")); nodeNameList.add(new PartialPath("root.abc.**")); req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantUser, "tempuser", "", @@ -529,12 +530,12 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { permissions, false, nodeNameList); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // grant role req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantRoleToUser, "tempuser", "temprole", @@ -543,12 +544,12 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { permissions, false, nodeNameList); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // grant role to user req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantRole, "", "temprole", @@ -557,12 +558,12 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { new HashSet<>(), false, new ArrayList<>()); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // revoke user req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.RevokeUser, "tempuser", "", @@ -571,12 +572,12 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { permissions, false, nodeNameList); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // revoke role req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.RevokeRole, "", "temprole", @@ -585,12 +586,12 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { permissions, false, nodeNameList); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // revoke role from user req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.RevokeRoleFromUser, "tempuser", "temprole", @@ -599,12 +600,12 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { new HashSet<>(), false, new ArrayList<>()); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // drop user req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.DropUser, "xiaoming", "", @@ -613,12 +614,12 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { new HashSet<>(), false, new ArrayList<>()); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); // drop role req0 = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.DropRole, "", "admin", @@ -627,7 +628,7 @@ public void AuthorPlanTest() throws IOException, IllegalPathException { new HashSet<>(), false, new ArrayList<>()); - req1 = (AuthorPlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); + req1 = (AuthorTreePlan) ConfigPhysicalPlan.Factory.create(req0.serializeToByteBuffer()); Assert.assertEquals(req0, req1); } diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/extractor/PipeConfigPhysicalPlanTreePatternParseVisitorTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/extractor/PipeConfigPhysicalPlanTreePatternParseVisitorTest.java index b845a78be319..7d3c31aae34e 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/extractor/PipeConfigPhysicalPlanTreePatternParseVisitorTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/extractor/PipeConfigPhysicalPlanTreePatternParseVisitorTest.java @@ -24,7 +24,7 @@ import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.commons.pipe.datastructure.pattern.IoTDBTreePattern; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.database.DatabaseSchemaPlan; import org.apache.iotdb.confignode.consensus.request.write.database.DeleteDatabasePlan; import org.apache.iotdb.confignode.consensus.request.write.database.SetTTLPlan; @@ -219,10 +219,10 @@ public void testExtendSchemaTemplate() { public void testGrantUser() throws IllegalPathException { Assert.assertEquals( Collections.singletonList(new PartialPath("root.db.device.**")), - ((AuthorPlan) + ((AuthorTreePlan) IoTDBConfigRegionExtractor.TREE_PATTERN_PARSE_VISITOR .visitGrantUser( - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantUser, "tempUser", "", @@ -241,10 +241,10 @@ public void testGrantUser() throws IllegalPathException { public void testRevokeUser() throws IllegalPathException { Assert.assertEquals( Collections.singletonList(new PartialPath("root.db.device.**")), - ((AuthorPlan) + ((AuthorTreePlan) IoTDBConfigRegionExtractor.TREE_PATTERN_PARSE_VISITOR .visitRevokeUser( - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.RevokeUser, "tempUser", "", @@ -263,10 +263,10 @@ public void testRevokeUser() throws IllegalPathException { public void testGrantRole() throws IllegalPathException { Assert.assertEquals( Collections.singletonList(new PartialPath("root.db.device.**")), - ((AuthorPlan) + ((AuthorTreePlan) IoTDBConfigRegionExtractor.TREE_PATTERN_PARSE_VISITOR .visitGrantRole( - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantRole, "", "tempRole", @@ -285,10 +285,10 @@ public void testGrantRole() throws IllegalPathException { public void testRevokeRole() throws IllegalPathException { Assert.assertEquals( Collections.singletonList(new PartialPath("root.db.device.**")), - ((AuthorPlan) + ((AuthorTreePlan) IoTDBConfigRegionExtractor.TREE_PATTERN_PARSE_VISITOR .visitRevokeRole( - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.RevokeRole, "", "tempRole", diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java index 963808bf7cc1..8766109ac8c3 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/AuthorInfoTest.java @@ -21,18 +21,18 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; -import org.apache.iotdb.commons.auth.authorizer.BasicAuthorizer; -import org.apache.iotdb.commons.auth.entity.PriPrivilegeType; +import org.apache.iotdb.commons.auth.entity.ModelType; import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.utils.AuthUtils; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.read.auth.AuthorReadPlan; import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorRelationalPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.response.auth.PermissionInfoResp; -import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp; import org.apache.iotdb.rpc.TSStatusCode; @@ -49,10 +49,11 @@ import java.util.Collections; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; import static org.apache.iotdb.db.utils.constant.TestConstant.BASE_OUTPUT_PATH; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; public class AuthorInfoTest { @@ -93,7 +94,7 @@ public static void cleanup() throws IOException, AuthException { } @Test - public void permissionTest() throws TException, AuthException, IllegalPathException { + public void permissionTest() throws AuthException, IllegalPathException { TSStatus status; @@ -107,8 +108,6 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept roleList.add("role1"); AuthorPlan authorPlan; - AuthorReadPlan authorReadPlan; - TCheckUserPrivilegesReq checkUserPrivilegesReq; Set privilegeList = new HashSet<>(); privilegeList.add(PrivilegeType.READ_DATA.ordinal()); @@ -119,18 +118,12 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept Set revokePrivilege = new HashSet<>(); revokePrivilege.add(PrivilegeType.READ_DATA.ordinal()); - List privilege = new ArrayList<>(); - privilege.add("root.** : MANAGE_USER"); - - List paths = new ArrayList<>(); - paths.add(new PartialPath("root.ln")); - cleanUserAndRole(); // create user { authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.CreateUser, "user0", "", @@ -141,22 +134,22 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); Assert.assertNull(status.getMessage()); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); authorPlan.setUserName("user1"); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); } // check user privileges status = authorInfo - .checkUserPrivileges("user0", paths, PrivilegeType.MANAGE_USER.ordinal()) + .checkUserPrivileges("user0", new PrivilegeUnion(PrivilegeType.MANAGE_USER)) .getStatus(); - Assert.assertEquals(TSStatusCode.NO_PERMISSION.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.NO_PERMISSION.getStatusCode(), status.getCode()); // drop user authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.DropUser, "user1", "", @@ -166,11 +159,11 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // list user - authorReadPlan = - new AuthorReadPlan( + authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListUser, "", "", @@ -179,15 +172,15 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept new HashSet<>(), false, new ArrayList<>()); - PermissionInfoResp permissionInfoResp = authorInfo.executeListUsers(authorReadPlan); + PermissionInfoResp permissionInfoResp = authorInfo.executeListUsers(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); userList.remove("user1"); - Assert.assertEquals(userList, permissionInfoResp.getMemberList()); + assertEquals(userList, permissionInfoResp.getMemberList()); // create role authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.CreateRole, "", "role0", @@ -197,14 +190,14 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); authorPlan.setRoleName("role1"); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // drop role authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.DropRole, "", "role1", @@ -214,11 +207,11 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // list role - authorReadPlan = - new AuthorReadPlan( + authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListRole, "", "", @@ -227,15 +220,15 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept new HashSet<>(), false, new ArrayList<>()); - permissionInfoResp = authorInfo.executeListRoles(authorReadPlan); + permissionInfoResp = authorInfo.executeListRoles(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); roleList.remove("role1"); - Assert.assertEquals(roleList, permissionInfoResp.getMemberList()); + assertEquals(roleList, permissionInfoResp.getMemberList()); // alter user authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.UpdateUser, "user0", "", @@ -245,13 +238,13 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // grant user path privilege List nodeNameList = new ArrayList<>(); nodeNameList.add(new PartialPath("root.ln.**")); authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantUser, "user0", "", @@ -261,36 +254,36 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, nodeNameList); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - Assert.assertEquals( + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), authorInfo - .checkUserPrivileges("user0", nodeNameList, PrivilegeType.READ_DATA.ordinal()) + .checkUserPrivileges("user0", new PrivilegeUnion(nodeNameList, PrivilegeType.READ_DATA)) .getStatus() - .getCode(), - TSStatusCode.SUCCESS_STATUS.getStatusCode()); + .getCode()); // grant user system privilege authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantUser, "user0", "", "", "", sysPriList, false, null); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - Assert.assertEquals( + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), authorInfo - .checkUserPrivileges("user0", new ArrayList<>(), PrivilegeType.MANAGE_ROLE.ordinal()) + .checkUserPrivileges("user0", new PrivilegeUnion(PrivilegeType.MANAGE_ROLE)) .getStatus() - .getCode(), - TSStatusCode.SUCCESS_STATUS.getStatusCode()); + .getCode()); // check user privileges status = authorInfo - .checkUserPrivileges("user0", new ArrayList<>(), PrivilegeType.MANAGE_ROLE.ordinal()) + .checkUserPrivileges("user0", new PrivilegeUnion(PrivilegeType.MANAGE_ROLE)) .getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // grant role authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantRole, "", "role0", @@ -300,11 +293,11 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, nodeNameList); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // grant role to user authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantRoleToUser, "user0", "role0", @@ -314,11 +307,11 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // revoke user authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.RevokeUser, "user0", "", @@ -328,11 +321,11 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, nodeNameList); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // revoke role authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.RevokeRole, "", "role0", @@ -342,11 +335,11 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, nodeNameList); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // list privileges user - authorReadPlan = - new AuthorReadPlan( + authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListUserPrivilege, "user0", "", @@ -355,15 +348,16 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept new HashSet<>(), false, new ArrayList<>()); - permissionInfoResp = authorInfo.executeListUserPrivileges(authorReadPlan); + permissionInfoResp = authorInfo.executeListUserPrivileges(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - Assert.assertEquals( - authorInfo.getUserPermissionInfo("user0"), permissionInfoResp.getPermissionInfoResp()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals( + authorInfo.getUserPermissionInfo("user0", ModelType.ALL), + permissionInfoResp.getPermissionInfoResp()); // list privileges role - authorReadPlan = - new AuthorReadPlan( + authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListRolePrivilege, "", "role0", @@ -372,13 +366,13 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept new HashSet<>(), false, new ArrayList<>()); - permissionInfoResp = authorInfo.executeListRolePrivileges(authorReadPlan); + permissionInfoResp = authorInfo.executeListRolePrivileges(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // list all role of user - authorReadPlan = - new AuthorReadPlan( + authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListRole, "user0", "", @@ -387,15 +381,15 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept new HashSet<>(), false, new ArrayList<>()); - permissionInfoResp = authorInfo.executeListRoles(authorReadPlan); + permissionInfoResp = authorInfo.executeListRoles(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); roleList.remove("role1"); - Assert.assertEquals(roleList, permissionInfoResp.getMemberList()); + assertEquals(roleList, permissionInfoResp.getMemberList()); // list all user of role - authorReadPlan = - new AuthorReadPlan( + authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListUser, "", "role0", @@ -404,16 +398,16 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept new HashSet<>(), false, new ArrayList<>()); - permissionInfoResp = authorInfo.executeListUsers(authorReadPlan); + permissionInfoResp = authorInfo.executeListUsers(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); userList.remove("user1"); userList.remove("root"); - Assert.assertEquals(userList, permissionInfoResp.getMemberList()); + assertEquals(userList, permissionInfoResp.getMemberList()); // revoke role from user authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.RevokeRoleFromUser, "user0", "role0", @@ -423,18 +417,15 @@ public void permissionTest() throws TException, AuthException, IllegalPathExcept false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); } - private void cleanUserAndRole() throws TException, AuthException { + private void cleanUserAndRole() throws AuthException { TSStatus status; - AuthorPlan authorPlan; - AuthorReadPlan authorReadPlan; - // clean user - authorReadPlan = - new AuthorReadPlan( + AuthorPlan authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListUser, "", "", @@ -443,15 +434,15 @@ private void cleanUserAndRole() throws TException, AuthException { new HashSet<>(), false, new ArrayList<>()); - PermissionInfoResp permissionInfoResp = authorInfo.executeListUsers(authorReadPlan); + PermissionInfoResp permissionInfoResp = authorInfo.executeListUsers(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); List allUsers = permissionInfoResp.getMemberList(); for (String user : allUsers) { if (!user.equals("root")) { authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.DropUser, user, "", @@ -461,13 +452,13 @@ private void cleanUserAndRole() throws TException, AuthException { false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); } } // clean role - authorReadPlan = - new AuthorReadPlan( + authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListRole, "", "", @@ -476,14 +467,14 @@ private void cleanUserAndRole() throws TException, AuthException { new HashSet<>(), false, new ArrayList<>()); - permissionInfoResp = authorInfo.executeListRoles(authorReadPlan); + permissionInfoResp = authorInfo.executeListRoles(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); List roleList = permissionInfoResp.getMemberList(); for (String roleN : roleList) { authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.DropRole, "", roleN, @@ -493,7 +484,7 @@ private void cleanUserAndRole() throws TException, AuthException { false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); } } @@ -501,18 +492,18 @@ private void cleanUserAndRole() throws TException, AuthException { public void takeSnapshot() throws TException, IOException, AuthException { cleanUserAndRole(); // create role - AuthorPlan createRoleReq = new AuthorPlan(ConfigPhysicalPlanType.CreateRole); + AuthorPlan createRoleReq = new AuthorTreePlan(ConfigPhysicalPlanType.CreateRole); createRoleReq.setRoleName("testRole"); TSStatus status = authorInfo.authorNonQuery(createRoleReq); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - AuthorPlan createUserReq = new AuthorPlan(ConfigPhysicalPlanType.CreateUser); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + AuthorPlan createUserReq = new AuthorTreePlan(ConfigPhysicalPlanType.CreateUser); createUserReq.setUserName("testUser"); createUserReq.setPassword("testPassword"); status = authorInfo.authorNonQuery(createUserReq); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - AuthorReadPlan listUserPlan = - new AuthorReadPlan( + AuthorPlan listUserPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListUser, "", "", @@ -521,8 +512,8 @@ public void takeSnapshot() throws TException, IOException, AuthException { new HashSet<>(), false, new ArrayList<>()); - AuthorReadPlan listRolePlan = - new AuthorReadPlan( + AuthorPlan listRolePlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListRole, "", "", @@ -531,42 +522,25 @@ public void takeSnapshot() throws TException, IOException, AuthException { new HashSet<>(), false, new ArrayList<>()); - Assert.assertEquals(1, authorInfo.executeListRoles(listRolePlan).getMemberList().size()); - Assert.assertEquals(2, authorInfo.executeListUsers(listUserPlan).getMemberList().size()); - Assert.assertTrue(authorInfo.processTakeSnapshot(snapshotDir)); + assertEquals(1, authorInfo.executeListRoles(listRolePlan).getMemberList().size()); + assertEquals(2, authorInfo.executeListUsers(listUserPlan).getMemberList().size()); + assertTrue(authorInfo.processTakeSnapshot(snapshotDir)); authorInfo.clear(); authorInfo.processLoadSnapshot(snapshotDir); - Assert.assertEquals(1, authorInfo.executeListRoles(listRolePlan).getMemberList().size()); - Assert.assertEquals(2, authorInfo.executeListUsers(listUserPlan).getMemberList().size()); + assertEquals(1, authorInfo.executeListRoles(listRolePlan).getMemberList().size()); + assertEquals(2, authorInfo.executeListUsers(listUserPlan).getMemberList().size()); } @Test - public void testMultPathsPermission() throws TException, AuthException, IllegalPathException { + public void testMultiPathsPermission() throws AuthException, IllegalPathException { TSStatus status; AuthorPlan authorPlan; - AuthorReadPlan authorReadPlan; Set privilegeList = new HashSet<>(); privilegeList.add(PrivilegeType.WRITE_DATA.ordinal()); privilegeList.add(PrivilegeType.READ_DATA.ordinal()); - Map> permissionInfo; - List userPrivilege = new ArrayList<>(); - userPrivilege.add("root.sg.** : READ_DATA WRITE_DATA"); - userPrivilege.add("root.ln.** : READ_DATA WRITE_DATA"); - Collections.sort(userPrivilege); - - List rolePrivilege = new ArrayList<>(); - rolePrivilege.add("root.abc.** : READ_DATA WRITE_DATA"); - rolePrivilege.add("root.role_1.** : READ_DATA WRITE_DATA"); - Collections.sort(rolePrivilege); - - List allPrivilege = new ArrayList<>(); - allPrivilege.addAll(userPrivilege); - allPrivilege.addAll(rolePrivilege); - Collections.sort(allPrivilege); - List userPaths = new ArrayList<>(); userPaths.add(new PartialPath("root.ln.**")); userPaths.add(new PartialPath("root.sg.**")); @@ -579,7 +553,7 @@ public void testMultPathsPermission() throws TException, AuthException, IllegalP // create user authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.CreateUser, "user0", "", @@ -590,11 +564,11 @@ public void testMultPathsPermission() throws TException, AuthException, IllegalP new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); Assert.assertNull(status.getMessage()); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // create role authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.CreateRole, "", "role0", @@ -604,32 +578,32 @@ public void testMultPathsPermission() throws TException, AuthException, IllegalP false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // grant user authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantUser, "user0", "", "", "", privilegeList, false, userPaths); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // check user privileges status = authorInfo - .checkUserPrivileges("user0", userPaths, PrivilegeType.WRITE_DATA.ordinal()) + .checkUserPrivileges("user0", new PrivilegeUnion(userPaths, PrivilegeType.WRITE_DATA)) .getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // grant role authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantRole, "", "role0", "", "", privilegeList, false, rolePaths); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // grant role to user authorPlan = - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.GrantRoleToUser, "user0", "role0", @@ -639,11 +613,11 @@ public void testMultPathsPermission() throws TException, AuthException, IllegalP false, new ArrayList<>()); status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // list privileges user - authorReadPlan = - new AuthorReadPlan( + authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListUserPrivilege, "user0", "", @@ -653,13 +627,13 @@ public void testMultPathsPermission() throws TException, AuthException, IllegalP false, new ArrayList<>()); PermissionInfoResp permissionInfoResp; - permissionInfoResp = authorInfo.executeListUserPrivileges(authorReadPlan); + permissionInfoResp = authorInfo.executeListUserPrivileges(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); // list privileges role - authorReadPlan = - new AuthorReadPlan( + authorPlan = + new AuthorTreePlan( ConfigPhysicalPlanType.ListRolePrivilege, "", "role0", @@ -668,383 +642,320 @@ public void testMultPathsPermission() throws TException, AuthException, IllegalP new HashSet<>(), false, new ArrayList<>()); - permissionInfoResp = authorInfo.executeListRolePrivileges(authorReadPlan); + permissionInfoResp = authorInfo.executeListRolePrivileges(authorPlan); status = permissionInfoResp.getStatus(); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); } @Test - public void testDepAuthorPlan() throws TException, AuthException, IllegalPathException { - - AuthorPlan authorPlan; + public void createUserWithRawPassword() { TSStatus status; + AuthorPlan authorPlan; + authorPlan = + new AuthorTreePlan( + ConfigPhysicalPlanType.CreateUserWithRawPassword, + "testuser", + "", + AuthUtils.encryptPassword("password"), + "", + new HashSet<>(), + false, + new ArrayList<>()); + status = authorInfo.authorNonQuery(authorPlan); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + TPermissionInfoResp result = authorInfo.login("testuser", "password"); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), result.getStatus().getCode()); + } + + private void checkAuthorNonQueryReturn(AuthorPlan plan) { + TSStatus status = authorInfo.authorNonQuery(plan); + Assert.assertNull(status.getMessage()); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + } + + @Test + public void relationalPermissionTest() throws AuthException { cleanUserAndRole(); + TSStatus status; - /*--TEST FOR USER CREATE 、UPDATE AND DROP -*/ - // this operation will success for pre version. - { - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.CreateUserDep, - "user1", - "", - "password1", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + // create user + AuthorPlan plan = + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RCreateUser, + "user", + "", + "", + "", + Collections.emptySet(), + false, + "password"); - // this operation will success for pre version. --length~(32,64) - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.CreateUserDep, - "user1234567user1234567user1234567user1234567", - "", - "password1", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + checkAuthorNonQueryReturn(plan); - // this operation will fail for pre version. --length > 64 - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.CreateUserDep, - "user1234567user1234567user1234567user1234567user1234567user1234567user1234567user1234567", - "", - "password1", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.ILLEGAL_PARAMETER.getStatusCode(), status.getCode()); + // check user permission + status = + authorInfo + .checkUserPrivileges("user", new PrivilegeUnion(PrivilegeType.MANAGE_USER)) + .getStatus(); + assertEquals(TSStatusCode.NO_PERMISSION.getStatusCode(), status.getCode()); - // this operation will fail for pre version. -- contain &%*@ - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.CreateUserDep, - "user1*&%", - "", - "password1", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.ILLEGAL_PARAMETER.getStatusCode(), status.getCode()); - - // root, user1, user1234567user1234567user1234567user1234567 - Assert.assertEquals( - 3, - authorInfo - .executeListUsers( - new AuthorReadPlan( - ConfigPhysicalPlanType.ListUser, - "", - "", - "", - "", - new HashSet<>(), - false, - new ArrayList<>())) - .getMemberList() - .size()); + plan = + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RDropUser, + "user", + "", + "", + "", + Collections.emptySet(), + false, + ""); + checkAuthorNonQueryReturn(plan); - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.DropUserDep, - "user1234567user1234567user1234567user1234567", - "", - "", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - - Assert.assertEquals( - 2, - authorInfo - .executeListUsers( - new AuthorReadPlan( - ConfigPhysicalPlanType.ListUserDep, - "", - "", - "", - "", - new HashSet<>(), - false, - new ArrayList<>())) - .getMemberList() - .size()); - - // for pre version, password with &% will meet error. - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.UpdateUserDep, - "user1", - "", - "password*&S", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.ILLEGAL_PARAMETER.getStatusCode(), status.getCode()); + // list user + plan = + new AuthorTreePlan( + ConfigPhysicalPlanType.ListUser, + "", + "", + "", + "", + new HashSet<>(), + false, + new ArrayList<>()); + PermissionInfoResp permissionInfoResp = authorInfo.executeListUsers(plan); + status = permissionInfoResp.getStatus(); + assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + assertEquals(1, permissionInfoResp.getMemberList().size()); // Only root - /*--TEST FOR ROLE CREATE AND DROP -*/ - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.CreateRoleDep, - "", - "role1", - "", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + // create role + plan = + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RCreateRole, + "", + "role", + "", + "", + Collections.emptySet(), + false, + ""); - // name longer than 32, It's ok. - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.CreateRoleDep, - "", - "role1234567role1234567role1234567role1234567", - "", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); + checkAuthorNonQueryReturn(plan); - // contain wrong character, error. - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.CreateRoleDep, - "", - "role1234567role1%%234567role1234567role1234567", - "", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.ILLEGAL_PARAMETER.getStatusCode(), status.getCode()); + // create user + plan = + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RCreateUser, + "user", + "", + "", + "", + Collections.emptySet(), + false, + "password"); - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.DropRoleDep, - "", - "role1234567role1234567role1234567role1234567", - "", - "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - - Assert.assertEquals( - 1, - authorInfo - .executeListRoles( - new AuthorReadPlan( - ConfigPhysicalPlanType.ListRoleDep, - "", - "", - "", - "", - new HashSet<>(), - false, - new ArrayList<>())) - .getMemberList() - .size()); - } - // NOW WE HAVE USER:user1, root; ROLE: role1 + checkAuthorNonQueryReturn(plan); + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantUserRole, + "user", + "role", + "", + "", + Collections.emptySet(), + false, + "")); - for (PriPrivilegeType item : PriPrivilegeType.values()) { - /*-- TEST IGNORE PRIVILEGES --*/ - if (!item.isAccept()) { - // for user to grant - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.GrantUserDep, - "user1", - "", - "", - "", - Collections.singleton(item.ordinal()), - false, - Collections.singletonList(new PartialPath("root.**"))); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - Assert.assertEquals( - 0, BasicAuthorizer.getInstance().getUser("user1").getPathPrivilegeList().size()); - Assert.assertEquals( - 0, BasicAuthorizer.getInstance().getUser("user1").getSysPrivilege().size()); + // grant privileges + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantUserSysPri, + "user", + "", + "", + "", + PrivilegeType.MAINTAIN.ordinal(), + false)); - // for role to grant - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.GrantRoleDep, - "", - "role1", - "", - "", - Collections.singleton(item.ordinal()), - false, - Collections.singletonList(new PartialPath("root.**"))); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - Assert.assertEquals( - 0, BasicAuthorizer.getInstance().getRole("role1").getPathPrivilegeList().size()); - Assert.assertEquals( - 0, BasicAuthorizer.getInstance().getRole("role1").getSysPrivilege().size()); + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantUserSysPri, + "user", + "", + "", + "", + PrivilegeType.MANAGE_USER.ordinal(), + true)); - // for user to revoke - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.RevokeUserDep, - "user1", - "", - "", - "", - Collections.singleton(item.ordinal()), - false, - Collections.singletonList(new PartialPath("root.**"))); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - Assert.assertEquals( - 0, BasicAuthorizer.getInstance().getUser("user1").getPathPrivilegeList().size()); - Assert.assertEquals( - 0, BasicAuthorizer.getInstance().getUser("user1").getSysPrivilege().size()); + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantUserAny, + "user", + "", + "", + "", + PrivilegeType.DELETE.ordinal(), + false)); - // for role to revoke - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.RevokeRoleDep, - "", - "role1", - "", - "", - Collections.singleton(item.ordinal()), - false, - Collections.singletonList(new PartialPath("root.**"))); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - Assert.assertEquals( - 0, BasicAuthorizer.getInstance().getRole("role1").getPathPrivilegeList().size()); - Assert.assertEquals( - 0, BasicAuthorizer.getInstance().getRole("role1").getSysPrivilege().size()); - - } else { - if (item == PriPrivilegeType.ALL) { - continue; - } - if (item.isPrePathRelevant()) { - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.GrantUserDep, - "user1", - "", - "", - "", - Collections.singleton(item.ordinal()), - false, - Collections.singletonList(new PartialPath("root.t1.*.t2"))); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - Assert.assertEquals( - 1, - BasicAuthorizer.getInstance() - .getUser("user1") - .getPathPrivileges(new PartialPath("root.t1.*.t2")) - .size()); - authorInfo.checkUserPathPrivilege(); - PartialPath path1 = AuthUtils.convertPatternPath(new PartialPath("root.t1.*.t2")); - for (PrivilegeType pri : item.getSubPri()) { - if (pri.isPathRelevant()) { - Assert.assertTrue( - BasicAuthorizer.getInstance() - .getUser("user1") - .checkPathPrivilege(path1, pri.ordinal())); - BasicAuthorizer.getInstance() - .getUser("user1") - .removePathPrivilege(path1, pri.ordinal()); - } else { - Assert.assertTrue( - BasicAuthorizer.getInstance().getUser("user1").checkSysPrivilege(pri.ordinal())); - BasicAuthorizer.getInstance().getUser("user1").removeSysPrivilege(pri.ordinal()); - } - } - } else { - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.GrantUserDep, - "user1", - "", - "", - "", - Collections.singleton(item.ordinal()), - false, - Collections.singletonList(new PartialPath("root.**"))); - - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - authorInfo.checkUserPathPrivilege(); - Assert.assertTrue( - BasicAuthorizer.getInstance() - .getUser("user1") - .getSysPrivilege() - .containsAll(item.getSubSysPriOrd())); - - for (PrivilegeType pri : item.getSubPri()) { - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.RevokeUser, - "user1", - "", - "", - "", - Collections.singleton(pri.ordinal()), - false, - Collections.emptyList()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - Assert.assertEquals( - 0, BasicAuthorizer.getInstance().getUser("user1").getSysPrivilege().size()); - } - } - } - } - } + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantUserDBPriv, + "user", + "", + "testdb", + "", + PrivilegeType.SELECT.ordinal(), + false)); + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantUserDBPriv, + "user", + "", + "testdb2", + "", + PrivilegeType.INSERT.ordinal(), + false)); - @Test - public void createUserWithRawPassword() throws AuthException { - TSStatus status; - AuthorPlan authorPlan; - authorPlan = - new AuthorPlan( - ConfigPhysicalPlanType.CreateUserWithRawPassword, - "testuser", + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantUserTBPriv, + "user", "", - AuthUtils.encryptPassword("password"), + "testdb", + "table", + PrivilegeType.CREATE.ordinal(), + false)); + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantUserTBPriv, + "user", "", - new HashSet<>(), - false, - new ArrayList<>()); - status = authorInfo.authorNonQuery(authorPlan); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode()); - TPermissionInfoResp result = authorInfo.login("testuser", "password"); - Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), result.getStatus().getCode()); + "testdb", + "table2", + PrivilegeType.DELETE.ordinal(), + true)); + + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantRoleSysPri, + "", + "role", + "", + "", + PrivilegeType.MANAGE_ROLE.ordinal(), + false)); + checkAuthorNonQueryReturn( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.RGrantRoleTBPriv, + "", + "role", + "database", + "table", + PrivilegeType.ALTER.ordinal(), + false)); + + // privileges status: + // user <-- role + // user + // SYS: MAINTAIN, MANAGE_USER(with grant option) + // ANY: DELETE + // DB: testdb.* SELECT + // testdb2.* INSERT + // TB: testdb.table CREATE + // testdb.table2 DELETE(with grant option) + + // role + // SYS: MANAGE_ROLE + // TB: database.table ALTER + + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo.checkRoleOfUser("user", "role").getStatus().getCode()); + assertEquals( + TSStatusCode.USER_NOT_HAS_ROLE.getStatusCode(), + authorInfo.checkRoleOfUser("user", "role2").getStatus().getCode()); + // check visible + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges("user", new PrivilegeUnion("testdb", null)) + .getStatus() + .getCode()); + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges("user", new PrivilegeUnion("database", null)) + .getStatus() + .getCode()); + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges("user", new PrivilegeUnion("database", "table", null)) + .getStatus() + .getCode()); + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges("user", new PrivilegeUnion("database", "table2", null)) + .getStatus() + .getCode()); + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges("user", new PrivilegeUnion("database2", "table2", null)) + .getStatus() + .getCode()); + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges("user", new PrivilegeUnion("testdb", PrivilegeType.SELECT)) + .getStatus() + .getCode()); + + assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + authorInfo + .checkUserPrivileges( + "user", new PrivilegeUnion("testdb", "testtb", PrivilegeType.INSERT)) + .getStatus() + .getCode()); + + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges( + "user", new PrivilegeUnion("testdb", "table", PrivilegeType.CREATE)) + .getStatus() + .getCode()); + + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges("user", new PrivilegeUnion(PrivilegeType.MANAGE_ROLE)) + .getStatus() + .getCode()); + + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges( + "user", new PrivilegeUnion("database", "table", PrivilegeType.ALTER)) + .getStatus() + .getCode()); + + assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorInfo + .checkUserPrivileges( + "user", new PrivilegeUnion("testdb", "table2", PrivilegeType.DELETE, true)) + .getStatus() + .getCode()); + + assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + authorInfo + .checkUserPrivileges( + "user", new PrivilegeUnion("database", "table", PrivilegeType.ALTER, true)) + .getStatus() + .getCode()); } } diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/CNPhysicalPlanGeneratorTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/CNPhysicalPlanGeneratorTest.java index 960233dbba76..b782b8668478 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/CNPhysicalPlanGeneratorTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/CNPhysicalPlanGeneratorTest.java @@ -28,7 +28,7 @@ import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlan; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.database.DatabaseSchemaPlan; import org.apache.iotdb.confignode.consensus.request.write.database.SetTTLPlan; import org.apache.iotdb.confignode.consensus.request.write.template.CommitSetSchemaTemplatePlan; @@ -115,7 +115,7 @@ public void roleGeneratorTest() throws Exception { final HashSet answerSet = new HashSet<>(); String roleName = "test1"; setupAuthorInfo(); - AuthorPlan plan = new AuthorPlan(ConfigPhysicalPlanType.CreateRole); + AuthorTreePlan plan = new AuthorTreePlan(ConfigPhysicalPlanType.CreateRole); plan.setRoleName(roleName); plan.setPermissions(new HashSet<>()); plan.setNodeNameList(new ArrayList<>()); @@ -124,7 +124,7 @@ public void roleGeneratorTest() throws Exception { authorInfo.authorNonQuery(plan); // Step 2: grant role path privileges - plan2 - plan = new AuthorPlan(ConfigPhysicalPlanType.GrantRole); + plan = new AuthorTreePlan(ConfigPhysicalPlanType.GrantRole); plan.setRoleName(roleName); plan.setUserName(""); plan.setNodeNameList(Collections.singletonList(new PartialPath("root.db.t1"))); @@ -143,7 +143,7 @@ public void roleGeneratorTest() throws Exception { answerSet.add(plan.hashCode()); // Step 3: grant role sys privileges - plan3 - plan = new AuthorPlan(ConfigPhysicalPlanType.GrantRole); + plan = new AuthorTreePlan(ConfigPhysicalPlanType.GrantRole); plan.setRoleName(roleName); plan.setUserName(""); plan.setNodeNameList(Collections.emptyList()); @@ -192,25 +192,28 @@ public void userGeneratorTest() throws Exception { final String userName = "test1"; final Set answerSet = new HashSet<>(); setupAuthorInfo(); - AuthorPlan plan = new AuthorPlan(ConfigPhysicalPlanType.CreateUser); + AuthorTreePlan plan = new AuthorTreePlan(ConfigPhysicalPlanType.CreateUser); plan.setPassword("password"); plan.setUserName(userName); plan.setPermissions(new HashSet<>()); plan.setNodeNameList(new ArrayList<>()); // Create user plan 1 authorInfo.authorNonQuery(plan); - plan.setAuthorType(ConfigPhysicalPlanType.CreateUserWithRawPassword); + plan = new AuthorTreePlan(ConfigPhysicalPlanType.CreateUserWithRawPassword); plan.setPassword(AuthUtils.encryptPassword("password")); + plan.setUserName(userName); + plan.setPermissions(new HashSet<>()); + plan.setNodeNameList(new ArrayList<>()); answerSet.add(plan.hashCode()); - plan = new AuthorPlan(ConfigPhysicalPlanType.CreateRole); + plan = new AuthorTreePlan(ConfigPhysicalPlanType.CreateRole); plan.setRoleName("role1"); plan.setPermissions(new HashSet<>()); plan.setNodeNameList(new ArrayList<>()); authorInfo.authorNonQuery(plan); // Grant path privileges, plan 2 , plan 3 - plan = new AuthorPlan(ConfigPhysicalPlanType.GrantUser); + plan = new AuthorTreePlan(ConfigPhysicalPlanType.GrantUser); plan.setUserName(userName); plan.setRoleName(""); plan.setNodeNameList(Collections.singletonList(new PartialPath("root.db1.t2"))); @@ -230,7 +233,7 @@ public void userGeneratorTest() throws Exception { answerSet.add(plan.hashCode()); // Grant system privileges, plan 4 - plan = new AuthorPlan(ConfigPhysicalPlanType.GrantUser); + plan = new AuthorTreePlan(ConfigPhysicalPlanType.GrantUser); plan.setUserName(userName); plan.setRoleName(""); plan.setNodeNameList(Collections.emptyList()); @@ -240,7 +243,7 @@ public void userGeneratorTest() throws Exception { answerSet.add(plan.hashCode()); // Grant role to user, plan 5 - plan = new AuthorPlan(ConfigPhysicalPlanType.GrantRoleToUser); + plan = new AuthorTreePlan(ConfigPhysicalPlanType.GrantRoleToUser); plan.setRoleName("role1"); plan.setUserName(""); plan.setUserName(userName); diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ConfigRegionListeningQueueTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ConfigRegionListeningQueueTest.java index dec72d181f1b..f25dbe0bc987 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ConfigRegionListeningQueueTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/persistence/ConfigRegionListeningQueueTest.java @@ -22,7 +22,7 @@ import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.pipe.datastructure.queue.ConcurrentIterableLinkedQueue; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.database.DatabaseSchemaPlan; import org.apache.iotdb.confignode.consensus.request.write.pipe.payload.PipeEnrichedPlan; import org.apache.iotdb.confignode.manager.pipe.agent.PipeConfigNodeAgent; @@ -71,7 +71,7 @@ public void testSnapshot() throws TException, IOException, AuthException { ConfigPhysicalPlanType.CreateDatabase, new TDatabaseSchema("root.test1")); final PipeEnrichedPlan plan2 = new PipeEnrichedPlan( - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.CreateUser, "user0", "", diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/pipe/receiver/PipeEnrichedProcedureTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/pipe/receiver/PipeEnrichedProcedureTest.java index cec2aaf88e94..a0a1dec3092a 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/pipe/receiver/PipeEnrichedProcedureTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/pipe/receiver/PipeEnrichedProcedureTest.java @@ -31,7 +31,7 @@ import org.apache.iotdb.commons.schema.view.viewExpression.leaf.ConstantViewOperand; import org.apache.iotdb.commons.trigger.TriggerInformation; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.request.write.database.SetTTLPlan; import org.apache.iotdb.confignode.procedure.impl.schema.AlterLogicalViewProcedure; import org.apache.iotdb.confignode.procedure.impl.schema.DeactivateTemplateProcedure; @@ -319,7 +319,7 @@ public void authOperationTest() throws AuthException { AuthOperationProcedure p1 = new AuthOperationProcedure( - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.DropRole, "", "test", diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedureTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedureTest.java index 6a4c818c14a3..04bd150ae18a 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedureTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/procedure/impl/sync/AuthOperationProcedureTest.java @@ -23,9 +23,11 @@ import org.apache.iotdb.common.rpc.thrift.TDataNodeLocation; import org.apache.iotdb.common.rpc.thrift.TEndPoint; import org.apache.iotdb.common.rpc.thrift.TNodeResource; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.confignode.consensus.request.ConfigPhysicalPlanType; -import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorRelationalPlan; +import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.procedure.store.ProcedureFactory; import org.apache.tsfile.utils.PublicBAOS; @@ -37,6 +39,7 @@ import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; +import java.util.HashSet; import java.util.List; import static org.junit.Assert.fail; @@ -69,7 +72,7 @@ public void serializeDeserializeTest() throws IOException { for (int i = begin; i <= end; i++) { final AuthOperationProcedure proc = new AuthOperationProcedure( - new AuthorPlan( + new AuthorTreePlan( ConfigPhysicalPlanType.values()[i], "user1", "role1", @@ -94,5 +97,37 @@ public void serializeDeserializeTest() throws IOException { e.printStackTrace(); fail(); } + + try { + final int begin = ConfigPhysicalPlanType.RCreateUser.ordinal(); + final int end = ConfigPhysicalPlanType.RRevokeRoleSysPri.ordinal(); + for (int i = begin; i <= end; i++) { + final AuthOperationProcedure proc = + new AuthOperationProcedure( + new AuthorRelationalPlan( + ConfigPhysicalPlanType.values()[i], + "user1", + "role1", + "database", + "table", + new HashSet<>(PrivilegeType.CREATE.ordinal(), PrivilegeType.SELECT.ordinal()), + false, + "password"), + datanodes, + false); + proc.serialize(outputStream); + final ByteBuffer buffer = + ByteBuffer.wrap(byteArrayOutputStream.getBuf(), 0, byteArrayOutputStream.size()); + + final AuthOperationProcedure proc2 = + (AuthOperationProcedure) ProcedureFactory.getInstance().create(buffer); + Assert.assertEquals(proc, proc2); + buffer.clear(); + byteArrayOutputStream.reset(); + } + } catch (final Exception e) { + e.printStackTrace(); + fail(); + } } } 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 06009372d795..0d6d2727250c 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 @@ -29,12 +29,15 @@ import org.apache.iotdb.commons.schema.column.ColumnHeaderConstant; import org.apache.iotdb.commons.service.metric.PerformanceOverviewMetrics; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; +import org.apache.iotdb.confignode.rpc.thrift.TDBPrivilege; import org.apache.iotdb.confignode.rpc.thrift.TPathPrivilege; import org.apache.iotdb.confignode.rpc.thrift.TRoleResp; +import org.apache.iotdb.confignode.rpc.thrift.TTablePrivilege; import org.apache.iotdb.confignode.rpc.thrift.TUserResp; import org.apache.iotdb.db.protocol.session.IClientSession; import org.apache.iotdb.db.queryengine.common.header.DatasetHeader; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.statement.Statement; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; import org.apache.iotdb.rpc.TSStatusCode; @@ -48,16 +51,15 @@ import java.util.ArrayList; import java.util.Collections; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.stream.Collectors; -import static org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.LIST_USER_PRIVILEGES_Column_HEADERS; +import static org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.LIST_USER_OR_ROLE_PRIVILEGES_COLUMN_HEADERS; // Authority checker is SingleTon working at datanode. -// It checks permission in local. DCL statement will send to confignode. +// It checks permission in local. DCL statement will send to configNode. public class AuthorityChecker { public static final String SUPER_USER = CommonDescriptor.getInstance().getConfig().getAdminName(); @@ -67,6 +69,9 @@ public class AuthorityChecker { private static final String NO_PERMISSION_PROMOTION = "No permissions for this operation, please add privilege "; + private static final String NO_GRANT_OPT_PERMISSION_PROMOTION = + "No permissions for this operation, please add grant option to privilege "; + private static final MemoizedSupplier authorityFetcher = MemoizedSupplier.valueOf(() -> new ClusterAuthorityFetcher(new BasicAuthorityCache())); @@ -81,8 +86,8 @@ public static IAuthorityFetcher getAuthorityFetcher() { return authorityFetcher.get(); } - public static boolean invalidateCache(String username, String rolename) { - return authorityFetcher.get().getAuthorCache().invalidateCache(username, rolename); + public static boolean invalidateCache(String username, String roleName) { + return authorityFetcher.get().getAuthorCache().invalidateCache(username, roleName); } public static TSStatus checkUser(String userName, String password) { @@ -98,6 +103,16 @@ public static SettableFuture operatePermission( return authorityFetcher.get().operatePermission(authorStatement); } + public static SettableFuture queryPermission( + RelationalAuthorStatement authorStatement) { + return authorityFetcher.get().queryPermission(authorStatement); + } + + public static SettableFuture operatePermission( + RelationalAuthorStatement authorStatement) { + return authorityFetcher.get().operatePermission(authorStatement); + } + /** Check whether specific Session has the authorization to given plan. */ public static TSStatus checkAuthority(Statement statement, IClientSession session) { long startTime = System.nanoTime(); @@ -117,10 +132,11 @@ public static TSStatus checkAuthority(Statement statement, String userName) { } } - public static TSStatus getOptTSStatus(boolean hasGrantOpt, String errMsg) { + public static TSStatus getGrantOptTSStatus(boolean hasGrantOpt, PrivilegeType privilegeType) { return hasGrantOpt ? SUCCEED - : new TSStatus(TSStatusCode.NOT_HAS_PRIVILEGE_GRANTOPT.getStatusCode()).setMessage(errMsg); + : new TSStatus(TSStatusCode.NOT_HAS_PRIVILEGE_GRANTOPT.getStatusCode()) + .setMessage(NO_GRANT_OPT_PERMISSION_PROMOTION + privilegeType); } public static TSStatus getTSStatus(boolean hasPermission, String errMsg) { @@ -136,6 +152,45 @@ public static TSStatus getTSStatus(boolean hasPermission, PrivilegeType neededPr .setMessage(NO_PERMISSION_PROMOTION + neededPrivilege); } + public static TSStatus getGrantOptTSStatus( + boolean hasPermission, PrivilegeType neededPrivilege, String database) { + return hasPermission + ? SUCCEED + : new TSStatus(TSStatusCode.NOT_HAS_PRIVILEGE_GRANTOPT.getStatusCode()) + .setMessage(NO_GRANT_OPT_PERMISSION_PROMOTION + neededPrivilege + " ON DB:" + database); + } + + public static TSStatus getGrantOptTSStatus( + boolean hasPermission, PrivilegeType neededPrivilege, String database, String table) { + return hasPermission + ? SUCCEED + : new TSStatus(TSStatusCode.NOT_HAS_PRIVILEGE_GRANTOPT.getStatusCode()) + .setMessage( + NO_GRANT_OPT_PERMISSION_PROMOTION + + neededPrivilege + + " ON " + + database + + "." + + table); + } + + public static TSStatus getTSStatus( + boolean hasPermission, PrivilegeType neededPrivilege, String database) { + return hasPermission + ? SUCCEED + : new TSStatus(TSStatusCode.NO_PERMISSION.getStatusCode()) + .setMessage(NO_PERMISSION_PROMOTION + neededPrivilege + " ON DB:" + database); + } + + public static TSStatus getTSStatus( + boolean hasPermission, PrivilegeType neededPrivilege, String database, String table) { + return hasPermission + ? SUCCEED + : new TSStatus(TSStatusCode.NO_PERMISSION.getStatusCode()) + .setMessage( + NO_PERMISSION_PROMOTION + neededPrivilege + " ON " + database + "." + table); + } + public static TSStatus getTSStatus( boolean hasPermission, PartialPath path, PrivilegeType neededPrivilege) { return hasPermission @@ -165,7 +220,7 @@ public static TSStatus getTSStatus( } public static boolean checkFullPathPermission( - String userName, PartialPath fullPath, int permission) { + String userName, PartialPath fullPath, PrivilegeType permission) { return authorityFetcher .get() .checkUserPathPrivileges(userName, Collections.singletonList(fullPath), permission) @@ -173,40 +228,94 @@ public static boolean checkFullPathPermission( } public static List checkFullPathListPermission( - String userName, List fullPaths, int permission) { + String userName, List fullPaths, PrivilegeType permission) { return authorityFetcher.get().checkUserPathPrivileges(userName, fullPaths, permission); } public static List checkPatternPermission( - String userName, List pathPatterns, int permission) { + String userName, List pathPatterns, PrivilegeType permission) { return authorityFetcher.get().checkUserPathPrivileges(userName, pathPatterns, permission); } - public static PathPatternTree getAuthorizedPathTree(String userName, int permission) + public static PathPatternTree getAuthorizedPathTree(String userName, PrivilegeType permission) throws AuthException { return authorityFetcher.get().getAuthorizedPatternTree(userName, permission); } - public static boolean checkSystemPermission(String userName, int permission) { + public static boolean checkSystemPermission(String userName, PrivilegeType permission) { return authorityFetcher.get().checkUserSysPrivileges(userName, permission).getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode(); } - public static boolean checkGrantOption( - String userName, String[] privilegeList, List nodeNameList) { - for (String s : privilegeList) { - if (!authorityFetcher - .get() - .checkUserPrivilegeGrantOpt( - userName, nodeNameList, PrivilegeType.valueOf(s.toUpperCase()).ordinal())) { - return false; - } - } - return true; + public static boolean checkSystemPermissionGrantOption( + String userName, PrivilegeType permission) { + return authorityFetcher.get().checkUserSysPrivilegesGrantOpt(userName, permission).getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode(); } - public static boolean checkRole(String username, String rolename) { - return authorityFetcher.get().checkRole(username, rolename); + public static boolean checkAnyScopePermissionGrantOption( + String userName, PrivilegeType permission) { + return authorityFetcher + .get() + .checkUserAnyScopePrivilegeGrantOption(userName, permission) + .getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + + public static boolean checkDBPermission( + String userName, String database, PrivilegeType permission) { + return authorityFetcher.get().checkUserDBPrivileges(userName, database, permission).getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + + public static boolean checkDBPermissionGrantOption( + String userName, String database, PrivilegeType permission) { + return authorityFetcher + .get() + .checkUserDBPrivilegesGrantOpt(userName, database, permission) + .getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + + public static boolean checkTablePermission( + String userName, String database, String table, PrivilegeType permission) { + return authorityFetcher + .get() + .checkUserTBPrivileges(userName, database, table, permission) + .getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + + public static boolean checkTablePermissionGrantOption( + String userName, String database, String table, PrivilegeType permission) { + return authorityFetcher + .get() + .checkUserTBPrivilegesGrantOpt(userName, database, table, permission) + .getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + + public static boolean checkDBVisible(String userName, String database) { + return authorityFetcher.get().checkDBVisible(userName, database).getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + + public static boolean checkTableVisible(String userName, String database, String table) { + return authorityFetcher.get().checkTBVisible(userName, database, table).getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + + public static boolean checkPathPermissionGrantOption( + String userName, PrivilegeType privilegeType, List nodeNameList) { + return authorityFetcher + .get() + .checkUserPathPrivilegesGrantOpt(userName, nodeNameList, privilegeType) + .getCode() + == TSStatusCode.SUCCESS_STATUS.getStatusCode(); + } + + public static boolean checkRole(String username, String roleName) { + return authorityFetcher.get().checkRole(username, roleName); } public static void buildTSBlock( @@ -228,33 +337,20 @@ public static void buildTSBlock( builder.declarePosition(); } } else { - headerList = LIST_USER_PRIVILEGES_Column_HEADERS; + headerList = LIST_USER_OR_ROLE_PRIVILEGES_COLUMN_HEADERS; types = - LIST_USER_PRIVILEGES_Column_HEADERS.stream() + LIST_USER_OR_ROLE_PRIVILEGES_COLUMN_HEADERS.stream() .map(ColumnHeader::getColumnType) .collect(Collectors.toList()); builder = new TsBlockBuilder(types); TUserResp user = authResp.getPermissionInfo().getUserInfo(); if (user != null) { - appendPriBuilder("", "root.**", user.getSysPriSet(), user.getSysPriSetGrantOpt(), builder); - for (TPathPrivilege path : user.getPrivilegeList()) { - appendPriBuilder("", path.getPath(), path.getPriSet(), path.getPriGrantOpt(), builder); - } + appendEntryInfo("", user.getPermissionInfo(), builder); } - Iterator> it = - authResp.getPermissionInfo().getRoleInfo().entrySet().iterator(); - while (it.hasNext()) { - TRoleResp role = it.next().getValue(); - appendPriBuilder( - role.getRoleName(), - "root.**", - role.getSysPriSet(), - role.getSysPriSetGrantOpt(), - builder); - for (TPathPrivilege path : role.getPrivilegeList()) { - appendPriBuilder( - role.getRoleName(), path.getPath(), path.getPriSet(), path.getPriGrantOpt(), builder); - } + for (Map.Entry stringTRoleRespEntry : + authResp.getPermissionInfo().getRoleInfo().entrySet()) { + TRoleResp role = stringTRoleRespEntry.getValue(); + appendEntryInfo(role.getName(), role, builder); } } DatasetHeader datasetHeader = new DatasetHeader(headerList, true); @@ -262,10 +358,10 @@ public static void buildTSBlock( } private static void appendPriBuilder( - String name, String path, Set priv, Set grantOpt, TsBlockBuilder builder) { + String name, String scope, Set priv, Set grantOpt, TsBlockBuilder builder) { for (int i : priv) { builder.getColumnBuilder(0).writeBinary(new Binary(name, TSFileConfig.STRING_CHARSET)); - builder.getColumnBuilder(1).writeBinary(new Binary(path, TSFileConfig.STRING_CHARSET)); + builder.getColumnBuilder(1).writeBinary(new Binary(scope, TSFileConfig.STRING_CHARSET)); builder .getColumnBuilder(2) .writeBinary( @@ -275,4 +371,29 @@ private static void appendPriBuilder( builder.declarePosition(); } } + + private static void appendEntryInfo(String name, TRoleResp resp, TsBlockBuilder builder) { + // System privilege. + appendPriBuilder(name, "", resp.getSysPriSet(), resp.getSysPriSetGrantOpt(), builder); + // Any scope privilege. + appendPriBuilder(name, "*.*", resp.getAnyScopeSet(), resp.getAnyScopeGrantSet(), builder); + // Path privilege. + for (TPathPrivilege path : resp.getPrivilegeList()) { + appendPriBuilder(name, path.getPath(), path.getPriSet(), path.getPriGrantOpt(), builder); + } + for (Map.Entry entry : resp.getDbPrivilegeMap().entrySet()) { + TDBPrivilege priv = entry.getValue(); + appendPriBuilder( + name, entry.getKey() + ".*", priv.getPrivileges(), priv.getGrantOpt(), builder); + for (Map.Entry tbEntry : priv.getTablePrivilegeMap().entrySet()) { + TTablePrivilege tb = tbEntry.getValue(); + appendPriBuilder( + name, + entry.getKey() + "." + tbEntry.getKey(), + tb.getPrivileges(), + tb.getGrantOption(), + builder); + } + } + } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/BasicAuthorityCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/BasicAuthorityCache.java index 31393b05ee4d..66c1e4e26a0d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/BasicAuthorityCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/BasicAuthorityCache.java @@ -28,7 +28,7 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.util.List; +import java.util.Set; import java.util.concurrent.TimeUnit; public class BasicAuthorityCache implements IAuthorCache { @@ -79,9 +79,9 @@ public void putRoleCache(String roleName, Role role) { public boolean invalidateCache(String userName, String roleName) { if (userName != null) { if (userCache.getIfPresent(userName) != null) { - List roleList = userCache.getIfPresent(userName).getRoleList(); - if (!roleList.isEmpty()) { - roleCache.invalidateAll(roleList); + Set roleSet = userCache.getIfPresent(userName).getRoleSet(); + if (!roleSet.isEmpty()) { + roleCache.invalidateAll(roleSet); } userCache.invalidate(userName); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java index debce14f7a8e..1370e019f106 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/ClusterAuthorityFetcher.java @@ -22,7 +22,9 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.PathPrivilege; +import org.apache.iotdb.commons.auth.entity.PrivilegeModelType; import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.client.IClientManager; @@ -35,18 +37,21 @@ import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.commons.utils.AuthUtils; +import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.confignode.rpc.thrift.TAuthizedPatternTreeResp; +import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerRelationalReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; import org.apache.iotdb.confignode.rpc.thrift.TLoginReq; import org.apache.iotdb.confignode.rpc.thrift.TPathPrivilege; import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp; +import org.apache.iotdb.confignode.rpc.thrift.TRoleResp; import org.apache.iotdb.db.protocol.client.ConfigNodeClient; import org.apache.iotdb.db.protocol.client.ConfigNodeClientManager; import org.apache.iotdb.db.protocol.client.ConfigNodeInfo; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; -import org.apache.iotdb.db.queryengine.plan.statement.AuthorType; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; import org.apache.iotdb.rpc.RpcUtils; import org.apache.iotdb.rpc.TSStatusCode; @@ -60,7 +65,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.List; -import java.util.Locale; +import java.util.function.BiFunction; public class ClusterAuthorityFetcher implements IAuthorityFetcher { private static final Logger LOGGER = LoggerFactory.getLogger(ClusterAuthorityFetcher.class); @@ -69,8 +74,7 @@ public class ClusterAuthorityFetcher implements IAuthorityFetcher { private boolean cacheOutDate = false; private long heartBeatTimeStamp = 0; - // for test only. - private boolean acceptCache = true; + private boolean acceptCache = false; private static final IClientManager CONFIG_NODE_CLIENT_MANAGER = ConfigNodeClientManager.getInstance(); @@ -81,9 +85,61 @@ public ClusterAuthorityFetcher(IAuthorCache iAuthorCache) { this.iAuthorCache = iAuthorCache; } + /** -- check user privileges SYSTEM, TREE, RELATIONAL-- * */ + private TSStatus checkPrivilege( + String username, + PrivilegeUnion union, + BiFunction privilegeCheck, + TCheckUserPrivilegesReq req) { + User user = iAuthorCache.getUserCache(username); + if (user != null) { + if (privilegeCheck.apply(user, union)) { + return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS); + } + boolean remoteCheck = false; + for (String rolename : user.getRoleSet()) { + Role role = iAuthorCache.getRoleCache(rolename); + if (role == null) { + remoteCheck = true; + break; + } + if (privilegeCheck.apply(role, union)) { + return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS); + } + } + if (remoteCheck) { + return checkPrivilegeFromConfigNode(req).getStatus(); + } + return RpcUtils.getStatus(TSStatusCode.NO_PERMISSION); + } + return checkPrivilegeFromConfigNode(req).getStatus(); + } + + @Override + public TSStatus checkUserSysPrivileges(String username, PrivilegeType permission) { + checkCacheAvailable(); + return checkPrivilege( + username, + new PrivilegeUnion(permission, false), + (role, union) -> role.checkSysPrivilege(union.getPrivilegeType()), + new TCheckUserPrivilegesReq( + username, PrivilegeModelType.SYSTEM.ordinal(), permission.ordinal(), false)); + } + + @Override + public TSStatus checkUserSysPrivilegesGrantOpt(String username, PrivilegeType permission) { + checkCacheAvailable(); + return checkPrivilege( + username, + new PrivilegeUnion(permission, true), + (role, union) -> role.checkSysPriGrantOpt(union.getPrivilegeType()), + new TCheckUserPrivilegesReq( + username, PrivilegeModelType.SYSTEM.ordinal(), permission.ordinal(), true)); + } + @Override public List checkUserPathPrivileges( - String username, List allPath, int permission) { + String username, List allPath, PrivilegeType permission) { checkCacheAvailable(); List posList = new ArrayList<>(); User user = iAuthorCache.getUserCache(username); @@ -95,7 +151,7 @@ public List checkUserPathPrivileges( for (PartialPath path : allPath) { if (!user.checkPathPrivilege(path, permission)) { boolean checkFromRole = false; - for (String rolename : user.getRoleList()) { + for (String rolename : user.getRoleSet()) { Role cachedRole = iAuthorCache.getRoleCache(rolename); if (cachedRole == null) { return checkPathFromConfigNode(username, allPath, permission); @@ -118,30 +174,27 @@ public List checkUserPathPrivileges( } @Override - public boolean checkUserPrivilegeGrantOpt( - String username, List paths, int permission) { - checkCacheAvailable(); - if (PrivilegeType.values()[permission].isPathRelevant()) { - return checkUserPathPriGrantOpt(username, paths, permission); - } else { - return checkUserSysPriGrantOpt(username, permission); - } - } - - private boolean checkUserPathPriGrantOpt( - String username, List paths, int permission) { + public TSStatus checkUserPathPrivilegesGrantOpt( + String username, List paths, PrivilegeType permission) { User user = iAuthorCache.getUserCache(username); if (user != null) { if (user.isOpenIdUser()) { - return true; + return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS); } for (PartialPath path : paths) { if (!user.checkPathPrivilegeGrantOpt(path, permission)) { boolean checkFromRole = false; - for (String roleName : user.getRoleList()) { + for (String roleName : user.getRoleSet()) { Role role = iAuthorCache.getRoleCache(roleName); if (role == null) { - return checkUserPrivilegeGrantOptFromConfigNode(username, paths, permission); + return checkPrivilegeFromConfigNode( + new TCheckUserPrivilegesReq( + username, + PrivilegeModelType.TREE.ordinal(), + permission.ordinal(), + true) + .setPaths(AuthUtils.serializePartialPathList(paths))) + .getStatus(); } if (role.checkPathPrivilegeGrantOpt(path, permission)) { checkFromRole = true; @@ -149,71 +202,116 @@ private boolean checkUserPathPriGrantOpt( } } if (!checkFromRole) { - return false; + return RpcUtils.getStatus(TSStatusCode.NO_PERMISSION); } } } - return true; + return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS); } else { - return checkUserPrivilegeGrantOptFromConfigNode(username, paths, permission); + return checkPrivilegeFromConfigNode( + new TCheckUserPrivilegesReq( + username, PrivilegeModelType.TREE.ordinal(), permission.ordinal(), true) + .setPaths(AuthUtils.serializePartialPathList(paths))) + .getStatus(); } } - private boolean checkUserSysPriGrantOpt(String username, int permission) { - User user = iAuthorCache.getUserCache(username); - if (user != null) { - if (user.isOpenIdUser()) { - return true; - } - if (!user.checkSysPriGrantOpt(permission)) { - for (String roleName : user.getRoleList()) { - Role role = iAuthorCache.getRoleCache(roleName); - if (role == null) { - return checkUserPrivilegeGrantOptFromConfigNode( - username, Collections.emptyList(), permission); - } - if (role.checkSysPriGrantOpt(permission)) { - return true; - } - } - return false; - } - return true; - } else { - return checkUserPrivilegeGrantOptFromConfigNode( - username, Collections.emptyList(), permission); - } + @Override + public TSStatus checkUserDBPrivileges( + String username, String database, PrivilegeType permission) { + checkCacheAvailable(); + return checkPrivilege( + username, + new PrivilegeUnion(database, permission), + (role, union) -> role.checkDatabasePrivilege(union.getDBName(), union.getPrivilegeType()), + new TCheckUserPrivilegesReq( + username, PrivilegeModelType.RELATIONAL.ordinal(), permission.ordinal(), false) + .setDatabase(database)); } - private boolean checkUserPrivilegeGrantOptFromConfigNode( - String username, List paths, int permission) { - TCheckUserPrivilegesReq req = + @Override + public TSStatus checkUserDBPrivilegesGrantOpt( + String username, String database, PrivilegeType permission) { + checkCacheAvailable(); + return checkPrivilege( + username, + new PrivilegeUnion(database, permission, true), + (role, union) -> + role.checkDatabasePrivilegeGrantOption(union.getDBName(), union.getPrivilegeType()), new TCheckUserPrivilegesReq( - username, AuthUtils.serializePartialPathList(paths), permission); - req.setGrantOpt(true); - TPermissionInfoResp permissionInfoResp; - try (ConfigNodeClient configNodeClient = - CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { - // Send request to some API server - permissionInfoResp = configNodeClient.checkUserPrivilegeGrantOpt(req); - } catch (ClientManagerException | TException e) { - LOGGER.error(CONNECTERROR); - permissionInfoResp = new TPermissionInfoResp(); - permissionInfoResp.setStatus( - RpcUtils.getStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR, CONNECTERROR)); - } - if (permissionInfoResp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { - if (acceptCache) { - iAuthorCache.putUserCache(username, cacheUser(permissionInfoResp)); - } - return true; - } else { - return false; - } + username, PrivilegeModelType.RELATIONAL.ordinal(), permission.ordinal(), true) + .setDatabase(database)); + } + + @Override + public TSStatus checkUserTBPrivileges( + String username, String database, String table, PrivilegeType permission) { + checkCacheAvailable(); + return checkPrivilege( + username, + new PrivilegeUnion(database, table, permission), + (role, union) -> + role.checkTablePrivilege( + union.getDBName(), union.getTbName(), union.getPrivilegeType()), + new TCheckUserPrivilegesReq( + username, PrivilegeModelType.RELATIONAL.ordinal(), permission.ordinal(), false) + .setDatabase(database) + .setTable(table)); + } + + @Override + public TSStatus checkUserTBPrivilegesGrantOpt( + String username, String database, String table, PrivilegeType permission) { + checkCacheAvailable(); + return checkPrivilege( + username, + new PrivilegeUnion(database, table, permission, true), + (role, union) -> + role.checkTablePrivilegeGrantOption( + union.getDBName(), union.getTbName(), union.getPrivilegeType()), + new TCheckUserPrivilegesReq( + username, PrivilegeModelType.RELATIONAL.ordinal(), permission.ordinal(), true) + .setDatabase(database) + .setTable(table)); + } + + @Override + public TSStatus checkUserAnyScopePrivilegeGrantOption(String username, PrivilegeType permission) { + checkCacheAvailable(); + return checkPrivilege( + username, + new PrivilegeUnion(permission, false, true), + (role, union) -> role.checkAnyScopePrivilegeGrantOption(union.getPrivilegeType()), + new TCheckUserPrivilegesReq( + username, PrivilegeModelType.RELATIONAL.ordinal(), permission.ordinal(), true)); + } + + /** -- check database/table visible -- * */ + @Override + public TSStatus checkDBVisible(String username, String database) { + checkCacheAvailable(); + return checkPrivilege( + username, + new PrivilegeUnion(database, null, false), + (role, union) -> role.checkDBVisible(union.getDBName()), + new TCheckUserPrivilegesReq(username, PrivilegeModelType.RELATIONAL.ordinal(), -1, false) + .setDatabase(database)); + } + + @Override + public TSStatus checkTBVisible(String username, String database, String table) { + checkCacheAvailable(); + return checkPrivilege( + username, + new PrivilegeUnion(database, table, null, false), + (role, union) -> role.checkTBVisible(union.getDBName(), union.getTbName()), + new TCheckUserPrivilegesReq(username, PrivilegeModelType.RELATIONAL.ordinal(), -1, false) + .setDatabase(database) + .setTable(table)); } @Override - public PathPatternTree getAuthorizedPatternTree(String username, int permission) + public PathPatternTree getAuthorizedPatternTree(String username, PrivilegeType permission) throws AuthException { PathPatternTree patternTree = new PathPatternTree(); User user = iAuthorCache.getUserCache(username); @@ -223,7 +321,7 @@ public PathPatternTree getAuthorizedPatternTree(String username, int permission) patternTree.appendPathPattern(path.getPath()); } } - for (String roleName : user.getRoleList()) { + for (String roleName : user.getRoleSet()) { Role role = iAuthorCache.getRoleCache(roleName); if (role != null) { for (PathPrivilege path : role.getPathPrivilegeList()) { @@ -242,11 +340,11 @@ public PathPatternTree getAuthorizedPatternTree(String username, int permission) } } - private PathPatternTree fetchAuthizedPatternTree(String username, int permission) + private PathPatternTree fetchAuthizedPatternTree(String username, PrivilegeType permission) throws AuthException { TCheckUserPrivilegesReq req = new TCheckUserPrivilegesReq( - username, AuthUtils.serializePartialPathList(Collections.emptyList()), permission); + username, PrivilegeModelType.TREE.ordinal(), permission.ordinal(), false); TAuthizedPatternTreeResp authizedPatternTree = new TAuthizedPatternTreeResp(); try (ConfigNodeClient configNodeClient = CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { @@ -267,101 +365,83 @@ private PathPatternTree fetchAuthizedPatternTree(String username, int permission } } - @Override - public TSStatus checkUserSysPrivileges(String username, int permission) { - checkCacheAvailable(); - User user = iAuthorCache.getUserCache(username); - if (user != null) { - if (!user.isOpenIdUser() && (!user.checkSysPrivilege(permission))) { - if (user.getRoleList().isEmpty()) { - return RpcUtils.getStatus(TSStatusCode.NO_PERMISSION); - } - boolean status = false; - for (String rolename : user.getRoleList()) { - Role cacheRole = iAuthorCache.getRoleCache(rolename); - if (cacheRole == null) { - return checkSysPriFromConfigNode(username, permission); - } - if (cacheRole.checkSysPrivilege(permission)) { - status = true; - break; - } - } - if (!status) { - return RpcUtils.getStatus(TSStatusCode.NO_PERMISSION); - } - } - return RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS); - } else { - return checkSysPriFromConfigNode(username, permission); - } - } - - @Override - public SettableFuture operatePermission(AuthorStatement authorStatement) { + private SettableFuture operatePermissionInternal( + Object plan, boolean isRelational) { SettableFuture future = SettableFuture.create(); try (ConfigNodeClient configNodeClient = CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { - // Construct request using statement - TAuthorizerReq authorizerReq = statementToAuthorizerReq(authorStatement); - // Send request to some API server - TSStatus tsStatus = configNodeClient.operatePermission(authorizerReq); - // Get response or throw exception + TSStatus tsStatus = + isRelational + ? configNodeClient.operateRPermission( + statementToAuthorizerReq((RelationalAuthorStatement) plan)) + : configNodeClient.operatePermission( + statementToAuthorizerReq((AuthorStatement) plan)); if (TSStatusCode.SUCCESS_STATUS.getStatusCode() != tsStatus.getCode()) { - LOGGER.warn( - "Failed to execute {} in config node, status is {}.", - AuthorType.values()[authorizerReq.getAuthorType()].toString().toLowerCase(Locale.ROOT), - tsStatus); future.setException(new IoTDBException(tsStatus.message, tsStatus.code)); } else { future.set(new ConfigTaskResult(TSStatusCode.SUCCESS_STATUS)); } + } catch (AuthException e) { + future.setException(e); } catch (ClientManagerException | TException e) { LOGGER.error(CONNECTERROR); future.setException(e); - } catch (AuthException e) { - future.setException(e); } - // If the action is executed successfully, return the Future. - // If your operation is async, you can return the corresponding future directly. return future; } @Override - public SettableFuture queryPermission(AuthorStatement authorStatement) { + public SettableFuture operatePermission(AuthorStatement authorStatement) { + return operatePermissionInternal(authorStatement, false); + } + + @Override + public SettableFuture operatePermission( + RelationalAuthorStatement authorStatement) { + return operatePermissionInternal(authorStatement, true); + } + + private SettableFuture queryPermissionInternal( + Object plan, boolean isRelational) { SettableFuture future = SettableFuture.create(); TAuthorizerResp authorizerResp = new TAuthorizerResp(); - try (ConfigNodeClient configNodeClient = CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { - // Construct request using statement - TAuthorizerReq authorizerReq = statementToAuthorizerReq(authorStatement); - // Send request to some API server - authorizerResp = configNodeClient.queryPermission(authorizerReq); - // Get response or throw exception + authorizerResp = + isRelational + ? configNodeClient.queryRPermission( + statementToAuthorizerReq((RelationalAuthorStatement) plan)) + : configNodeClient.queryPermission(statementToAuthorizerReq((AuthorStatement) plan)); if (TSStatusCode.SUCCESS_STATUS.getStatusCode() != authorizerResp.getStatus().getCode()) { - LOGGER.error( - "Failed to execute {} in config node, status is {}.", - AuthorType.values()[authorizerReq.getAuthorType()].toString().toLowerCase(Locale.ROOT), - authorizerResp.getStatus()); future.setException( new IoTDBException( authorizerResp.getStatus().message, authorizerResp.getStatus().code)); } else { AuthorityChecker.buildTSBlock(authorizerResp, future); } + } catch (AuthException e) { + future.setException(e); } catch (ClientManagerException | TException e) { LOGGER.error(CONNECTERROR); authorizerResp.setStatus( RpcUtils.getStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR, CONNECTERROR)); future.setException( new IoTDBException(authorizerResp.getStatus().message, authorizerResp.getStatus().code)); - } catch (AuthException e) { - future.setException(e); } return future; } + @Override + public SettableFuture queryPermission(AuthorStatement authorStatement) { + return queryPermissionInternal(authorStatement, false); + } + + @Override + public SettableFuture queryPermission( + RelationalAuthorStatement authorStatement) { + return queryPermissionInternal(authorStatement, true); + } + @Override public IAuthorCache getAuthorCache() { return iAuthorCache; @@ -387,6 +467,11 @@ private void checkCacheAvailable() { cacheOutDate = false; } + @TestOnly + public void setAcceptCache(boolean acceptCache) { + this.acceptCache = acceptCache; + } + @Override public TSStatus checkUser(String username, String password) { checkCacheAvailable(); @@ -431,16 +516,13 @@ public boolean checkRole(String userName, String roleName) { checkCacheAvailable(); User user = iAuthorCache.getUserCache(userName); if (user != null) { - return user.isOpenIdUser() || user.getRoleList().contains(roleName); + return user.isOpenIdUser() || user.getRoleSet().contains(roleName); } else { return checkRoleFromConfigNode(userName, roleName); } } - private TSStatus checkSysPriFromConfigNode(String username, int permission) { - TCheckUserPrivilegesReq req = - new TCheckUserPrivilegesReq( - username, AuthUtils.serializePartialPathList(Collections.emptyList()), permission); + private TPermissionInfoResp checkPrivilegeFromConfigNode(TCheckUserPrivilegesReq req) { TPermissionInfoResp permissionInfoResp; try (ConfigNodeClient configNodeClient = CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { @@ -454,34 +536,19 @@ private TSStatus checkSysPriFromConfigNode(String username, int permission) { } if (permissionInfoResp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { if (acceptCache) { - iAuthorCache.putUserCache(username, cacheUser(permissionInfoResp)); + iAuthorCache.putUserCache(req.getUsername(), cacheUser(permissionInfoResp)); } } - return permissionInfoResp.getStatus(); + return permissionInfoResp; } private List checkPathFromConfigNode( - String username, List allPath, int permission) { + String username, List allPath, PrivilegeType permission) { TCheckUserPrivilegesReq req = new TCheckUserPrivilegesReq( - username, AuthUtils.serializePartialPathList(allPath), permission); - TPermissionInfoResp permissionInfoResp; - try (ConfigNodeClient configNodeClient = - CONFIG_NODE_CLIENT_MANAGER.borrowClient(ConfigNodeInfo.CONFIG_REGION_ID)) { - // Send request to some API server - permissionInfoResp = configNodeClient.checkUserPrivileges(req); - } catch (ClientManagerException | TException e) { - LOGGER.error(CONNECTERROR); - permissionInfoResp = new TPermissionInfoResp(); - permissionInfoResp.setStatus( - RpcUtils.getStatus(TSStatusCode.EXECUTE_STATEMENT_ERROR, CONNECTERROR)); - } - if (permissionInfoResp.getStatus().getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { - if (acceptCache) { - iAuthorCache.putUserCache(username, cacheUser(permissionInfoResp)); - } - } - return permissionInfoResp.getFailPos(); + username, PrivilegeModelType.TREE.ordinal(), permission.ordinal(), false); + req.setPaths(AuthUtils.serializePartialPathList(allPath)); + return checkPrivilegeFromConfigNode(req).getFailPos(); } private boolean checkRoleFromConfigNode(String username, String rolename) { @@ -525,53 +592,50 @@ private boolean checkRoleFromConfigNode(String username, String rolename) { /** Cache user. */ public User cacheUser(TPermissionInfoResp tPermissionInfoResp) { User user = new User(); - List privilegeList = tPermissionInfoResp.getUserInfo().getPrivilegeList(); - List pathPrivilegeList = new ArrayList<>(); - user.setName(tPermissionInfoResp.getUserInfo().getUsername()); + List privilegeList = + tPermissionInfoResp.getUserInfo().getPermissionInfo().getPrivilegeList(); + user.setName(tPermissionInfoResp.getUserInfo().getPermissionInfo().getName()); user.setPassword(tPermissionInfoResp.getUserInfo().getPassword()); - for (TPathPrivilege tPathPrivilege : privilegeList) { - try { - PathPrivilege pathPri = new PathPrivilege(); - pathPri.setPath(new PartialPath(tPathPrivilege.getPath())); - pathPri.setPrivileges(tPathPrivilege.getPriSet()); - pathPri.setGrantOpt(tPathPrivilege.getPriGrantOpt()); - pathPrivilegeList.add(pathPri); - } catch (MetadataException e) { - LOGGER.error("Failed to parse path {}.", tPathPrivilege.getPath(), e); - } - } + user.loadDatabaseAndTablePrivilegeInfo( + tPermissionInfoResp.getUserInfo().getPermissionInfo().getDbPrivilegeMap()); + user.setAnyScopePrivilegeSetInt( + tPermissionInfoResp.getUserInfo().getPermissionInfo().getAnyScopeSet()); + user.setAnyScopePrivilegeGrantOptSetInt( + tPermissionInfoResp.getUserInfo().getPermissionInfo().getAnyScopeGrantSet()); user.setOpenIdUser(tPermissionInfoResp.getUserInfo().isIsOpenIdUser()); - user.setPrivilegeList(pathPrivilegeList); - user.setRoleList(tPermissionInfoResp.getUserInfo().getRoleList()); - user.setSysPrivilegeSet(tPermissionInfoResp.getUserInfo().getSysPriSet()); - user.setSysPriGrantOpt(tPermissionInfoResp.getUserInfo().getSysPriSetGrantOpt()); - for (String roleName : tPermissionInfoResp.getRoleInfo().keySet()) { - iAuthorCache.putRoleCache(roleName, cacheRole(roleName, tPermissionInfoResp)); + user.setRoleSet(tPermissionInfoResp.getUserInfo().getRoleSet()); + user.setSysPrivilegeSetInt( + tPermissionInfoResp.getUserInfo().getPermissionInfo().getSysPriSet()); + user.setSysPriGrantOptInt( + tPermissionInfoResp.getUserInfo().getPermissionInfo().getSysPriSetGrantOpt()); + try { + user.loadTreePrivilegeInfo(privilegeList); + } catch (MetadataException e) { + LOGGER.error("cache user's path privileges error", e); + } + if (tPermissionInfoResp.isSetRoleInfo()) { + for (String roleName : tPermissionInfoResp.getRoleInfo().keySet()) { + iAuthorCache.putRoleCache(roleName, cacheRole(roleName, tPermissionInfoResp)); + } } return user; } /** Cache role. */ public Role cacheRole(String roleName, TPermissionInfoResp tPermissionInfoResp) { - Role role = new Role(); - List privilegeList = - tPermissionInfoResp.getRoleInfo().get(roleName).getPrivilegeList(); - List pathPrivilegeList = new ArrayList<>(); - role.setName(tPermissionInfoResp.getRoleInfo().get(roleName).getRoleName()); - for (TPathPrivilege tPathPrivilege : privilegeList) { - try { - PathPrivilege pathPri = new PathPrivilege(); - pathPri.setPath(new PartialPath(tPathPrivilege.getPath())); - pathPri.setPrivileges(tPathPrivilege.getPriSet()); - pathPri.setGrantOpt(tPathPrivilege.getPriGrantOpt()); - pathPrivilegeList.add(pathPri); - } catch (MetadataException e) { - LOGGER.error("Failed to parse path {}.", tPathPrivilege.getPath(), e); - } + TRoleResp resp = tPermissionInfoResp.getRoleInfo().get(roleName); + Role role = new Role(resp.getName()); + role.setAnyScopePrivilegeSetInt(resp.getAnyScopeSet()); + role.setAnyScopePrivilegeGrantOptSetInt(resp.getAnyScopeGrantSet()); + role.loadDatabaseAndTablePrivilegeInfo(resp.getDbPrivilegeMap()); + role.setSysPriGrantOptInt( + tPermissionInfoResp.getRoleInfo().get(roleName).getSysPriSetGrantOpt()); + role.setSysPrivilegeSetInt(tPermissionInfoResp.getRoleInfo().get(roleName).getSysPriSet()); + try { + role.loadTreePrivilegeInfo(resp.getPrivilegeList()); + } catch (MetadataException e) { + LOGGER.error("cache role's path privileges error", e); } - role.setSysPriGrantOpt(tPermissionInfoResp.getRoleInfo().get(roleName).getSysPriSetGrantOpt()); - role.setSysPrivilegeSet(tPermissionInfoResp.getRoleInfo().get(roleName).getSysPriSet()); - role.setPrivilegeList(pathPrivilegeList); return role; } @@ -590,4 +654,19 @@ private TAuthorizerReq statementToAuthorizerReq(AuthorStatement authorStatement) authorStatement.getGrantOpt(), AuthUtils.serializePartialPathList(authorStatement.getNodeNameList())); } + + private TAuthorizerRelationalReq statementToAuthorizerReq( + RelationalAuthorStatement authorStatement) { + return new TAuthorizerRelationalReq( + authorStatement.getAuthorType().ordinal(), + authorStatement.getUserName() == null ? "" : authorStatement.getUserName(), + authorStatement.getRoleName() == null ? "" : authorStatement.getRoleName(), + authorStatement.getPassword() == null ? "" : authorStatement.getPassword(), + authorStatement.getDatabase() == null ? "" : authorStatement.getDatabase(), + authorStatement.getTableName() == null ? "" : authorStatement.getTableName(), + authorStatement.getPrivilegeTypes() == null + ? Collections.emptySet() + : authorStatement.getPrivilegeIds(), + authorStatement.isGrantOption()); + } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java index 939b328174ff..c9f2970ad469 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/IAuthorityFetcher.java @@ -21,9 +21,11 @@ import org.apache.iotdb.common.rpc.thrift.TSStatus; import org.apache.iotdb.commons.auth.AuthException; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathPatternTree; import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.statement.sys.AuthorStatement; import com.google.common.util.concurrent.SettableFuture; @@ -34,21 +36,46 @@ public interface IAuthorityFetcher { TSStatus checkUser(String username, String password); - boolean checkRole(String username, String rolename); + boolean checkRole(String username, String roleName); List checkUserPathPrivileges( - String username, List allPath, int permission); + String username, List allPath, PrivilegeType permission); - TSStatus checkUserSysPrivileges(String username, int permisssion); + TSStatus checkUserPathPrivilegesGrantOpt( + String username, List allPath, PrivilegeType permission); - boolean checkUserPrivilegeGrantOpt(String username, List paths, int permission); + TSStatus checkUserSysPrivileges(String username, PrivilegeType permission); - PathPatternTree getAuthorizedPatternTree(String username, int permission) throws AuthException; + TSStatus checkUserDBPrivileges(String username, String database, PrivilegeType permission); + + TSStatus checkUserTBPrivileges( + String username, String database, String table, PrivilegeType permission); + + TSStatus checkUserSysPrivilegesGrantOpt(String username, PrivilegeType permission); + + TSStatus checkUserDBPrivilegesGrantOpt( + String username, String database, PrivilegeType permission); + + TSStatus checkUserTBPrivilegesGrantOpt( + String username, String database, String table, PrivilegeType permission); + + TSStatus checkUserAnyScopePrivilegeGrantOption(String username, PrivilegeType permission); + + TSStatus checkDBVisible(String username, String database); + + TSStatus checkTBVisible(String username, String database, String table); + + PathPatternTree getAuthorizedPatternTree(String username, PrivilegeType permission) + throws AuthException; SettableFuture operatePermission(AuthorStatement authorStatement); SettableFuture queryPermission(AuthorStatement authorStatement); + SettableFuture operatePermission(RelationalAuthorStatement authorStatement); + + SettableFuture queryPermission(RelationalAuthorStatement authorStatement); + IAuthorCache getAuthorCache(); void refreshToken(); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java index 40df0e5e2ff9..1a2e46d07e4f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/protocol/client/ConfigNodeClient.java @@ -53,6 +53,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TAlterPipeReq; import org.apache.iotdb.confignode.rpc.thrift.TAlterSchemaTemplateReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthizedPatternTreeResp; +import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerRelationalReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq; import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp; import org.apache.iotdb.confignode.rpc.thrift.TCheckUserPrivilegesReq; @@ -670,12 +671,24 @@ public TSStatus operatePermission(TAuthorizerReq req) throws TException { () -> client.operatePermission(req), status -> !updateConfigNodeLeader(status)); } + @Override + public TSStatus operateRPermission(TAuthorizerRelationalReq req) throws TException { + return executeRemoteCallWithRetry( + () -> client.operateRPermission(req), status -> !updateConfigNodeLeader(status)); + } + @Override public TAuthorizerResp queryPermission(TAuthorizerReq req) throws TException { return executeRemoteCallWithRetry( () -> client.queryPermission(req), resp -> !updateConfigNodeLeader(resp.status)); } + @Override + public TAuthorizerResp queryRPermission(TAuthorizerRelationalReq req) throws TException { + return executeRemoteCallWithRetry( + () -> client.queryRPermission(req), resp -> !updateConfigNodeLeader(resp.status)); + } + @Override public TPermissionInfoResp login(TLoginReq req) throws TException { return executeRemoteCallWithRetry( @@ -695,13 +708,6 @@ public TAuthizedPatternTreeResp fetchAuthizedPatternTree(TCheckUserPrivilegesReq () -> client.fetchAuthizedPatternTree(req), resp -> !updateConfigNodeLeader(resp.status)); } - @Override - public TPermissionInfoResp checkUserPrivilegeGrantOpt(TCheckUserPrivilegesReq req) - throws TException { - return executeRemoteCallWithRetry( - () -> client.checkUserPrivilegeGrantOpt(req), resp -> !updateConfigNodeLeader(resp.status)); - } - public TPermissionInfoResp checkRoleOfUser(TAuthorizerReq req) throws TException { return executeRemoteCallWithRetry( () -> client.checkRoleOfUser(req), resp -> !updateConfigNodeLeader(resp.status)); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java index ccb75ebb22f9..a798a1879c15 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/InformationSchemaContentSupplierFactory.java @@ -20,6 +20,7 @@ package org.apache.iotdb.db.queryengine.execution.operator.source.relational; import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.commons.exception.IoTDBException; import org.apache.iotdb.commons.schema.table.InformationSchema; import org.apache.iotdb.commons.schema.table.TableNodeStatus; import org.apache.iotdb.commons.schema.table.TsTable; @@ -48,7 +49,6 @@ import org.apache.tsfile.utils.BytesUtils; import org.apache.tsfile.utils.Pair; -import java.security.AccessControlException; import java.util.Arrays; import java.util.Collections; import java.util.Iterator; @@ -341,8 +341,11 @@ public boolean hasNext() { private static boolean canShowDB(final String userName, final String dbName) { try { Coordinator.getInstance().getAccessControl().checkCanShowOrUseDatabase(userName, dbName); - } catch (final AccessControlException e) { - return false; + } catch (final RuntimeException e) { + if (e.getCause() instanceof IoTDBException) { + return false; + } + throw e; } return true; } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java index 11704844f2a1..a0602bec83dc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java @@ -56,7 +56,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.LogicalOptimizeFactory; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanOptimizer; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; -import org.apache.iotdb.db.queryengine.plan.relational.security.AllowAllAccessControl; +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.sql.ast.AddColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterDB; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache; @@ -72,6 +73,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Flush; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.KillQuery; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.PipeStatement; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveDataNode; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetConfiguration; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.SetProperties; @@ -181,7 +183,7 @@ private Coordinator() { new PlannerContext( LocalExecutionPlanner.getInstance().metadata, new InternalTypeManager())) .getPlanOptimizers(); - this.accessControl = new AllowAllAccessControl(); + this.accessControl = new AccessControlImpl(new ITableAuthCheckerImpl()); this.dataNodeLocationSupplier = DataNodeLocationSupplierFactory.getSupplier(); } @@ -420,7 +422,8 @@ private IQueryExecution createQueryExecutionForTableModel( || statement instanceof KillQuery || statement instanceof CreateFunction || statement instanceof DropFunction - || statement instanceof ShowFunctions) { + || statement instanceof ShowFunctions + || statement instanceof RelationalAuthorStatement) { return new ConfigExecution( queryContext, null, diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java index aefae721cfc6..a51c7b1a8bd1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/cache/partition/PartitionCache.java @@ -297,7 +297,7 @@ private void createDatabaseAndUpdateCache( final TSStatus status = AuthorityChecker.getTSStatus( AuthorityChecker.checkSystemPermission( - userName, PrivilegeType.MANAGE_DATABASE.ordinal()), + userName, PrivilegeType.MANAGE_DATABASE), PrivilegeType.MANAGE_DATABASE); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { throw new RuntimeException( @@ -355,8 +355,7 @@ private void createDatabaseAndUpdateCache(final String database, final String us if (!AuthorityChecker.SUPER_USER.equals(userName)) { final TSStatus status = AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission( - userName, PrivilegeType.MANAGE_DATABASE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_DATABASE), PrivilegeType.MANAGE_DATABASE); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { throw new RuntimeException(new IoTDBException(status.getMessage(), status.getCode())); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/TreeSchemaAutoCreatorAndVerifier.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/TreeSchemaAutoCreatorAndVerifier.java index 3348a7f83f35..25604976c590 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/TreeSchemaAutoCreatorAndVerifier.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/analyze/load/TreeSchemaAutoCreatorAndVerifier.java @@ -159,7 +159,7 @@ public void autoCreateAndVerify( status = AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, paths, PrivilegeType.WRITE_DATA.ordinal()), + userName, paths, PrivilegeType.WRITE_DATA), paths, PrivilegeType.WRITE_DATA); } catch (IllegalPathException e) { 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 83b9f1d389cd..076807a6c0bd 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 @@ -204,8 +204,7 @@ void autoExtendTemplate( if (!AuthorityChecker.SUPER_USER.equals(userName)) { TSStatus status = AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission( - userName, PrivilegeType.EXTEND_TEMPLATE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.EXTEND_TEMPLATE), PrivilegeType.EXTEND_TEMPLATE); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { throw new RuntimeException(new IoTDBException(status.getMessage(), status.getCode())); @@ -226,8 +225,7 @@ void autoExtendTemplate( if (!AuthorityChecker.SUPER_USER.equals(userName)) { TSStatus status = AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission( - userName, PrivilegeType.EXTEND_TEMPLATE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.EXTEND_TEMPLATE), PrivilegeType.EXTEND_TEMPLATE); if (status.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { throw new RuntimeException(new IoTDBException(status.getMessage(), status.getCode())); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java index 86b355f929b4..18dd15c3ae2f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java @@ -21,6 +21,7 @@ import org.apache.iotdb.common.rpc.thrift.Model; import org.apache.iotdb.commons.exception.IllegalPathException; +import org.apache.iotdb.commons.exception.auth.AccessDeniedException; import org.apache.iotdb.commons.executable.ExecutableManager; import org.apache.iotdb.commons.pipe.config.constant.SystemConstant; import org.apache.iotdb.commons.schema.table.TsTable; @@ -57,6 +58,7 @@ import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.DescribeTableTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.DropDBTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.DropTableTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.RelationalAuthorizerTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowAINodesTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowConfigNodesTask; import org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational.ShowDBTask; @@ -121,6 +123,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Node; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Property; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.QualifiedName; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveDataNode; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameTable; @@ -167,7 +170,6 @@ import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.Pair; -import java.security.AccessControlException; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -322,7 +324,7 @@ protected IConfigTask visitShowDB(final ShowDB node, final MPPQueryContext conte accessControl.checkCanShowOrUseDatabase( context.getSession().getUserName(), databaseName); return true; - } catch (final AccessControlException e) { + } catch (final AccessDeniedException e) { return false; } }); @@ -676,7 +678,7 @@ protected IConfigTask visitShowTables(final ShowTables node, final MPPQueryConte context.getSession().getUserName(), new QualifiedObjectName(finalDatabase, tableName)); return true; - } catch (final AccessControlException e) { + } catch (final AccessDeniedException e) { return false; } }; @@ -952,6 +954,15 @@ protected IConfigTask visitShowCurrentTimestamp( return new ShowCurrentTimestampTask(); } + @Override + protected IConfigTask visitRelationalAuthorPlan( + RelationalAuthorStatement node, MPPQueryContext context) { + accessControl.checkUserCanRunRelationalAuthorStatement( + context.getSession().getUserName(), node); + context.setQueryType(node.getQueryType()); + return new RelationalAuthorizerTask(node); + } + @Override protected IConfigTask visitKillQuery(KillQuery node, MPPQueryContext context) { context.setQueryType(QueryType.WRITE); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/RelationalAuthorizerTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/RelationalAuthorizerTask.java new file mode 100644 index 000000000000..a3f8fbd92b16 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/RelationalAuthorizerTask.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.db.queryengine.plan.execution.config.metadata.relational; + +import org.apache.iotdb.db.auth.AuthorityChecker; +import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; +import org.apache.iotdb.db.queryengine.plan.execution.config.ConfigTaskResult; +import org.apache.iotdb.db.queryengine.plan.execution.config.IConfigTask; +import org.apache.iotdb.db.queryengine.plan.execution.config.executor.IConfigTaskExecutor; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; + +import com.google.common.util.concurrent.ListenableFuture; + +public class RelationalAuthorizerTask implements IConfigTask { + private final RelationalAuthorStatement statement; + + public RelationalAuthorizerTask(RelationalAuthorStatement statement) { + this.statement = statement; + } + + @Override + public ListenableFuture execute(IConfigTaskExecutor configTaskExecutor) { + if (statement.getQueryType() == QueryType.WRITE) { + return AuthorityChecker.operatePermission(statement); + } else { + return AuthorityChecker.queryPermission(statement); + } + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java index d60faf12d3f6..36dda4558146 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/parser/ASTVisitor.java @@ -2491,7 +2491,7 @@ private void checkGrantRevokePrivileges(String[] privileges, List n if ("ALL".equalsIgnoreCase(privilege) || (!"READ".equalsIgnoreCase(privilege) && !"WRITE".equalsIgnoreCase(privilege) - && !PrivilegeType.valueOf(privilege.toUpperCase()).isPathRelevant())) { + && !PrivilegeType.valueOf(privilege.toUpperCase()).isPathPrivilege())) { hasSystemPri = true; errorPrivilegeName = privilege.toUpperCase(); break; diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/view/AlterLogicalViewNode.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/view/AlterLogicalViewNode.java index 1c8f2645a016..81581c48d29f 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/view/AlterLogicalViewNode.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/planner/plan/node/metadata/write/view/AlterLogicalViewNode.java @@ -71,7 +71,7 @@ public TSStatus checkPermissionBeforeProcess(final String userName) { final List targetPathList = new ArrayList<>(viewPathToSourceMap.keySet()); return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, targetPathList, PrivilegeType.WRITE_SCHEMA.ordinal()), + userName, targetPathList, PrivilegeType.WRITE_SCHEMA), targetPathList, PrivilegeType.WRITE_SCHEMA); } 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 6b9dcf457b9b..ad6494ab582b 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,6 +21,7 @@ import org.apache.iotdb.commons.exception.auth.AccessDeniedException; import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; public interface AccessControl { @@ -130,4 +131,13 @@ public interface AccessControl { * @throws AccessDeniedException if not allowed */ void checkUserHasMaintainPrivilege(String userName); + + /** + * Check if user can run relational author statement. + * + * @param userName name of user + * @throws AccessDeniedException if not allowed + */ + void checkUserCanRunRelationalAuthorStatement( + String userName, RelationalAuthorStatement statement); } 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 009db9967a88..fd9fd8012f25 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 @@ -19,7 +19,14 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; +import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.exception.IoTDBException; +import org.apache.iotdb.db.auth.AuthorityChecker; import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; +import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType; +import org.apache.iotdb.rpc.TSStatusCode; public class AccessControlImpl implements AccessControl { @@ -88,4 +95,214 @@ public void checkCanShowOrDescTable(String userName, QualifiedObjectName tableNa public void checkUserHasMaintainPrivilege(String userName) { authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MAINTAIN); } + + @Override + public void checkUserCanRunRelationalAuthorStatement( + String userName, RelationalAuthorStatement statement) { + AuthorRType type = statement.getAuthorType(); + TSStatus status; + switch (type) { + case CREATE_USER: + // admin cannot be created. + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName())) { + status = + AuthorityChecker.getTSStatus( + false, "Cannot create user has same name with admin user"); + throw new RuntimeException(new IoTDBException(status.getMessage(), status.getCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_USER); + return; + case DROP_USER: + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName()) + || statement.getUserName().equals(userName)) { + status = AuthorityChecker.getTSStatus(false, "Cannot drop admin user or yourself"); + throw new RuntimeException(new IoTDBException(status.getMessage(), status.getCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_USER); + return; + case UPDATE_USER: + case LIST_USER_PRIV: + if (AuthorityChecker.SUPER_USER.equals(userName) + || statement.getUserName().equals(userName)) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_USER); + return; + case LIST_USER: + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_USER); + return; + case CREATE_ROLE: + if (AuthorityChecker.SUPER_USER.equals(statement.getRoleName())) { + throw new RuntimeException( + new IoTDBException( + "Cannot create role has same name with admin user", + TSStatusCode.NO_PERMISSION.getStatusCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE); + return; + + case DROP_ROLE: + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName())) { + throw new RuntimeException( + new IoTDBException( + "Cannot drop role with admin name", TSStatusCode.NO_PERMISSION.getStatusCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE); + return; + + case GRANT_USER_ROLE: + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName())) { + throw new RuntimeException( + new IoTDBException( + "Cannot grant role to admin", TSStatusCode.NO_PERMISSION.getStatusCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE); + return; + + case REVOKE_USER_ROLE: + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName())) { + throw new RuntimeException( + new IoTDBException( + "Cannot revoke role from admin", TSStatusCode.NO_PERMISSION.getStatusCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE); + return; + case LIST_ROLE: + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE); + return; + case LIST_ROLE_PRIV: + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + if (AuthorityChecker.checkRole(userName, statement.getRoleName())) { + return; + } + authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MANAGE_ROLE); + return; + case GRANT_ROLE_ANY: + case GRANT_USER_ANY: + case REVOKE_ROLE_ANY: + case REVOKE_USER_ANY: + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName())) { + throw new RuntimeException( + new IoTDBException( + "Cannot grant/revoke privileges to/from admin", + TSStatusCode.NO_PERMISSION.getStatusCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + for (PrivilegeType privilegeType : statement.getPrivilegeTypes()) { + authChecker.checkAnyScopePrivilegeGrantOption( + userName, TableModelPrivilege.getTableModelType(privilegeType)); + } + return; + case GRANT_ROLE_ALL: + case REVOKE_ROLE_ALL: + case GRANT_USER_ALL: + case REVOKE_USER_ALL: + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName())) { + throw new RuntimeException( + new IoTDBException( + "Cannot grant/revoke all privileges to/from admin", + TSStatusCode.NO_PERMISSION.getStatusCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + for (PrivilegeType privilegeType : statement.getPrivilegeTypes()) { + if (privilegeType.isRelationalPrivilege()) { + AuthorityChecker.checkAnyScopePermissionGrantOption(userName, privilegeType); + } + if (privilegeType.forRelationalSys()) { + AuthorityChecker.checkSystemPermissionGrantOption(userName, privilegeType); + } + } + return; + case GRANT_USER_DB: + case GRANT_ROLE_DB: + case REVOKE_USER_DB: + case REVOKE_ROLE_DB: + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName())) { + throw new RuntimeException( + new IoTDBException( + "Cannot grant/revoke privileges of admin user", + TSStatusCode.NO_PERMISSION.getStatusCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + for (PrivilegeType privilegeType : statement.getPrivilegeTypes()) { + authChecker.checkDatabasePrivilegeGrantOption( + userName, + statement.getDatabase(), + TableModelPrivilege.getTableModelType(privilegeType)); + } + return; + case GRANT_USER_TB: + case GRANT_ROLE_TB: + case REVOKE_USER_TB: + case REVOKE_ROLE_TB: + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName())) { + throw new RuntimeException( + new IoTDBException( + "Cannot grant/revoke privileges of admin user", + TSStatusCode.NO_PERMISSION.getStatusCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + for (PrivilegeType privilegeType : statement.getPrivilegeTypes()) { + authChecker.checkTablePrivilegeGrantOption( + userName, + new QualifiedObjectName(statement.getDatabase(), statement.getTableName()), + TableModelPrivilege.getTableModelType(privilegeType)); + } + return; + + case GRANT_USER_SYS: + case GRANT_ROLE_SYS: + case REVOKE_USER_SYS: + case REVOKE_ROLE_SYS: + if (AuthorityChecker.SUPER_USER.equals(statement.getUserName())) { + throw new RuntimeException( + new IoTDBException( + "Cannot grant/revoke privileges of admin user", + TSStatusCode.NO_PERMISSION.getStatusCode())); + } + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + for (PrivilegeType privilegeType : statement.getPrivilegeTypes()) { + authChecker.checkGlobalPrivilegeGrantOption( + userName, TableModelPrivilege.getTableModelType(privilegeType)); + } + default: + break; + } + } } 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 99cb78b7a92c..cea0393c7dcc 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 @@ -20,6 +20,7 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; public class AllowAllAccessControl implements AccessControl { @Override @@ -81,4 +82,10 @@ public void checkCanShowOrDescTable(String userName, QualifiedObjectName tableNa public void checkUserHasMaintainPrivilege(String userName) { // allow anything } + + @Override + public void checkUserCanRunRelationalAuthorStatement( + String userName, RelationalAuthorStatement statement) { + // allow anything + } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java index 490c76622741..bd32c7a0b75e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java @@ -44,6 +44,9 @@ public interface ITableAuthChecker { */ void checkDatabasePrivilege(String userName, String databaseName, TableModelPrivilege privilege); + void checkDatabasePrivilegeGrantOption( + String userName, String databaseName, TableModelPrivilege privilege); + /** * Check if user has specified privilege on the specified table or bigger scope (like database or * ANY). @@ -56,6 +59,9 @@ public interface ITableAuthChecker { void checkTablePrivilege( String userName, QualifiedObjectName tableName, TableModelPrivilege privilege); + void checkTablePrivilegeGrantOption( + String userName, QualifiedObjectName tableName, TableModelPrivilege privilege); + /** * Check if user has any privilege on the specified table or bigger scope (like database or ANY). * @@ -73,4 +79,15 @@ void checkTablePrivilege( * @throws AccessDeniedException if not allowed */ void checkGlobalPrivilege(String userName, TableModelPrivilege privilege); + + /** + * Check if user has the specified global privilege + * + * @param userName name of user + * @param privilege specified global privilege to be checked + * @throws AccessDeniedException if not allowed + */ + void checkGlobalPrivilegeGrantOption(String userName, TableModelPrivilege privilege); + + void checkAnyScopePrivilegeGrantOption(String userName, TableModelPrivilege privilege); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java new file mode 100644 index 000000000000..90b20c0664c7 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthCheckerImpl.java @@ -0,0 +1,169 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.db.queryengine.plan.relational.security; + +import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.commons.exception.auth.AccessDeniedException; +import org.apache.iotdb.db.auth.AuthorityChecker; +import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; +import org.apache.iotdb.rpc.TSStatusCode; + +public class ITableAuthCheckerImpl implements ITableAuthChecker { + + @Override + public void checkDatabaseVisibility(String userName, String databaseName) { + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + if (!AuthorityChecker.checkDBVisible(userName, databaseName)) { + throw new AccessDeniedException("DATABASE " + databaseName); + } + } + + @Override + public void checkDatabasePrivilege( + String userName, String databaseName, TableModelPrivilege privilege) { + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + TSStatus result = + AuthorityChecker.getTSStatus( + AuthorityChecker.checkDBPermission( + userName, databaseName, privilege.getPrivilegeType()), + privilege.getPrivilegeType(), + databaseName); + if (result.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new AccessDeniedException(result.getMessage()); + } + } + + @Override + public void checkDatabasePrivilegeGrantOption( + String userName, String databaseName, TableModelPrivilege privilege) { + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + TSStatus result = + AuthorityChecker.getGrantOptTSStatus( + AuthorityChecker.checkDBPermissionGrantOption( + userName, databaseName, privilege.getPrivilegeType()), + privilege.getPrivilegeType(), + databaseName); + if (result.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new AccessDeniedException(result.getMessage()); + } + } + + @Override + public void checkTablePrivilege( + String userName, QualifiedObjectName tableName, TableModelPrivilege privilege) { + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + TSStatus result = + AuthorityChecker.getTSStatus( + AuthorityChecker.checkTablePermission( + userName, + tableName.getDatabaseName(), + tableName.getObjectName(), + privilege.getPrivilegeType()), + privilege.getPrivilegeType(), + tableName.getDatabaseName(), + tableName.getObjectName()); + if (result.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new AccessDeniedException(result.getMessage()); + } + } + + @Override + public void checkTablePrivilegeGrantOption( + String userName, QualifiedObjectName tableName, TableModelPrivilege privilege) { + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + TSStatus result = + AuthorityChecker.getGrantOptTSStatus( + AuthorityChecker.checkTablePermissionGrantOption( + userName, + tableName.getDatabaseName(), + tableName.getObjectName(), + privilege.getPrivilegeType()), + privilege.getPrivilegeType(), + tableName.getDatabaseName(), + tableName.getObjectName()); + if (result.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new AccessDeniedException(result.getMessage()); + } + } + + @Override + public void checkTableVisibility(String userName, QualifiedObjectName tableName) { + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + if (!AuthorityChecker.checkTableVisible( + userName, tableName.getDatabaseName(), tableName.getObjectName())) { + throw new AccessDeniedException("TABLE " + tableName); + } + } + + @Override + public void checkGlobalPrivilege(String userName, TableModelPrivilege privilege) { + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + TSStatus result = + AuthorityChecker.getTSStatus( + AuthorityChecker.checkSystemPermission(userName, privilege.getPrivilegeType()), + privilege.getPrivilegeType()); + if (result.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new AccessDeniedException(result.getMessage()); + } + } + + @Override + public void checkGlobalPrivilegeGrantOption(String userName, TableModelPrivilege privilege) { + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + TSStatus result = + AuthorityChecker.getGrantOptTSStatus( + AuthorityChecker.checkSystemPermissionGrantOption( + userName, privilege.getPrivilegeType()), + privilege.getPrivilegeType()); + if (result.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new AccessDeniedException(result.getMessage()); + } + } + + @Override + public void checkAnyScopePrivilegeGrantOption(String userName, TableModelPrivilege privilege) { + if (AuthorityChecker.SUPER_USER.equals(userName)) { + return; + } + TSStatus result = + AuthorityChecker.getGrantOptTSStatus( + AuthorityChecker.checkAnyScopePermissionGrantOption( + userName, privilege.getPrivilegeType()), + privilege.getPrivilegeType()); + if (result.getCode() != TSStatusCode.SUCCESS_STATUS.getStatusCode()) { + throw new AccessDeniedException(result.getMessage()); + } + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TableModelPrivilege.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TableModelPrivilege.java index 1c9991aa234f..2fd59af8b6d6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TableModelPrivilege.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TableModelPrivilege.java @@ -19,6 +19,8 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; + public enum TableModelPrivilege { // global privilege MANAGE_USER, @@ -31,5 +33,55 @@ public enum TableModelPrivilege { ALTER, SELECT, INSERT, - DELETE, + DELETE; + + PrivilegeType getPrivilegeType() { + switch (this) { + case MANAGE_ROLE: + return PrivilegeType.MANAGE_ROLE; + case MANAGE_USER: + return PrivilegeType.MANAGE_USER; + case MAINTAIN: + return PrivilegeType.MAINTAIN; + case CREATE: + return PrivilegeType.CREATE; + case DROP: + return PrivilegeType.DROP; + case ALTER: + return PrivilegeType.ALTER; + case SELECT: + return PrivilegeType.SELECT; + case INSERT: + return PrivilegeType.INSERT; + case DELETE: + return PrivilegeType.DELETE; + default: + throw new IllegalStateException("Unexpected value:" + this); + } + } + + public static TableModelPrivilege getTableModelType(PrivilegeType privilegeType) { + switch (privilegeType) { + case MANAGE_ROLE: + return TableModelPrivilege.MANAGE_ROLE; + case MANAGE_USER: + return TableModelPrivilege.MANAGE_USER; + case MAINTAIN: + return TableModelPrivilege.MAINTAIN; + case CREATE: + return TableModelPrivilege.CREATE; + case DROP: + return TableModelPrivilege.DROP; + case ALTER: + return TableModelPrivilege.ALTER; + case SELECT: + return TableModelPrivilege.SELECT; + case INSERT: + return TableModelPrivilege.INSERT; + case DELETE: + return TableModelPrivilege.DELETE; + default: + throw new IllegalStateException("Unexpected value:" + privilegeType); + } + } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java index 3d52bb87d163..ed67e02098de 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/AstVisitor.java @@ -632,4 +632,8 @@ protected R visitCountStatement(CountStatement node, C context) { protected R visitKillQuery(KillQuery node, C context) { return visitStatement(node, context); } + + protected R visitRelationalAuthorPlan(RelationalAuthorStatement node, C context) { + return visitStatement(node, context); + } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java new file mode 100644 index 000000000000..71c1962f2eaa --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/ast/RelationalAuthorStatement.java @@ -0,0 +1,247 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.db.queryengine.plan.relational.sql.ast; + +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.db.queryengine.plan.analyze.QueryType; +import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType; + +import com.google.common.collect.ImmutableList; + +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; +import java.util.stream.Collectors; + +public class RelationalAuthorStatement extends Statement { + + private final AuthorRType authorType; + + private String tableName; + private String database; + private String userName; + private String roleName; + + private String password; + + private Set privilegeType; + + private boolean grantOption; + + public RelationalAuthorStatement( + AuthorRType authorType, + String userName, + String roleName, + String database, + String table, + Set type, + boolean grantOption, + String password) { + super(null); + this.authorType = authorType; + this.database = database; + this.tableName = table; + this.privilegeType = type; + this.roleName = roleName; + this.userName = userName; + this.grantOption = grantOption; + this.password = password; + } + + public RelationalAuthorStatement(AuthorRType statementType) { + super(null); + this.authorType = statementType; + } + + public RelationalAuthorStatement( + AuthorRType statementType, + Set type, + String userName, + String roleName, + boolean grantOption) { + super(null); + this.authorType = statementType; + this.privilegeType = type; + this.userName = userName; + this.roleName = roleName; + this.grantOption = grantOption; + this.tableName = null; + this.database = null; + this.password = null; + } + + public RelationalAuthorStatement( + AuthorRType statementType, String userName, String roleName, boolean grantOption) { + super(null); + this.authorType = statementType; + this.userName = userName; + this.roleName = roleName; + this.grantOption = grantOption; + } + + public AuthorRType getAuthorType() { + return authorType; + } + + public String getTableName() { + return tableName; + } + + public String getDatabase() { + return this.database; + } + + public String getUserName() { + return userName; + } + + public String getRoleName() { + return roleName; + } + + public String getPassword() { + return this.password; + } + + public boolean isGrantOption() { + return grantOption; + } + + public Set getPrivilegeTypes() { + return privilegeType; + } + + public String getPrivilegesString() { + return privilegeType.stream().map(Enum::name).collect(Collectors.joining(",")); + } + + public Set getPrivilegeIds() { + Set privilegeIds = new HashSet<>(); + for (PrivilegeType privilegeType : privilegeType) { + privilegeIds.add(privilegeType.ordinal()); + } + return privilegeIds; + } + + public void setDatabase(String database) { + this.database = database; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public void setPassword(String password) { + this.password = password; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + RelationalAuthorStatement that = (RelationalAuthorStatement) o; + return grantOption == that.grantOption + && authorType == that.authorType + && Objects.equals(database, that.database) + && Objects.equals(tableName, that.tableName) + && Objects.equals(userName, that.userName) + && Objects.equals(roleName, that.roleName) + && Objects.equals(privilegeType, that.privilegeType) + && Objects.equals(password, that.password); + } + + @Override + public R accept(final AstVisitor visitor, final C context) { + return visitor.visitRelationalAuthorPlan(this, context); + } + + @Override + public List getChildren() { + return ImmutableList.of(); + } + + @Override + public int hashCode() { + return Objects.hash( + authorType, database, tableName, privilegeType, userName, roleName, grantOption, password); + } + + public QueryType getQueryType() { + switch (this.authorType) { + case CREATE_ROLE: + case CREATE_USER: + case DROP_ROLE: + case DROP_USER: + case UPDATE_USER: + case GRANT_ROLE_ANY: + case GRANT_USER_ANY: + case GRANT_ROLE_ALL: + case GRANT_USER_ALL: + case GRANT_ROLE_DB: + case GRANT_USER_DB: + case GRANT_ROLE_TB: + case GRANT_USER_TB: + case GRANT_USER_ROLE: + case GRANT_USER_SYS: + case GRANT_ROLE_SYS: + case REVOKE_ROLE_DB: + case REVOKE_USER_DB: + case REVOKE_ROLE_TB: + case REVOKE_USER_TB: + case REVOKE_ROLE_ANY: + case REVOKE_USER_ANY: + case REVOKE_ROLE_ALL: + case REVOKE_USER_ALL: + case REVOKE_ROLE_SYS: + case REVOKE_USER_SYS: + case REVOKE_USER_ROLE: + return QueryType.WRITE; + case LIST_ROLE: + case LIST_USER: + case LIST_ROLE_PRIV: + case LIST_USER_PRIV: + return QueryType.READ; + default: + throw new IllegalArgumentException("Unknown authorType:" + this.authorType); + } + } + + @Override + public String toString() { + return "auth statement: " + + authorType + + "to Database: " + + database + + "." + + tableName + + ", user name: " + + userName + + ", role name: " + + roleName + + ", privileges:" + + privilegeType + + ", grantOption:" + + grantOption; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java index 1cca371cc2c2..27465268674c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AstBuilder.java @@ -20,6 +20,7 @@ package org.apache.iotdb.db.queryengine.plan.relational.sql.parser; import org.apache.iotdb.common.rpc.thrift.TConsensusGroupType; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.schema.cache.CacheClearOptions; @@ -124,6 +125,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.QueryBody; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.QuerySpecification; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Relation; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RemoveDataNode; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameTable; @@ -180,6 +182,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.With; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.WithQuery; import org.apache.iotdb.db.queryengine.plan.relational.sql.util.AstUtil; +import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType; import org.apache.iotdb.db.queryengine.plan.statement.StatementType; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowStatement; import org.apache.iotdb.db.queryengine.plan.statement.crud.InsertRowsStatement; @@ -1342,6 +1345,263 @@ public Node visitExplainAnalyze(RelationalSqlParser.ExplainAnalyzeContext ctx) { getLocation(ctx), ctx.VERBOSE() != null, (Statement) visit(ctx.query())); } + // ********************** author expressions ******************** + + private String stripQuotes(String text) { + if (text != null && text.length() >= 2 && text.startsWith("'") && text.endsWith("'")) { + return text.substring(1, text.length() - 1).replace("''", "'"); + } + return text; + } + + @Override + public Node visitCreateUserStatement(RelationalSqlParser.CreateUserStatementContext ctx) { + RelationalAuthorStatement stmt = new RelationalAuthorStatement(AuthorRType.CREATE_USER); + stmt.setUserName(ctx.userName.getText()); + stmt.setPassword(stripQuotes(ctx.password.getText())); + return stmt; + } + + @Override + public Node visitCreateRoleStatement(RelationalSqlParser.CreateRoleStatementContext ctx) { + RelationalAuthorStatement stmt = new RelationalAuthorStatement(AuthorRType.CREATE_ROLE); + stmt.setRoleName(ctx.roleName.getText()); + return stmt; + } + + @Override + public Node visitDropUserStatement(RelationalSqlParser.DropUserStatementContext ctx) { + RelationalAuthorStatement stmt = new RelationalAuthorStatement(AuthorRType.DROP_USER); + stmt.setUserName(ctx.userName.getText()); + return stmt; + } + + @Override + public Node visitDropRoleStatement(RelationalSqlParser.DropRoleStatementContext ctx) { + RelationalAuthorStatement stmt = new RelationalAuthorStatement(AuthorRType.DROP_ROLE); + stmt.setRoleName(ctx.roleName.getText()); + return stmt; + } + + @Override + public Node visitAlterUserStatement(RelationalSqlParser.AlterUserStatementContext ctx) { + RelationalAuthorStatement stmt = new RelationalAuthorStatement(AuthorRType.UPDATE_USER); + stmt.setUserName(ctx.userName.getText()); + stmt.setPassword(stripQuotes(ctx.password.getText())); + return stmt; + } + + @Override + public Node visitGrantUserRoleStatement(RelationalSqlParser.GrantUserRoleStatementContext ctx) { + RelationalAuthorStatement stmt = new RelationalAuthorStatement(AuthorRType.GRANT_USER_ROLE); + stmt.setUserName(ctx.userName.getText()); + stmt.setRoleName(ctx.roleName.getText()); + return stmt; + } + + @Override + public Node visitRevokeUserRoleStatement(RelationalSqlParser.RevokeUserRoleStatementContext ctx) { + RelationalAuthorStatement stmt = new RelationalAuthorStatement(AuthorRType.REVOKE_USER_ROLE); + stmt.setUserName(ctx.userName.getText()); + stmt.setRoleName(ctx.roleName.getText()); + return stmt; + } + + @Override + public Node visitListUserPrivilegeStatement( + RelationalSqlParser.ListUserPrivilegeStatementContext ctx) { + RelationalAuthorStatement stmt = new RelationalAuthorStatement(AuthorRType.LIST_USER_PRIV); + stmt.setUserName(ctx.userName.getText()); + return stmt; + } + + @Override + public Node visitListRolePrivilegeStatement( + RelationalSqlParser.ListRolePrivilegeStatementContext ctx) { + RelationalAuthorStatement stmt = new RelationalAuthorStatement(AuthorRType.LIST_ROLE_PRIV); + stmt.setRoleName(ctx.roleName.getText()); + return stmt; + } + + @Override + public Node visitListUserStatement(RelationalSqlParser.ListUserStatementContext ctx) { + return new RelationalAuthorStatement(AuthorRType.LIST_USER); + } + + @Override + public Node visitListRoleStatement(RelationalSqlParser.ListRoleStatementContext ctx) { + return new RelationalAuthorStatement(AuthorRType.LIST_ROLE); + } + + private Set parseSystemPrivilege(RelationalSqlParser.SystemPrivilegesContext ctx) { + List privilegeContexts = ctx.systemPrivilege(); + Set privileges = new HashSet<>(); + for (RelationalSqlParser.SystemPrivilegeContext privilege : privilegeContexts) { + privileges.add(PrivilegeType.valueOf(privilege.getText().toUpperCase())); + } + return privileges; + } + + private Set parseRelationalPrivilege( + RelationalSqlParser.ObjectPrivilegesContext ctx) { + List privilegeContexts = ctx.objectPrivilege(); + Set privileges = new HashSet<>(); + for (RelationalSqlParser.ObjectPrivilegeContext privilege : privilegeContexts) { + privileges.add(PrivilegeType.valueOf(privilege.getText().toUpperCase())); + } + return privileges; + } + + @Override + public Node visitGrantStatement(RelationalSqlParser.GrantStatementContext ctx) { + boolean toUser; + String name; + toUser = ctx.holderType().getText().equalsIgnoreCase("user"); + name = ctx.holderName.getText(); + boolean grantOption = ctx.grantOpt() != null; + boolean toTable; + Set privileges; + // SYSTEM PRIVILEGES OR ALL PRIVILEGES + if (ctx.privilegeObjectScope().ON() == null) { + if (ctx.privilegeObjectScope().ALL() != null) { + return new RelationalAuthorStatement( + toUser ? AuthorRType.GRANT_USER_ALL : AuthorRType.GRANT_ROLE_ALL, + toUser ? name : "", + toUser ? "" : name, + grantOption); + } else { + privileges = parseSystemPrivilege(ctx.privilegeObjectScope().systemPrivileges()); + return new RelationalAuthorStatement( + toUser ? AuthorRType.GRANT_USER_SYS : AuthorRType.GRANT_ROLE_SYS, + privileges, + toUser ? name : "", + toUser ? "" : name, + grantOption); + } + } else { + privileges = parseRelationalPrivilege(ctx.privilegeObjectScope().objectPrivileges()); + // ON TABLE / DB + if (ctx.privilegeObjectScope().objectType() != null) { + toTable = ctx.privilegeObjectScope().objectType().getText().equalsIgnoreCase("table"); + String databaseName = ""; + if (toTable) { + databaseName = clientSession.getDatabaseName(); + if (databaseName == null) { + throw new SemanticException("Database is not set yet."); + } + } + String obj = ctx.privilegeObjectScope().objectName.getText(); + return new RelationalAuthorStatement( + toUser + ? toTable ? AuthorRType.GRANT_USER_TB : AuthorRType.GRANT_USER_DB + : toTable ? AuthorRType.GRANT_ROLE_TB : AuthorRType.GRANT_ROLE_DB, + toUser ? name : "", + toUser ? "" : name, + toTable ? databaseName.toLowerCase() : obj.toLowerCase(), + toTable ? obj.toLowerCase() : "", + privileges, + grantOption, + null); + } else if (ctx.privilegeObjectScope().objectScope() != null) { + String db = ctx.privilegeObjectScope().objectScope().dbname.getText().toLowerCase(); + String tb = ctx.privilegeObjectScope().objectScope().tbname.getText().toLowerCase(); + return new RelationalAuthorStatement( + toUser ? AuthorRType.GRANT_USER_TB : AuthorRType.GRANT_ROLE_TB, + toUser ? name : "", + toUser ? "" : name, + db, + tb, + privileges, + grantOption, + null); + } else if (ctx.privilegeObjectScope().ANY() != null) { + return new RelationalAuthorStatement( + toUser ? AuthorRType.GRANT_USER_ANY : AuthorRType.GRANT_ROLE_ANY, + privileges, + toUser ? name : "", + toUser ? "" : name, + grantOption); + } + } + // will not get here. + throw new SemanticException("author statement parser error"); + } + + public Node visitRevokeStatement(RelationalSqlParser.RevokeStatementContext ctx) { + boolean fromUser; + String name; + fromUser = ctx.holderType().getText().equalsIgnoreCase("user"); + name = ctx.holderName.getText(); + boolean grantOption = ctx.revokeGrantOpt() != null; + boolean fromTable; + Set privileges = new HashSet<>(); + + // SYSTEM PRIVILEGES OR ALL PRIVILEGES + if (ctx.privilegeObjectScope().ON() == null) { + if (ctx.privilegeObjectScope().ALL() != null) { + return new RelationalAuthorStatement( + fromUser ? AuthorRType.REVOKE_USER_ALL : AuthorRType.REVOKE_ROLE_ALL, + fromUser ? name : "", + fromUser ? "" : name, + grantOption); + } else { + privileges = parseSystemPrivilege(ctx.privilegeObjectScope().systemPrivileges()); + return new RelationalAuthorStatement( + fromUser ? AuthorRType.REVOKE_USER_SYS : AuthorRType.REVOKE_ROLE_SYS, + privileges, + fromUser ? name : "", + fromUser ? "" : name, + grantOption); + } + } else { + privileges = parseRelationalPrivilege(ctx.privilegeObjectScope().objectPrivileges()); + // ON TABLE / DB + if (ctx.privilegeObjectScope().objectType() != null) { + fromTable = ctx.privilegeObjectScope().objectType().getText().equalsIgnoreCase("table"); + String databaseName = ""; + if (fromTable) { + databaseName = clientSession.getDatabaseName(); + if (databaseName == null) { + throw new SemanticException("Database is not set yet."); + } + } + String obj = ctx.privilegeObjectScope().objectName.getText(); + return new RelationalAuthorStatement( + fromUser + ? fromTable ? AuthorRType.REVOKE_USER_TB : AuthorRType.REVOKE_USER_DB + : fromTable ? AuthorRType.REVOKE_ROLE_TB : AuthorRType.REVOKE_ROLE_DB, + fromUser ? name : "", + fromUser ? "" : name, + fromTable ? databaseName.toLowerCase() : obj.toLowerCase(), + fromTable ? obj.toLowerCase() : "", + privileges, + grantOption, + null); + } else if (ctx.privilegeObjectScope().objectScope() != null) { + String db = ctx.privilegeObjectScope().objectScope().dbname.getText().toLowerCase(); + String tb = ctx.privilegeObjectScope().objectScope().tbname.getText().toLowerCase(); + return new RelationalAuthorStatement( + fromUser ? AuthorRType.REVOKE_USER_TB : AuthorRType.REVOKE_ROLE_TB, + fromUser ? name : "", + fromUser ? "" : name, + db, + tb, + privileges, + grantOption, + null); + } else if (ctx.privilegeObjectScope().ANY() != null) { + return new RelationalAuthorStatement( + fromUser ? AuthorRType.REVOKE_USER_ANY : AuthorRType.REVOKE_ROLE_ANY, + privileges, + fromUser ? name : "", + fromUser ? "" : name, + grantOption); + } + } + // will not get here. + throw new SemanticException("author statement parser error"); + } + // ********************** query expressions ******************** @Override public Node visitQuery(RelationalSqlParser.QueryContext ctx) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java index 98c5a048809a..7eb19c5b7da5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/sql/util/SqlFormatter.java @@ -63,6 +63,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Query; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.QuerySpecification; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Relation; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RenameTable; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Row; @@ -1169,6 +1170,228 @@ protected Void visitShowTopics(ShowTopics node, Integer context) { return null; } + @Override + protected Void visitRelationalAuthorPlan(RelationalAuthorStatement node, Integer context) { + switch (node.getAuthorType()) { + case GRANT_USER_ANY: + builder.append( + "GRANT " + + node.getPrivilegesString() + + " ON ANY" + + " TO USER " + + node.getUserName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case GRANT_USER_ALL: + builder.append( + "GRANT ALL TO USER " + + node.getUserName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case GRANT_USER_DB: + builder.append( + "GRANT " + + node.getPrivilegesString() + + " ON DATABASE " + + node.getDatabase() + + " TO USER " + + node.getUserName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case GRANT_USER_SYS: + builder.append( + "GRANT " + + node.getPrivilegesString() + + " TO USER " + + node.getUserName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case GRANT_USER_TB: + builder.append( + "GRANT " + + node.getPrivilegesString() + + " ON TABLE " + + node.getDatabase() + + "." + + node.getTableName() + + " TO USER " + + node.getUserName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case GRANT_ROLE_ANY: + builder.append( + "GRANT " + + node.getPrivilegesString() + + " ON ANY" + + " TO ROLE " + + node.getRoleName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case GRANT_ROLE_ALL: + builder.append( + "GRANT ALL TO ROLE " + + node.getRoleName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case GRANT_ROLE_DB: + builder.append( + "GRANT " + + node.getPrivilegesString() + + " ON DATABASE " + + node.getDatabase() + + " TO ROLE " + + node.getRoleName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case GRANT_ROLE_SYS: + builder.append( + "GRANT " + + node.getPrivilegesString() + + " TO ROLE " + + node.getRoleName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case GRANT_ROLE_TB: + builder.append( + "GRANT " + + node.getPrivilegesString() + + " ON TABLE " + + node.getDatabase() + + "." + + node.getTableName() + + " TO ROLE " + + node.getRoleName() + + (node.isGrantOption() ? " WITH GRANT OPTION" : "")); + break; + case REVOKE_USER_ANY: + builder.append( + "REVOKE " + + (node.isGrantOption() ? "GRANT OPTION FOR " : "") + + node.getPrivilegesString() + + " ON ANY FROM USER " + + node.getUserName()); + break; + case REVOKE_USER_ALL: + builder.append( + "REVOKE" + + (node.isGrantOption() ? "GRANT OPTION FOR ALL" : "ALL") + + " FROM USER " + + node.getUserName()); + break; + case REVOKE_USER_DB: + builder.append( + "REVOKE " + + (node.isGrantOption() ? "GRANT OPTION FOR " : "") + + node.getPrivilegesString() + + " ON DATABASE " + + node.getDatabase() + + " FROM USER " + + node.getUserName()); + break; + case REVOKE_USER_SYS: + builder.append( + "REVOKE " + + (node.isGrantOption() ? "GRANT OPTION FOR " : "") + + node.getPrivilegesString() + + "FROM USER " + + node.getUserName()); + break; + case REVOKE_USER_TB: + builder.append( + "REVOKE " + + (node.isGrantOption() ? "GRANT OPTION FOR " : "") + + node.getPrivilegesString() + + " ON TABLE " + + node.getDatabase() + + "." + + node.getTableName() + + " FROM USER " + + node.getUserName()); + break; + case REVOKE_ROLE_ANY: + builder.append( + "REVOKE " + + (node.isGrantOption() ? "GRANT OPTION FOR " : "") + + node.getPrivilegesString() + + " ON ANY FROM ROLE " + + node.getRoleName()); + break; + case REVOKE_ROLE_ALL: + builder.append( + "REVOKE " + + (node.isGrantOption() ? "GRANT OPTION FOR ALL" : "ALL") + + " FROM ROLE " + + node.getRoleName()); + break; + case REVOKE_ROLE_DB: + builder.append( + "REVOKE " + + (node.isGrantOption() ? "GRANT OPTION FOR " : "") + + node.getPrivilegesString() + + " ON DATABASE " + + node.getDatabase() + + " FROM ROLE " + + node.getRoleName()); + break; + case REVOKE_ROLE_SYS: + builder.append( + "REVOKE " + + (node.isGrantOption() ? "GRANT OPTION FOR " : "") + + node.getPrivilegesString() + + " FROM ROLE " + + node.getRoleName()); + break; + case REVOKE_ROLE_TB: + builder.append( + "REVOKE " + + (node.isGrantOption() ? "GRANT OPTION FOR " : "") + + node.getPrivilegesString() + + " ON TABLE " + + node.getDatabase() + + "." + + node.getTableName() + + " FROM ROLE " + + node.getRoleName()); + break; + case GRANT_USER_ROLE: + builder.append("GRANT ROLE " + node.getRoleName() + " TO " + node.getUserName()); + break; + case REVOKE_USER_ROLE: + builder.append("REVOKE ROLE " + node.getRoleName() + " FROM " + node.getUserName()); + break; + case CREATE_USER: + builder.append("CREATE USER " + node.getUserName()); + break; + case CREATE_ROLE: + builder.append("CREATE ROLE " + node.getRoleName()); + break; + case UPDATE_USER: + builder.append("ALTER USER " + node.getUserName() + " SET PASSWORD"); + break; + case LIST_USER: + builder.append("LIST USER "); + break; + case LIST_ROLE: + builder.append("LIST ROLE "); + break; + case LIST_USER_PRIV: + builder.append("LIST PRIVILEGES OF USER " + node.getUserName()); + break; + case LIST_ROLE_PRIV: + builder.append("LIST PRIVILEGES OF ROLE " + node.getRoleName()); + break; + case DROP_ROLE: + builder.append("DROP ROLE " + node.getRoleName()); + break; + case DROP_USER: + builder.append("DROP USER " + node.getUserName()); + break; + default: + break; + } + return null; + } + @Override protected Void visitShowSubscriptions(ShowSubscriptions node, Integer context) { if (Objects.isNull(node.getTopicName())) { diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/type/AuthorRType.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/type/AuthorRType.java new file mode 100644 index 000000000000..1cac0e1bacbc --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/type/AuthorRType.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.db.queryengine.plan.relational.type; + +public enum AuthorRType { + CREATE_USER, + CREATE_ROLE, + UPDATE_USER, + DROP_USER, + DROP_ROLE, + GRANT_USER_ROLE, + REVOKE_USER_ROLE, + GRANT_USER_ANY, + GRANT_ROLE_ANY, + GRANT_USER_ALL, + GRANT_ROLE_ALL, + GRANT_USER_DB, + GRANT_USER_TB, + GRANT_ROLE_DB, + GRANT_ROLE_TB, + REVOKE_USER_ANY, + REVOKE_ROLE_ANY, + REVOKE_USER_ALL, + REVOKE_ROLE_ALL, + REVOKE_USER_DB, + REVOKE_USER_TB, + REVOKE_ROLE_DB, + REVOKE_ROLE_TB, + GRANT_USER_SYS, + GRANT_ROLE_SYS, + REVOKE_USER_SYS, + REVOKE_ROLE_SYS, + LIST_USER, + LIST_ROLE, + LIST_USER_PRIV, + LIST_ROLE_PRIV +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/AuthorityInformationStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/AuthorityInformationStatement.java index 3ed7e3bfbbb7..30552ee72695 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/AuthorityInformationStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/AuthorityInformationStatement.java @@ -38,7 +38,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { try { if (!AuthorityChecker.SUPER_USER.equals(userName)) { this.authorityScope = - AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_SCHEMA.ordinal()); + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_SCHEMA); } } catch (AuthException e) { return new TSStatus(e.getCode().getStatusCode()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/DeleteDataStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/DeleteDataStatement.java index c5d6b18d7330..29d48f6cfe12 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/DeleteDataStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/DeleteDataStatement.java @@ -55,8 +55,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { } List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( - AuthorityChecker.checkPatternPermission( - userName, checkedPaths, PrivilegeType.WRITE_DATA.ordinal()), + AuthorityChecker.checkPatternPermission(userName, checkedPaths, PrivilegeType.WRITE_DATA), checkedPaths, PrivilegeType.WRITE_DATA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertBaseStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertBaseStatement.java index 34b73b678fcc..8897ee80ef09 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertBaseStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertBaseStatement.java @@ -180,7 +180,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { List checkedPaths = getPaths().stream().distinct().collect(Collectors.toList()); return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, checkedPaths, PrivilegeType.WRITE_DATA.ordinal()), + userName, checkedPaths, PrivilegeType.WRITE_DATA), checkedPaths, PrivilegeType.WRITE_DATA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertStatement.java index 79d229a6a290..2886aa216282 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/InsertStatement.java @@ -65,7 +65,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, checkedPaths, PrivilegeType.WRITE_DATA.ordinal()), + userName, checkedPaths, PrivilegeType.WRITE_DATA), checkedPaths, PrivilegeType.WRITE_DATA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/QueryStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/QueryStatement.java index 2ff3e8149c89..cf408552f740 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/QueryStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/crud/QueryStatement.java @@ -213,7 +213,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { try { if (!AuthorityChecker.SUPER_USER.equals(userName)) { this.authorityScope = - AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_DATA.ordinal()); + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_DATA); } } catch (AuthException e) { return new TSStatus(e.getCode().getStatusCode()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalBatchActivateTemplateStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalBatchActivateTemplateStatement.java index 4cb8268d1899..2bb8a36b7b47 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalBatchActivateTemplateStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalBatchActivateTemplateStatement.java @@ -78,8 +78,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { } List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( - AuthorityChecker.checkPatternPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + AuthorityChecker.checkPatternPermission(userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalCreateMultiTimeSeriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalCreateMultiTimeSeriesStatement.java index d734a5734ff1..0b695679597c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalCreateMultiTimeSeriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalCreateMultiTimeSeriesStatement.java @@ -68,7 +68,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalCreateTimeSeriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalCreateTimeSeriesStatement.java index 3a3834d3695a..4ee24eb08dcf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalCreateTimeSeriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/internal/InternalCreateTimeSeriesStatement.java @@ -103,7 +103,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/AlterTimeSeriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/AlterTimeSeriesStatement.java index 251ebc1d4614..4f68bc70cd57 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/AlterTimeSeriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/AlterTimeSeriesStatement.java @@ -140,8 +140,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkFullPathPermission( - userName, path, PrivilegeType.WRITE_SCHEMA.ordinal()), + AuthorityChecker.checkFullPathPermission(userName, path, PrivilegeType.WRITE_SCHEMA), PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountDevicesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountDevicesStatement.java index 9b73e1f9b0a5..05d4c6384a23 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountDevicesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountDevicesStatement.java @@ -56,10 +56,8 @@ public TSStatus checkPermissionBeforeProcess(String userName) { if (!AuthorityChecker.SUPER_USER.equals(userName)) { this.authorityScope = PathPatternTreeUtils.intersectWithFullPathPrefixTree( - AuthorityChecker.getAuthorizedPathTree( - userName, PrivilegeType.READ_SCHEMA.ordinal()), - AuthorityChecker.getAuthorizedPathTree( - userName, PrivilegeType.READ_DATA.ordinal())); + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_SCHEMA), + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_DATA)); } } catch (AuthException e) { return new TSStatus(e.getCode().getStatusCode()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountTimeSeriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountTimeSeriesStatement.java index c0910185421b..f116e1900920 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountTimeSeriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CountTimeSeriesStatement.java @@ -67,10 +67,8 @@ public TSStatus checkPermissionBeforeProcess(String userName) { if (!AuthorityChecker.SUPER_USER.equals(userName)) { this.authorityScope = PathPatternTreeUtils.intersectWithFullPathPrefixTree( - AuthorityChecker.getAuthorizedPathTree( - userName, PrivilegeType.READ_SCHEMA.ordinal()), - AuthorityChecker.getAuthorizedPathTree( - userName, PrivilegeType.READ_DATA.ordinal())); + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_SCHEMA), + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_DATA)); } } catch (AuthException e) { return new TSStatus(e.getCode().getStatusCode()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateAlignedTimeSeriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateAlignedTimeSeriesStatement.java index 065f28b9d7e5..2a93848aa091 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateAlignedTimeSeriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateAlignedTimeSeriesStatement.java @@ -78,7 +78,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateContinuousQueryStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateContinuousQueryStatement.java index 71690519857a..fb8ba32b7b5a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateContinuousQueryStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateContinuousQueryStatement.java @@ -173,7 +173,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_CQ.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_CQ), PrivilegeType.USE_CQ); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateFunctionStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateFunctionStatement.java index c8eabd9a22ae..4859a6c596e9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateFunctionStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateFunctionStatement.java @@ -81,7 +81,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_UDF.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_UDF), PrivilegeType.USE_UDF); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateMultiTimeSeriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateMultiTimeSeriesStatement.java index 694332070962..4ab78c9b6a80 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateMultiTimeSeriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateMultiTimeSeriesStatement.java @@ -66,7 +66,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateTimeSeriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateTimeSeriesStatement.java index a6e06432d2da..e76f88e8bc4d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateTimeSeriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateTimeSeriesStatement.java @@ -73,8 +73,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkFullPathPermission( - userName, path, PrivilegeType.WRITE_SCHEMA.ordinal()), + AuthorityChecker.checkFullPathPermission(userName, path, PrivilegeType.WRITE_SCHEMA), PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateTriggerStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateTriggerStatement.java index 2ad99cc016e9..cf35785af48a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateTriggerStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/CreateTriggerStatement.java @@ -128,7 +128,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_TRIGGER.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_TRIGGER), PrivilegeType.USE_TRIGGER); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DatabaseSchemaStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DatabaseSchemaStatement.java index 9ba1c5fad995..3efd42e5758b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DatabaseSchemaStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DatabaseSchemaStatement.java @@ -149,7 +149,7 @@ public TSStatus checkPermissionBeforeProcess(final String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_DATABASE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_DATABASE), PrivilegeType.MANAGE_DATABASE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DeleteDatabaseStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DeleteDatabaseStatement.java index dadc186da4cf..33786815e162 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DeleteDatabaseStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DeleteDatabaseStatement.java @@ -67,7 +67,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_DATABASE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_DATABASE), PrivilegeType.MANAGE_DATABASE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DeleteTimeSeriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DeleteTimeSeriesStatement.java index 02cad6e003ee..a0c2d258ace7 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DeleteTimeSeriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DeleteTimeSeriesStatement.java @@ -58,8 +58,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { } List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( - AuthorityChecker.checkPatternPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + AuthorityChecker.checkPatternPermission(userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropContinuousQueryStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropContinuousQueryStatement.java index eeb4186c8734..695e489ddc95 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropContinuousQueryStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropContinuousQueryStatement.java @@ -68,7 +68,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_CQ.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_CQ), PrivilegeType.USE_CQ); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropFunctionStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropFunctionStatement.java index d18b2bdd69c4..ec777e44d493 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropFunctionStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropFunctionStatement.java @@ -68,7 +68,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_UDF.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_UDF), PrivilegeType.USE_UDF); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropTriggerStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropTriggerStatement.java index 056df1fd7038..3b3cb6483911 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropTriggerStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/DropTriggerStatement.java @@ -67,7 +67,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_TRIGGER.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_TRIGGER), PrivilegeType.USE_TRIGGER); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/RemoveDataNodeStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/RemoveDataNodeStatement.java index 45fa94822288..00be9c88c1cf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/RemoveDataNodeStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/RemoveDataNodeStatement.java @@ -53,7 +53,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN), PrivilegeType.MAINTAIN); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/SetTTLStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/SetTTLStatement.java index 77e950f22dc4..7c3ab1d286a3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/SetTTLStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/SetTTLStatement.java @@ -82,8 +82,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { } List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( - AuthorityChecker.checkPatternPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + AuthorityChecker.checkPatternPermission(userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowClusterStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowClusterStatement.java index dfda53b4cf08..e7e031a0c87a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowClusterStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowClusterStatement.java @@ -42,7 +42,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN), PrivilegeType.MAINTAIN); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowContinuousQueriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowContinuousQueriesStatement.java index f0e6a3a2c3cf..e40354beb10b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowContinuousQueriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowContinuousQueriesStatement.java @@ -60,7 +60,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_CQ.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_CQ), PrivilegeType.USE_CQ); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowDevicesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowDevicesStatement.java index b0b7f8bba317..fe9c57fd920a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowDevicesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowDevicesStatement.java @@ -91,10 +91,8 @@ public TSStatus checkPermissionBeforeProcess(String userName) { if (!AuthorityChecker.SUPER_USER.equals(userName)) { this.authorityScope = PathPatternTreeUtils.intersectWithFullPathPrefixTree( - AuthorityChecker.getAuthorizedPathTree( - userName, PrivilegeType.READ_SCHEMA.ordinal()), - AuthorityChecker.getAuthorizedPathTree( - userName, PrivilegeType.READ_DATA.ordinal())); + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_SCHEMA), + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_DATA)); } } catch (AuthException e) { return new TSStatus(e.getCode().getStatusCode()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowFunctionsStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowFunctionsStatement.java index 83c6541c9271..289d4e259cc3 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowFunctionsStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowFunctionsStatement.java @@ -54,7 +54,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_UDF.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_UDF), PrivilegeType.USE_UDF); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTimeSeriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTimeSeriesStatement.java index 7d615cac3364..49e478860f5b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTimeSeriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTimeSeriesStatement.java @@ -90,10 +90,8 @@ public TSStatus checkPermissionBeforeProcess(String userName) { if (!AuthorityChecker.SUPER_USER.equals(userName)) { this.authorityScope = PathPatternTreeUtils.intersectWithFullPathPrefixTree( - AuthorityChecker.getAuthorizedPathTree( - userName, PrivilegeType.READ_SCHEMA.ordinal()), - AuthorityChecker.getAuthorizedPathTree( - userName, PrivilegeType.READ_DATA.ordinal())); + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_SCHEMA), + AuthorityChecker.getAuthorizedPathTree(userName, PrivilegeType.READ_DATA)); } } catch (AuthException e) { return new TSStatus(e.getCode().getStatusCode()); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTriggersStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTriggersStatement.java index 133a310cc183..8b86427e38f9 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTriggersStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/ShowTriggersStatement.java @@ -54,7 +54,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_TRIGGER.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_TRIGGER), PrivilegeType.USE_TRIGGER); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/CreateModelStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/CreateModelStatement.java index 2f43a1cfe762..e54792ac8315 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/CreateModelStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/CreateModelStatement.java @@ -74,7 +74,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_MODEL.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_MODEL), PrivilegeType.USE_MODEL); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/DropModelStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/DropModelStatement.java index 3e207608241d..21871b58751b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/DropModelStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/DropModelStatement.java @@ -60,7 +60,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_MODEL.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_MODEL), PrivilegeType.USE_MODEL); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/ShowModelsStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/ShowModelsStatement.java index 0b810b49ad82..50241dad114c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/ShowModelsStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/model/ShowModelsStatement.java @@ -68,7 +68,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_MODEL.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_MODEL), PrivilegeType.USE_MODEL); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/AlterPipeStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/AlterPipeStatement.java index 44065cf31850..5736d0c75702 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/AlterPipeStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/AlterPipeStatement.java @@ -138,7 +138,7 @@ public TSStatus checkPermissionBeforeProcess(final String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/CreatePipePluginStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/CreatePipePluginStatement.java index f0975b560126..6c3351eba91e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/CreatePipePluginStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/CreatePipePluginStatement.java @@ -82,7 +82,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/CreatePipeStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/CreatePipeStatement.java index a7b7471ffd05..3430128247f1 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/CreatePipeStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/CreatePipeStatement.java @@ -102,7 +102,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/DropPipePluginStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/DropPipePluginStatement.java index f5c140c1cf36..e40ae63d01d8 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/DropPipePluginStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/DropPipePluginStatement.java @@ -89,7 +89,7 @@ public TSStatus checkPermissionBeforeProcess(final String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/DropPipeStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/DropPipeStatement.java index c61a15d777d4..82d2f777be6d 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/DropPipeStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/DropPipeStatement.java @@ -83,7 +83,7 @@ public TSStatus checkPermissionBeforeProcess(final String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/ShowPipePluginsStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/ShowPipePluginsStatement.java index 3cf09eb2eb55..56c79e37a5de 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/ShowPipePluginsStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/ShowPipePluginsStatement.java @@ -62,7 +62,7 @@ public TSStatus checkPermissionBeforeProcess(final String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/ShowPipesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/ShowPipesStatement.java index 658f36a0635f..51de7b7c0055 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/ShowPipesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/ShowPipesStatement.java @@ -76,7 +76,7 @@ public TSStatus checkPermissionBeforeProcess(final String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/StartPipeStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/StartPipeStatement.java index 9023d77c06ec..346eaf05cd30 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/StartPipeStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/StartPipeStatement.java @@ -74,7 +74,7 @@ public TSStatus checkPermissionBeforeProcess(final String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/StopPipeStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/StopPipeStatement.java index ea14a3dbd1b2..70447ae7b908 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/StopPipeStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/pipe/StopPipeStatement.java @@ -74,7 +74,7 @@ public TSStatus checkPermissionBeforeProcess(final String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/ExtendRegionStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/ExtendRegionStatement.java index 560adee4e9ef..6d3460942011 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/ExtendRegionStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/ExtendRegionStatement.java @@ -57,7 +57,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN), PrivilegeType.MAINTAIN); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/MigrateRegionStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/MigrateRegionStatement.java index 5b290a533096..df788c1414ac 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/MigrateRegionStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/MigrateRegionStatement.java @@ -73,7 +73,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN), PrivilegeType.MAINTAIN); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/ReconstructRegionStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/ReconstructRegionStatement.java index 7307381bf4e4..0620e34dda0e 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/ReconstructRegionStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/ReconstructRegionStatement.java @@ -55,7 +55,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN), PrivilegeType.MAINTAIN); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/RemoveRegionStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/RemoveRegionStatement.java index 186656c820d9..16839a438200 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/RemoveRegionStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/region/RemoveRegionStatement.java @@ -57,7 +57,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN), PrivilegeType.MAINTAIN); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/CreateTopicStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/CreateTopicStatement.java index a98a1d5d2739..8f3f237918fb 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/CreateTopicStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/CreateTopicStatement.java @@ -85,7 +85,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/DropTopicStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/DropTopicStatement.java index 495a0167cbbe..97d173b9fdd4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/DropTopicStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/DropTopicStatement.java @@ -80,7 +80,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowSubscriptionsStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowSubscriptionsStatement.java index 1eb22b475df5..ff7267f9881b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowSubscriptionsStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowSubscriptionsStatement.java @@ -71,7 +71,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowTopicsStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowTopicsStatement.java index 7201b375027f..82a4a87dc936 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowTopicsStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/subscription/ShowTopicsStatement.java @@ -71,7 +71,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.USE_PIPE), PrivilegeType.USE_PIPE); } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/ActivateTemplateStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/ActivateTemplateStatement.java index df5839914ba5..4b78953347e2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/ActivateTemplateStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/ActivateTemplateStatement.java @@ -70,8 +70,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { } List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( - AuthorityChecker.checkPatternPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + AuthorityChecker.checkPatternPermission(userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/BatchActivateTemplateStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/BatchActivateTemplateStatement.java index 6b8507e20a54..0d92800ef1f0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/BatchActivateTemplateStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/BatchActivateTemplateStatement.java @@ -53,8 +53,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { } List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( - AuthorityChecker.checkPatternPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + AuthorityChecker.checkPatternPermission(userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/DeactivateTemplateStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/DeactivateTemplateStatement.java index eb8612a0379e..357221eba87a 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/DeactivateTemplateStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/template/DeactivateTemplateStatement.java @@ -84,8 +84,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { } List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( - AuthorityChecker.checkPatternPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + AuthorityChecker.checkPatternPermission(userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/AlterLogicalViewStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/AlterLogicalViewStatement.java index 0d2b5d345b9d..41a34ef592a2 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/AlterLogicalViewStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/AlterLogicalViewStatement.java @@ -71,7 +71,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { status = AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, sourcePathList, PrivilegeType.READ_SCHEMA.ordinal()), + userName, sourcePathList, PrivilegeType.READ_SCHEMA), sourcePathList, PrivilegeType.READ_SCHEMA); } @@ -80,7 +80,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { status = AuthorityChecker.getTSStatus( AuthorityChecker.checkPatternPermission( - userName, sourcePathList, PrivilegeType.READ_SCHEMA.ordinal()), + userName, sourcePathList, PrivilegeType.READ_SCHEMA), sourcePathList, PrivilegeType.READ_SCHEMA); } @@ -88,7 +88,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, getTargetPathList(), PrivilegeType.WRITE_SCHEMA.ordinal()), + userName, getTargetPathList(), PrivilegeType.WRITE_SCHEMA), getTargetPathList(), PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/CreateLogicalViewStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/CreateLogicalViewStatement.java index e35e766d4a43..7fcd37378f28 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/CreateLogicalViewStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/CreateLogicalViewStatement.java @@ -85,7 +85,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { status = AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, sourcePathList, PrivilegeType.READ_SCHEMA.ordinal()), + userName, sourcePathList, PrivilegeType.READ_SCHEMA), sourcePathList, PrivilegeType.READ_SCHEMA); } @@ -94,7 +94,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { status = AuthorityChecker.getTSStatus( AuthorityChecker.checkPatternPermission( - userName, sourcePathList, PrivilegeType.READ_SCHEMA.ordinal()), + userName, sourcePathList, PrivilegeType.READ_SCHEMA), sourcePathList, PrivilegeType.READ_SCHEMA); } @@ -102,7 +102,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) { return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, getTargetPathList(), PrivilegeType.WRITE_SCHEMA.ordinal()), + userName, getTargetPathList(), PrivilegeType.WRITE_SCHEMA), getTargetPathList(), PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/DeleteLogicalViewStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/DeleteLogicalViewStatement.java index c9c447bd7041..aa569a8f81c5 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/DeleteLogicalViewStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/DeleteLogicalViewStatement.java @@ -57,8 +57,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { } List checkedPaths = getPaths(); return AuthorityChecker.getTSStatus( - AuthorityChecker.checkPatternPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + AuthorityChecker.checkPatternPermission(userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/RenameLogicalViewStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/RenameLogicalViewStatement.java index 3e4bd2adf853..d28d113f41c6 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/RenameLogicalViewStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/metadata/view/RenameLogicalViewStatement.java @@ -75,7 +75,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { List checkedPaths = ImmutableList.of(oldName, newName); return AuthorityChecker.getTSStatus( AuthorityChecker.checkFullPathListPermission( - userName, checkedPaths, PrivilegeType.WRITE_SCHEMA.ordinal()), + userName, checkedPaths, PrivilegeType.WRITE_SCHEMA), checkedPaths, PrivilegeType.WRITE_SCHEMA); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/AuthorStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/AuthorStatement.java index 1b325793fd2c..0c905c45d189 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/AuthorStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/AuthorStatement.java @@ -226,7 +226,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER), PrivilegeType.MANAGE_USER); case UPDATE_USER: @@ -235,7 +235,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER), PrivilegeType.MANAGE_USER); case DROP_USER: @@ -246,7 +246,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER), PrivilegeType.MANAGE_USER); case LIST_USER: @@ -254,7 +254,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER), PrivilegeType.MANAGE_USER); case LIST_USER_PRIVILEGE: @@ -262,7 +262,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_USER), PrivilegeType.MANAGE_USER); case LIST_ROLE_PRIVILEGE: @@ -271,7 +271,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { } if (!AuthorityChecker.checkRole(userName, roleName)) { return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_ROLE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_ROLE), PrivilegeType.MANAGE_ROLE); } else { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); @@ -285,7 +285,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } else { return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_ROLE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_ROLE), PrivilegeType.MANAGE_ROLE); } @@ -301,11 +301,13 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_ROLE.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MANAGE_ROLE), PrivilegeType.MANAGE_ROLE); case REVOKE_USER: case GRANT_USER: + case GRANT_ROLE: + case REVOKE_ROLE: if (AuthorityChecker.SUPER_USER.equals(this.userName)) { return AuthorityChecker.getTSStatus( false, "Cannot grant/revoke privileges of admin user"); @@ -313,22 +315,32 @@ public TSStatus checkPermissionBeforeProcess(String userName) { if (AuthorityChecker.SUPER_USER.equals(userName)) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } - return AuthorityChecker.getOptTSStatus( - AuthorityChecker.checkGrantOption(userName, privilegeList, nodeNameList), - "Has no permission to execute " - + authorType - + ", please ensure you have these privileges and the grant option is TRUE when granted"); - case GRANT_ROLE: - case REVOKE_ROLE: - if (AuthorityChecker.SUPER_USER.equals(userName)) { - return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); + for (String s : privilegeList) { + PrivilegeType privilegeType = PrivilegeType.valueOf(s.toUpperCase()); + if (privilegeType.isSystemPrivilege()) { + if (!AuthorityChecker.checkSystemPermissionGrantOption(userName, privilegeType)) { + return AuthorityChecker.getTSStatus( + false, + "Has no permission to execute " + + authorType + + ", please ensure you have these privileges and the grant option is TRUE when granted)"); + } + } else if (privilegeType.isPathPrivilege()) { + if (!AuthorityChecker.checkPathPermissionGrantOption( + userName, privilegeType, nodeNameList)) { + return AuthorityChecker.getTSStatus( + false, + "Has no permission to execute " + + authorType + + ", please ensure you have these privileges and the grant option is TRUE when granted)"); + } + } else { + return AuthorityChecker.getTSStatus( + false, "Not support Relation statement in tree sql_dialect"); + } } - return AuthorityChecker.getOptTSStatus( - AuthorityChecker.checkGrantOption(userName, privilegeList, nodeNameList), - "Has no permission to execute " - + authorType - + ", please ensure you have these privileges and the grant option is TRUE when granted"); + return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); default: throw new IllegalArgumentException("Unknown authorType: " + authorType); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/KillQueryStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/KillQueryStatement.java index 85e32ab48b10..1a438d2a6b10 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/KillQueryStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/KillQueryStatement.java @@ -62,7 +62,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN), PrivilegeType.MAINTAIN); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/ShowQueriesStatement.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/ShowQueriesStatement.java index f582ccec6c80..472511ffc66b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/ShowQueriesStatement.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/statement/sys/ShowQueriesStatement.java @@ -64,7 +64,7 @@ public TSStatus checkPermissionBeforeProcess(String userName) { return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); } return AuthorityChecker.getTSStatus( - AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN.ordinal()), + AuthorityChecker.checkSystemPermission(userName, PrivilegeType.MAINTAIN), PrivilegeType.MAINTAIN); } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/AuthorizerManagerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/AuthorizerManagerTest.java index fea562a9489a..2d82bc0870fd 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/AuthorizerManagerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/AuthorizerManagerTest.java @@ -19,16 +19,16 @@ package org.apache.iotdb.db.auth; +import org.apache.iotdb.commons.auth.entity.ModelType; import org.apache.iotdb.commons.auth.entity.PathPrivilege; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.confignode.rpc.thrift.TPathPrivilege; import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp; -import org.apache.iotdb.confignode.rpc.thrift.TRoleResp; import org.apache.iotdb.confignode.rpc.thrift.TUserResp; +import org.apache.iotdb.rpc.TSStatusCode; import org.junit.Assert; import org.junit.Test; @@ -38,7 +38,6 @@ import java.util.HashMap; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.Set; public class AuthorizerManagerTest { @@ -50,15 +49,14 @@ public void permissionCacheTest() throws IllegalPathException { User user = new User(); Role role1 = new Role(); Role role2 = new Role(); - List roleList = new ArrayList<>(); - Set privilegesIds = new HashSet<>(); - Set sysPriIds = new HashSet<>(); - PathPrivilege privilege = new PathPrivilege(); + Set roleSet = new HashSet<>(); + Set privilegesIds = new HashSet<>(); + Set sysPriIds = new HashSet<>(); + PathPrivilege privilege = new PathPrivilege(new PartialPath("root.ln")); List privilegeList = new ArrayList<>(); - privilegesIds.add(PrivilegeType.READ_DATA.ordinal()); - sysPriIds.add(PrivilegeType.MAINTAIN.ordinal()); + privilegesIds.add(PrivilegeType.READ_DATA); + sysPriIds.add(PrivilegeType.MAINTAIN); - privilege.setPath(new PartialPath("root.ln")); privilege.setPrivileges(privilegesIds); privilegeList.add(privilege); @@ -68,38 +66,22 @@ public void permissionCacheTest() throws IllegalPathException { role2.setName("role2"); role2.setPrivilegeList(new ArrayList<>()); - roleList.add("role1"); - roleList.add("role2"); + roleSet.add("role1"); + roleSet.add("role2"); user.setName("user"); user.setPassword("password"); user.setPrivilegeList(privilegeList); user.setSysPrivilegeSet(sysPriIds); - user.setRoleList(roleList); + user.setRoleSet(roleSet); + user.grantDBPrivilege("test", PrivilegeType.ALTER, false); + user.grantTBPrivilege("test", "table", PrivilegeType.SELECT, true); + user.grantDBPrivilege("test2", PrivilegeType.SELECT, true); + user.grantAnyScopePrivilege(PrivilegeType.ALTER, false); + user.grantAnyScopePrivilege(PrivilegeType.SELECT, true); + TPermissionInfoResp result = new TPermissionInfoResp(); - TUserResp tUserResp = new TUserResp(); - Map tRoleRespMap = new HashMap(); - List userPrivilegeList = new ArrayList<>(); - List rolePrivilegeList = new ArrayList<>(); - List roleList1 = new ArrayList<>(); - roleList1.add(role1); - roleList1.add(role2); - - // User permission information - for (PathPrivilege pathPrivilege : user.getPathPrivilegeList()) { - TPathPrivilege pathPri = new TPathPrivilege(); - pathPri.setPath(pathPrivilege.getPath().getFullPath()); - pathPri.setPriSet(pathPrivilege.getPrivileges()); - pathPri.setPriGrantOpt(pathPrivilege.getGrantOpt()); - userPrivilegeList.add(pathPri); - } - tUserResp.setUsername(user.getName()); - tUserResp.setPassword(user.getPassword()); - tUserResp.setPrivilegeList(userPrivilegeList); - tUserResp.setSysPriSet(user.getSysPrivilege()); - - tUserResp.setRoleList(new ArrayList<>()); - result.setUserInfo(tUserResp); + result.setUserInfo(user.getUserInfo(ModelType.ALL)); result.setRoleInfo(new HashMap<>()); // User authentication permission without role @@ -108,78 +90,23 @@ public void permissionCacheTest() throws IllegalPathException { .putUserCache(user.getName(), authorityFetcher.cacheUser(result)); User user1 = authorityFetcher.getAuthorCache().getUserCache(user.getName()); assert user1 != null; - Assert.assertEquals(user.getName(), user1.getName()); - Assert.assertEquals(user.getPassword(), user1.getPassword()); - Assert.assertEquals(user.getPathPrivilegeList(), user1.getPathPrivilegeList()); - Assert.assertEquals(user.getSysPrivilege(), user1.getSysPrivilege()); - - // User has permission - Assert.assertEquals( - authorityFetcher - .checkUserPathPrivileges( - "user", - Collections.singletonList(new PartialPath("root.ln")), - PrivilegeType.READ_DATA.ordinal()) - .size(), - 0); - // User does not have permission - Assert.assertEquals( - authorityFetcher - .checkUserPathPrivileges( - "user", - Collections.singletonList(new PartialPath("root.ln")), - PrivilegeType.WRITE_DATA.ordinal()) - .size(), - 1); + Assert.assertEquals(user, user1); // Authenticate users with roles authorityFetcher.getAuthorCache().invalidateCache(user.getName(), ""); - tUserResp.setPrivilegeList(new ArrayList<>()); - tUserResp.setRoleList(user.getRoleList()); + result = new TPermissionInfoResp(); + result.setUserInfo(user.getUserInfo(ModelType.ALL)); + result.putToRoleInfo(role1.getName(), role1.getRoleInfo(ModelType.ALL)); + result.putToRoleInfo(role2.getName(), role2.getRoleInfo(ModelType.ALL)); // Permission information for roles owned by users - for (Role role : roleList1) { - TRoleResp tRoleResp = new TRoleResp(); - rolePrivilegeList = new ArrayList<>(); - tRoleResp.setRoleName(role.getName()); - for (PathPrivilege pathPrivilege : role.getPathPrivilegeList()) { - TPathPrivilege pathPri = new TPathPrivilege(); - pathPri.setPath(pathPrivilege.getPath().getFullPath()); - pathPri.setPriSet(pathPrivilege.getPrivileges()); - pathPri.setPriGrantOpt(pathPrivilege.getGrantOpt()); - rolePrivilegeList.add(pathPri); - } - tRoleResp.setPrivilegeList(rolePrivilegeList); - tRoleRespMap.put(role.getName(), tRoleResp); - } - result.setRoleInfo(tRoleRespMap); authorityFetcher .getAuthorCache() .putUserCache(user.getName(), authorityFetcher.cacheUser(result)); Role role3 = authorityFetcher.getAuthorCache().getRoleCache(role1.getName()); - Assert.assertEquals(role1.getName(), role3.getName()); - Assert.assertEquals(role1.getPathPrivilegeList(), role3.getPathPrivilegeList()); - - // role has permission - Assert.assertEquals( - authorityFetcher - .checkUserPathPrivileges( - "user", - Collections.singletonList(new PartialPath("root.ln")), - PrivilegeType.READ_DATA.ordinal()) - .size(), - 0); - // role does not have permission - Assert.assertEquals( - authorityFetcher - .checkUserPathPrivileges( - "user", - Collections.singletonList(new PartialPath("root.ln")), - PrivilegeType.MANAGE_USER.ordinal()) - .size(), - 1); + Assert.assertEquals(role1, role3); - authorityFetcher.getAuthorCache().invalidateCache(user.getName(), ""); + authorityFetcher.getAuthorCache().invalidateCache(user1.getName(), ""); user1 = authorityFetcher.getAuthorCache().getUserCache(user.getName()); role1 = authorityFetcher.getAuthorCache().getRoleCache(role1.getName()); @@ -190,114 +117,145 @@ public void permissionCacheTest() throws IllegalPathException { @Test public void grantOptTest() throws IllegalPathException { - User user = new User(); - Role role = new Role(); - - Set sysPri = new HashSet<>(); - sysPri.add(PrivilegeType.MANAGE_DATABASE.ordinal()); - sysPri.add(PrivilegeType.USE_PIPE.ordinal()); - user.setSysPrivilegeSet(sysPri); + User user = new User("user1", "123456"); + Role role = new Role("role1"); - Set sysGrantOpt = new HashSet<>(); - sysGrantOpt.add(PrivilegeType.USE_PIPE.ordinal()); - user.setSysPriGrantOpt(sysGrantOpt); + user.grantSysPrivilege(PrivilegeType.MANAGE_DATABASE, false); + user.grantSysPrivilege(PrivilegeType.USE_PIPE, true); - List pathList = new ArrayList<>(); - PartialPath pathRoot = new PartialPath("root.**"); - PartialPath path1 = new PartialPath("root.d1.**"); - PathPrivilege priv1 = new PathPrivilege(path1); - priv1.grantPrivilege(PrivilegeType.READ_DATA.ordinal(), false); - priv1.grantPrivilege(PrivilegeType.WRITE_SCHEMA.ordinal(), true); - pathList.add(priv1); + user.grantPathPrivilege(new PartialPath("root.d1.**"), PrivilegeType.READ_DATA, false); + user.grantPathPrivilege(new PartialPath("root.d1.**"), PrivilegeType.WRITE_SCHEMA, true); - user.setPrivilegeList(pathList); + user.grantDBPrivilege("database", PrivilegeType.SELECT, false); + user.grantDBPrivilege("database", PrivilegeType.ALTER, true); + user.grantTBPrivilege("database", "table", PrivilegeType.DELETE, true); + user.grantAnyScopePrivilege(PrivilegeType.ALTER, true); - user.setName("user1"); - user.setPassword("123456"); + role.grantSysPrivilege(PrivilegeType.USE_UDF, false); + role.grantSysPrivilege(PrivilegeType.USE_CQ, true); + role.grantSysPrivilegeGrantOption(PrivilegeType.USE_CQ); + role.grantPathPrivilege(new PartialPath("root.t.**"), PrivilegeType.READ_DATA, true); + role.grantDBPrivilege("database", PrivilegeType.INSERT, true); // user's priv: // 1. MANAGE_DATABASE // 2. USE_PIPE with grant option // 3. READ_DATA root.d1.** // 4. WRITE_SCHEMA root.d1.** with grant option + // 5. SELECT on database, ALTER on database with grant option + // 6. DELETE on database.table with grant option + // 7. ALTER on any with grant option // role's priv: // 1. USE_UDF // 2. USE_CQ with grant option - // 3. READ_DATA root.t9.** with grant option + // 3. READ_DATA root.t.** with grant option + // 4. INSERT on database with grant option + user.addRole("role1"); - role.setName("role1"); - Set sysPriRole = new HashSet<>(); - sysPriRole.add(PrivilegeType.USE_UDF.ordinal()); - sysPriRole.add(PrivilegeType.USE_CQ.ordinal()); - role.setSysPrivilegeSet(sysPriRole); - - Set sysGrantOptRole = new HashSet<>(); - sysGrantOptRole.add(PrivilegeType.USE_CQ.ordinal()); - role.setSysPriGrantOpt(sysGrantOptRole); + TPermissionInfoResp result = new TPermissionInfoResp(); + TUserResp tUserResp = user.getUserInfo(ModelType.ALL); + result.setUserInfo(tUserResp); + result.putToRoleInfo(role.getName(), role.getRoleInfo(ModelType.ALL)); - PathPrivilege privRole = new PathPrivilege(new PartialPath("root.t9.**")); - privRole.grantPrivilege(PrivilegeType.READ_DATA.ordinal(), true); - role.setPrivilegeList(Collections.singletonList(privRole)); - user.setRoleList(Collections.singletonList("role1")); + authorityFetcher + .getAuthorCache() + .putUserCache(user.getName(), authorityFetcher.cacheUser(result)); - authorityFetcher.getAuthorCache().putUserCache("user1", user); - authorityFetcher.getAuthorCache().putRoleCache("role1", role); + Assert.assertEquals(user, authorityFetcher.getAuthorCache().getUserCache(user.getName())); + Assert.assertEquals(role, authorityFetcher.getAuthorCache().getRoleCache(role.getName())); // for system priv. we have USE_PIPE grant option. - Assert.assertTrue( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", Collections.singletonList(pathRoot), PrivilegeType.USE_PIPE.ordinal())); - Assert.assertFalse( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", Collections.singletonList(pathRoot), PrivilegeType.MANAGE_USER.ordinal())); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorityFetcher.checkUserSysPrivilegesGrantOpt("user1", PrivilegeType.USE_PIPE).getCode()); + Assert.assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + authorityFetcher + .checkUserSysPrivilegesGrantOpt("user1", PrivilegeType.MANAGE_USER) + .getCode()); // for path priv. we have write_schema on root.d1.** with grant option. // require root.d1.** with write_schema, return true - Assert.assertTrue( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", Collections.singletonList(path1), PrivilegeType.WRITE_SCHEMA.ordinal())); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorityFetcher + .checkUserPathPrivilegesGrantOpt( + "user1", + Collections.singletonList(new PartialPath("root.d1.**")), + PrivilegeType.WRITE_SCHEMA) + .getCode()); // require root.** with write_schema, return false - Assert.assertFalse( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", Collections.singletonList(pathRoot), PrivilegeType.WRITE_SCHEMA.ordinal())); - // reuqire root.d1.d2 with write_schema, return true - Assert.assertTrue( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", - Collections.singletonList(new PartialPath(new String("root.d1.d2"))), - PrivilegeType.WRITE_SCHEMA.ordinal())); + Assert.assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + authorityFetcher + .checkUserPathPrivilegesGrantOpt( + "user1", + Collections.singletonList(new PartialPath("root.**")), + PrivilegeType.WRITE_SCHEMA) + .getCode()); + // require root.d1.d2 with write_schema, return true + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorityFetcher + .checkUserPathPrivilegesGrantOpt( + "user1", + Collections.singletonList(new PartialPath("root.d1.d2")), + PrivilegeType.WRITE_SCHEMA) + .getCode()); // require root.d1.d2 with read_schema, return false - Assert.assertFalse( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", - Collections.singletonList(new PartialPath(new String("root.d1.d2"))), - PrivilegeType.READ_SCHEMA.ordinal())); + Assert.assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + authorityFetcher + .checkUserPathPrivilegesGrantOpt( + "user1", + Collections.singletonList(new PartialPath("root.d1.d2")), + PrivilegeType.READ_SCHEMA) + .getCode()); + + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorityFetcher + .checkUserDBPrivilegesGrantOpt("user1", "database", PrivilegeType.ALTER) + .getCode()); + + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorityFetcher + .checkUserTBPrivilegesGrantOpt("user1", "database", "table", PrivilegeType.INSERT) + .getCode()); + Assert.assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + authorityFetcher + .checkUserTBPrivilegesGrantOpt("user1", "database", "table", PrivilegeType.SELECT) + .getCode()); // role test - Assert.assertTrue( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", - Collections.singletonList(new PartialPath(new String("root.t9.**"))), - PrivilegeType.READ_DATA.ordinal())); - - Assert.assertTrue( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", - Collections.singletonList(new PartialPath(new String("root.t9.t10"))), - PrivilegeType.READ_DATA.ordinal())); - - Assert.assertFalse( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", - Collections.singletonList(new PartialPath(new String("root.t9.**"))), - PrivilegeType.WRITE_DATA.ordinal())); - Assert.assertFalse( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", Collections.singletonList(pathRoot), PrivilegeType.USE_TRIGGER.ordinal())); - Assert.assertTrue( - authorityFetcher.checkUserPrivilegeGrantOpt( - "user1", Collections.singletonList(pathRoot), PrivilegeType.USE_CQ.ordinal())); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorityFetcher + .checkUserPathPrivilegesGrantOpt( + "user1", + Collections.singletonList(new PartialPath("root.t.**")), + PrivilegeType.READ_DATA) + .getCode()); + + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorityFetcher + .checkUserPathPrivilegesGrantOpt( + "user1", + Collections.singletonList(new PartialPath("root.t.t1")), + PrivilegeType.READ_DATA) + .getCode()); + Assert.assertEquals( + TSStatusCode.NO_PERMISSION.getStatusCode(), + authorityFetcher + .checkUserSysPrivilegesGrantOpt("user1", PrivilegeType.USE_TRIGGER) + .getCode()); + Assert.assertEquals( + TSStatusCode.SUCCESS_STATUS.getStatusCode(), + authorityFetcher.checkUserSysPrivilegesGrantOpt("user1", PrivilegeType.USE_CQ).getCode()); } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/LocalFileAuthorizerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/LocalFileAuthorizerTest.java index ee555bf258b0..15e120426919 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/LocalFileAuthorizerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/authorizer/LocalFileAuthorizerTest.java @@ -22,10 +22,8 @@ import org.apache.iotdb.commons.auth.authorizer.BasicAuthorizer; import org.apache.iotdb.commons.auth.authorizer.IAuthorizer; import org.apache.iotdb.commons.auth.entity.PrivilegeType; -import org.apache.iotdb.commons.auth.entity.Role; -import org.apache.iotdb.commons.auth.entity.User; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.conf.CommonDescriptor; -import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.db.utils.EnvironmentUtils; @@ -34,7 +32,6 @@ import org.junit.Before; import org.junit.Test; -import java.util.HashMap; import java.util.List; import java.util.Set; @@ -45,18 +42,18 @@ public class LocalFileAuthorizerTest { IAuthorizer authorizer; - User user; PartialPath nodeName; - String roleName = "role"; - String admin = "root"; + static final String roleName = "role"; + static final String userName = "user"; + static final String password = "password"; + static final String database = "testDB"; + static final String table = "testTB"; @Before public void setUp() throws Exception { EnvironmentUtils.envSetUp(); - authorizer = BasicAuthorizer.getInstance(); authorizer.reset(); - user = new User("user", "password"); nodeName = new PartialPath("root.laptop.d1"); } @@ -73,16 +70,16 @@ public void testLogin() throws AuthException { @Test public void createAndDeleteUser() throws AuthException { - authorizer.createUser(user.getName(), user.getPassword()); + authorizer.createUser(userName, password); try { - authorizer.createUser(user.getName(), user.getPassword()); + authorizer.createUser(userName, password); } catch (AuthException e) { assertEquals("User user already exists", e.getMessage()); } - Assert.assertTrue(authorizer.login(user.getName(), user.getPassword())); - authorizer.deleteUser(user.getName()); + Assert.assertTrue(authorizer.login(userName, password)); + authorizer.deleteUser(userName); try { - authorizer.deleteUser(user.getName()); + authorizer.deleteUser(userName); } catch (AuthException e) { assertEquals("User user does not exist", e.getMessage()); } @@ -101,157 +98,129 @@ public void createAndDeleteUser() throws AuthException { @Test public void createAndDeleteRole() throws AuthException { - authorizer.createRole(user.getName()); + authorizer.createRole(roleName); try { - authorizer.createRole(user.getName()); + authorizer.createRole(roleName); } catch (AuthException e) { - assertEquals("Role user already exists", e.getMessage()); + assertEquals("Role role already exists", e.getMessage()); } - authorizer.deleteRole(user.getName()); + authorizer.createUser(userName, "password"); + authorizer.grantRoleToUser(roleName, userName); + authorizer.deleteRole(roleName); try { - authorizer.deleteRole(user.getName()); + authorizer.deleteRole(roleName); } catch (AuthException e) { - assertEquals("Role user does not exist", e.getMessage()); + assertEquals("Role role does not exist", e.getMessage()); } + assertEquals(0, authorizer.getUser(userName).getRoleSet().size()); } @Test - public void testUserPermission() throws AuthException { - authorizer.createUser(user.getName(), user.getPassword()); - String admin = "root"; + public void testTreePermission() throws AuthException { + authorizer.createUser(userName, password); authorizer.grantPrivilegeToUser( - user.getName(), nodeName, PrivilegeType.READ_DATA.ordinal(), false); + userName, new PrivilegeUnion(nodeName, PrivilegeType.READ_DATA, false)); try { authorizer.grantPrivilegeToUser( - user.getName(), nodeName, PrivilegeType.READ_DATA.ordinal(), false); + userName, new PrivilegeUnion(nodeName, PrivilegeType.READ_DATA, false)); } catch (AuthException e) { assertEquals("User user already has READ_DATA on root.laptop.d1", e.getMessage()); } try { - authorizer.grantPrivilegeToUser("error", nodeName, PrivilegeType.READ_DATA.ordinal(), false); + authorizer.grantPrivilegeToUser( + "error", new PrivilegeUnion(nodeName, PrivilegeType.READ_DATA, false)); } catch (AuthException e) { assertEquals("No such user error", e.getMessage()); } try { - authorizer.grantPrivilegeToUser("root", nodeName, PrivilegeType.READ_DATA.ordinal(), false); + authorizer.grantPrivilegeToUser( + "root", new PrivilegeUnion(nodeName, PrivilegeType.READ_DATA, false)); } catch (AuthException e) { Assert.assertEquals( "Invalid operation, administrator already has all privileges", e.getMessage()); } - try { - authorizer.grantPrivilegeToUser(user.getName(), nodeName, 100, false); - } catch (AuthException e) { - assertEquals("Invalid privilegeId 100", e.getMessage()); - } - - authorizer.revokePrivilegeFromUser(user.getName(), nodeName, PrivilegeType.READ_DATA.ordinal()); - try { - authorizer.revokePrivilegeFromUser( - user.getName(), nodeName, PrivilegeType.READ_DATA.ordinal()); - } catch (AuthException e) { - assertEquals("User user does not have READ_DATA on root.laptop.d1", e.getMessage()); - } - - try { - authorizer.revokePrivilegeFromUser(user.getName(), nodeName, 100); - } catch (AuthException e) { - assertEquals("Invalid privilegeId 100", e.getMessage()); - } - - try { - authorizer.deleteUser(user.getName()); - authorizer.revokePrivilegeFromUser(user.getName(), nodeName, 1); - } catch (AuthException e) { - assertEquals("No such user user", e.getMessage()); - } - - try { - authorizer.revokePrivilegeFromUser("root", new PartialPath("root"), 1); - } catch (AuthException | MetadataException e) { - Assert.assertEquals( - "Invalid operation, administrator must have all privileges", e.getMessage()); - } + authorizer.revokePrivilegeFromUser( + userName, new PrivilegeUnion(nodeName, PrivilegeType.READ_DATA)); + authorizer.revokePrivilegeFromUser( + userName, new PrivilegeUnion(nodeName, PrivilegeType.READ_DATA)); } @Test - public void testCreateAndDeleteRole() throws AuthException { - authorizer.createRole(roleName); - try { - authorizer.createRole(roleName); - } catch (AuthException e) { - assertEquals("Role role already exists", e.getMessage()); - } - authorizer.deleteRole(roleName); - try { - authorizer.deleteRole(roleName); - } catch (AuthException e) { - assertEquals("Role role does not exist", e.getMessage()); - } - } - - @Test - public void testRolePermission() throws AuthException { - authorizer.createRole(roleName); - authorizer.grantPrivilegeToRole(roleName, nodeName, 1, false); - try { - authorizer.grantPrivilegeToRole(roleName, nodeName, 1, false); - } catch (AuthException e) { - assertEquals("Role role already has WRITE_DATA on root.laptop.d1", e.getMessage()); - } - authorizer.revokePrivilegeFromRole(roleName, nodeName, 1); - try { - authorizer.revokePrivilegeFromRole(roleName, nodeName, 1); - } catch (AuthException e) { - assertEquals("Role role does not have WRITE_DATA on root.laptop.d1", e.getMessage()); - } - authorizer.deleteRole(roleName); - try { - authorizer.revokePrivilegeFromRole(roleName, nodeName, 1); - } catch (AuthException e) { - assertEquals("No such role role", e.getMessage()); - } - try { - authorizer.grantPrivilegeToRole(roleName, nodeName, 1, false); - } catch (AuthException e) { - assertEquals("No such role role", e.getMessage()); - } + public void testRelationalPermission() throws AuthException { + authorizer.createUser(userName, password); + authorizer.grantPrivilegeToUser( + userName, new PrivilegeUnion(database, PrivilegeType.SELECT, true)); + authorizer.grantPrivilegeToUser( + userName, new PrivilegeUnion(database, PrivilegeType.ALTER, false)); + authorizer.grantPrivilegeToUser( + userName, new PrivilegeUnion(database, table, PrivilegeType.INSERT, true)); + authorizer.grantPrivilegeToUser( + userName, new PrivilegeUnion(database, table, PrivilegeType.DELETE, true)); + assertEquals(1, authorizer.getUser(userName).getDBScopePrivilegeMap().size()); + assertEquals( + 1, + authorizer + .getUser(userName) + .getDBScopePrivilegeMap() + .get(database) + .getTablePrivilegeMap() + .size()); + + assertTrue( + authorizer.checkUserPrivileges( + userName, new PrivilegeUnion(database, PrivilegeType.SELECT))); + assertTrue( + authorizer.checkUserPrivileges( + userName, new PrivilegeUnion(database, table, PrivilegeType.SELECT))); // db privilege + assertTrue( + authorizer.checkUserPrivileges( + userName, new PrivilegeUnion(database, table, PrivilegeType.DELETE))); + assertTrue( + authorizer.checkUserPrivileges( + userName, new PrivilegeUnion(database, table, PrivilegeType.INSERT, true))); } @Test public void testUserRole() throws AuthException { - authorizer.createUser(user.getName(), user.getPassword()); + authorizer.createUser(userName, password); authorizer.createRole(roleName); - authorizer.grantRoleToUser(roleName, user.getName()); - authorizer.grantPrivilegeToUser(user.getName(), nodeName, 1, false); - authorizer.grantPrivilegeToRole(roleName, nodeName, 3, false); + authorizer.grantRoleToUser(roleName, userName); + authorizer.grantPrivilegeToUser( + userName, new PrivilegeUnion(nodeName, PrivilegeType.WRITE_DATA, false)); + authorizer.grantPrivilegeToRole( + roleName, new PrivilegeUnion(nodeName, PrivilegeType.WRITE_SCHEMA, false)); // a user can get all role permissions. - Set permissions = authorizer.getPrivileges(user.getName(), nodeName); + Set permissions = authorizer.getPrivileges(userName, nodeName); assertEquals(2, permissions.size()); - assertTrue(permissions.contains(1)); - assertTrue(permissions.contains(3)); - assertFalse(permissions.contains(4)); + assertTrue(permissions.contains(PrivilegeType.WRITE_DATA)); + assertTrue(permissions.contains(PrivilegeType.WRITE_SCHEMA)); + assertFalse(permissions.contains(PrivilegeType.READ_DATA)); try { - authorizer.grantRoleToUser(roleName, user.getName()); + authorizer.grantRoleToUser(roleName, userName); } catch (AuthException e) { Assert.assertEquals("User user already has role role", e.getMessage()); } // revoke a role from a user, the user will lose all role's permission - authorizer.revokeRoleFromUser(roleName, user.getName()); - Set revokeRolePermissions = authorizer.getPrivileges(user.getName(), nodeName); + authorizer.revokeRoleFromUser(roleName, userName); + Set revokeRolePermissions = authorizer.getPrivileges(userName, nodeName); assertEquals(1, revokeRolePermissions.size()); - assertTrue(revokeRolePermissions.contains(1)); - assertFalse(revokeRolePermissions.contains(2)); + assertTrue(revokeRolePermissions.contains(PrivilegeType.WRITE_DATA)); + assertFalse(revokeRolePermissions.contains(PrivilegeType.READ_SCHEMA)); // check the users' permission again - Assert.assertTrue(authorizer.checkUserPrivileges(user.getName(), nodeName, 1)); - Assert.assertFalse(authorizer.checkUserPrivileges(user.getName(), nodeName, 2)); + Assert.assertTrue( + authorizer.checkUserPrivileges( + userName, new PrivilegeUnion(nodeName, PrivilegeType.WRITE_DATA))); + Assert.assertFalse( + authorizer.checkUserPrivileges( + userName, new PrivilegeUnion(nodeName, PrivilegeType.READ_SCHEMA))); try { - authorizer.grantRoleToUser("role1", user.getName()); + authorizer.grantRoleToUser("role1", userName); } catch (AuthException e) { Assert.assertEquals("No such role : role1", e.getMessage()); } @@ -259,16 +228,9 @@ public void testUserRole() throws AuthException { @Test public void testUpdatePassword() throws AuthException { - authorizer.createUser(user.getName(), user.getPassword()); - authorizer.updateUserPassword(user.getName(), "newPassword"); - Assert.assertTrue(authorizer.login(user.getName(), "newPassword")); - } - - @Test - public void testUserWaterMark() throws AuthException { - authorizer.setUserUseWaterMark("root", true); - assertTrue(authorizer.getAllUserWaterMarkStatus().get("root")); - Assert.assertTrue(authorizer.isUserUseWaterMark("root")); + authorizer.createUser(userName, password); + authorizer.updateUserPassword(userName, "newPassword"); + Assert.assertTrue(authorizer.login(userName, "newPassword")); } @Test @@ -343,14 +305,4 @@ public void testListRole() throws AuthException { } } } - - @Test - public void testReplaceAllRole() throws AuthException { - IAuthorizer authorizer = BasicAuthorizer.getInstance(); - Role role = new Role("role"); - HashMap roles = new HashMap<>(); - roles.put("role", role); - authorizer.replaceAllRoles(roles); - Assert.assertEquals("role", authorizer.listAllRoles().get(0)); - } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/DataBasePrivilegeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/DataBasePrivilegeTest.java new file mode 100644 index 000000000000..04ae4dfddee7 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/DataBasePrivilegeTest.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.db.auth.entity; + +import org.apache.iotdb.commons.auth.entity.DatabasePrivilege; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.IOException; +import java.nio.ByteBuffer; + +public class DataBasePrivilegeTest { + @Test + public void test() { + DatabasePrivilege dbPrivilege = new DatabasePrivilege("database"); + dbPrivilege.grantDBPrivilege(PrivilegeType.ALTER); + dbPrivilege.grantDBPrivilege(PrivilegeType.INSERT); + dbPrivilege.grantDBGrantOption(PrivilegeType.ALTER); + dbPrivilege.grantTablePrivilege("test", PrivilegeType.SELECT); + dbPrivilege.grantTablePrivilege("test", PrivilegeType.DELETE); + dbPrivilege.grantTablePrivilege("test2", PrivilegeType.SELECT); + dbPrivilege.grantTableGrantOption("test", PrivilegeType.SELECT); + Assert.assertTrue(dbPrivilege.checkDBPrivilege(PrivilegeType.ALTER)); + Assert.assertFalse(dbPrivilege.checkDBPrivilege(PrivilegeType.DELETE)); + Assert.assertTrue(dbPrivilege.checkDBGrantOption(PrivilegeType.ALTER)); + Assert.assertTrue(dbPrivilege.checkTablePrivilege("test", PrivilegeType.SELECT)); + Assert.assertFalse(dbPrivilege.checkTablePrivilege("test2", PrivilegeType.CREATE)); + Assert.assertTrue(dbPrivilege.checkTableGrantOption("test", PrivilegeType.SELECT)); + DatabasePrivilege dbPrivilege2 = new DatabasePrivilege(); + try { + ByteBuffer byteBuffer = dbPrivilege.serialize(); + dbPrivilege2.deserialize(byteBuffer); + } catch (IOException e) { + Assert.fail(); + } + Assert.assertEquals(dbPrivilege, dbPrivilege2); + String toString = dbPrivilege.toString(); + Assert.assertEquals( + toString, + "Database(database):{ALTER_with_grant_option,INSERT,;" + + " Tables: [ test2(SELECT,) test(SELECT_with_grant_option,DELETE,)]}"); + int mask = dbPrivilege.getAllPrivileges(); + dbPrivilege2.revokeTablePrivilege("test", PrivilegeType.SELECT); + dbPrivilege2.revokeTableGrantOption("test", PrivilegeType.SELECT); + dbPrivilege2.revokeTablePrivilege("test", PrivilegeType.DELETE); + Assert.assertEquals(dbPrivilege2.getTablePrivilegeMap().size(), 1); + dbPrivilege2.revokeTablePrivilege("test2", PrivilegeType.SELECT); + dbPrivilege2.revokeDBPrivilege(PrivilegeType.INSERT); + dbPrivilege2.revokeDBGrantOption(PrivilegeType.ALTER); + Assert.assertEquals(dbPrivilege2.getTablePrivilegeMap().size(), 0); + Assert.assertFalse(dbPrivilege2.checkTablePrivilege("test", PrivilegeType.SELECT)); + dbPrivilege2.setPrivileges(mask); + Assert.assertEquals(dbPrivilege.getPrivilegeSet(), dbPrivilege2.getPrivilegeSet()); + Assert.assertEquals( + dbPrivilege.getPrivilegeGrantOptSet(), dbPrivilege2.getPrivilegeGrantOptSet()); + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/PathPrivilegeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/PathPrivilegeTest.java index b8e61223cf08..9f65b674e36b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/PathPrivilegeTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/PathPrivilegeTest.java @@ -26,22 +26,22 @@ import org.junit.Assert; import org.junit.Test; +import java.io.IOException; import java.util.Collections; import java.util.HashSet; -import java.util.Objects; public class PathPrivilegeTest { @Test - public void testPathPrivilege_Init() throws IllegalPathException { + public void testPathPrivilege_Init() throws IllegalPathException, IOException { PathPrivilege pathPrivilege = new PathPrivilege(); pathPrivilege.setPath(new PartialPath("root.ln")); - pathPrivilege.setPrivileges(Collections.singleton(1)); + pathPrivilege.setPrivileges(Collections.singleton(PrivilegeType.WRITE_DATA)); pathPrivilege.setGrantOpt(new HashSet<>()); Assert.assertEquals("root.ln : WRITE_DATA", pathPrivilege.toString()); PathPrivilege pathPrivilege1 = new PathPrivilege(); pathPrivilege1.setPath(new PartialPath("root.sg")); - pathPrivilege1.setPrivileges(Collections.singleton(1)); + pathPrivilege1.setPrivileges(Collections.singleton(PrivilegeType.WRITE_DATA)); pathPrivilege1.setGrantOpt(new HashSet<>()); Assert.assertNotEquals(pathPrivilege, pathPrivilege1); pathPrivilege.deserialize(pathPrivilege1.serialize()); @@ -52,28 +52,28 @@ public void testPathPrivilege_Init() throws IllegalPathException { public void testPathPrivilege_GrantAndRevoke() throws IllegalPathException { PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.ln")); - pathPrivilege.grantPrivilege(PrivilegeType.READ_DATA.ordinal(), false); - pathPrivilege.grantPrivilege(PrivilegeType.READ_SCHEMA.ordinal(), true); - pathPrivilege.grantPrivilege(PrivilegeType.WRITE_SCHEMA.ordinal(), true); + pathPrivilege.grantPrivilege(PrivilegeType.READ_DATA, false); + pathPrivilege.grantPrivilege(PrivilegeType.READ_SCHEMA, true); + pathPrivilege.grantPrivilege(PrivilegeType.WRITE_SCHEMA, true); - Assert.assertEquals(pathPrivilege.getPrivileges().size(), 3); + Assert.assertEquals(pathPrivilege.getPrivilegeIntSet().size(), 3); Assert.assertEquals(pathPrivilege.getGrantOpt().size(), 2); - Assert.assertTrue(pathPrivilege.getGrantOpt().contains(PrivilegeType.READ_SCHEMA.ordinal())); - Assert.assertTrue(pathPrivilege.getGrantOpt().contains(PrivilegeType.WRITE_SCHEMA.ordinal())); + Assert.assertTrue(pathPrivilege.getGrantOpt().contains(PrivilegeType.READ_SCHEMA)); + Assert.assertTrue(pathPrivilege.getGrantOpt().contains(PrivilegeType.WRITE_SCHEMA)); - pathPrivilege.revokePrivilege(PrivilegeType.READ_SCHEMA.ordinal()); - Assert.assertFalse(pathPrivilege.revokePrivilege(PrivilegeType.READ_SCHEMA.ordinal())); + pathPrivilege.revokePrivilege(PrivilegeType.READ_SCHEMA); + Assert.assertFalse(pathPrivilege.revokePrivilege(PrivilegeType.READ_SCHEMA)); - HashSet privs = new HashSet<>(); - privs.add(PrivilegeType.WRITE_SCHEMA.ordinal()); + HashSet privs = new HashSet<>(); + privs.add(PrivilegeType.WRITE_SCHEMA); Assert.assertEquals(pathPrivilege.getGrantOpt(), privs); - privs.add(PrivilegeType.READ_DATA.ordinal()); + privs.add(PrivilegeType.READ_DATA); Assert.assertEquals(pathPrivilege.getPrivileges(), privs); - Assert.assertFalse(pathPrivilege.revokeGrantOpt(PrivilegeType.READ_SCHEMA.ordinal())); - Assert.assertTrue(pathPrivilege.revokeGrantOpt(PrivilegeType.WRITE_SCHEMA.ordinal())); + Assert.assertFalse(pathPrivilege.revokeGrantOpt(PrivilegeType.READ_SCHEMA)); + Assert.assertTrue(pathPrivilege.revokeGrantOpt(PrivilegeType.WRITE_SCHEMA)); Assert.assertEquals(pathPrivilege.getGrantOpt().size(), 0); } @@ -82,14 +82,14 @@ public void testPrivilegePath_GetAllPrivilegeMask() throws IllegalPathException PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.ln")); - pathPrivilege.grantPrivilege(PrivilegeType.READ_DATA.ordinal(), false); - pathPrivilege.grantPrivilege(PrivilegeType.READ_SCHEMA.ordinal(), true); - pathPrivilege.grantPrivilege(PrivilegeType.WRITE_SCHEMA.ordinal(), true); + pathPrivilege.grantPrivilege(PrivilegeType.READ_DATA, false); + pathPrivilege.grantPrivilege(PrivilegeType.READ_SCHEMA, true); + pathPrivilege.grantPrivilege(PrivilegeType.WRITE_SCHEMA, true); // mask as : 0000-0000-0000-1100|0000-0000-0000-1101 Assert.assertEquals(pathPrivilege.getAllPrivileges(), (0b11 << (2 + 16)) | (0b1101)); PathPrivilege pathPrivilege2 = new PathPrivilege(new PartialPath("root.ln")); pathPrivilege2.setAllPrivileges((0b11 << (2 + 16)) | (0b1101)); - Assert.assertTrue(Objects.equals(pathPrivilege, pathPrivilege2)); + Assert.assertEquals(pathPrivilege, pathPrivilege2); } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/RoleTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/RoleTest.java index 8b527eb235eb..83b410ba8efd 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/RoleTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/RoleTest.java @@ -18,9 +18,11 @@ */ package org.apache.iotdb.db.auth.entity; +import org.apache.iotdb.commons.auth.entity.DatabasePrivilege; import org.apache.iotdb.commons.auth.entity.PathPrivilege; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.Role; +import org.apache.iotdb.commons.auth.entity.TablePrivilege; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; @@ -37,39 +39,60 @@ public void testRole_InitAndSerialize() throws IllegalPathException { Role role = new Role("role"); PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.ln")); role.setPrivilegeList(Collections.singletonList(pathPrivilege)); - role.addPathPrivilege(new PartialPath("root.ln"), 1, false); - role.addPathPrivilege(new PartialPath("root.ln"), 2, true); + role.grantPathPrivilege(new PartialPath("root.ln"), PrivilegeType.READ_SCHEMA, true); + role.grantPathPrivilege(new PartialPath("root.ln"), PrivilegeType.READ_DATA, false); + Assert.assertEquals( - "Role{name='role', pathPrivilegeList=[root.ln : WRITE_DATA" - + " READ_SCHEMA_with_grant_option], systemPrivilegeSet=[]}", + "Role{name='role', pathPrivilegeList=[root.ln : " + + "READ_DATA READ_SCHEMA_with_grant_option], systemPrivilegeSet=[], " + + "AnyScopePrivilegeMap=[], objectPrivilegeSet={}}", role.toString()); Role role1 = new Role("role1"); role1.deserialize(role.serialize()); Assert.assertEquals( "Role{name='role', pathPrivilegeList=[root.ln : " - + "WRITE_DATA READ_SCHEMA_with_grant_option], systemPrivilegeSet=[]}", + + "READ_DATA READ_SCHEMA_with_grant_option], systemPrivilegeSet=[], " + + "AnyScopePrivilegeMap=[], objectPrivilegeSet={}}", role1.toString()); Role admin = new Role("root"); PartialPath rootPath = new PartialPath(IoTDBConstant.PATH_ROOT + ".**"); PathPrivilege pathPri = new PathPrivilege(rootPath); + DatabasePrivilege databasePrivilege = new DatabasePrivilege("testDB"); + TablePrivilege tablePrivilege = new TablePrivilege("testTable"); + databasePrivilege.getTablePrivilegeMap().put("testTable", tablePrivilege); for (PrivilegeType item : PrivilegeType.values()) { - if (!item.isPathRelevant()) { - admin.getSysPrivilege().add(item.ordinal()); - admin.getSysPriGrantOpt().add(item.ordinal()); - } else { - pathPri.grantPrivilege(item.ordinal(), true); + if (item.isSystemPrivilege()) { + admin.getSysPrivilege().add(item); + admin.getSysPriGrantOpt().add(item); + } else if (item.isPathPrivilege()) { + pathPri.grantPrivilege(item, true); + } else if (item.isRelationalPrivilege()) { + databasePrivilege.grantDBPrivilege(item); + databasePrivilege.grantDBGrantOption(item); + databasePrivilege.grantTablePrivilege("testTable", item); + databasePrivilege.grantTableGrantOption("testTable", item); + admin.grantAnyScopePrivilege(item, true); } } + admin.getDBScopePrivilegeMap().put("testDB", databasePrivilege); admin.getPathPrivilegeList().add(pathPri); Assert.assertEquals( - "Role{name='root', pathPrivilegeList=[root.** : READ_DAT" - + "A_with_grant_option WRITE_DATA_with_grant_option READ_SCHEMA_with" - + "_grant_option WRITE_SCHEMA_with_grant_option], systemPrivilegeSet=[MANAGE_ROLE" - + "_with_grant_option , USE_UDF_with_grant_option , USE_CQ_with_grant_option , USE" - + "_PIPE_with_grant_option , USE_TRIGGER_with_grant_option , MANAGE_DATABASE_with_g" - + "rant_option , MANAGE_USER_with_grant_option , MAINTAIN_with_grant_option , EXTEND" - + "_TEMPLATE_with_grant_option , USE_MODEL_with_grant_option ]}", + "Role{name='root', pathPrivilegeList=[root.** : READ_DATA_wi" + + "th_grant_option WRITE_DATA_with_grant_option READ_SCHEMA" + + "_with_grant_option WRITE_SCHEMA_with_grant_option], systemPr" + + "ivilegeSet=[MANAGE_USER_with_grant_option, USE_TRIGGER_with_gra" + + "nt_option, USE_MODEL_with_grant_option, MAINTAIN_with_grant_option" + + ", USE_CQ_with_grant_option, USE_PIPE_with_grant_option, USE_UDF_wit" + + "h_grant_option, EXTEND_TEMPLATE_with_grant_option, MANAGE_ROLE_with_gr" + + "ant_option, MANAGE_DATABASE_with_grant_option], AnyScopePrivilegeMap=[D" + + "ELETE_with_grant_option, DROP_with_grant_option, ALTER_with_grant_optio" + + "n, CREATE_with_grant_option, SELECT_with_grant_option, INSERT_with_gran" + + "t_option], objectPrivilegeSet={testDB=Database(testDB):{CREATE_with_gran" + + "t_option,DROP_with_grant_option,ALTER_with_grant_option,SELECT_with_grant_" + + "option,INSERT_with_grant_option,DELETE_with_grant_option,; Tables: [ testTa" + + "ble(CREATE_with_grant_option,DROP_with_grant_option,ALTER_with_grant_option" + + ",SELECT_with_grant_option,INSERT_with_grant_option,DELETE_with_grant_option,)]}}}", admin.toString()); } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/TablePrivilegeTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/TablePrivilegeTest.java new file mode 100644 index 000000000000..22a0d45629b4 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/TablePrivilegeTest.java @@ -0,0 +1,67 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.db.auth.entity; + +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.TablePrivilege; + +import org.junit.Assert; +import org.junit.Test; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; + +public class TablePrivilegeTest { + @Test + public void testTablePrivilege_Init() { + TablePrivilege tablePrivilege = new TablePrivilege("table"); + tablePrivilege.grantPrivilege(PrivilegeType.SELECT); + tablePrivilege.grantPrivilege(PrivilegeType.ALTER); + tablePrivilege.grantOption(PrivilegeType.DROP); + Assert.assertEquals(2, tablePrivilege.getPrivileges().size()); + Assert.assertEquals(1, tablePrivilege.getGrantOption().size()); + tablePrivilege.grantOption(PrivilegeType.SELECT); + Assert.assertEquals(2, tablePrivilege.getGrantOption().size()); + tablePrivilege.revokePrivilege(PrivilegeType.SELECT); + Assert.assertEquals(2, tablePrivilege.getGrantOption().size()); + Assert.assertEquals(1, tablePrivilege.getPrivileges().size()); + tablePrivilege.grantOption(PrivilegeType.DROP); + Assert.assertEquals(2, tablePrivilege.getGrantOption().size()); + tablePrivilege.revokeGrantOption(PrivilegeType.DROP); + Assert.assertEquals(1, tablePrivilege.getGrantOption().size()); + } + + @Test + public void testTablePrivilegeSerialize() throws IOException { + TablePrivilege tablePrivilege = new TablePrivilege("test"); + tablePrivilege.grantPrivilege(PrivilegeType.SELECT); + tablePrivilege.grantPrivilege(PrivilegeType.ALTER); + tablePrivilege.grantPrivilege(PrivilegeType.DROP); + tablePrivilege.grantOption(PrivilegeType.SELECT); + tablePrivilege.grantOption(PrivilegeType.ALTER); + TablePrivilege tablePrivilege1 = new TablePrivilege(); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); + tablePrivilege.serialize(dataOutputStream); + tablePrivilege1.deserialize(ByteBuffer.wrap(byteArrayOutputStream.toByteArray())); + Assert.assertEquals(tablePrivilege, tablePrivilege1); + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/UserTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/UserTest.java index 4e97ac3a3594..42d9c7c497c9 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/UserTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/entity/UserTest.java @@ -19,6 +19,7 @@ package org.apache.iotdb.db.auth.entity; import org.apache.iotdb.commons.auth.entity.PathPrivilege; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; @@ -35,17 +36,18 @@ public void testUser() throws IllegalPathException { User user = new User("user", "password"); PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.ln")); user.setPrivilegeList(Collections.singletonList(pathPrivilege)); - user.setPathPrivileges(new PartialPath("root.ln"), Collections.singleton(1)); + user.setPathPrivileges( + new PartialPath("root.ln"), Collections.singleton(PrivilegeType.WRITE_DATA)); Assert.assertEquals( - "User{name='user', password='password', pathPrivilegeList=[root.ln : WRITE_DATA], sysPrivilegeSet=[], roleList=[], " - + "isOpenIdUser=false, useWaterMark=false}", + "User{name='user', password='password', pathPrivilegeList=[root.ln : WRITE_DATA], " + + "sysPrivilegeSet=[], AnyScopePrivilegeMap=[], objectPrivilegeMap={}, roleList=[], isOpenIdUser=false}", user.toString()); User user1 = new User("user1", "password1"); user1.deserialize(user.serialize()); Assert.assertEquals( - "User{name='user', password='password', pathPrivilegeList=[root.ln : WRITE_DATA], sysPrivilegeSet=[], roleList=[], " - + "isOpenIdUser=false, useWaterMark=false}", + "User{name='user', password='password', pathPrivilegeList=[root.ln : WRITE_DATA], " + + "sysPrivilegeSet=[], AnyScopePrivilegeMap=[], objectPrivilegeMap={}, roleList=[], isOpenIdUser=false}", user1.toString()); - Assert.assertTrue(user1.equals(user)); + Assert.assertEquals(user1, user); } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/role/LocalFileRoleAccessorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/role/LocalFileRoleAccessorTest.java index 4153c3701bd0..df94f950113c 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/role/LocalFileRoleAccessorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/role/LocalFileRoleAccessorTest.java @@ -19,7 +19,7 @@ package org.apache.iotdb.db.auth.role; import org.apache.iotdb.commons.auth.entity.PathPrivilege; -import org.apache.iotdb.commons.auth.entity.PriPrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.role.LocalFileRoleAccessor; import org.apache.iotdb.commons.exception.IllegalPathException; @@ -34,8 +34,6 @@ import java.io.File; import java.io.IOException; -import java.util.ArrayList; -import java.util.HashSet; import java.util.List; import static org.junit.Assert.assertEquals; @@ -69,81 +67,56 @@ public void test() throws IOException, IllegalPathException { roles[i] = new Role("role" + i); for (int j = 0; j <= i; j++) { PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.a.b.c" + j)); - pathPrivilege.getPrivileges().add(j); + pathPrivilege.grantPrivilege(PrivilegeType.values()[j], true); roles[i].getPathPrivilegeList().add(pathPrivilege); - roles[i].getSysPrivilege().add(i + 4); - if (i % 2 != 0) { - roles[i].getSysPriGrantOpt().add(i + 4); + } + roles[i].grantSysPrivilege(PrivilegeType.values()[i + 4], false); + roles[i].grantDBPrivilege("testdb", PrivilegeType.CREATE, false); + roles[i].grantTBPrivilege("testdb", "table", PrivilegeType.ALTER, true); + roles[i].grantAnyScopePrivilege(PrivilegeType.INSERT, true); + if (i % 2 != 0) { + roles[i].grantSysPrivilegeGrantOption(PrivilegeType.values()[i + 4]); + } + } + + for (Role role : roles) { + for (PrivilegeType item : PrivilegeType.values()) { + if (item.isRelationalPrivilege()) { + role.grantDBPrivilege("testdb", item, true); + if (item.ordinal() % 2 == 0) { + role.grantTBPrivilege("testdb", "testtb", item, false); + } } } } // save for (Role role : roles) { - accessor.saveRole(role); + accessor.saveEntity(role); } // load for (Role role : roles) { - Role loadedRole = accessor.loadRole(role.getName()); + Role loadedRole = accessor.loadEntity(role.getName()); assertEquals(role, loadedRole); } - assertNull(accessor.loadRole("not a role")); + assertNull(accessor.loadEntity("not a role")); // delete - assertTrue(accessor.deleteRole(roles[roles.length - 1].getName())); - assertFalse(accessor.deleteRole(roles[roles.length - 1].getName())); - assertNull(accessor.loadRole(roles[roles.length - 1].getName())); + assertTrue(accessor.deleteEntity(roles[roles.length - 1].getName())); + assertFalse(accessor.deleteEntity(roles[roles.length - 1].getName())); + assertNull(accessor.loadEntity(roles[roles.length - 1].getName())); // list - List roleNames = accessor.listAllRoles(); + List roleNames = accessor.listAllEntities(); roleNames.sort(null); for (int i = 0; i < roleNames.size(); i++) { assertEquals(roles[i].getName(), roleNames.get(i)); + accessor.deleteEntity(roleNames.get(i)); } - } - - @Test - public void testLoadOldVersion() throws IOException, IllegalPathException { - // In this test, we will store role with old func and role might have illegal path. - Role role = new Role(); - role.setName("root"); - List pathPriList = new ArrayList<>(); - PathPrivilege rootPathPriv = new PathPrivilege(new PartialPath("root.**")); - PathPrivilege normalPathPriv = new PathPrivilege(new PartialPath("root.b.c.**")); - PathPrivilege wroPathPriv = new PathPrivilege(new PartialPath("root.c.*.d")); - PathPrivilege wroPathPriv2 = new PathPrivilege(new PartialPath("root.c.*.**")); - for (PriPrivilegeType item : PriPrivilegeType.values()) { - // ALL will never appear in file. - if (item.ordinal() == PriPrivilegeType.ALL.ordinal()) { - continue; - } - if (item.isPrePathRelevant()) { - normalPathPriv.grantPrivilege(item.ordinal(), false); - wroPathPriv.grantPrivilege(item.ordinal(), false); - wroPathPriv2.grantPrivilege(item.ordinal(), false); - } - rootPathPriv.grantPrivilege(item.ordinal(), false); - } - - pathPriList.add(rootPathPriv); - pathPriList.add(normalPathPriv); - pathPriList.add(wroPathPriv); - pathPriList.add(wroPathPriv2); - role.setPrivilegeList(pathPriList); - role.setSysPriGrantOpt(new HashSet<>()); - role.setSysPrivilegeSet(new HashSet<>()); - accessor.saveRoleOldVer(role); - Role newRole = accessor.loadRole("root"); - assertEquals("root", newRole.getName()); - assertFalse(newRole.getServiceReady()); - assertEquals(4, newRole.getPathPrivilegeList().size()); - for (PathPrivilege path : newRole.getPathPrivilegeList()) { - if (!path.getPath().equals(new PartialPath("root.**"))) { - assertEquals(17, path.getPrivileges().size()); - } else { - assertEquals(33, path.getPrivileges().size()); - } + String[] files = testFolder.list(); + if (files != null) { + assertEquals(0, files.length); } } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/role/LocalFileRoleManagerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/role/LocalFileRoleManagerTest.java index b838ae25a968..f0b9f5baf7a5 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/role/LocalFileRoleManagerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/role/LocalFileRoleManagerTest.java @@ -20,17 +20,15 @@ import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.PathPrivilege; -import org.apache.iotdb.commons.auth.entity.PriPrivilegeType; import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.role.LocalFileRoleManager; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.commons.utils.AuthUtils; import org.apache.iotdb.db.utils.EnvironmentUtils; import org.apache.iotdb.db.utils.constant.TestConstant; -import io.jsonwebtoken.lang.Assert; import org.apache.commons.io.FileUtils; import org.junit.After; import org.junit.Before; @@ -70,164 +68,78 @@ public void test() throws AuthException, IllegalPathException { roles[i] = new Role("role" + i); for (int j = 0; j <= i; j++) { PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.a.b.c" + j)); - pathPrivilege.getPrivileges().add(j); + pathPrivilege.grantPrivilege(PrivilegeType.values()[j], false); roles[i].getPathPrivilegeList().add(pathPrivilege); - roles[i].getSysPrivilege().add(j + 4); + roles[i].getSysPrivilege().add(PrivilegeType.values()[j + 4]); } } // create - Role role = manager.getRole(roles[0].getName()); + Role role = manager.getEntity(roles[0].getName()); assertNull(role); for (Role role1 : roles) { assertTrue(manager.createRole(role1.getName())); } for (Role role1 : roles) { - role = manager.getRole(role1.getName()); + role = manager.getEntity(role1.getName()); assertEquals(role1.getName(), role.getName()); } assertFalse(manager.createRole(roles[0].getName())); - boolean caught = false; // delete - assertFalse(manager.deleteRole("not a role")); - assertTrue(manager.deleteRole(roles[roles.length - 1].getName())); - assertNull(manager.getRole(roles[roles.length - 1].getName())); - assertFalse(manager.deleteRole(roles[roles.length - 1].getName())); + assertFalse(manager.deleteEntity("not a role")); + assertTrue(manager.deleteEntity(roles[roles.length - 1].getName())); + assertNull(manager.getEntity(roles[roles.length - 1].getName())); + assertFalse(manager.deleteEntity(roles[roles.length - 1].getName())); // grant privilege - role = manager.getRole(roles[0].getName()); + role = manager.getEntity(roles[0].getName()); PartialPath path = new PartialPath("root.a.b.c"); - int privilegeId = 0; - assertFalse(role.hasPrivilegeToRevoke(path, privilegeId)); - manager.grantPrivilegeToRole(role.getName(), path, privilegeId, false); - manager.grantPrivilegeToRole(role.getName(), path, privilegeId + 1, false); + assertFalse(role.hasPrivilegeToRevoke(path, PrivilegeType.READ_DATA)); + manager.grantPrivilegeToEntity( + role.getName(), new PrivilegeUnion(path, PrivilegeType.READ_DATA)); + manager.grantPrivilegeToEntity( + role.getName(), new PrivilegeUnion(path, PrivilegeType.WRITE_DATA)); + // grant again will success - manager.grantPrivilegeToRole(role.getName(), path, privilegeId, false); - role = manager.getRole(roles[0].getName()); - assertTrue(role.hasPrivilegeToRevoke(path, privilegeId)); - manager.grantPrivilegeToRole(role.getName(), null, PrivilegeType.MAINTAIN.ordinal(), true); - manager.grantPrivilegeToRole(role.getName(), null, PrivilegeType.MAINTAIN.ordinal(), true); - caught = false; + manager.grantPrivilegeToEntity( + role.getName(), new PrivilegeUnion(path, PrivilegeType.WRITE_DATA)); + role = manager.getEntity(roles[0].getName()); + assertTrue(role.hasPrivilegeToRevoke(path, PrivilegeType.READ_DATA)); + manager.grantPrivilegeToEntity(role.getName(), new PrivilegeUnion(PrivilegeType.MAINTAIN)); + manager.grantPrivilegeToEntity( + role.getName(), new PrivilegeUnion(PrivilegeType.MANAGE_ROLE, true)); + boolean caught = false; try { - manager.grantPrivilegeToRole("not a role", path, privilegeId, false); + manager.grantPrivilegeToEntity("not a role", new PrivilegeUnion(PrivilegeType.MANAGE_ROLE)); } catch (AuthException e) { caught = true; } assertTrue(caught); // revoke privilege - role = manager.getRole(roles[0].getName()); - assertTrue(manager.revokePrivilegeFromRole(role.getName(), path, privilegeId)); - assertFalse(manager.revokePrivilegeFromRole(role.getName(), path, privilegeId)); - assertFalse( - manager.revokePrivilegeFromRole(role.getName(), null, PrivilegeType.USE_PIPE.ordinal())); - assertTrue( - manager.revokePrivilegeFromRole(role.getName(), null, PrivilegeType.MAINTAIN.ordinal())); - assertEquals(manager.getRole(role.getName()).getSysPriGrantOpt().size(), 0); + role = manager.getEntity(roles[0].getName()); + manager.revokePrivilegeFromEntity(role.getName(), new PrivilegeUnion(PrivilegeType.MAINTAIN)); + manager.revokePrivilegeFromEntity( + role.getName(), new PrivilegeUnion(PrivilegeType.MANAGE_USER)); + manager.revokePrivilegeFromEntity( + role.getName(), + new PrivilegeUnion(new PartialPath("root.test"), PrivilegeType.WRITE_SCHEMA)); + assertEquals(1, manager.getEntity(role.getName()).getSysPriGrantOpt().size()); caught = false; try { - manager.revokePrivilegeFromRole("not a role", path, privilegeId); + manager.revokePrivilegeFromEntity("not a role", new PrivilegeUnion(PrivilegeType.MAINTAIN)); } catch (AuthException e) { caught = true; } assertTrue(caught); // list roles - List rolenames = manager.listAllRoles(); - rolenames.sort(null); + List roleNames = manager.listAllEntities(); + roleNames.sort(null); for (int i = 0; i < roles.length - 1; i++) { - assertEquals(roles[i].getName(), rolenames.get(i)); - } - } - - @Test - public void testPathCheckForUpgrade() throws AuthException, IllegalPathException { - manager.createRole("test"); - manager.setPreVersion(true); - // In this case. We will grant role some preversion. And try to refresh its privilege. - // the result: - // 1. all privileges will be turned to current privileges. - // 2. all illegal paths will be turned to legal path. - - // for Pre version, all global system (including ALL) will have a default path:root.** - for (PriPrivilegeType item : PriPrivilegeType.values()) { - if (item == PriPrivilegeType.ALL) { - continue; - } - if (item.isAccept()) { - if (item.isPrePathRelevant()) { - // turn to root.d.a -- legal path - manager.grantPrivilegeToRole("test", new PartialPath("root.d.a"), item.ordinal(), false); - // turn to root.ds.a.** -- illegal path - manager.grantPrivilegeToRole( - "test", new PartialPath("root.ds.a.b*"), item.ordinal(), false); - // will be turned to path "root.ds.a.**" like previous - manager.grantPrivilegeToRole( - "test", new PartialPath("root.ds.a.c*"), item.ordinal(), false); - } else { - manager.grantPrivilegeToRole("test", new PartialPath("root.**"), item.ordinal(), false); - } - } - } - Assert.isTrue(manager.getRole("test").getPathPrivilegeList().size() == 4); - Assert.isTrue(!manager.getRole("test").getServiceReady()); - manager.checkAndRefreshPathPri(); - - // after refresh. we will have three path: - // 1. root.d.a - // 2. root.ds.a.** - // 3. root.** - - // only: write_data, write_schema, read_data - assertEquals(3, manager.getRole("test").getPathPrivileges(new PartialPath("root.d.a")).size()); - assertEquals( - 3, manager.getRole("test").getPathPrivileges(new PartialPath("root.ds.a.**")).size()); - // All system privileges in root.** will be turned into system privileges set. - assertEquals(0, manager.getRole("test").getPathPrivileges(new PartialPath("root.**")).size()); - assertEquals( - PrivilegeType.getSysPriCount() - 3, manager.getRole("test").getSysPrivilege().size()); - - manager.getRole("test").getPathPrivilegeList().clear(); - manager.getRole("test").getSysPrivilege().clear(); - } - - @Test - public void testPrivRefreshSingle() throws AuthException, IllegalPathException { - manager.createRole("test"); - manager.setPreVersion(true); - for (PriPrivilegeType item : PriPrivilegeType.values()) { - if (item == PriPrivilegeType.ALL) { - continue; - } - if (item.isAccept()) { - if (item.isPrePathRelevant()) { - // turn to root.d.a -- legal path - manager.grantPrivilegeToRole("test", new PartialPath("root.d.a"), item.ordinal(), false); - // turn to root.ds.a.** -- illegal path - manager.grantPrivilegeToRole( - "test", new PartialPath("root.ds.a.b*"), item.ordinal(), false); - } else { - manager.grantPrivilegeToRole("test", new PartialPath("root.**"), item.ordinal(), false); - } - } - manager.checkAndRefreshPathPri(); - PartialPath path1 = AuthUtils.convertPatternPath(new PartialPath("root.ds.a.b*")); - PartialPath path2 = new PartialPath("root.d.a"); - for (PrivilegeType pri : item.getSubPri()) { - if (pri.isPathRelevant()) { - Assert.isTrue(manager.getRole("test").checkPathPrivilege(path1, pri.ordinal())); - Assert.isTrue(manager.getRole("test").checkPathPrivilege(path2, pri.ordinal())); - manager.getRole("test").removePathPrivilege(path1, pri.ordinal()); - manager.getRole("test").removePathPrivilege(path2, pri.ordinal()); - } else { - Assert.isTrue(manager.getRole("test").checkSysPrivilege(pri.ordinal())); - manager.getRole("test").removeSysPrivilege(pri.ordinal()); - } - } - Assert.isTrue(manager.getRole("test").getPathPrivilegeList().isEmpty()); - Assert.isTrue(manager.getRole("test").getSysPrivilege().isEmpty()); + assertEquals(roles[i].getName(), roleNames.get(i)); } } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserAccessorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserAccessorTest.java index f4a9d7dd195a..72484993ad3b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserAccessorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserAccessorTest.java @@ -16,10 +16,11 @@ * specific language governing permissions and limitations * under the License. */ + package org.apache.iotdb.db.auth.user; import org.apache.iotdb.commons.auth.entity.PathPrivilege; -import org.apache.iotdb.commons.auth.entity.PriPrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.auth.user.LocalFileUserAccessor; import org.apache.iotdb.commons.exception.IllegalPathException; @@ -42,7 +43,6 @@ import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; public class LocalFileUserAccessorTest { @@ -65,100 +65,71 @@ public void tearDown() throws Exception { @Test public void test() throws IOException, IllegalPathException { - User[] users = new User[4]; - for (int i = 0; i < users.length; i++) { - users[i] = new User("user" + i, "password" + i); - for (int j = 0; j <= i; j++) { - PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.a.b.c" + j)); - pathPrivilege.getPrivileges().add(j); - users[i].getPathPrivilegeList().add(pathPrivilege); - users[i].getSysPrivilege().add(j + 5); - users[i].getRoleList().add("role" + j); - } - } - - // save - for (User user : users) { - try { - accessor.saveUser(user); - } catch (IOException e) { - fail(e.getMessage()); - } - } - - // load - for (User user : users) { - try { - User loadedUser = accessor.loadUser(user.getName()); - assertEquals(user, loadedUser); - } catch (IOException e) { - fail(e.getMessage()); - } - } - assertNull(accessor.loadUser("not a user")); + User user = new User("test", "password"); + user.grantSysPrivilege(PrivilegeType.EXTEND_TEMPLATE, false); + user.grantSysPrivilege(PrivilegeType.MANAGE_USER, false); + PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.test")); + pathPrivilege.grantPrivilege(PrivilegeType.READ_DATA, true); + pathPrivilege.grantPrivilege(PrivilegeType.WRITE_DATA, false); + user.getPathPrivilegeList().add(pathPrivilege); + user.grantAnyScopePrivilege(PrivilegeType.SELECT, false); + user.grantAnyScopePrivilege(PrivilegeType.ALTER, true); + user.grantDBPrivilege("testdb", PrivilegeType.SELECT, false); + user.grantTBPrivilege("testdb", "testtb", PrivilegeType.ALTER, true); + user.addRole("testRole1"); + user.addRole("testRole2"); + accessor.saveEntity(user); + accessor.reset(); + User loadUser = accessor.loadEntity("test"); + assertEquals(user, loadUser); + user.setName("test1"); + accessor.saveEntity(user); // list - List usernames = accessor.listAllUsers(); + List usernames = accessor.listAllEntities(); usernames.sort(null); - for (int i = 0; i < users.length; i++) { - assertEquals(users[i].getName(), usernames.get(i)); - } + assertTrue(usernames.contains("test")); + assertTrue(usernames.contains("test1")); // delete - assertTrue(accessor.deleteUser("not a user")); - assertTrue(accessor.deleteUser(users[users.length - 1].getName())); - usernames = accessor.listAllUsers(); - assertEquals(users.length - 1, usernames.size()); - usernames.sort(null); - for (int i = 0; i < users.length - 1; i++) { - assertEquals(users[i].getName(), usernames.get(i)); - } - User nullUser = accessor.loadUser(users[users.length - 1].getName()); + assertFalse(accessor.deleteEntity("not a user")); + assertTrue(accessor.deleteEntity(user.getName())); + usernames = accessor.listAllEntities(); + assertEquals(1, usernames.size()); + assertTrue(usernames.contains("test")); + User nullUser = accessor.loadEntity(user.getName()); assertNull(nullUser); } @Test public void testLoadOldVersion() throws IOException, IllegalPathException { - // In this test, we will store role with old func and role might have illegal path. User role = new User(); role.setName("root"); + role.setPassword("password"); List pathPriList = new ArrayList<>(); PathPrivilege rootPathPriv = new PathPrivilege(new PartialPath("root.**")); PathPrivilege normalPathPriv = new PathPrivilege(new PartialPath("root.b.c.**")); - PathPrivilege wroPathPriv = new PathPrivilege(new PartialPath("root.c.*.d")); - PathPrivilege wroPathPriv2 = new PathPrivilege(new PartialPath("root.c.*.**")); - for (PriPrivilegeType item : PriPrivilegeType.values()) { - // ALL will never appear in file. - if (item.ordinal() == PriPrivilegeType.ALL.ordinal()) { - continue; + for (PrivilegeType privilegeType : PrivilegeType.values()) { + if (privilegeType.isRelationalPrivilege()) continue; + if (privilegeType.isSystemPrivilege()) { + role.grantSysPrivilege(privilegeType, true); + } else if (privilegeType.isPathPrivilege()) { + rootPathPriv.grantPrivilege(privilegeType, true); + normalPathPriv.grantPrivilege(privilegeType, true); } - if (item.isPrePathRelevant()) { - normalPathPriv.grantPrivilege(item.ordinal(), false); - wroPathPriv.grantPrivilege(item.ordinal(), false); - wroPathPriv2.grantPrivilege(item.ordinal(), false); - } - rootPathPriv.grantPrivilege(item.ordinal(), false); } - pathPriList.add(rootPathPriv); pathPriList.add(normalPathPriv); - pathPriList.add(wroPathPriv); - pathPriList.add(wroPathPriv2); role.setPrivilegeList(pathPriList); role.setSysPriGrantOpt(new HashSet<>()); role.setSysPrivilegeSet(new HashSet<>()); - role.setRoleList(new ArrayList<>()); + role.setRoleSet(new HashSet<>()); accessor.saveUserOldVersion(role); - User newRole = accessor.loadUser("root"); - assertEquals("root", newRole.getName()); - assertFalse(newRole.getServiceReady()); - assertEquals(4, newRole.getPathPrivilegeList().size()); - for (PathPrivilege path : newRole.getPathPrivilegeList()) { - if (!path.getPath().equals(new PartialPath("root.**"))) { - assertEquals(17, path.getPrivileges().size()); - } else { - assertEquals(33, path.getPrivileges().size()); - } - } + User newRole = accessor.loadEntity("root"); + assertEquals(role, newRole); + newRole.setName("root2"); + accessor.saveEntity(newRole); + User newRole2 = accessor.loadEntity("root2"); + assertEquals(newRole, newRole2); } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserManagerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserManagerTest.java index 43cd6df68dde..55ea7b97f2fa 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserManagerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/auth/user/LocalFileUserManagerTest.java @@ -19,12 +19,8 @@ package org.apache.iotdb.db.auth.user; import org.apache.iotdb.commons.auth.AuthException; -import org.apache.iotdb.commons.auth.entity.PathPrivilege; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.auth.user.LocalFileUserManager; -import org.apache.iotdb.commons.conf.CommonDescriptor; -import org.apache.iotdb.commons.exception.IllegalPathException; -import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.utils.AuthUtils; import org.apache.iotdb.db.utils.EnvironmentUtils; import org.apache.iotdb.db.utils.constant.TestConstant; @@ -36,11 +32,8 @@ import org.junit.Test; import java.io.File; -import java.util.List; -import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class LocalFileUserManagerTest { @@ -63,7 +56,7 @@ public void tearDown() throws Exception { } @Test - public void testIllegalInput() throws AuthException { + public void testIllegalInput() { // Password contains space try { manager.createUser("username1", "password_ ", true); @@ -78,117 +71,11 @@ public void testIllegalInput() throws AuthException { } } - @Test - public void test() throws AuthException, IllegalPathException { - User[] users = new User[5]; - for (int i = 0; i < users.length; i++) { - users[i] = new User("user" + i, "password" + i); - for (int j = 0; j <= i; j++) { - PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.a.b.c" + j)); - pathPrivilege.getPrivileges().add(j); - users[i].getPathPrivilegeList().add(pathPrivilege); - users[i].getRoleList().add("role" + j); - } - } - - // create - User user = manager.getUser(users[0].getName()); - assertNull(user); - for (User user1 : users) { - assertTrue(manager.createUser(user1.getName(), user1.getPassword(), false)); - } - for (User user1 : users) { - user = manager.getUser(user1.getName()); - assertEquals(user1.getName(), user.getName()); - assertTrue(AuthUtils.validatePassword(user1.getPassword(), user.getPassword())); - } - - assertFalse(manager.createUser(users[0].getName(), users[0].getPassword(), false)); - - Assert.assertThrows(AuthException.class, () -> manager.createUser("too", "short", true)); - Assert.assertThrows(AuthException.class, () -> manager.createUser("short", "too", true)); - - // delete - assertFalse(manager.deleteUser("not a user")); - assertTrue(manager.deleteUser(users[users.length - 1].getName())); - assertNull(manager.getUser(users[users.length - 1].getName())); - assertFalse(manager.deleteUser(users[users.length - 1].getName())); - - // grant privilege - user = manager.getUser(users[0].getName()); - PartialPath path = new PartialPath("root.a.b.c"); - int privilegeId = 0; - - assertFalse(user.hasPrivilegeToRevoke(path, privilegeId)); - assertTrue(manager.grantPrivilegeToUser(user.getName(), path, privilegeId, false)); - assertTrue(manager.grantPrivilegeToUser(user.getName(), path, privilegeId + 1, false)); - // grant again will success - assertTrue(manager.grantPrivilegeToUser(user.getName(), path, privilegeId, false)); - user = manager.getUser(users[0].getName()); - assertTrue(user.hasPrivilegeToRevoke(path, privilegeId)); - - Assert.assertThrows( - AuthException.class, - () -> manager.grantPrivilegeToUser("not a user", path, privilegeId, false)); - // We will check the privilegeid before we process it. - // Assert.assertThrows( - // AuthException.class, - // () -> manager.grantPrivilegeToUser(users[0].getName(), path, -1, false)); - - // revoke privilege - user = manager.getUser(users[0].getName()); - assertTrue(manager.revokePrivilegeFromUser(user.getName(), path, privilegeId)); - assertFalse(manager.revokePrivilegeFromUser(user.getName(), path, privilegeId)); - - Assert.assertThrows( - AuthException.class, - () -> manager.revokePrivilegeFromUser("not a user", path, privilegeId)); - - // update password - String newPassword = "newPassword"; - String illegalPW = "new"; - assertTrue(manager.updateUserPassword(user.getName(), newPassword)); - assertFalse(manager.updateUserPassword(user.getName(), illegalPW)); - user = manager.getUser(user.getName()); - assertTrue(AuthUtils.validatePassword(newPassword, user.getPassword())); - - Assert.assertThrows( - AuthException.class, () -> manager.updateUserPassword("not a user", newPassword)); - - // grant role - String roleName = "newrole"; - assertTrue(manager.grantRoleToUser(roleName, user.getName())); - assertFalse(manager.grantRoleToUser(roleName, user.getName())); - user = manager.getUser(user.getName()); - assertTrue(user.hasRole(roleName)); - - Assert.assertThrows(AuthException.class, () -> manager.grantRoleToUser("not a user", roleName)); - - boolean caught = false; - - // revoke role - assertTrue(manager.revokeRoleFromUser(roleName, user.getName())); - assertFalse(manager.revokeRoleFromUser(roleName, user.getName())); - user = manager.getUser(user.getName()); - assertFalse(user.hasRole(roleName)); - - Assert.assertThrows( - AuthException.class, () -> manager.revokeRoleFromUser("not a user", roleName)); - - // list users - List usernames = manager.listAllUsers(); - usernames.sort(null); - assertEquals(CommonDescriptor.getInstance().getConfig().getAdminName(), usernames.get(0)); - for (int i = 0; i < users.length - 1; i++) { - assertEquals(users[i].getName(), usernames.get(i + 1)); - } - } - @Test public void testCreateUserRawPassword() throws AuthException { Assert.assertTrue( manager.createUser("testRaw", AuthUtils.encryptPassword("password1"), true, false)); - User user = manager.getUser("testRaw"); + User user = manager.getEntity("testRaw"); Assert.assertEquals(user.getPassword(), AuthUtils.encryptPassword("password1")); } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java index 9abe74153a0c..06b51954d533 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/parser/StatementGeneratorTest.java @@ -718,12 +718,15 @@ public void testNormalGrantRevoke() { // 1. check simple privilege grant to user/role with/without grant option. for (PrivilegeType privilege : PrivilegeType.values()) { + if (privilege.isRelationalPrivilege()) { + continue; + } testGrant.checkParser(privilege.toString(), name, true, path, true); testGrant.checkParser(privilege.toString(), name, true, path, false); testGrant.checkParser(privilege.toString(), name, false, path, true); testGrant.checkParser(privilege.toString(), name, false, path, false); // 2. if grant stmt has system privilege, path should be root.** - if (!privilege.isPathRelevant()) { + if (!privilege.isPathPrivilege()) { assertThrows( SemanticException.class, () -> @@ -753,11 +756,14 @@ public void testNormalGrantRevoke() { // 3. check simple privilege revoke from user/role on simple path for (PrivilegeType type : PrivilegeType.values()) { + if (type.isRelationalPrivilege()) { + continue; + } testRevoke.checkParser(type.toString(), name, true, path, false); testRevoke.checkParser(type.toString(), name, false, path, false); // 4. check system privilege revoke from user on wrong paths. - if (!type.isPathRelevant()) { + if (!type.isPathPrivilege()) { assertThrows( SemanticException.class, () -> @@ -781,6 +787,9 @@ public void testComplexGrantRevoke() { } for (PrivilegeType type : PrivilegeType.values()) { + if (type.isRelationalPrivilege()) { + continue; + } { AuthorStatement stmt = createAuthDclStmt( diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AuthorStatementTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AuthorStatementTest.java new file mode 100644 index 000000000000..d83d8115c314 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/sql/parser/AuthorStatementTest.java @@ -0,0 +1,351 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.sql.parser; + +import org.apache.iotdb.commons.auth.entity.PrivilegeModelType; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.db.protocol.session.IClientSession; +import org.apache.iotdb.db.protocol.session.InternalClientSession; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.RelationalAuthorStatement; +import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement; +import org.apache.iotdb.db.queryengine.plan.relational.type.AuthorRType; + +import org.junit.Test; + +import java.time.ZonedDateTime; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; + +public class AuthorStatementTest { + private SqlParser parser = new SqlParser(); + IClientSession clientSession = new InternalClientSession("internal"); + private final String databaseName = "test"; + private final String idName = "iotdb_user"; + + public AuthorStatementTest() { + clientSession.setDatabaseName(databaseName); + } + + private RelationalAuthorStatement createAuthorStatement(String sql) { + Statement stmt = parser.createStatement(sql, ZonedDateTime.now().getOffset(), clientSession); + return (RelationalAuthorStatement) stmt; + } + + private void checkNullStatementFields(RelationalAuthorStatement authorStatement) { + assertNull(authorStatement.getDatabase()); + assertNull(authorStatement.getTableName()); + assertNull(authorStatement.getPrivilegeTypes()); + assertFalse(authorStatement.isGrantOption()); + } + + @Test + public void testAuthorUserAndRole() { + RelationalAuthorStatement stmt = createAuthorStatement("create user user1 'passw'"); + assertEquals(AuthorRType.CREATE_USER, stmt.getAuthorType()); + assertEquals("user1", stmt.getUserName()); + assertEquals("passw", stmt.getPassword()); + checkNullStatementFields(stmt); + stmt = createAuthorStatement("create role role1"); + assertEquals("role1", stmt.getRoleName()); + assertEquals(AuthorRType.CREATE_ROLE, stmt.getAuthorType()); + assertNull(stmt.getUserName()); + checkNullStatementFields(stmt); + stmt = createAuthorStatement("GRANT ROLE role1 to user1"); + assertEquals("role1", stmt.getRoleName()); + assertEquals("user1", stmt.getUserName()); + assertEquals(AuthorRType.GRANT_USER_ROLE, stmt.getAuthorType()); + checkNullStatementFields(stmt); + stmt = createAuthorStatement("REVOKE ROLE role1 from user1"); + assertEquals("role1", stmt.getRoleName()); + assertEquals("user1", stmt.getUserName()); + assertEquals(AuthorRType.REVOKE_USER_ROLE, stmt.getAuthorType()); + checkNullStatementFields(stmt); + + stmt = createAuthorStatement("DROP USER user1"); + assertEquals("user1", stmt.getUserName()); + assertEquals(AuthorRType.DROP_USER, stmt.getAuthorType()); + checkNullStatementFields(stmt); + + stmt = createAuthorStatement("DROP ROLE role1"); + assertEquals("role1", stmt.getRoleName()); + assertEquals(AuthorRType.DROP_ROLE, stmt.getAuthorType()); + checkNullStatementFields(stmt); + } + + private void checkRevokePrivileges( + boolean isUser, + List privilegeTypeList, + String scope, + boolean isGrantOption, + boolean checkCatch) { + + StringBuilder privileges = new StringBuilder(); + for (int i = 0; i < privilegeTypeList.size(); i++) { + privileges.append(privilegeTypeList.get(i).toString()); + if (i != privilegeTypeList.size() - 1) { + privileges.append(","); + } + } + String sql = + String.format( + "REVOKE %s %s %s FROM %s %s", + isGrantOption ? "GRANT OPTION FOR" : "", + privileges, + scope.isEmpty() ? "" : " on " + scope, + isUser ? "USER" : "ROLE", + idName); + if (checkCatch) { + assertThrows(ParsingException.class, () -> createAuthorStatement(sql)); + return; + } + RelationalAuthorStatement stmt = createAuthorStatement(sql); + PrivilegeModelType modelType = PrivilegeModelType.INVALID; + int ind = 0; + for (PrivilegeType privilegeType : stmt.getPrivilegeTypes()) { + if (ind == 0) { + modelType = privilegeType.getModelType(); + } + assertEquals(modelType, privilegeType.getModelType()); + ind++; + } + + AuthorRType authorRType = AuthorRType.CREATE_ROLE; + if (scope.toLowerCase().contains("database")) { + authorRType = isUser ? AuthorRType.REVOKE_USER_DB : AuthorRType.REVOKE_ROLE_DB; + assertTrue(scope.contains(stmt.getDatabase())); + assertEquals("", stmt.getTableName()); + } else if (scope.toLowerCase().contains("table")) { + authorRType = isUser ? AuthorRType.REVOKE_USER_TB : AuthorRType.REVOKE_ROLE_TB; + assertTrue(scope.contains(stmt.getTableName())); + assertEquals(databaseName, stmt.getDatabase()); + } else if (scope.toLowerCase().contains(".")) { + authorRType = isUser ? AuthorRType.REVOKE_USER_TB : AuthorRType.REVOKE_ROLE_TB; + assertTrue(scope.toLowerCase().contains(stmt.getTableName())); + assertTrue(scope.toLowerCase().contains(stmt.getDatabase())); + } else if (scope.toLowerCase().contains("any")) { + authorRType = isUser ? AuthorRType.REVOKE_USER_ANY : AuthorRType.REVOKE_ROLE_ANY; + } + if (scope.isEmpty()) { + authorRType = isUser ? AuthorRType.REVOKE_USER_SYS : AuthorRType.REVOKE_ROLE_SYS; + } + + assertEquals(authorRType, stmt.getAuthorType()); + assertEquals(isUser ? idName : "", stmt.getUserName()); + assertEquals(isUser ? "" : idName, stmt.getRoleName()); + Set privilegeTypes = stmt.getPrivilegeTypes(); + Set privilegeTypeSet = new HashSet<>(privilegeTypeList); + assertEquals(privilegeTypes, privilegeTypeSet); + assertNull(stmt.getPassword()); + assertEquals(isGrantOption, stmt.isGrantOption()); + } + + private void checkGrantPrivileges( + boolean isUser, + List privilegeTypeList, + String scope, + boolean isGrantOption, + boolean checkCatch) { + StringBuilder privileges = new StringBuilder(); + for (int i = 0; i < privilegeTypeList.size(); i++) { + privileges.append(privilegeTypeList.get(i).toString()); + if (i != privilegeTypeList.size() - 1) { + privileges.append(","); + } + } + String sql = + String.format( + "GRANT %s %s TO %s %s %s", + privileges, + scope.isEmpty() ? "" : " on " + scope, + isUser ? "USER" : "ROLE", + idName, + isGrantOption ? "WITH GRANT OPTION" : ""); + if (checkCatch) { + assertThrows(ParsingException.class, () -> createAuthorStatement(sql)); + return; + } + RelationalAuthorStatement stmt = createAuthorStatement(sql); + PrivilegeModelType modelType = PrivilegeModelType.INVALID; + // 1. All privileges are same model. + int ind = 0; + for (PrivilegeType privilegeType : stmt.getPrivilegeTypes()) { + if (ind == 0) { + modelType = privilegeType.getModelType(); + } + assertEquals(modelType, privilegeType.getModelType()); + ind++; + } + AuthorRType authType = AuthorRType.CREATE_ROLE; + if (scope.toLowerCase().contains("database")) { + authType = isUser ? AuthorRType.GRANT_USER_DB : AuthorRType.GRANT_ROLE_DB; + assertTrue(scope.contains(stmt.getDatabase())); + assertEquals("", stmt.getTableName()); + } else if (scope.toLowerCase().contains("table")) { + authType = isUser ? AuthorRType.GRANT_USER_TB : AuthorRType.GRANT_ROLE_TB; + assertTrue(scope.contains(stmt.getTableName())); + assertEquals(databaseName, stmt.getDatabase()); + } else if (scope.toLowerCase().contains(".")) { + authType = isUser ? AuthorRType.GRANT_USER_TB : AuthorRType.GRANT_ROLE_TB; + assertTrue(scope.toLowerCase().contains(stmt.getTableName())); + assertTrue(scope.toLowerCase().contains(stmt.getDatabase())); + } else if (scope.toLowerCase().contains("any")) { + authType = isUser ? AuthorRType.GRANT_USER_ANY : AuthorRType.GRANT_ROLE_ANY; + } + if (scope.isEmpty()) { + authType = isUser ? AuthorRType.GRANT_USER_SYS : AuthorRType.GRANT_ROLE_SYS; + } + assertEquals(authType, stmt.getAuthorType()); + assertEquals(isUser ? idName : "", stmt.getUserName()); + assertEquals(isUser ? "" : idName, stmt.getRoleName()); + Set privilegeTypes = stmt.getPrivilegeTypes(); + Set privilegeTypeSet = new HashSet<>(privilegeTypeList); + assertEquals(privilegeTypeSet, privilegeTypes); + assertNull(stmt.getPassword()); + assertEquals(isGrantOption, stmt.isGrantOption()); + } + + @Test + public void testGrantStatement() { + // System privileges + checkGrantPrivileges( + true, Collections.singletonList(PrivilegeType.MANAGE_USER), "", true, false); + checkGrantPrivileges( + true, Collections.singletonList(PrivilegeType.MANAGE_ROLE), "", false, false); + checkGrantPrivileges( + false, Collections.singletonList(PrivilegeType.MAINTAIN), "", false, false); + checkGrantPrivileges( + true, Arrays.asList(PrivilegeType.MANAGE_USER, PrivilegeType.MANAGE_ROLE), "", true, false); + checkGrantPrivileges( + true, Collections.singletonList(PrivilegeType.MANAGE_USER), "ANY", true, true); + // Illegal privileges combination. + checkGrantPrivileges( + true, Arrays.asList(PrivilegeType.MAINTAIN, PrivilegeType.SELECT), "", true, true); + + // Illegal privilege + checkGrantPrivileges( + true, Collections.singletonList(PrivilegeType.MANAGE_DATABASE), "", true, true); + + // Relational privileges + checkGrantPrivileges( + true, Collections.singletonList(PrivilegeType.CREATE), "Database testdb", true, false); + checkGrantPrivileges( + false, Collections.singletonList(PrivilegeType.SELECT), "Table testtb", true, false); + checkGrantPrivileges( + true, Collections.singletonList(PrivilegeType.INSERT), "testdb.testtb", false, false); + checkGrantPrivileges(true, Collections.singletonList(PrivilegeType.INSERT), "ANY", true, false); + + checkGrantPrivileges( + true, + Arrays.asList(PrivilegeType.CREATE, PrivilegeType.INSERT), + "Database testdb", + true, + false); + checkGrantPrivileges( + false, + Arrays.asList(PrivilegeType.CREATE, PrivilegeType.INSERT), + "Table testtb", + true, + false); + checkGrantPrivileges( + true, + Arrays.asList(PrivilegeType.CREATE, PrivilegeType.INSERT), + "testdb.testtb", + false, + false); + + // Illegal privileges combination. + checkGrantPrivileges( + true, + Arrays.asList(PrivilegeType.CREATE, PrivilegeType.MANAGE_ROLE), + "testdb.testtb", + false, + true); + + // test all privileges + + RelationalAuthorStatement stmt = createAuthorStatement("GRANT ALL TO USER test"); + assertEquals(AuthorRType.GRANT_USER_ALL, stmt.getAuthorType()); + + stmt = createAuthorStatement("GRANT ALL TO ROLE test with grant option"); + assertEquals(AuthorRType.GRANT_ROLE_ALL, stmt.getAuthorType()); + assertTrue(stmt.isGrantOption()); + } + + @Test + public void testRevokeStatement() { + // System privileges + checkRevokePrivileges( + true, Collections.singletonList(PrivilegeType.MANAGE_USER), "", true, false); + checkRevokePrivileges( + true, Collections.singletonList(PrivilegeType.MANAGE_ROLE), "", false, false); + checkRevokePrivileges( + true, + Arrays.asList(PrivilegeType.MANAGE_USER, PrivilegeType.MANAGE_ROLE), + "", + false, + false); + checkRevokePrivileges( + true, Arrays.asList(PrivilegeType.MANAGE_USER, PrivilegeType.MANAGE_ROLE), "", true, false); + + // Illegal privileges combination + checkRevokePrivileges( + true, Arrays.asList(PrivilegeType.MANAGE_USER, PrivilegeType.ALTER), "", false, true); + + // Relational privileges + checkRevokePrivileges( + true, Collections.singletonList(PrivilegeType.CREATE), "Database testdb", true, false); + + checkRevokePrivileges( + true, Collections.singletonList(PrivilegeType.CREATE), "table testtb", false, false); + + checkRevokePrivileges( + true, Collections.singletonList(PrivilegeType.CREATE), "testdb.testtb", false, false); + + checkRevokePrivileges( + true, + Arrays.asList(PrivilegeType.SELECT, PrivilegeType.INSERT), + "testdb.testtb", + false, + false); + + checkRevokePrivileges( + true, + Arrays.asList(PrivilegeType.SELECT, PrivilegeType.MANAGE_USER), + "testdb.testtb", + false, + true); + + RelationalAuthorStatement stmt = createAuthorStatement("REVOKE ALL FROM USER test"); + assertEquals(AuthorRType.REVOKE_USER_ALL, stmt.getAuthorType()); + + stmt = createAuthorStatement("REVOKE GRANT OPTION FOR ALL FROM ROLE test"); + assertEquals(AuthorRType.REVOKE_ROLE_ALL, stmt.getAuthorType()); + assertTrue(stmt.isGrantOption()); + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/security/encrypt/MessageDigestEncryptTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/security/encrypt/MessageDigestEncryptTest.java index 2b4dd753f734..e93894dbf7f8 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/security/encrypt/MessageDigestEncryptTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/security/encrypt/MessageDigestEncryptTest.java @@ -70,20 +70,20 @@ public void testMessageDigestEncrypt() throws AuthException, IllegalPathExceptio users[i] = new User("user" + i, "password" + i); for (int j = 0; j <= i; j++) { PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.a.b.c" + j)); - pathPrivilege.getPrivileges().add(j); + pathPrivilege.getPrivilegeIntSet().add(j); users[i].getPathPrivilegeList().add(pathPrivilege); - users[i].getRoleList().add("role" + j); + users[i].getRoleSet().add("role" + j); } } // create - User user = manager.getUser(users[0].getName()); + User user = manager.getEntity(users[0].getName()); assertNull(user); for (User user1 : users) { assertTrue(manager.createUser(user1.getName(), user1.getPassword(), false)); } for (User user1 : users) { - user = manager.getUser(user1.getName()); + user = manager.getEntity(user1.getName()); assertEquals(user1.getName(), user.getName()); assertEquals(messageDigestEncrypt.encrypt(user1.getPassword()), user.getPassword()); } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java index e8359bcd070a..1867a207d088 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/BasicAuthorizer.java @@ -20,17 +20,17 @@ import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; -import org.apache.iotdb.commons.auth.role.IRoleManager; -import org.apache.iotdb.commons.auth.user.IUserManager; +import org.apache.iotdb.commons.auth.role.BasicRoleManager; +import org.apache.iotdb.commons.auth.user.BasicUserManager; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.exception.StartupException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.service.IService; import org.apache.iotdb.commons.service.ServiceType; import org.apache.iotdb.commons.utils.AuthUtils; -import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.thrift.TException; @@ -50,26 +50,10 @@ public abstract class BasicAuthorizer implements IAuthorizer, IService { private static final String NO_SUCH_ROLE_EXCEPTION = "No such role : %s"; private static final String NO_SUCH_USER_EXCEPTION = "No such user : %s"; - IUserManager userManager; - IRoleManager roleManager; + BasicUserManager userManager; + BasicRoleManager roleManager; - /** - * This filed only for pre version. When we do a major version upgrade, it can be removed - * directly. - */ - // FOR PRE VERSION BEGIN ----- - - @Override - public void checkUserPathPrivilege() { - userManager.checkAndRefreshPathPri(); - roleManager.checkAndRefreshPathPri(); - userManager.setPreVersion(false); - roleManager.setPreVersion(false); - } - - // FOR PRE VERSION END ----- - - public BasicAuthorizer(IUserManager userManager, IRoleManager roleManager) throws AuthException { + public BasicAuthorizer(BasicUserManager userManager, BasicRoleManager roleManager) { this.userManager = userManager; this.roleManager = roleManager; } @@ -115,9 +99,15 @@ private static class InstanceHolder { /** Checks if a user has admin privileges */ protected abstract boolean isAdmin(String username); + private void checkAdmin(String username, String errmsg) throws AuthException { + if (isAdmin(username)) { + throw new AuthException(TSStatusCode.NO_PERMISSION, errmsg); + } + } + @Override public boolean login(String username, String password) throws AuthException { - User user = userManager.getUser(username); + User user = userManager.getEntity(username); return user != null && password != null && AuthUtils.validatePassword(password, user.getPassword()); @@ -149,45 +139,28 @@ public void createUserWithRawPassword(String username, String password) throws A @Override public void deleteUser(String username) throws AuthException { - if (isAdmin(username)) { - throw new AuthException( - TSStatusCode.NO_PERMISSION, "Default administrator cannot be deleted"); - } - if (!userManager.deleteUser(username)) { + checkAdmin(username, "Default administrator cannot be deleted"); + if (!userManager.deleteEntity(username)) { throw new AuthException( TSStatusCode.USER_NOT_EXIST, String.format("User %s does not exist", username)); } } @Override - public void grantPrivilegeToUser( - String username, PartialPath path, int privilegeId, boolean grantOpt) throws AuthException { - if (isAdmin(username)) { - throw new AuthException( - TSStatusCode.NO_PERMISSION, - "Invalid operation, administrator already has all privileges"); - } - userManager.grantPrivilegeToUser(username, path, privilegeId, grantOpt); + public void grantPrivilegeToUser(String username, PrivilegeUnion union) throws AuthException { + checkAdmin(username, "Invalid operation, administrator already has all privileges"); + userManager.grantPrivilegeToEntity(username, union); } @Override - public void revokePrivilegeFromUser(String username, PartialPath path, int privilegeId) - throws AuthException { - if (isAdmin(username)) { - throw new AuthException( - TSStatusCode.NO_PERMISSION, "Invalid operation, administrator must have all privileges"); - } - if (!userManager.revokePrivilegeFromUser(username, path, privilegeId)) { - throw new AuthException( - TSStatusCode.NOT_HAS_PRIVILEGE, - String.format( - "User %s does not have %s on %s", - username, PrivilegeType.values()[privilegeId], path != null ? path : "system")); - } + public void revokePrivilegeFromUser(String username, PrivilegeUnion union) throws AuthException { + checkAdmin(username, "Invalid operation, administrator must have all privileges"); + userManager.revokePrivilegeFromEntity(username, union); } @Override public void createRole(String roleName) throws AuthException { + AuthUtils.validateRolename(roleName); if (!roleManager.createRole(roleName)) { LOGGER.error("Role {} already exists", roleName); throw new AuthException( @@ -197,13 +170,13 @@ public void createRole(String roleName) throws AuthException { @Override public void deleteRole(String roleName) throws AuthException { - boolean success = roleManager.deleteRole(roleName); + boolean success = roleManager.deleteEntity(roleName); if (!success) { throw new AuthException( TSStatusCode.ROLE_NOT_EXIST, String.format("Role %s does not exist", roleName)); } else { // proceed to revoke the role in all users - List users = userManager.listAllUsers(); + List users = userManager.listAllEntities(); for (String user : users) { try { userManager.revokeRoleFromUser(roleName, user); @@ -219,39 +192,27 @@ public void deleteRole(String roleName) throws AuthException { } @Override - public void grantPrivilegeToRole( - String roleName, PartialPath path, int privilegeId, boolean grantOpt) throws AuthException { - roleManager.grantPrivilegeToRole(roleName, path, privilegeId, grantOpt); + public void grantPrivilegeToRole(String rolename, PrivilegeUnion union) throws AuthException { + roleManager.grantPrivilegeToEntity(rolename, union); } @Override - public void revokePrivilegeFromRole(String roleName, PartialPath path, int privilegeId) - throws AuthException { - if (!roleManager.revokePrivilegeFromRole(roleName, path, privilegeId)) { - throw new AuthException( - TSStatusCode.NOT_HAS_PRIVILEGE, - String.format( - "Role %s does not have %s on %s", - roleName, PrivilegeType.values()[privilegeId], path)); - } + public void revokePrivilegeFromRole(String roleName, PrivilegeUnion union) throws AuthException { + roleManager.revokePrivilegeFromEntity(roleName, union); } @Override - public void grantRoleToUser(String roleName, String username) throws AuthException { - if (isAdmin(username)) { - throw new AuthException( - TSStatusCode.NO_PERMISSION, "Invalid operation, cannot grant role to administrator "); - } - - Role role = roleManager.getRole(roleName); + public void grantRoleToUser(String roleName, String userName) throws AuthException { + checkAdmin(userName, "Invalid operation, cannot grant role to administrator"); + Role role = roleManager.getEntity(roleName); if (role == null) { throw new AuthException( TSStatusCode.ROLE_NOT_EXIST, String.format(NO_SUCH_ROLE_EXCEPTION, roleName)); } // the role may be deleted before it ts granted to the user, so a double check is necessary. - boolean success = userManager.grantRoleToUser(roleName, username); + boolean success = userManager.grantRoleToUser(roleName, userName); if (success) { - role = roleManager.getRole(roleName); + role = roleManager.getEntity(roleName); if (role == null) { throw new AuthException( TSStatusCode.ROLE_NOT_EXIST, String.format(NO_SUCH_ROLE_EXCEPTION, roleName)); @@ -259,41 +220,41 @@ public void grantRoleToUser(String roleName, String username) throws AuthExcepti } else { throw new AuthException( TSStatusCode.USER_ALREADY_HAS_ROLE, - String.format("User %s already has role %s", username, roleName)); + String.format("User %s already has role %s", userName, roleName)); } } @Override - public void revokeRoleFromUser(String roleName, String username) throws AuthException { - if (isAdmin(username)) { + public void revokeRoleFromUser(String roleName, String userName) throws AuthException { + if (isAdmin(userName)) { throw new AuthException( TSStatusCode.NO_PERMISSION, "Invalid operation, cannot revoke role from administrator "); } - Role role = roleManager.getRole(roleName); + Role role = roleManager.getEntity(roleName); if (role == null) { throw new AuthException( TSStatusCode.ROLE_NOT_EXIST, String.format(NO_SUCH_ROLE_EXCEPTION, roleName)); } - if (!userManager.revokeRoleFromUser(roleName, username)) { + if (!userManager.revokeRoleFromUser(roleName, userName)) { throw new AuthException( TSStatusCode.USER_NOT_HAS_ROLE, - String.format("User %s does not have role %s", username, roleName)); + String.format("User %s does not have role %s", userName, roleName)); } } @Override - public Set getPrivileges(String username, PartialPath path) throws AuthException { - User user = userManager.getUser(username); + public Set getPrivileges(String userName, PartialPath path) throws AuthException { + User user = userManager.getEntity(userName); if (user == null) { throw new AuthException( - TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_EXCEPTION, username)); + TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_EXCEPTION, userName)); } // get privileges of the user - Set privileges = user.getPathPrivileges(path); + Set privileges = user.getPathPrivileges(path); // merge the privileges of the roles of the user - for (String roleName : user.getRoleList()) { - Role role = roleManager.getRole(roleName); + for (String roleName : user.getRoleSet()) { + Role role = roleManager.getEntity(roleName); if (role != null) { privileges.addAll(role.getPathPrivileges(path)); } @@ -302,63 +263,77 @@ public Set getPrivileges(String username, PartialPath path) throws Auth } @Override - public void updateUserPassword(String username, String newPassword) throws AuthException { - if (!userManager.updateUserPassword(username, newPassword)) { + public void updateUserPassword(String userName, String newPassword) throws AuthException { + if (!userManager.updateUserPassword(userName, newPassword)) { throw new AuthException( TSStatusCode.ILLEGAL_PARAMETER, "password " + newPassword + " is illegal"); } } @Override - public boolean checkUserPrivileges(String username, PartialPath path, int privilegeId) - throws AuthException { - if (isAdmin(username)) { + public boolean checkUserPrivileges(String userName, PrivilegeUnion union) throws AuthException { + if (isAdmin(userName)) { return true; } - User user = userManager.getUser(username); + User user = userManager.getEntity(userName); if (user == null) { throw new AuthException( - TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_EXCEPTION, username)); + TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_EXCEPTION, userName)); } - if (path != null) { - // get privileges of the user - if (user.checkPathPrivilege(path, privilegeId)) { - return true; - } - // merge the privileges of the roles of the user - for (String roleName : user.getRoleList()) { - Role role = roleManager.getRole(roleName); - if (role.checkPathPrivilege(path, privilegeId)) { - return true; - } - } - } else { - if (user.checkSysPrivilege(privilegeId)) { + if (checkEntityPrivileges(user, union)) { + return true; + } + + for (String roleName : user.getRoleSet()) { + Role role = roleManager.getEntity(roleName); + if (checkEntityPrivileges(role, union)) { return true; } - for (String roleName : user.getRoleList()) { - Role role = roleManager.getRole(roleName); - if (role.checkSysPrivilege(privilegeId)) { - return true; - } - } } - return false; } - @Override - public Map getAllUserWaterMarkStatus() { - Map userWaterMarkStatus = new HashMap<>(); - List allUsers = listAllUsers(); - for (String user : allUsers) { - try { - userWaterMarkStatus.put(user, isUserUseWaterMark(user)); - } catch (AuthException e) { - LOGGER.error("No such user: {}", user); - } + private boolean checkEntityPrivileges(Role role, PrivilegeUnion union) { + switch (union.getModelType()) { + case TREE: + if (union.isGrantOption()) { + return role.checkPathPrivilegeGrantOpt(union.getPath(), union.getPrivilegeType()); + } + return role.checkPathPrivilege(union.getPath(), union.getPrivilegeType()); + case RELATIONAL: + // check any scope privilege + if (union.isForAny()) { + if (union.isGrantOption()) { + return role.checkAnyScopePrivilegeGrantOption(union.getPrivilegeType()); + } + return role.checkAnyScopePrivilege(union.getPrivilegeType()); + } else if (union.getTbName() == null) { + if (union.getPrivilegeType() == null) { + return role.checkDBVisible(union.getDBName()); + } + if (union.isGrantOption()) { + return role.checkDatabasePrivilegeGrantOption( + union.getDBName(), union.getPrivilegeType()); + } + return role.checkDatabasePrivilege(union.getDBName(), union.getPrivilegeType()); + } else { + if (union.getPrivilegeType() == null) { + return role.checkTBVisible(union.getDBName(), union.getTbName()); + } + if (union.isGrantOption()) { + return role.checkTablePrivilegeGrantOption( + union.getDBName(), union.getTbName(), union.getPrivilegeType()); + } + return role.checkTablePrivilege( + union.getDBName(), union.getTbName(), union.getPrivilegeType()); + } + case SYSTEM: + if (union.isGrantOption()) { + return role.checkSysPriGrantOpt(union.getPrivilegeType()); + } + return role.checkSysPrivilege(union.getPrivilegeType()); } - return userWaterMarkStatus; + return false; } @Override @@ -415,42 +390,22 @@ public ServiceType getID() { @Override public List listAllUsers() { - return userManager.listAllUsers(); + return userManager.listAllEntities(); } @Override public List listAllRoles() { - return roleManager.listAllRoles(); + return roleManager.listAllEntities(); } @Override public Role getRole(String roleName) throws AuthException { - return roleManager.getRole(roleName); + return roleManager.getEntity(roleName); } @Override public User getUser(String username) throws AuthException { - return userManager.getUser(username); - } - - @Override - public boolean isUserUseWaterMark(String userName) throws AuthException { - return userManager.isUserUseWaterMark(userName); - } - - @Override - public void setUserUseWaterMark(String userName, boolean useWaterMark) throws AuthException { - userManager.setUserUseWaterMark(userName, useWaterMark); - } - - @Override - public void replaceAllUsers(Map users) throws AuthException { - userManager.replaceAllUsers(users); - } - - @Override - public void replaceAllRoles(Map roles) throws AuthException { - roleManager.replaceAllRoles(roles); + return userManager.getEntity(username); } @Override @@ -464,26 +419,4 @@ public void processLoadSnapshot(File snapshotDir) throws TException, IOException userManager.processLoadSnapshot(snapshotDir); roleManager.processLoadSnapshot(snapshotDir); } - - @Override - public void setUserForPreVersion(boolean preVersion) { - userManager.setPreVersion(preVersion); - } - - @Override - public void setRoleForPreVersion(boolean preVersion) { - roleManager.setPreVersion(preVersion); - } - - @Override - @TestOnly - public boolean forUserPreVersion() { - return this.userManager.preVersion(); - } - - @Override - @TestOnly - public boolean forRolePreVersion() { - return this.roleManager.preVersion(); - } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java index 8b6d81a40f1b..d7f8cd26770b 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/IAuthorizer.java @@ -20,6 +20,8 @@ package org.apache.iotdb.commons.auth.authorizer; import org.apache.iotdb.commons.auth.AuthException; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.path.PartialPath; @@ -56,39 +58,31 @@ public interface IAuthorizer extends SnapshotProcessor { * * @param username the username of the user. * @throws AuthException When attempting to delete the default administrator or the user does not - * exists. + * exist. */ void deleteUser(String username) throws AuthException; /** - * Grant a privilege on a seriesPath to a user. + * Grant a privilege to a user. * - * @param username The username of the user to which the privilege should be added. - * @param path The seriesPath on which the privilege takes effect. If the privilege is a - * seriesPath-free privilege, this should be "root". - * @param privilegeId An integer that represents a privilege. - * @param grantOpt Whether the privilege is grant option. - * @throws AuthException If the user does not exist or the privilege or the seriesPath is illegal - * or the permission already exists. + * @param userName The username of the user to which the privilege should be added. + * @param union A combination of user permissions, scope, and tags + * @throws AuthException If the user does not exist or the privilege or the seriesPath is illegal. */ - void grantPrivilegeToUser(String username, PartialPath path, int privilegeId, boolean grantOpt) - throws AuthException; + void grantPrivilegeToUser(String userName, PrivilegeUnion union) throws AuthException; /** - * Revoke a privilege on seriesPath from a user. + * Revoke a privilege from a user. * - * @param username The username of the user from which the privilege should be removed. - * @param path The seriesPath on which the privilege takes effect. If the privilege is a - * seriesPath-free privilege, this should be "root". - * @param privilegeId An integer that represents a privilege. + * @param userName The name of the user from which the privilege should be removed. + * @param union A combination of user permissions, scope, and tags * @throws AuthException If the user does not exist or the privilege or the seriesPath is illegal * or if the permission does not exist. */ - void revokePrivilegeFromUser(String username, PartialPath path, int privilegeId) - throws AuthException; + void revokePrivilegeFromUser(String userName, PrivilegeUnion union) throws AuthException; /** - * Add a role. + * Create a role. * * @param roleName the name of the role to be added. * @throws AuthException if exception raised when adding the role or the role already exists. @@ -99,86 +93,74 @@ void revokePrivilegeFromUser(String username, PartialPath path, int privilegeId) * Delete a role. * * @param roleName the name of the role tobe deleted. - * @throws AuthException if exception raised when deleting the role or the role does not exists. + * @throws AuthException if exception raised when deleting the role or the role does not exist. */ void deleteRole(String roleName) throws AuthException; /** - * Add a privilege on a seriesPath to a role. + * Add a privilege to a role. * * @param roleName The name of the role to which the privilege is added. - * @param path The seriesPath on which the privilege takes effect. If the privilege is a - * seriesPath-free privilege, this should be "root". - * @param privilegeId An integer that represents a privilege. - * @throws AuthException If the role does not exist or the privilege or the seriesPath is illegal - * or the privilege already exists. + * @param union A combination of user permissions, scope, and tags. + * @throws AuthException If the role does not exist or the privilege or the seriesPath is illegal. */ - void grantPrivilegeToRole(String roleName, PartialPath path, int privilegeId, boolean grantOpt) - throws AuthException; + void grantPrivilegeToRole(String roleName, PrivilegeUnion union) throws AuthException; /** - * Remove a privilege on a seriesPath from a role. + * Remove a privilege from a role. * * @param roleName The name of the role from which the privilege is removed. - * @param path The seriesPath on which the privilege takes effect. If the privilege is a - * seriesPath-free privilege, this should be "root". - * @param privilegeId An integer that represents a privilege. + * @param union A combination of user permissions, scope, and tags * @throws AuthException If the role does not exist or the privilege or the seriesPath is illegal - * or the privilege does not exists. */ - void revokePrivilegeFromRole(String roleName, PartialPath path, int privilegeId) - throws AuthException; + void revokePrivilegeFromRole(String roleName, PrivilegeUnion union) throws AuthException; /** - * Add a role to a user. + * Grant a role to a user. * * @param roleName The name of the role to be added. - * @param username The name of the user to which the role is added. + * @param userName The name of the user to which the role is added. * @throws AuthException If either the role or the user does not exist or the role already exists. */ - void grantRoleToUser(String roleName, String username) throws AuthException; + void grantRoleToUser(String roleName, String userName) throws AuthException; /** * Revoke a role from a user. * * @param roleName The name of the role to be removed. - * @param username The name of the user from which the role is removed. + * @param userName The name of the user from which the role is removed. * @throws AuthException If either the role or the user does not exist or the role already exists. */ - void revokeRoleFromUser(String roleName, String username) throws AuthException; + void revokeRoleFromUser(String roleName, String userName) throws AuthException; /** * Get the all the privileges of a user on a seriesPath. * - * @param username The user whose privileges are to be queried. - * @param path The seriesPath on which the privileges take effect. If the privilege is a - * seriesPath-free privilege, this should be "root". - * @return A set of integers each present a privilege. + * @param userName The user whose privileges are to be queried. + * @param path The path where the privileges take effect. + * @return A set of privilege each present a privilege. * @throws AuthException if exception raised when finding the privileges. */ - Set getPrivileges(String username, PartialPath path) throws AuthException; + Set getPrivileges(String userName, PartialPath path) throws AuthException; /** * Modify the password of a user. * - * @param username The user whose password is to be modified. + * @param userName The user whose password is to be modified. * @param newPassword The new password. - * @throws AuthException If the user does not exists or the new password is illegal. + * @throws AuthException If the user does not exist or the new password is illegal. */ - void updateUserPassword(String username, String newPassword) throws AuthException; + void updateUserPassword(String userName, String newPassword) throws AuthException; /** - * Check if the user have the privilege on the seriesPath. + * Check if the user have the privilege or grant option on the target. * - * @param username The name of the user whose privileges are checked. - * @param path The seriesPath on which the privilege takes effect. If the privilege is system - * privilege, path should be null. - * @param privilegeId An integer that represents a privilege. + * @param userName The name of the user whose privileges are checked. + * @param union A combination of user permissions, scope, and tags * @return True if the user has such privilege, false if the user does not have such privilege. * @throws AuthException If the seriesPath or the privilege is illegal. */ - boolean checkUserPrivileges(String username, PartialPath path, int privilegeId) - throws AuthException; + boolean checkUserPrivileges(String userName, PrivilegeUnion union) throws AuthException; /** Reset the Authorizer to initiative status. */ void reset() throws AuthException; @@ -213,30 +195,6 @@ boolean checkUserPrivileges(String username, PartialPath path, int privilegeId) */ User getUser(String username) throws AuthException; - /** - * Whether data water-mark is enabled for user 'userName'. - * - * @param userName the name of user - * @throws AuthException if the user does not exist - */ - boolean isUserUseWaterMark(String userName) throws AuthException; - - /** - * Enable or disable data water-mark for user 'userName'. - * - * @param userName the name of user - * @param useWaterMark whether to use water-mark or not - * @throws AuthException if the user does not exist. - */ - void setUserUseWaterMark(String userName, boolean useWaterMark) throws AuthException; - - /** - * get all user water mark status - * - * @return key->userName, value->useWaterMark or not - */ - Map getAllUserWaterMarkStatus(); - /** * get all user * @@ -251,30 +209,6 @@ boolean checkUserPrivileges(String username, PartialPath path, int privilegeId) */ Map getAllRoles(); - /** - * clear all old user info, replace the old users with the new one - * - * @param users new users info - * @throws AuthException IOException - */ - void replaceAllUsers(Map users) throws AuthException; - - /** - * clear all old role info, replace the old roles with the new one - * - * @param roles new roles info - * @throws AuthException IOException - */ - void replaceAllRoles(Map roles) throws AuthException; - - void setUserForPreVersion(boolean preVersion); - - void setRoleForPreVersion(boolean preVersion); - - boolean forUserPreVersion(); - - boolean forRolePreVersion(); - /** * Create a user with given username and password. New users will only be granted no privileges. * @@ -285,6 +219,4 @@ boolean checkUserPrivileges(String username, PartialPath path, int privilegeId) void createUserWithoutCheck(String username, String password) throws AuthException; void createUserWithRawPassword(String username, String password) throws AuthException; - - void checkUserPathPrivilege(); } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizer.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizer.java index 55720d950801..f46ba20348ee 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizer.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/authorizer/OpenIdAuthorizer.java @@ -19,11 +19,11 @@ package org.apache.iotdb.commons.auth.authorizer; import org.apache.iotdb.commons.auth.AuthException; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.auth.role.LocalFileRoleManager; import org.apache.iotdb.commons.auth.user.LocalFileUserManager; import org.apache.iotdb.commons.conf.CommonConfig; import org.apache.iotdb.commons.conf.CommonDescriptor; -import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.rpc.TSStatusCode; import com.nimbusds.jose.JOSEException; @@ -256,13 +256,12 @@ public boolean isAdmin(String token) { } @Override - public boolean checkUserPrivileges(String username, PartialPath path, int privilegeId) - throws AuthException { - return isAdmin(username); + public boolean checkUserPrivileges(String userName, PrivilegeUnion union) throws AuthException { + return isAdmin(userName); } @Override - public void updateUserPassword(String username, String newPassword) { + public void updateUserPassword(String userName, String newPassword) { throwUnsupportedOperationException(); } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/DatabasePrivilege.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/DatabasePrivilege.java new file mode 100644 index 000000000000..d7faad6ed9d4 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/DatabasePrivilege.java @@ -0,0 +1,250 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.commons.auth.entity; + +import org.apache.iotdb.commons.utils.AuthUtils; +import org.apache.iotdb.commons.utils.SerializeUtils; + +import org.apache.tsfile.utils.ReadWriteIOUtils; + +import java.io.ByteArrayOutputStream; +import java.io.DataOutputStream; +import java.io.IOException; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Set; + +public class DatabasePrivilege { + private String databaseName; + + private Map tablePrivilegeMap; + private Set privilegeSet; + private Set grantOptionSet; + + public DatabasePrivilege(String databaseName) { + this.databaseName = databaseName; + this.tablePrivilegeMap = new HashMap<>(); + this.privilegeSet = new HashSet<>(); + this.grantOptionSet = new HashSet<>(); + } + + public DatabasePrivilege() { + // This construction is used for deserializing from thrift. + } + + public String getDatabaseName() { + return this.databaseName; + } + + public Map getTablePrivilegeMap() { + return this.tablePrivilegeMap; + } + + public void setPrivileges(int mask) { + final int PRI_SIZE = PrivilegeType.getPrivilegeCount(PrivilegeModelType.RELATIONAL); + for (int i = 0; i < PRI_SIZE; i++) { + if (((1 << i) & mask) != 0) { + this.privilegeSet.add(AuthUtils.posToObjPri(i)); + if (((1 << (i + 16)) & mask) != 0) { + this.grantOptionSet.add(AuthUtils.posToObjPri(i)); + } + } + } + } + + public int getAllPrivileges() { + int privilege = 0; + for (PrivilegeType pri : privilegeSet) { + privilege |= 1 << AuthUtils.objPriToPos(pri); + } + for (PrivilegeType pri : grantOptionSet) { + privilege |= 1 << (AuthUtils.objPriToPos(pri) + 16); + } + return privilege; + } + + public Set getPrivilegeSet() { + Set res = new HashSet<>(); + for (PrivilegeType priv : this.privilegeSet) { + res.add(priv.ordinal()); + } + return res; + } + + public Set getPrivilegeGrantOptSet() { + Set res = new HashSet<>(); + for (PrivilegeType priv : this.grantOptionSet) { + res.add(priv.ordinal()); + } + return res; + } + + public void grantDBPrivilege(PrivilegeType privilegeType) { + this.privilegeSet.add(privilegeType); + } + + public void revokeDBPrivilege(PrivilegeType privilegeType) { + this.privilegeSet.remove(privilegeType); + revokeDBGrantOption(privilegeType); + } + + public void grantDBGrantOption(PrivilegeType privilegeType) { + this.grantOptionSet.add(privilegeType); + } + + public void revokeDBGrantOption(PrivilegeType privilegeType) { + this.grantOptionSet.remove(privilegeType); + } + + public void grantTablePrivilege(String tableName, PrivilegeType privilegeType) { + if (!this.tablePrivilegeMap.containsKey(tableName)) { + TablePrivilege tablePrivilege = new TablePrivilege(tableName); + tablePrivilege.grantPrivilege(privilegeType); + this.tablePrivilegeMap.put(tableName, tablePrivilege); + } else { + tablePrivilegeMap.get(tableName).grantPrivilege(privilegeType); + } + } + + public void revokeTablePrivilege(String tableName, PrivilegeType privilegeType) { + if (this.tablePrivilegeMap.containsKey(tableName)) { + TablePrivilege tablePrivilege = this.tablePrivilegeMap.get(tableName); + tablePrivilege.revokePrivilege(privilegeType); + tablePrivilege.revokeGrantOption(privilegeType); + if (tablePrivilege.getPrivileges().isEmpty()) { + this.tablePrivilegeMap.remove(tableName); + } + } + } + + public void grantTableGrantOption(String tableName, PrivilegeType privilegeType) { + if (this.tablePrivilegeMap.containsKey(tableName)) { + TablePrivilege tablePrivilege = this.tablePrivilegeMap.get(tableName); + tablePrivilege.grantOption(privilegeType); + } + } + + public void revokeTableGrantOption(String tableName, PrivilegeType privilegeType) { + if (this.tablePrivilegeMap.containsKey(tableName)) { + TablePrivilege tablePrivilege = this.tablePrivilegeMap.get(tableName); + tablePrivilege.revokeGrantOption(privilegeType); + } + } + + public boolean checkDBPrivilege(PrivilegeType privilegeType) { + return this.privilegeSet.contains(privilegeType); + } + + public boolean checkTablePrivilege(String tableName, PrivilegeType privilegeType) { + TablePrivilege privileges = tablePrivilegeMap.get(tableName); + if (privileges == null) { + return false; + } + return privileges.getPrivileges().contains(privilegeType); + } + + public boolean checkDBGrantOption(PrivilegeType type) { + return this.privilegeSet.contains(type) && this.grantOptionSet.contains(type); + } + + public boolean checkTableGrantOption(String tableName, PrivilegeType type) { + TablePrivilege tablePrivilege = this.tablePrivilegeMap.get(tableName); + if (tablePrivilege == null) { + return false; + } + return tablePrivilege.getPrivileges().contains(type) + && tablePrivilege.getGrantOption().contains(type); + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + + DatabasePrivilege that = (DatabasePrivilege) o; + return databaseName.equals(that.databaseName) + && Objects.equals(privilegeSet, that.privilegeSet) + && Objects.equals(grantOptionSet, that.grantOptionSet) + && Objects.equals(tablePrivilegeMap, that.tablePrivilegeMap); + } + + public int hashCode() { + return Objects.hash(databaseName, tablePrivilegeMap, privilegeSet, grantOptionSet); + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append("Database(").append(databaseName).append("):{"); + List list = new ArrayList<>(this.privilegeSet); + Collections.sort(list); + for (PrivilegeType type : list) { + builder.append(type); + if (grantOptionSet.contains(type)) { + builder.append("_with_grant_option"); + } + builder.append(","); + } + builder.append("; Tables: ["); + + for (Map.Entry tablePriv : this.tablePrivilegeMap.entrySet()) { + builder.append(" ").append(tablePriv.getValue().toString()); + } + builder.append("]}"); + return builder.toString(); + } + + public ByteBuffer serialize() throws IOException { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); + SerializeUtils.serialize(this.databaseName, dataOutputStream); + SerializeUtils.serializePrivilegeTypeSet(this.privilegeSet, dataOutputStream); + SerializeUtils.serializePrivilegeTypeSet(this.grantOptionSet, dataOutputStream); + ReadWriteIOUtils.write(this.tablePrivilegeMap.size(), dataOutputStream); + for (Map.Entry tablePrivilegeEntry : + this.tablePrivilegeMap.entrySet()) { + tablePrivilegeEntry.getValue().serialize(dataOutputStream); + } + return ByteBuffer.wrap(byteArrayOutputStream.toByteArray()); + } + + public void deserialize(ByteBuffer buffer) { + this.privilegeSet = new HashSet<>(); + this.grantOptionSet = new HashSet<>(); + this.tablePrivilegeMap = new HashMap<>(); + this.databaseName = SerializeUtils.deserializeString(buffer); + SerializeUtils.deserializePrivilegeTypeSet(this.privilegeSet, buffer); + SerializeUtils.deserializePrivilegeTypeSet(this.grantOptionSet, buffer); + int length = buffer.getInt(); + for (int i = 0; i < length; i++) { + TablePrivilege tablePrivilege = new TablePrivilege(); + tablePrivilege.deserialize(buffer); + this.tablePrivilegeMap.put(tablePrivilege.getTableName(), tablePrivilege); + } + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserAccessor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java similarity index 60% rename from iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserAccessor.java rename to iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java index 812785e7dc9c..3c42d1a1dec6 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserAccessor.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java @@ -16,60 +16,52 @@ * specific language governing permissions and limitations * under the License. */ -package org.apache.iotdb.commons.auth.user; +package org.apache.iotdb.commons.auth.entity; -import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import java.io.IOException; import java.util.List; -/** This interface manages the serialization/deserialization of the user objects. */ -public interface IUserAccessor extends SnapshotProcessor { +/** This interface manages the serialization/deserialization of the entity objects. */ +public interface IEntityAccessor extends SnapshotProcessor { /** - * Deserialize a user from lower storage. + * Deserialize an entity from lower storage. * - * @param username The name of the user to be deserialized. - * @return The user object or null if no such user. + * @param entityName The name of the user/role to be deserialized. + * @return The user object or null if no such entity. * @throws IOException if an exception is raised when interacting with the lower storage. */ - User loadUser(String username) throws IOException; + Role loadEntity(String entityName) throws IOException; /** - * Serialize the user object to lower storage. + * Serialize the entity object to lower storage. * - * @param user The user object that is to be saved. + * @param entity The user/role object that is to be saved. * @throws IOException if an exception is raised when interacting with the lower storage. */ - void saveUser(User user) throws IOException; + void saveEntity(Role entity) throws IOException; /** * Delete a user's from lower storage. * - * @param username The name of the user to be deleted. - * @return True if the user is successfully deleted, false if the user does not exists. + * @param entityName The name of the user/role to be deleted. + * @return True if the user/role is successfully deleted, false if the user does not exist. * @throws IOException if an exception is raised when interacting with the lower storage. */ - boolean deleteUser(String username) throws IOException; + boolean deleteEntity(String entityName) throws IOException; /** - * List all users existing in the database. + * List all entities existing in the database. * * @return A list that contains names of all users. */ - List listAllUsers(); + List listAllEntities(); - /** Delete user's folders. */ - void cleanUserFolder(); + /** Delete entities' folders. */ + void cleanEntityFolder(); /** Re-initialize this object. */ void reset(); - - /** - * get UserDirPath - * - * @return userDirPath - */ - public String getDirPath(); } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/ModelType.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/ModelType.java new file mode 100644 index 000000000000..19f00124dca6 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/ModelType.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.commons.auth.entity; + +public enum ModelType { + TREE, + RELATIONAL, + ALL +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PathPrivilege.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PathPrivilege.java index e54ca063199d..6e40ac1fe1eb 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PathPrivilege.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PathPrivilege.java @@ -24,29 +24,25 @@ import org.apache.iotdb.commons.utils.AuthUtils; import org.apache.iotdb.commons.utils.SerializeUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; import java.util.HashSet; +import java.util.List; import java.util.Objects; import java.util.Set; /** This class represents a privilege on a specific seriesPath. */ public class PathPrivilege { - private static final Logger LOGGER = LoggerFactory.getLogger(PathPrivilege.class); - - private static final int PATH_PRI_SIZE = PrivilegeType.getPathPriCount(); - private Set privileges; - // grantopt show whether the privileges can be grant to / revoke from others. - // The privilege that can be grant to others must exist in privileges. - // The set of grantopt must be a subset of privileges. - private Set grantOpts; private PartialPath path; + private Set privileges; + private Set grantOpts; + + private static final int PRI_SIZE = PrivilegeType.getPrivilegeCount(PrivilegeModelType.TREE); public PathPrivilege() { // Empty constructor @@ -58,80 +54,115 @@ public PathPrivilege(PartialPath path) { this.grantOpts = new HashSet<>(); } - public Set getPrivileges() { - return privileges; + /** -------- set -------- * */ + public void setPrivileges(Set privs) { + this.privileges = privs; } - public void setPrivileges(Set privileges) { - this.privileges = privileges; + public void setPrivilegesInt(Set privs) { + this.privileges = new HashSet<>(); + for (Integer priv : privs) { + this.privileges.add(PrivilegeType.values()[priv]); + } } - public Set getGrantOpt() { - return grantOpts; + public void setGrantOpt(Set grantOpts) { + this.grantOpts = grantOpts; } - public void setGrantOpt(Set grantOpts) { - this.grantOpts = grantOpts; + public void setGrantOptInt(Set grantOpts) { + this.grantOpts = new HashSet<>(); + for (Integer priv : grantOpts) { + this.grantOpts.add(PrivilegeType.values()[priv]); + } } - public void grantPrivilege(int privilege, boolean grantOpt) { + public void setPath(PartialPath path) { + this.path = path; + } + + public void setAllPrivileges(int privs) { + for (int i = 0; i < PRI_SIZE; i++) { + if (((1 << i) & privs) != 0) { + privileges.add(PrivilegeType.values()[AuthUtils.pathPosToPri(i)]); + } + if ((1 << (i + 16) & privs) != 0) { + grantOpts.add(PrivilegeType.values()[AuthUtils.pathPosToPri(i)]); + } + } + } + + public void grantPrivilege(PrivilegeType privilege, boolean grantOpt) { privileges.add(privilege); if (grantOpt) { grantOpts.add(privilege); } } - public boolean revokePrivilege(int privilege) { + public boolean revokePrivilege(PrivilegeType privilege) { if (!privileges.contains(privilege)) { - LOGGER.warn("not find privilege{} on path {}", PrivilegeType.values()[privilege], path); return false; } privileges.remove(privilege); - // when we revoke privilege from path, remove its grant option grantOpts.remove(privilege); return true; } - public boolean revokeGrantOpt(int privilege) { + public boolean revokeGrantOpt(PrivilegeType privilege) { if (!privileges.contains(privilege)) { - LOGGER.warn("path {} dont have privilege {}", path, PrivilegeType.values()[privilege]); return false; } grantOpts.remove(privilege); return true; } - public boolean checkPrivilege(int privilege) { + /** -------- get -------- * */ + public Set getGrantOpt() { + return grantOpts; + } + + public Set getGrantOptIntSet() { + Set res = new HashSet<>(); + for (PrivilegeType item : grantOpts) { + res.add(item.ordinal()); + } + return res; + } + + public Set getPrivilegeIntSet() { + Set res = new HashSet<>(); + for (PrivilegeType item : privileges) { + res.add(item.ordinal()); + } + return res; + } + + public Set getPrivileges() { + return privileges; + } + + public boolean checkPrivilege(PrivilegeType privilege) { if (privileges.contains(privilege)) { return true; } - if (privilege == PrivilegeType.READ_DATA.ordinal()) { - return privileges.contains(PrivilegeType.WRITE_DATA.ordinal()); - } - if (privilege == PrivilegeType.READ_SCHEMA.ordinal()) { - return privileges.contains(PrivilegeType.WRITE_SCHEMA.ordinal()); + + if (privilege == PrivilegeType.READ_DATA) { + return privileges.contains(PrivilegeType.WRITE_DATA); } - return false; - } - public void setAllPrivileges(int privs) { - for (int i = 0; i < PATH_PRI_SIZE; i++) { - if (((1 << i) & privs) != 0) { - privileges.add(AuthUtils.pathPosToPri(i)); - } - if ((1 << (i + 16) & privs) != 0) { - grantOpts.add(AuthUtils.pathPosToPri(i)); - } + if (privilege == PrivilegeType.READ_SCHEMA) { + return privileges.contains(PrivilegeType.WRITE_SCHEMA); } + return false; } public int getAllPrivileges() { int privilege = 0; - for (Integer pri : privileges) { - privilege |= 1 << AuthUtils.pathPriToPos(PrivilegeType.values()[pri]); + for (PrivilegeType pri : privileges) { + privilege |= 1 << AuthUtils.pathPriToPos(pri); } - for (Integer grantOpt : grantOpts) { - privilege |= 1 << (AuthUtils.pathPriToPos(PrivilegeType.values()[grantOpt]) + 16); + for (PrivilegeType grantOpt : grantOpts) { + privilege |= 1 << (AuthUtils.pathPriToPos(grantOpt) + 16); } return privilege; } @@ -140,10 +171,6 @@ public PartialPath getPath() { return path; } - public void setPath(PartialPath path) { - this.path = path; - } - @Override public boolean equals(Object o) { if (this == o) { @@ -155,7 +182,7 @@ public boolean equals(Object o) { PathPrivilege that = (PathPrivilege) o; return Objects.equals(privileges, that.privileges) && Objects.equals(path, that.path) - && Objects.equals(grantOpts, this.grantOpts); + && Objects.equals(grantOpts, that.grantOpts); } @Override @@ -167,34 +194,32 @@ public int hashCode() { public String toString() { StringBuilder builder = new StringBuilder(path.getFullPath()); builder.append(" :"); - for (Integer privilegeId : privileges) { - builder.append(" ").append(PrivilegeType.values()[privilegeId]); - if (grantOpts.contains(privilegeId)) { + List sortedPrivileges = new ArrayList<>(privileges); + Collections.sort(sortedPrivileges); + for (PrivilegeType privilege : sortedPrivileges) { + builder.append(" ").append(privilege); + if (grantOpts.contains(privilege)) { builder.append("_").append("with_grant_option"); } } return builder.toString(); } - public ByteBuffer serialize() { + public ByteBuffer serialize() throws IOException { ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); DataOutputStream dataOutputStream = new DataOutputStream(byteArrayOutputStream); - SerializeUtils.serializeIntSet(privileges, dataOutputStream); - SerializeUtils.serializeIntSet(grantOpts, dataOutputStream); - try { - path.serialize(dataOutputStream); - } catch (IOException exception) { - LOGGER.error("Unexpected exception when serialize path", exception); - } + SerializeUtils.serializePrivilegeTypeSet(privileges, dataOutputStream); + SerializeUtils.serializePrivilegeTypeSet(grantOpts, dataOutputStream); + path.serialize(dataOutputStream); return ByteBuffer.wrap(byteArrayOutputStream.toByteArray()); } public void deserialize(ByteBuffer buffer) { privileges = new HashSet<>(); - SerializeUtils.deserializeIntSet(privileges, buffer); + SerializeUtils.deserializePrivilegeTypeSet(privileges, buffer); grantOpts = new HashSet<>(); - SerializeUtils.deserializeIntSet(grantOpts, buffer); + SerializeUtils.deserializePrivilegeTypeSet(grantOpts, buffer); path = (PartialPath) PathDeserializeUtil.deserialize(buffer); } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PriPrivilegeType.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PriPrivilegeType.java deleted file mode 100644 index 4dee3b57421e..000000000000 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PriPrivilegeType.java +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.iotdb.commons.auth.entity; - -import org.apache.iotdb.commons.utils.TestOnly; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashSet; -import java.util.List; -import java.util.Set; - -// This file contains the relationship of privilege between old and new version. -// To handle online upgrade from old version to new version. We need to handle information: -// 1. Pre privilege store in user.profile -// 2. Pre Privilege store in raftlog. -// Upgrade action has these stage: -// 1. load old version's profile from snapshot -// 2. redo raft log. -// 3. do new version's raftlog. - -public enum PriPrivilegeType { - CREATE_DATABASE(true, PrivilegeType.MANAGE_DATABASE), - INSERT_TIMESERIES(true, PrivilegeType.WRITE_DATA), - UPDATE_TIMESERIES(true, PrivilegeType.WRITE_DATA), - READ_TIMESERIES(true, PrivilegeType.READ_DATA), - CREATE_TIMESERIES(true, PrivilegeType.WRITE_SCHEMA), - DELETE_TIMESERIES(true, PrivilegeType.WRITE_SCHEMA), - CREATE_USER(false, PrivilegeType.MANAGE_USER), - DELETE_USER(false, PrivilegeType.MANAGE_USER), - MODIFY_PASSWORD(false), - LIST_USER(false), - GRANT_USER_PRIVILEGE(false), - REVOKE_USER_PRIVILEGE(false), - GRANT_USER_ROLE(false, PrivilegeType.MANAGE_ROLE), - REVOKE_USER_ROLE(false, PrivilegeType.MANAGE_ROLE), - CREATE_ROLE(false, PrivilegeType.MANAGE_ROLE), - DELETE_ROLE(false, PrivilegeType.MANAGE_ROLE), - LIST_ROLE(false), - GRANT_ROLE_PRIVILEGE(false), - REVOKE_ROLE_PRIVILEGE(false), - CREATE_FUNCTION(false, PrivilegeType.USE_UDF), - DROP_FUNCTION(false, PrivilegeType.USE_UDF), - CREATE_TRIGGER(true, PrivilegeType.USE_TRIGGER), - DROP_TRIGGER(true, PrivilegeType.USE_TRIGGER), - START_TRIGGER(true, PrivilegeType.USE_TRIGGER), - STOP_TRIGGER(true, PrivilegeType.USE_TRIGGER), - CREATE_CONTINUOUS_QUERY(false, PrivilegeType.USE_CQ), - DROP_CONTINUOUS_QUERY(false, PrivilegeType.USE_CQ), - ALL( - true, - PrivilegeType.USE_PIPE, - PrivilegeType.USE_UDF, - PrivilegeType.USE_CQ, - PrivilegeType.USE_MODEL, - PrivilegeType.USE_TRIGGER, - PrivilegeType.MANAGE_USER, - PrivilegeType.MANAGE_ROLE, - PrivilegeType.MANAGE_DATABASE, - PrivilegeType.EXTEND_TEMPLATE, - PrivilegeType.WRITE_SCHEMA, - PrivilegeType.WRITE_DATA, - PrivilegeType.READ_DATA, - PrivilegeType.READ_SCHEMA, - PrivilegeType.MAINTAIN), - DELETE_DATABASE(true, PrivilegeType.MANAGE_DATABASE), - ALTER_TIMESERIES(true, PrivilegeType.WRITE_SCHEMA), - UPDATE_TEMPLATE(false), - READ_TEMPLATE(false), - APPLY_TEMPLATE(true, PrivilegeType.WRITE_SCHEMA), - READ_TEMPLATE_APPLICATION(false), - SHOW_CONTINUOUS_QUERIES(false), - CREATE_PIPEPLUGIN(false, PrivilegeType.USE_PIPE), - DROP_PIPEPLUGIN(false, PrivilegeType.USE_PIPE), - SHOW_PIPEPLUGINS(false), - CREATE_PIPE(false, PrivilegeType.USE_PIPE), - START_PIPE(false, PrivilegeType.USE_PIPE), - STOP_PIPE(false, PrivilegeType.USE_PIPE), - DROP_PIPE(false, PrivilegeType.USE_PIPE), - SHOW_PIPES(false), - CREATE_VIEW(true, PrivilegeType.WRITE_SCHEMA), - ALTER_VIEW(true, PrivilegeType.WRITE_SCHEMA), - RENAME_VIEW(true, PrivilegeType.WRITE_SCHEMA), - DELETE_VIEW(true, PrivilegeType.WRITE_SCHEMA), - ; - - boolean accept = false; - private final boolean preIsPathRelevant; - private final List refPri = new ArrayList<>(); - - PriPrivilegeType(boolean accept) { - this.accept = accept; - this.preIsPathRelevant = false; - } - - PriPrivilegeType(boolean preIsPathRelevant, PrivilegeType... privilegeTypes) { - this.accept = true; - this.preIsPathRelevant = preIsPathRelevant; - this.refPri.addAll(Arrays.asList(privilegeTypes)); - } - - public boolean isAccept() { - return this.accept; - } - - @TestOnly - public boolean isPrePathRelevant() { - return this.preIsPathRelevant; - } - - public Set getSubPri() { - Set result = new HashSet<>(); - for (PrivilegeType peivType : refPri) { - result.add(peivType); - } - return result; - } - - public Set getSubPriOrd() { - Set result = new HashSet<>(); - for (PrivilegeType peivType : refPri) { - result.add(peivType.ordinal()); - } - return result; - } - - public Set getSubSysPriOrd() { - Set result = new HashSet<>(); - for (PrivilegeType peivType : refPri) { - if (!peivType.isPathRelevant()) { - result.add(peivType.ordinal()); - } - } - return result; - } -} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeModelType.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeModelType.java new file mode 100644 index 000000000000..d87ee2772c25 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeModelType.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.commons.auth.entity; + +public enum PrivilegeModelType { + INVALID, + TREE, + RELATIONAL, + SYSTEM; +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeType.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeType.java index 18c6c9b64c60..d6852d99a86a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeType.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeType.java @@ -24,59 +24,61 @@ /** This enum class contains all available privileges in IoTDB. */ public enum PrivilegeType { - READ_DATA(true), - WRITE_DATA(true), - READ_SCHEMA(true), - WRITE_SCHEMA(true), - MANAGE_USER, - MANAGE_ROLE, - USE_TRIGGER, + READ_DATA(PrivilegeModelType.TREE), + WRITE_DATA(PrivilegeModelType.TREE), + READ_SCHEMA(PrivilegeModelType.TREE), + WRITE_SCHEMA(PrivilegeModelType.TREE), + MANAGE_USER(PrivilegeModelType.SYSTEM), + MANAGE_ROLE(PrivilegeModelType.SYSTEM), + USE_TRIGGER(PrivilegeModelType.SYSTEM), + USE_UDF(PrivilegeModelType.SYSTEM), + USE_CQ(PrivilegeModelType.SYSTEM), + USE_PIPE(PrivilegeModelType.SYSTEM), + USE_MODEL(PrivilegeModelType.SYSTEM), - USE_UDF, + EXTEND_TEMPLATE(PrivilegeModelType.SYSTEM), + MANAGE_DATABASE(PrivilegeModelType.SYSTEM), + MAINTAIN(PrivilegeModelType.SYSTEM), + CREATE(PrivilegeModelType.RELATIONAL), + DROP(PrivilegeModelType.RELATIONAL), + ALTER(PrivilegeModelType.RELATIONAL), + SELECT(PrivilegeModelType.RELATIONAL), + INSERT(PrivilegeModelType.RELATIONAL), + DELETE(PrivilegeModelType.RELATIONAL); - USE_CQ, - USE_PIPE, - USE_MODEL, + private final PrivilegeModelType modelType; - EXTEND_TEMPLATE, - MANAGE_DATABASE, - MAINTAIN; - - private static final int PRIVILEGE_COUNT = values().length; - - private final boolean isPathRelevant; - - PrivilegeType() { - this.isPathRelevant = false; + PrivilegeType(PrivilegeModelType modelType) { + this.modelType = modelType; } - PrivilegeType(boolean isPathRelevant) { - this.isPathRelevant = isPathRelevant; + public boolean isPathPrivilege() { + return this.modelType == PrivilegeModelType.TREE; } - public boolean isPathRelevant() { - return isPathRelevant; + public boolean isSystemPrivilege() { + return this.modelType == PrivilegeModelType.SYSTEM; } - public static boolean isPathRelevant(int ordinal) { - return ordinal < 4; - } - - public static int getSysPriCount() { - int size = 0; - for (PrivilegeType item : PrivilegeType.values()) { - if (!item.isPathRelevant()) { - size++; - } - } - return size; + public boolean isRelationalPrivilege() { + return this.modelType == PrivilegeModelType.RELATIONAL; } - public static int getPathPriCount() { + public static int getPrivilegeCount(PrivilegeModelType type) { int size = 0; for (PrivilegeType item : PrivilegeType.values()) { - if (item.isPathRelevant()) { - size++; + switch (type) { + case TREE: + size += item.isPathPrivilege() ? 1 : 0; + break; + case SYSTEM: + size += item.isSystemPrivilege() ? 1 : 0; + break; + case RELATIONAL: + size += item.isRelationalPrivilege() ? 1 : 0; + break; + default: + break; } } return size; @@ -89,4 +91,12 @@ public static Set toPriType(Set priSet) { } return typeSet; } + + public boolean forRelationalSys() { + return this == MAINTAIN || this == MANAGE_USER || this == MANAGE_ROLE; + } + + public PrivilegeModelType getModelType() { + return modelType; + } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeUnion.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeUnion.java new file mode 100644 index 000000000000..c3ae41b16c4c --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/PrivilegeUnion.java @@ -0,0 +1,151 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.commons.auth.entity; + +import org.apache.iotdb.commons.path.PartialPath; + +import java.util.List; + +public class PrivilegeUnion { + private String databaseName; + private String tableName; + private PartialPath path; + + private List paths; + + private boolean grantOption; + private boolean forAny; + private final PrivilegeType privilegeType; + + private final PrivilegeModelType modelType; + + public PrivilegeUnion(String databaseName, PrivilegeType type, boolean grantOption) { + this.databaseName = databaseName; + this.privilegeType = type; + this.modelType = PrivilegeModelType.RELATIONAL; + this.grantOption = grantOption; + } + + public PrivilegeUnion(String databaseName, PrivilegeType type) { + this.databaseName = databaseName; + this.privilegeType = type; + this.modelType = PrivilegeModelType.RELATIONAL; + } + + public PrivilegeUnion( + String databaseName, String tableName, PrivilegeType type, boolean grantOption) { + this.databaseName = databaseName; + this.tableName = tableName; + this.privilegeType = type; + this.modelType = PrivilegeModelType.RELATIONAL; + this.grantOption = grantOption; + } + + public PrivilegeUnion(String databaseName, String tableName, PrivilegeType type) { + this.databaseName = databaseName; + this.tableName = tableName; + this.privilegeType = type; + this.modelType = PrivilegeModelType.RELATIONAL; + } + + public PrivilegeUnion(PartialPath path, PrivilegeType type, boolean grantOption) { + this.path = path; + this.privilegeType = type; + this.modelType = PrivilegeModelType.TREE; + this.grantOption = grantOption; + } + + public PrivilegeUnion(PartialPath path, PrivilegeType type) { + this.path = path; + this.privilegeType = type; + this.modelType = PrivilegeModelType.TREE; + this.grantOption = false; + } + + public PrivilegeUnion(PrivilegeType type, boolean grantOption) { + this.privilegeType = type; + this.modelType = PrivilegeModelType.SYSTEM; + this.grantOption = grantOption; + } + + public PrivilegeUnion( + List paths, PrivilegeType type, boolean grantOption) { + this.paths = paths; + this.privilegeType = type; + this.modelType = PrivilegeModelType.TREE; + this.grantOption = grantOption; + } + + public PrivilegeUnion(List paths, PrivilegeType type) { + this.paths = paths; + this.privilegeType = type; + this.modelType = PrivilegeModelType.TREE; + this.grantOption = false; + } + + public PrivilegeUnion(PrivilegeType type, boolean grantOption, boolean forAny) { + this.privilegeType = type; + this.modelType = PrivilegeModelType.RELATIONAL; + this.grantOption = grantOption; + this.forAny = forAny; + } + + public PrivilegeUnion(PrivilegeType type) { + this.privilegeType = type; + this.modelType = PrivilegeModelType.SYSTEM; + this.grantOption = false; + } + + public void setGrantOption(boolean grantOption) { + this.grantOption = grantOption; + } + + public PrivilegeModelType getModelType() { + return this.modelType; + } + + public String getDBName() { + return this.databaseName; + } + + public String getTbName() { + return this.tableName; + } + + public PartialPath getPath() { + return this.path; + } + + public PrivilegeType getPrivilegeType() { + return this.privilegeType; + } + + public List getPaths() { + return this.paths; + } + + public boolean isGrantOption() { + return this.grantOption; + } + + public boolean isForAny() { + return this.forAny; + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java index 581df286ecd9..32ecdd9437af 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java @@ -18,36 +18,52 @@ */ package org.apache.iotdb.commons.auth.entity; +import org.apache.iotdb.commons.exception.MetadataException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.utils.AuthUtils; import org.apache.iotdb.commons.utils.SerializeUtils; +import org.apache.iotdb.commons.utils.TestOnly; +import org.apache.iotdb.confignode.rpc.thrift.TDBPrivilege; +import org.apache.iotdb.confignode.rpc.thrift.TPathPrivilege; +import org.apache.iotdb.confignode.rpc.thrift.TRoleResp; +import org.apache.iotdb.confignode.rpc.thrift.TTablePrivilege; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; import java.io.IOException; import java.nio.ByteBuffer; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Set; -/** This class contains all information of a role. */ public class Role { - private String name; - private List pathPrivilegeList; + protected String name; + protected List pathPrivilegeList; - private Set sysPrivilegeSet; + protected Map objectPrivilegeMap; - private Set sysPriGrantOpt; + protected Set sysPrivilegeSet; - private static final int SYS_PRI_SIZE = PrivilegeType.getSysPriCount(); + protected Set sysPriGrantOpt; - private boolean serviceReady = true; + protected Set anyScopePrivilegeSet; + protected Set anyScopePrivilegeGrantOptSet; + + @TestOnly public Role() { - // empty constructor + this.pathPrivilegeList = new ArrayList<>(); + this.sysPrivilegeSet = new HashSet<>(); + this.sysPriGrantOpt = new HashSet<>(); + this.objectPrivilegeMap = new HashMap<>(); + this.anyScopePrivilegeGrantOptSet = new HashSet<>(); + this.anyScopePrivilegeSet = new HashSet<>(); } public Role(String name) { @@ -55,6 +71,9 @@ public Role(String name) { this.pathPrivilegeList = new ArrayList<>(); this.sysPrivilegeSet = new HashSet<>(); this.sysPriGrantOpt = new HashSet<>(); + this.objectPrivilegeMap = new HashMap<>(); + this.anyScopePrivilegeGrantOptSet = new HashSet<>(); + this.anyScopePrivilegeSet = new HashSet<>(); } /** ------------- get func -----------------* */ @@ -66,31 +85,164 @@ public List getPathPrivilegeList() { return pathPrivilegeList; } - public Set getSysPrivilege() { + public Set getSysPrivilege() { return sysPrivilegeSet; } - public Set getPathPrivileges(PartialPath path) { + private Set getPrivilegeIntSet(Set privs) { + Set res = new HashSet<>(); + for (PrivilegeType priv : privs) { + res.add(priv.ordinal()); + } + return res; + } + + public Set getPathPrivileges(PartialPath path) { return AuthUtils.getPrivileges(path, pathPrivilegeList); } - public Set getSysPriGrantOpt() { + public Set getSysPriGrantOpt() { return sysPriGrantOpt; } - public int getAllSysPrivileges() { - int privs = 0; - for (Integer sysPri : sysPrivilegeSet) { - privs |= 1 << AuthUtils.sysPriTopos(sysPri); + public Set getAnyScopePrivilegeGrantOpt() { + return anyScopePrivilegeGrantOptSet; + } + + public Set getAnyScopePrivilegeSet() { + return anyScopePrivilegeSet; + } + + public Map getDBScopePrivilegeMap() { + return objectPrivilegeMap; + } + + private DatabasePrivilege getObjectPrivilege(String objectName) { + return this.objectPrivilegeMap.computeIfAbsent( + objectName, k -> new DatabasePrivilege(objectName)); + } + + public List getTreePrivilegeInfo() { + List privilegeList = new ArrayList<>(); + for (PathPrivilege pathPrivilege : pathPrivilegeList) { + TPathPrivilege pathPriv = new TPathPrivilege(); + pathPriv.setPath(pathPrivilege.getPath().getFullPath()); + pathPriv.setPriSet(pathPrivilege.getPrivilegeIntSet()); + pathPriv.setPriGrantOpt(pathPrivilege.getGrantOptIntSet()); + privilegeList.add(pathPriv); } - for (Integer sysGrantOpt : sysPriGrantOpt) { - privs |= 1 << (AuthUtils.sysPriTopos(sysGrantOpt) + 16); + return privilegeList; + } + + public void loadTreePrivilegeInfo(List pathPrivilegeInfo) + throws MetadataException { + for (TPathPrivilege tPathPrivilege : pathPrivilegeInfo) { + PathPrivilege pathPri = new PathPrivilege(); + pathPri.setPath(new PartialPath(tPathPrivilege.getPath())); + pathPri.setPrivilegesInt(tPathPrivilege.getPriSet()); + pathPri.setGrantOptInt(tPathPrivilege.getPriGrantOpt()); + pathPrivilegeList.add(pathPri); + } + } + + public Set getDatabaseAndTablePrivilegeInfo() { + Set privileges = new HashSet<>(); + for (DatabasePrivilege databasePrivilege : objectPrivilegeMap.values()) { + TDBPrivilege tdbPrivilege = new TDBPrivilege(); + tdbPrivilege.setDatabaseName(databasePrivilege.getDatabaseName()); + tdbPrivilege.setPrivileges(databasePrivilege.getPrivilegeSet()); + tdbPrivilege.setGrantOpt(databasePrivilege.getPrivilegeGrantOptSet()); + if (!databasePrivilege.getTablePrivilegeMap().isEmpty()) { + for (TablePrivilege tablePrivilege : databasePrivilege.getTablePrivilegeMap().values()) { + TTablePrivilege tTablePrivilege = new TTablePrivilege(); + tTablePrivilege.setTableName(tablePrivilege.getTableName()); + tTablePrivilege.setPrivileges(tablePrivilege.getPrivilegesIntSet()); + tTablePrivilege.setGrantOption(tablePrivilege.getGrantOptionIntSet()); + tdbPrivilege.putToTablePrivilegeMap(tablePrivilege.getTableName(), tTablePrivilege); + } + } else { + tdbPrivilege.setTablePrivilegeMap(new HashMap<>()); + } + privileges.add(tdbPrivilege); + } + return privileges; + } + + public void loadDatabaseAndTablePrivilegeInfo(Map info) { + for (TDBPrivilege tdbPrivilege : info.values()) { + DatabasePrivilege databasePrivilege = new DatabasePrivilege(tdbPrivilege.getDatabaseName()); + for (Integer privId : tdbPrivilege.getPrivileges()) { + databasePrivilege.grantDBPrivilege(PrivilegeType.values()[privId]); + } + for (Integer privId : tdbPrivilege.getGrantOpt()) { + databasePrivilege.grantDBGrantOption(PrivilegeType.values()[privId]); + } + if (tdbPrivilege.getTablePrivilegeMapSize() != 0) { + for (TTablePrivilege tablePrivilege : tdbPrivilege.getTablePrivilegeMap().values()) { + for (Integer privId : tablePrivilege.getPrivileges()) { + databasePrivilege.grantTablePrivilege( + tablePrivilege.getTableName(), PrivilegeType.values()[privId]); + } + for (Integer privId : tablePrivilege.getGrantOption()) { + databasePrivilege.grantTableGrantOption( + tablePrivilege.getTableName(), PrivilegeType.values()[privId]); + } + } + } + this.objectPrivilegeMap.put(tdbPrivilege.getDatabaseName(), databasePrivilege); } - return privs; } - public boolean getServiceReady() { - return serviceReady; + public TRoleResp getRoleInfo(ModelType modelType) { + TRoleResp roleResp = new TRoleResp(); + roleResp.setName(name); + switch (modelType) { + case RELATIONAL: + Set privs = new HashSet<>(); + for (PrivilegeType priv : sysPrivilegeSet) { + if (priv.forRelationalSys()) { + privs.add(priv.ordinal()); + } + } + roleResp.setSysPriSet(privs); + Set privGrantOpt = new HashSet<>(); + for (PrivilegeType priv : sysPriGrantOpt) { + if (priv.forRelationalSys()) { + privGrantOpt.add(priv.ordinal()); + } + } + roleResp.setSysPriSetGrantOpt(privGrantOpt); + roleResp.setAnyScopeSet(getPrivilegeIntSet(anyScopePrivilegeSet)); + roleResp.setAnyScopeGrantSet(getPrivilegeIntSet(anyScopePrivilegeGrantOptSet)); + roleResp.setPrivilegeList(new ArrayList<>()); + Set tdbPrivileges = getDatabaseAndTablePrivilegeInfo(); + roleResp.setDbPrivilegeMap(new HashMap<>()); + for (TDBPrivilege tdbPrivilege : tdbPrivileges) { + roleResp.putToDbPrivilegeMap(tdbPrivilege.getDatabaseName(), tdbPrivilege); + } + break; + case TREE: + roleResp.setSysPriSet(getPrivilegeIntSet(sysPrivilegeSet)); + roleResp.setSysPriSetGrantOpt(getPrivilegeIntSet(sysPriGrantOpt)); + roleResp.setAnyScopeSet(new HashSet<>()); + roleResp.setAnyScopeGrantSet(new HashSet<>()); + roleResp.setPrivilegeList(getTreePrivilegeInfo()); + roleResp.setDbPrivilegeMap(new HashMap<>()); + break; + case ALL: + roleResp.setSysPriSet(getPrivilegeIntSet(sysPrivilegeSet)); + roleResp.setSysPriSetGrantOpt(getPrivilegeIntSet(sysPriGrantOpt)); + roleResp.setAnyScopeSet(getPrivilegeIntSet(anyScopePrivilegeSet)); + roleResp.setAnyScopeGrantSet(getPrivilegeIntSet(anyScopePrivilegeGrantOptSet)); + roleResp.setPrivilegeList(getTreePrivilegeInfo()); + Set tdbPrivileges1 = getDatabaseAndTablePrivilegeInfo(); + roleResp.setDbPrivilegeMap(new HashMap<>()); + for (TDBPrivilege tdbPrivilege : tdbPrivileges1) { + roleResp.putToDbPrivilegeMap(tdbPrivilege.getDatabaseName(), tdbPrivilege); + } + break; + } + return roleResp; } /** -------------- set func ----------------* */ @@ -102,7 +254,7 @@ public void setPrivilegeList(List privilegeList) { this.pathPrivilegeList = privilegeList; } - public void setPathPrivileges(PartialPath path, Set privileges) { + public void setPathPrivileges(PartialPath path, Set privileges) { for (PathPrivilege pathPrivilege : pathPrivilegeList) { if (pathPrivilege.getPath().equals(path)) { pathPrivilege.setPrivileges(privileges); @@ -110,74 +262,301 @@ public void setPathPrivileges(PartialPath path, Set privileges) { } } - public void addPathPrivilege(PartialPath path, int privilegeId, boolean grantOpt) { - AuthUtils.addPrivilege(path, privilegeId, pathPrivilegeList, grantOpt); + public void setObjectPrivilegeMap(Map privilegeMap) { + this.objectPrivilegeMap = privilegeMap; } - public void removePathPrivilege(PartialPath path, int privilegeId) { - AuthUtils.removePrivilege(path, privilegeId, pathPrivilegeList); + public void grantPathPrivilege(PartialPath path, PrivilegeType priv, boolean grantOpt) { + AuthUtils.addPrivilege(path, priv, pathPrivilegeList, grantOpt); } - public void setSysPrivilegeSet(Set privilegeSet) { - this.sysPrivilegeSet = privilegeSet; + public void revokePathPrivilege(PartialPath path, PrivilegeType priv) { + AuthUtils.removePrivilege(path, priv, pathPrivilegeList); } - public void setSysPriGrantOpt(Set grantOpt) { - this.sysPriGrantOpt = grantOpt; + public void revokePathPrivilegeGrantOption(PartialPath path, PrivilegeType priv) { + AuthUtils.removePrivilegeGrantOption(path, priv, pathPrivilegeList); } - public void setSysPrivilegeSet(int privilegeMask) { - if (sysPrivilegeSet == null) { - sysPrivilegeSet = new HashSet<>(); - } - if (sysPriGrantOpt == null) { - sysPriGrantOpt = new HashSet<>(); + public void setSysPrivilegeSet(Set privilegeSet) { + this.sysPrivilegeSet = privilegeSet; + } + + public void setSysPrivilegeSetInt(Set privilegeSet) { + for (Integer priv : privilegeSet) { + this.sysPrivilegeSet.add(PrivilegeType.values()[priv]); } + } + + public void setSysPrivilegesWithMask(int privMask) { + final int SYS_PRI_SIZE = PrivilegeType.getPrivilegeCount(PrivilegeModelType.SYSTEM); for (int i = 0; i < SYS_PRI_SIZE; i++) { - if ((privilegeMask & (1 << i)) != 0) { + if ((privMask & (1 << i)) != 0) { sysPrivilegeSet.add(AuthUtils.posToSysPri(i)); + if ((privMask & (1 << (i + 16))) != 0) { + sysPriGrantOpt.add(AuthUtils.posToSysPri(i)); + } } - if ((privilegeMask & (1 << (i + 16))) != 0) { - sysPriGrantOpt.add(AuthUtils.posToSysPri(i)); + } + } + + public void setAnyScopePrivilegeSetWithMask(int privMask) { + final int PRI_COUNT = PrivilegeType.getPrivilegeCount(PrivilegeModelType.RELATIONAL); + for (int i = 0; i < PRI_COUNT; i++) { + if ((privMask & (1 << i)) != 0) { + anyScopePrivilegeSet.add(AuthUtils.posToObjPri(i)); + if ((privMask & (1 << (i + 16))) != 0) { + anyScopePrivilegeGrantOptSet.add(AuthUtils.posToObjPri(i)); + } } } } - public void addSysPrivilege(int privilegeId) { - sysPrivilegeSet.add(privilegeId); + public void setAnyScopePrivilegeSet(Set privilegeSet) { + this.anyScopePrivilegeSet = privilegeSet; + } + + public void setAnyScopePrivilegeSetInt(Set privilegeSet) { + for (Integer priv : privilegeSet) { + this.anyScopePrivilegeSet.add(PrivilegeType.values()[priv]); + } + } + + public void setAnyScopePrivilegeGrantOptSet(Set grantOpt) { + this.anyScopePrivilegeGrantOptSet = grantOpt; + } + + public void setAnyScopePrivilegeGrantOptSetInt(Set privilegeSet) { + for (Integer priv : privilegeSet) { + this.anyScopePrivilegeGrantOptSet.add(PrivilegeType.values()[priv]); + } + } + + public void setSysPriGrantOpt(Set grantOpt) { + this.sysPriGrantOpt = grantOpt; + } + + public void setSysPriGrantOptInt(Set grantOptInt) { + for (Integer priv : grantOptInt) { + this.sysPriGrantOpt.add(PrivilegeType.values()[priv]); + } + } + + public void grantSysPrivilege(PrivilegeType priv, boolean grantOpt) { + sysPrivilegeSet.add(priv); + if (grantOpt) { + sysPriGrantOpt.add(priv); + } + } + + public void grantAnyScopePrivilege(PrivilegeType priv, boolean grantOpt) { + anyScopePrivilegeSet.add(priv); + if (grantOpt) { + anyScopePrivilegeGrantOptSet.add(priv); + } + } + + public void revokeAnyScopePrivilege(PrivilegeType priv) { + anyScopePrivilegeSet.remove(priv); + anyScopePrivilegeGrantOptSet.remove(priv); + } + + public void revokeAnyScopePrivilegeGrantOption(PrivilegeType priv) { + anyScopePrivilegeGrantOptSet.remove(priv); + } + + public void grantDBPrivilege(String dbName, PrivilegeType priv, boolean grantOption) { + DatabasePrivilege databasePrivilege = getObjectPrivilege(dbName); + databasePrivilege.grantDBPrivilege(priv); + if (grantOption) { + databasePrivilege.grantDBGrantOption(priv); + } + } + + public void grantTBPrivilege( + String dbName, String tbName, PrivilegeType priv, boolean grantOption) { + DatabasePrivilege databasePrivilege = getObjectPrivilege(dbName); + databasePrivilege.grantTablePrivilege(tbName, priv); + if (grantOption) { + databasePrivilege.grantTableGrantOption(tbName, priv); + } + } + + public void revokeDBPrivilege(String dbName, PrivilegeType priv) { + if (!objectPrivilegeMap.containsKey(dbName)) { + return; + } + DatabasePrivilege databasePrivilege = objectPrivilegeMap.get(dbName); + databasePrivilege.revokeDBPrivilege(priv); + if (databasePrivilege.getTablePrivilegeMap().isEmpty() + && databasePrivilege.getPrivilegeSet().isEmpty()) { + objectPrivilegeMap.remove(dbName); + } } - public void removeSysPrivilege(int privilegeId) { - sysPrivilegeSet.remove(privilegeId); + public void revokeDBPrivilegeGrantOption(String dbName, PrivilegeType priv) { + if (!objectPrivilegeMap.containsKey(dbName)) { + return; + } + DatabasePrivilege databasePrivilege = objectPrivilegeMap.get(dbName); + databasePrivilege.revokeDBGrantOption(priv); } - public void setServiceReady(boolean ready) { - serviceReady = ready; + public void revokeTBPrivilege(String dbName, String tbName, PrivilegeType priv) { + if (!objectPrivilegeMap.containsKey(dbName)) { + return; + } + DatabasePrivilege databasePrivilege = objectPrivilegeMap.get(dbName); + databasePrivilege.revokeTablePrivilege(tbName, priv); + if (databasePrivilege.getTablePrivilegeMap().isEmpty() + && databasePrivilege.getPrivilegeSet().isEmpty()) { + this.objectPrivilegeMap.remove(dbName); + } + } + + public void revokeTBPrivilegeGrantOption(String dbName, String tbName, PrivilegeType priv) { + if (!objectPrivilegeMap.containsKey(dbName)) { + return; + } + DatabasePrivilege databasePrivilege = objectPrivilegeMap.get(dbName); + if (!databasePrivilege.getTablePrivilegeMap().containsKey(tbName)) { + return; + } + TablePrivilege tablePrivilege = databasePrivilege.getTablePrivilegeMap().get(tbName); + tablePrivilege.revokeGrantOption(priv); + } + + public void grantSysPrivilegeGrantOption(PrivilegeType priv) { + sysPriGrantOpt.add(priv); + } + + public void revokeSysPrivilege(PrivilegeType priv) { + sysPrivilegeSet.remove(priv); + } + + public void revokeSysPrivilegeGrantOption(PrivilegeType priv) { + sysPriGrantOpt.remove(priv); } /** ------------ check func ---------------* */ - public boolean hasPrivilegeToRevoke(PartialPath path, int privilegeId) { - if (path == null) { - return sysPrivilegeSet.contains(privilegeId); - } else { - return AuthUtils.hasPrivilegeToReovke(path, privilegeId, pathPrivilegeList); + public boolean hasPrivilegeToRevoke(PartialPath path, PrivilegeType priv) { + return AuthUtils.hasPrivilegeToRevoke(path, priv, pathPrivilegeList); + } + + public boolean hasPrivilegeToRevoke(PrivilegeType priv) { + return this.sysPrivilegeSet.contains(priv); + } + + public boolean hasPrivilegeToRevoke(String dbname, PrivilegeType priv) { + return this.objectPrivilegeMap.containsKey(dbname) + && this.objectPrivilegeMap.get(dbname).checkDBPrivilege(priv); + } + + public boolean hasPrivilegeToRevoke(String dbname, String tbName, PrivilegeType priv) { + return this.objectPrivilegeMap.containsKey(dbname) + && this.objectPrivilegeMap.get(dbname).checkTablePrivilege(tbName, priv); + } + + public boolean checkAnyScopePrivilege(PrivilegeType priv) { + return anyScopePrivilegeSet.contains(priv); + } + + public boolean checkDatabasePrivilege(String dbname, PrivilegeType priv) { + return checkAnyScopePrivilege(priv) + || (this.objectPrivilegeMap.containsKey(dbname) + && this.objectPrivilegeMap.get(dbname).checkDBPrivilege(priv)); + } + + public boolean checkTablePrivilege(String dbname, String table, PrivilegeType priv) { + return checkAnyScopePrivilege(priv) + || (this.objectPrivilegeMap.containsKey(dbname) + && (this.objectPrivilegeMap.get(dbname).checkDBPrivilege(priv) + || this.objectPrivilegeMap.get(dbname).checkTablePrivilege(table, priv))); + } + + public boolean checkDatabasePrivilegeGrantOption(String dbname, PrivilegeType priv) { + if (checkAnyScopePrivilegeGrantOption(priv)) { + return true; } + return this.objectPrivilegeMap.containsKey(dbname) + && this.objectPrivilegeMap.get(dbname).checkDBGrantOption(priv); } - public boolean checkPathPrivilege(PartialPath path, int privilegeId) { - return AuthUtils.checkPathPrivilege(path, privilegeId, pathPrivilegeList); + public boolean checkTablePrivilegeGrantOption(String dbname, String table, PrivilegeType priv) { + if (checkAnyScopePrivilegeGrantOption(priv)) { + return true; + } + if (checkDatabasePrivilegeGrantOption(dbname, priv)) { + return true; + } + return this.objectPrivilegeMap.containsKey(dbname) + && this.objectPrivilegeMap.get(dbname).checkTableGrantOption(table, priv); } - public boolean checkPathPrivilegeGrantOpt(PartialPath path, int privilegeId) { - return AuthUtils.checkPathPrivilegeGrantOpt(path, privilegeId, pathPrivilegeList); + public boolean checkAnyScopePrivilegeGrantOption(PrivilegeType priv) { + return this.anyScopePrivilegeGrantOptSet.contains(priv) + && this.anyScopePrivilegeSet.contains(priv); } - public boolean checkSysPrivilege(int privilegeId) { - return sysPrivilegeSet.contains(privilegeId); + public boolean checkPathPrivilege(PartialPath path, PrivilegeType priv) { + return AuthUtils.checkPathPrivilege(path, priv, pathPrivilegeList); } - public boolean checkSysPriGrantOpt(int privilegeId) { - return sysPrivilegeSet.contains(privilegeId) && sysPriGrantOpt.contains(privilegeId); + public boolean checkPathPrivilegeGrantOpt(PartialPath path, PrivilegeType priv) { + return AuthUtils.checkPathPrivilegeGrantOpt(path, priv, pathPrivilegeList); + } + + public boolean checkSysPrivilege(PrivilegeType priv) { + return sysPrivilegeSet.contains(priv); + } + + public boolean checkSysPriGrantOpt(PrivilegeType priv) { + return sysPrivilegeSet.contains(priv) && sysPriGrantOpt.contains(priv); + } + + public boolean checkDBVisible(String database) { + return !anyScopePrivilegeSet.isEmpty() || objectPrivilegeMap.containsKey(database); + } + + public boolean checkTBVisible(String database, String tbName) { + // Has any scope privileges + if (!anyScopePrivilegeSet.isEmpty()) { + return true; + } + + // Fail back early + if (!objectPrivilegeMap.containsKey(database)) { + return false; + } + + // Has db scope privileges + if (!objectPrivilegeMap.get(database).getPrivilegeSet().isEmpty()) { + return true; + } + + return objectPrivilegeMap.get(database).getTablePrivilegeMap().containsKey(tbName); + } + + public int getAllSysPrivileges() { + int privs = 0; + for (PrivilegeType sysPri : sysPrivilegeSet) { + privs |= 1 << AuthUtils.sysPriToPos(sysPri); + } + for (PrivilegeType sysGrantOpt : sysPriGrantOpt) { + privs |= 1 << (AuthUtils.sysPriToPos(sysGrantOpt) + 16); + } + return privs; + } + + public int getAnyScopePrivileges() { + int privs = 0; + for (PrivilegeType anyScope : anyScopePrivilegeSet) { + privs |= 1 << AuthUtils.objPriToPos(anyScope); + } + for (PrivilegeType anyScopeGrantOpt : anyScopePrivilegeGrantOptSet) { + privs |= 1 << (AuthUtils.objPriToPos(anyScopeGrantOpt) + 16); + } + return privs; } /** ----------- misc --------------------* */ @@ -193,12 +572,22 @@ public boolean equals(Object o) { return Objects.equals(name, role.name) && Objects.equals(pathPrivilegeList, role.pathPrivilegeList) && Objects.equals(sysPrivilegeSet, role.sysPrivilegeSet) - && Objects.equals(sysPriGrantOpt, role.sysPriGrantOpt); + && Objects.equals(sysPriGrantOpt, role.sysPriGrantOpt) + && Objects.equals(objectPrivilegeMap, role.objectPrivilegeMap) + && Objects.equals(anyScopePrivilegeSet, role.anyScopePrivilegeSet) + && Objects.equals(anyScopePrivilegeGrantOptSet, role.anyScopePrivilegeGrantOptSet); } @Override public int hashCode() { - return Objects.hash(name, pathPrivilegeList, sysPrivilegeSet); + return Objects.hash( + name, + pathPrivilegeList, + sysPrivilegeSet, + sysPriGrantOpt, + anyScopePrivilegeSet, + anyScopePrivilegeGrantOptSet, + objectPrivilegeMap); } public ByteBuffer serialize() { @@ -208,18 +597,19 @@ public ByteBuffer serialize() { SerializeUtils.serialize(name, dataOutputStream); try { - dataOutputStream.writeInt(sysPrivilegeSet.size()); - for (Integer item : sysPrivilegeSet) { - dataOutputStream.writeInt(item); - } - dataOutputStream.writeInt(sysPriGrantOpt.size()); - for (Integer item : sysPriGrantOpt) { - dataOutputStream.writeInt(item); - } + SerializeUtils.serializePrivilegeTypeSet(sysPrivilegeSet, dataOutputStream); + SerializeUtils.serializePrivilegeTypeSet(sysPriGrantOpt, dataOutputStream); dataOutputStream.writeInt(pathPrivilegeList.size()); for (PathPrivilege pathPrivilege : pathPrivilegeList) { dataOutputStream.write(pathPrivilege.serialize().array()); } + SerializeUtils.serializePrivilegeTypeSet(anyScopePrivilegeSet, dataOutputStream); + SerializeUtils.serializePrivilegeTypeSet(anyScopePrivilegeGrantOptSet, dataOutputStream); + dataOutputStream.writeInt(objectPrivilegeMap.size()); + for (Map.Entry item : objectPrivilegeMap.entrySet()) { + SerializeUtils.serialize(item.getKey(), dataOutputStream); + dataOutputStream.write(item.getValue().serialize().array()); + } } catch (IOException e) { // unreachable } @@ -232,12 +622,12 @@ public void deserialize(ByteBuffer buffer) { int sysPrivilegeSize = buffer.getInt(); sysPrivilegeSet = new HashSet<>(); for (int i = 0; i < sysPrivilegeSize; i++) { - sysPrivilegeSet.add(buffer.getInt()); + sysPrivilegeSet.add(PrivilegeType.values()[buffer.getInt()]); } int sysPriGrantOptSize = buffer.getInt(); sysPriGrantOpt = new HashSet<>(); for (int i = 0; i < sysPriGrantOptSize; i++) { - sysPriGrantOpt.add(buffer.getInt()); + sysPriGrantOpt.add(PrivilegeType.values()[buffer.getInt()]); } int privilegeListSize = buffer.getInt(); pathPrivilegeList = new ArrayList<>(privilegeListSize); @@ -246,6 +636,17 @@ public void deserialize(ByteBuffer buffer) { pathPrivilege.deserialize(buffer); pathPrivilegeList.add(pathPrivilege); } + + SerializeUtils.deserializePrivilegeTypeSet(anyScopePrivilegeSet, buffer); + SerializeUtils.deserializePrivilegeTypeSet(anyScopePrivilegeGrantOptSet, buffer); + + int objectPrivilegesSize = buffer.getInt(); + for (int i = 0; i < objectPrivilegesSize; i++) { + DatabasePrivilege databasePrivilege = new DatabasePrivilege(); + String objectName = SerializeUtils.deserializeString(buffer); + databasePrivilege.deserialize(buffer); + this.objectPrivilegeMap.put(objectName, databasePrivilege); + } } @Override @@ -257,16 +658,23 @@ public String toString() { + ", pathPrivilegeList=" + pathPrivilegeList + ", systemPrivilegeSet=" - + sysPriToString() + + priSetToString(sysPrivilegeSet, sysPriGrantOpt) + + ", AnyScopePrivilegeMap=" + + priSetToString(anyScopePrivilegeSet, anyScopePrivilegeGrantOptSet) + + ", objectPrivilegeSet=" + + objectPrivilegeMap + '}'; } - private Set sysPriToString() { + public Set priSetToString(Set privs, Set grantOpt) { Set priSet = new HashSet<>(); - for (Integer pri : sysPrivilegeSet) { - StringBuilder str = new StringBuilder(String.valueOf(PrivilegeType.values()[pri].toString())); - if (sysPriGrantOpt.contains(pri)) { - str.append("_with_grant_option "); + ArrayList privBak = new ArrayList<>(privs); + Collections.sort(privBak); + + for (PrivilegeType priv : privBak) { + StringBuilder str = new StringBuilder(String.valueOf(priv)); + if (grantOpt.contains(priv)) { + str.append("_with_grant_option"); } priSet.add(str.toString()); } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/TablePrivilege.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/TablePrivilege.java new file mode 100644 index 000000000000..606b2fbbe2ea --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/TablePrivilege.java @@ -0,0 +1,166 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.commons.auth.entity; + +import org.apache.iotdb.commons.utils.AuthUtils; +import org.apache.iotdb.commons.utils.SerializeUtils; + +import org.apache.tsfile.utils.ReadWriteIOUtils; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +// This class contain table's privileges. +public class TablePrivilege { + private String tableName; + private Set privileges; + private Set grantOption; + + public TablePrivilege(String tableName) { + this.tableName = tableName; + this.privileges = new HashSet<>(); + this.grantOption = new HashSet<>(); + } + + public TablePrivilege() { + // this construction just used for deserialize. + } + + public String getTableName() { + return this.tableName; + } + + public Set getPrivileges() { + return this.privileges; + } + + public Set getGrantOption() { + return this.grantOption; + } + + public Set getPrivilegesIntSet() { + Set res = new HashSet<>(); + for (PrivilegeType type : privileges) { + res.add(type.ordinal()); + } + return res; + } + + public Set getGrantOptionIntSet() { + Set res = new HashSet<>(); + for (PrivilegeType type : grantOption) { + res.add(type.ordinal()); + } + return res; + } + + public void grantPrivilege(PrivilegeType priv) { + this.privileges.add(priv); + } + + public void revokePrivilege(PrivilegeType priv) { + this.privileges.remove(priv); + } + + public void grantOption(PrivilegeType priv) { + this.grantOption.add(priv); + } + + public void revokeGrantOption(PrivilegeType priv) { + this.grantOption.remove(priv); + } + + public void setPrivileges(int mask) { + final int PRI_SIZE = PrivilegeType.getPrivilegeCount(PrivilegeModelType.RELATIONAL); + for (int i = 0; i < PRI_SIZE; i++) { + if (((1 << i) & mask) != 0) { + this.privileges.add(AuthUtils.posToObjPri(i)); + if (((1 << (i + 16)) & mask) != 0) { + this.grantOption.add(AuthUtils.posToObjPri(i)); + } + } + } + } + + public int getAllPrivileges() { + int privilege = 0; + for (PrivilegeType pri : privileges) { + privilege |= 1 << AuthUtils.objPriToPos(pri); + } + for (PrivilegeType pri : grantOption) { + privilege |= 1 << (AuthUtils.objPriToPos(pri) + 16); + } + return privilege; + } + + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + TablePrivilege that = (TablePrivilege) o; + return tableName.equals(that.tableName) + && privileges.equals(that.privileges) + && grantOption.equals(that.grantOption); + } + + public int hashCode() { + return Objects.hash(tableName, privileges, grantOption); + } + + public String toString() { + StringBuilder builder = new StringBuilder(); + builder.append(this.tableName).append("("); + List privs = new ArrayList<>(this.privileges); + Collections.sort(privs); + for (PrivilegeType type : privs) { + builder.append(type); + if (grantOption.contains(type)) { + builder.append("_with_grant_option"); + } + builder.append(","); + } + builder.append(")"); + return builder.toString(); + } + + public void serialize(OutputStream outputStream) throws IOException { + ReadWriteIOUtils.write(this.tableName, outputStream); + SerializeUtils.serializePrivilegeTypeSet(this.privileges, (DataOutputStream) outputStream); + SerializeUtils.serializePrivilegeTypeSet(this.grantOption, (DataOutputStream) outputStream); + } + + public void deserialize(ByteBuffer byteBuffer) { + this.privileges = new HashSet<>(); + this.grantOption = new HashSet<>(); + this.tableName = SerializeUtils.deserializeString(byteBuffer); + SerializeUtils.deserializePrivilegeTypeSet(this.privileges, byteBuffer); + SerializeUtils.deserializePrivilegeTypeSet(this.grantOption, byteBuffer); + } +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java index becfde356526..7fef5856c61a 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/User.java @@ -19,6 +19,7 @@ package org.apache.iotdb.commons.auth.entity; import org.apache.iotdb.commons.utils.SerializeUtils; +import org.apache.iotdb.confignode.rpc.thrift.TUserResp; import java.io.ByteArrayOutputStream; import java.io.DataOutputStream; @@ -35,12 +36,10 @@ public class User extends Role { private String password; - private List roleList; + private Set roleSet; private boolean isOpenIdUser = false; // default NO openIdUser - private boolean useWaterMark = false; // default NO watermark - public User() { // empty constructor } @@ -54,7 +53,7 @@ public User() { public User(String name, String password) { super(name); this.password = password; - this.roleList = new ArrayList<>(); + this.roleSet = new HashSet<>(); } /** ---------- set func ---------------* */ @@ -62,16 +61,16 @@ public void setPassword(String password) { this.password = password; } - public void setUseWaterMark(boolean useWaterMark) { - this.useWaterMark = useWaterMark; - } - public void setOpenIdUser(boolean openIdUser) { isOpenIdUser = openIdUser; } - public void setRoleList(List roles) { - roleList = roles; + public void setRoleSet(Set roles) { + roleSet = roles; + } + + public void addRole(String roleName) { + roleSet.add(roleName); } /** ------------ get func ----------------* */ @@ -79,20 +78,25 @@ public String getPassword() { return password; } - public boolean isUseWaterMark() { - return useWaterMark; - } - public boolean isOpenIdUser() { return isOpenIdUser; } public boolean hasRole(String role) { - return roleList.contains(role); + return roleSet.contains(role); + } + + public Set getRoleSet() { + return roleSet; } - public List getRoleList() { - return roleList; + public TUserResp getUserInfo(ModelType modelType) { + TUserResp resp = new TUserResp(); + resp.setPermissionInfo(getRoleInfo(modelType)); + resp.setPassword(password); + resp.setIsOpenIdUser(isOpenIdUser); + resp.setRoleSet(roleSet); + return resp; } /** -------------- misc ----------------* */ @@ -110,11 +114,10 @@ public boolean equals(Object o) { } private boolean contentEquals(User user) { - return Objects.equals(super.getName(), user.getName()) + return super.equals((Role) user) + && Objects.equals(roleSet, user.roleSet) && Objects.equals(password, user.password) - && Objects.equals(super.getPathPrivilegeList(), user.getPathPrivilegeList()) - && Objects.equals(super.getSysPrivilege(), user.getSysPrivilege()) - && Objects.equals(roleList, user.roleList); + && Objects.equals(isOpenIdUser, user.isOpenIdUser); } @Override @@ -124,7 +127,7 @@ public int hashCode() { password, super.getPathPrivilegeList(), super.getSysPrivilege(), - roleList, + roleSet, isOpenIdUser); } @@ -138,22 +141,21 @@ public ByteBuffer serialize() { try { dataOutputStream.writeInt(super.getSysPrivilege().size()); - for (Integer item : super.getSysPrivilege()) { - dataOutputStream.writeInt(item); + for (PrivilegeType item : super.getSysPrivilege()) { + dataOutputStream.writeInt(item.ordinal()); } dataOutputStream.writeInt(super.getSysPriGrantOpt().size()); - for (Integer item : super.getSysPriGrantOpt()) { - dataOutputStream.writeInt(item); + for (PrivilegeType item : super.getSysPriGrantOpt()) { + dataOutputStream.writeInt(item.ordinal()); } dataOutputStream.writeInt(super.getPathPrivilegeList().size()); for (PathPrivilege pathPrivilege : super.getPathPrivilegeList()) { dataOutputStream.write(pathPrivilege.serialize().array()); } - dataOutputStream.writeBoolean(useWaterMark); } catch (IOException e) { // unreachable } - SerializeUtils.serializeStringList(roleList, dataOutputStream); + SerializeUtils.serializeStringList(new ArrayList<>(roleSet), dataOutputStream); return ByteBuffer.wrap(byteArrayOutputStream.toByteArray()); } @@ -163,15 +165,15 @@ public void deserialize(ByteBuffer buffer) { super.setName(SerializeUtils.deserializeString(buffer)); password = SerializeUtils.deserializeString(buffer); int systemPriSize = buffer.getInt(); - Set sysPri = new HashSet<>(); + Set sysPri = new HashSet<>(); for (int i = 0; i < systemPriSize; i++) { - sysPri.add(buffer.getInt()); + sysPri.add(PrivilegeType.values()[buffer.getInt()]); } super.setSysPrivilegeSet(sysPri); int sysPriGrantOptSize = buffer.getInt(); - Set grantOpt = new HashSet<>(); + Set grantOpt = new HashSet<>(); for (int i = 0; i < sysPriGrantOptSize; i++) { - grantOpt.add(buffer.getInt()); + grantOpt.add(PrivilegeType.values()[buffer.getInt()]); } super.setSysPriGrantOpt(grantOpt); @@ -183,8 +185,7 @@ public void deserialize(ByteBuffer buffer) { privilegeList.add(pathPrivilege); } super.setPrivilegeList(privilegeList); - useWaterMark = buffer.get() == 1; - roleList = SerializeUtils.deserializeStringList(buffer); + roleSet = new HashSet<>(SerializeUtils.deserializeStringList(buffer)); } @Override @@ -197,15 +198,17 @@ public String toString() { + password + '\'' + ", pathPrivilegeList=" - + super.getPathPrivilegeList() + + pathPrivilegeList + ", sysPrivilegeSet=" - + super.getSysPrivilege() + + priSetToString(sysPrivilegeSet, sysPriGrantOpt) + + ", AnyScopePrivilegeMap=" + + priSetToString(anyScopePrivilegeSet, anyScopePrivilegeGrantOptSet) + + ", objectPrivilegeMap=" + + objectPrivilegeMap + ", roleList=" - + roleList + + roleSet + ", isOpenIdUser=" + isOpenIdUser - + ", useWaterMark=" - + useWaterMark + '}'; } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java index 43341bd75619..17cda526d34d 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/BasicRoleManager.java @@ -19,12 +19,13 @@ package org.apache.iotdb.commons.auth.role; import org.apache.iotdb.commons.auth.AuthException; -import org.apache.iotdb.commons.auth.entity.PriPrivilegeType; +import org.apache.iotdb.commons.auth.entity.IEntityAccessor; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.concurrent.HashLock; -import org.apache.iotdb.commons.path.PartialPath; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import org.apache.iotdb.commons.utils.AuthUtils; -import org.apache.iotdb.commons.utils.TestOnly; import org.apache.iotdb.rpc.TSStatusCode; import org.slf4j.Logger; @@ -35,7 +36,6 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Map.Entry; /** * This class reads roles from local files through LocalFileRoleAccessor and manages them in a hash @@ -43,189 +43,190 @@ * information from filesystem. Access filesystem only happens at starting、taking snapshot、 loading * snapshot. */ -public abstract class BasicRoleManager implements IRoleManager { +public abstract class BasicRoleManager implements IEntityManager, SnapshotProcessor { - protected Map roleMap; - protected IRoleAccessor accessor; - protected HashLock lock; - private boolean preVersion = false; + protected Map entityMap; + protected IEntityAccessor accessor; + protected HashLock lock; private static final Logger LOGGER = LoggerFactory.getLogger(BasicRoleManager.class); - BasicRoleManager(LocalFileRoleAccessor accessor) { - this.roleMap = new HashMap<>(); + protected TSStatusCode getEntityNotExistErrorCode() { + return TSStatusCode.ROLE_NOT_EXIST; + } + + protected String getNoSuchEntityError() { + return "No such role %s"; + } + + protected BasicRoleManager() { + this.entityMap = new HashMap<>(); + this.lock = new HashLock(); + } + + protected BasicRoleManager(IEntityAccessor accessor) { + this.entityMap = new HashMap<>(); this.accessor = accessor; this.lock = new HashLock(); this.accessor.reset(); } - @Override - public Role getRole(String rolename) { - lock.readLock(rolename); - Role role = roleMap.get(rolename); - lock.readUnlock(rolename); + public Role getEntity(String entityName) { + lock.readLock(entityName); + Role role = entityMap.get(entityName); + lock.readUnlock(entityName); return role; } - @Override - public boolean createRole(String rolename) throws AuthException { - - Role role = getRole(rolename); + public boolean createRole(String entityName) { + Role role = getEntity(entityName); if (role != null) { return false; } - lock.writeLock(rolename); - role = new Role(rolename); - roleMap.put(rolename, role); - lock.writeUnlock(rolename); + lock.writeLock(entityName); + role = new Role(entityName); + entityMap.put(entityName, role); + lock.writeUnlock(entityName); return true; } - @Override - public boolean deleteRole(String rolename) { - lock.writeLock(rolename); - try { - return roleMap.remove(rolename) != null; - } finally { - lock.writeUnlock(rolename); - } + public boolean deleteEntity(String entityName) { + lock.writeLock(entityName); + boolean result = entityMap.remove(entityName) != null; + lock.writeUnlock(entityName); + return result; } - @Override - public void grantPrivilegeToRole( - String rolename, PartialPath path, int privilegeId, boolean grantOpt) throws AuthException { - lock.writeLock(rolename); + public void grantPrivilegeToEntity(String entityName, PrivilegeUnion privilegeUnion) + throws AuthException { + lock.writeLock(entityName); try { - Role role = getRole(rolename); + Role role = getEntity(entityName); if (role == null) { throw new AuthException( - TSStatusCode.ROLE_NOT_EXIST, String.format("No such role %s", rolename)); + getEntityNotExistErrorCode(), String.format(getNoSuchEntityError(), entityName)); } - // Pre version's operation: - // all privileges are stored in path privileges. - // global privileges will come with root.** - // need to handle privileges ALL there. - if (preVersion) { - AuthUtils.validatePath(path); - if (privilegeId == PriPrivilegeType.ALL.ordinal()) { - for (PriPrivilegeType type : PriPrivilegeType.values()) { - role.addPathPrivilege(path, type.ordinal(), false); + switch (privilegeUnion.getModelType()) { + case TREE: + AuthUtils.validatePatternPath(privilegeUnion.getPath()); + role.grantPathPrivilege( + privilegeUnion.getPath(), + privilegeUnion.getPrivilegeType(), + privilegeUnion.isGrantOption()); + break; + case SYSTEM: + PrivilegeType type = privilegeUnion.getPrivilegeType(); + role.grantSysPrivilege(type, privilegeUnion.isGrantOption()); + break; + case RELATIONAL: + if (privilegeUnion.isForAny()) { + role.grantAnyScopePrivilege( + privilegeUnion.getPrivilegeType(), privilegeUnion.isGrantOption()); + break; } - } else { - role.addPathPrivilege(path, privilegeId, false); - } - // mark that the user has pre Version's privilege. - if (role.getServiceReady()) { - role.setServiceReady(false); - } - return; - } - - if (path != null) { - AuthUtils.validatePatternPath(path); - role.addPathPrivilege(path, privilegeId, grantOpt); - } else { - role.getSysPrivilege().add(privilegeId); - if (grantOpt) { - role.getSysPriGrantOpt().add(privilegeId); - } + if (privilegeUnion.getDBName() != null && privilegeUnion.getTbName() == null) { + role.grantDBPrivilege( + privilegeUnion.getDBName(), + privilegeUnion.getPrivilegeType(), + privilegeUnion.isGrantOption()); + } else if (privilegeUnion.getDBName() != null && privilegeUnion.getTbName() != null) { + role.grantTBPrivilege( + privilegeUnion.getDBName(), + privilegeUnion.getTbName(), + privilegeUnion.getPrivilegeType(), + privilegeUnion.isGrantOption()); + } + break; + default: + LOGGER.warn("Not support model type {}", privilegeUnion.getModelType()); } } finally { - lock.writeUnlock(rolename); + lock.writeUnlock(entityName); } } @Override - public boolean revokePrivilegeFromRole(String rolename, PartialPath path, int privilegeId) + public void revokePrivilegeFromEntity(String entityName, PrivilegeUnion privilegeUnion) throws AuthException { - lock.writeLock(rolename); + lock.writeLock(entityName); + PrivilegeType privilegeType = privilegeUnion.getPrivilegeType(); + boolean isGrantOption = privilegeUnion.isGrantOption(); + boolean isAnyScope = privilegeUnion.isForAny(); + String dbName = privilegeUnion.getDBName(); + String tbName = privilegeUnion.getTbName(); try { - Role role = getRole(rolename); + Role role = getEntity(entityName); if (role == null) { throw new AuthException( - TSStatusCode.ROLE_NOT_EXIST, String.format("No such role %s", rolename)); - } - if (preVersion) { - if (!AuthUtils.hasPrivilege(path, privilegeId, role.getPathPrivilegeList())) { - return false; - } - AuthUtils.removePrivilegePre(path, privilegeId, role.getPathPrivilegeList()); - return true; + getEntityNotExistErrorCode(), String.format(getNoSuchEntityError(), entityName)); } - - if (!role.hasPrivilegeToRevoke(path, privilegeId)) { - return false; - } - if (path != null) { - AuthUtils.validatePatternPath(path); - role.removePathPrivilege(path, privilegeId); - } else { - role.getSysPrivilege().remove(privilegeId); - role.getSysPriGrantOpt().remove(privilegeId); + switch (privilegeUnion.getModelType()) { + case TREE: + if (isGrantOption) { + role.revokePathPrivilegeGrantOption(privilegeUnion.getPath(), privilegeType); + return; + } + role.revokePathPrivilege(privilegeUnion.getPath(), privilegeType); + return; + case RELATIONAL: + if (isAnyScope) { + if (isGrantOption) { + role.revokeAnyScopePrivilegeGrantOption(privilegeType); + return; + } + role.revokeAnyScopePrivilege(privilegeType); + return; + } + // for tb + if (privilegeUnion.getTbName() != null) { + if (isGrantOption) { + role.revokeTBPrivilegeGrantOption(dbName, tbName, privilegeType); + return; + } + role.revokeTBPrivilege(dbName, tbName, privilegeType); + // for db + } else { + if (isGrantOption) { + role.revokeDBPrivilegeGrantOption(dbName, privilegeType); + return; + } + role.revokeDBPrivilege(dbName, privilegeType); + } + return; + case SYSTEM: + if (isGrantOption) { + role.revokeSysPrivilegeGrantOption(privilegeType); + return; + } + role.revokeSysPrivilege(privilegeType); + return; + default: + LOGGER.warn("Not support model type {}", privilegeUnion.getModelType()); } - return true; } finally { - lock.writeUnlock(rolename); + lock.writeUnlock(entityName); } } - @Override public void reset() throws AuthException { accessor.reset(); - roleMap.clear(); - for (String roleName : accessor.listAllRoles()) { + entityMap.clear(); + for (String entityName : accessor.listAllEntities()) { try { - roleMap.put(roleName, accessor.loadRole(roleName)); + entityMap.put(entityName, accessor.loadEntity(entityName)); } catch (IOException e) { - LOGGER.warn("Get exception when load role {}", roleName); + LOGGER.warn("Get exception when load role {}", entityName); throw new AuthException(TSStatusCode.AUTH_IO_EXCEPTION, e); } } } - @Override - public List listAllRoles() { + public List listAllEntities() { List rtlist = new ArrayList<>(); - roleMap.forEach((name, item) -> rtlist.add(name)); + entityMap.forEach((name, item) -> rtlist.add(name)); rtlist.sort(null); return rtlist; } - - @Override - public void replaceAllRoles(Map roles) throws AuthException { - synchronized (this) { - reset(); - roleMap = roles; - - for (Entry entry : roleMap.entrySet()) { - Role role = entry.getValue(); - try { - accessor.saveRole(role); - } catch (IOException e) { - throw new AuthException(TSStatusCode.AUTH_IO_EXCEPTION, e); - } - } - } - } - - @Override - public void setPreVersion(boolean preVersion) { - this.preVersion = preVersion; - } - - @Override - @TestOnly - public boolean preVersion() { - return this.preVersion; - } - - @Override - public void checkAndRefreshPathPri() { - roleMap.forEach( - (rolename, user) -> { - AuthUtils.checkAndRefreshPri(user); - }); - } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IEntityManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IEntityManager.java new file mode 100644 index 000000000000..d70c18273f01 --- /dev/null +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IEntityManager.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.apache.iotdb.commons.auth.role; + +import org.apache.iotdb.commons.auth.AuthException; +import org.apache.iotdb.commons.auth.entity.PrivilegeUnion; +import org.apache.iotdb.commons.auth.entity.Role; +import org.apache.iotdb.commons.snapshot.SnapshotProcessor; + +import java.util.List; + +/** We can call user or role as entity of access control, they all can obtain privileges */ +public interface IEntityManager extends SnapshotProcessor { + + /** + * Get an entity object. + * + * @param entityName The name of the entity. + * @return An entity object whose name is entityName or null if such entity does not exist. + * @throws AuthException if exception is raised while getting the entity. + */ + Role getEntity(String entityName) throws AuthException; + + /** + * Delete an entity. + * + * @param entityName the name of the entity. + * @return boolean, true means we have the entity in entityManager. + */ + boolean deleteEntity(String entityName); + + /** + * Grant a privilege to an entity. + * + * @param entityName The name of the entity to which the privilege should be added. + * @param privilegeUnion The privilege will be granted to entity. + * @throws AuthException If the entity does not exist or the privilege or the path is illegal. + */ + void grantPrivilegeToEntity(String entityName, PrivilegeUnion privilegeUnion) + throws AuthException; + + /** + * Revoke a privilege on path from an entity. + * + * @param entityName The name of the entity from which the privilege should be removed. + * @param privilegeUnion The privilege will be granted to entity. + * @throws AuthException If the entity does not exist or the privilege or the path is illegal. + */ + void revokePrivilegeFromEntity(String entityName, PrivilegeUnion privilegeUnion) + throws AuthException; + + /** Re-initialize this object. */ + void reset() throws AuthException; + + /** + * List all entities. + * + * @return A list that contains names of all entities. + */ + List listAllEntities(); +} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleAccessor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleAccessor.java deleted file mode 100644 index dd6a0b62384c..000000000000 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleAccessor.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.iotdb.commons.auth.role; - -import org.apache.iotdb.commons.auth.entity.Role; -import org.apache.iotdb.commons.snapshot.SnapshotProcessor; - -import java.io.IOException; -import java.util.List; - -/** This interface manages the serialization/deserialization of the role objects. */ -public interface IRoleAccessor extends SnapshotProcessor { - - /** - * Deserialize a role from lower storage. - * - * @throws IOException if IOException is raised when interacting with lower storage. - */ - Role loadRole(String rolename) throws IOException; - - /** - * Serialize the role object to lower storage. - * - * @param role The role object that is to be saved. - * @throws IOException if IOException is raised when interacting with lower storage. - */ - void saveRole(Role role) throws IOException; - - /** - * Delete a role's in lower storage. - * - * @param rolename The name of the role to be deleted. - * @return True if the role is successfully deleted, false if the role does not exists. - * @throws IOException if IOException is raised when interacting with lower storage. - */ - boolean deleteRole(String rolename) throws IOException; - - /** - * Delete role's folders. - * - * @throws IOException - */ - void cleanRoleFolder(); - - /** - * List all roles in this database. - * - * @return A list contains all names of the roles. - */ - List listAllRoles(); - - /** Re-initialize this object. */ - void reset(); -} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleManager.java deleted file mode 100644 index e58f0b5841eb..000000000000 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/IRoleManager.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.iotdb.commons.auth.role; - -import org.apache.iotdb.commons.auth.AuthException; -import org.apache.iotdb.commons.auth.entity.Role; -import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.commons.snapshot.SnapshotProcessor; - -import java.util.List; -import java.util.Map; - -/** This interface maintains roles in memory and is responsible for their modifications. */ -public interface IRoleManager extends SnapshotProcessor { - - /** - * Get a role object. - * - * @param rolename The name of the role. - * @return A role object whose name is rolename or null if such role does not exist. - * @throws AuthException if exception is raised while getting the role. - */ - Role getRole(String rolename) throws AuthException; - - /** - * Create a role with given rolename. New roles will only be granted no privileges. - * - * @param rolename is not null or empty - * @return True if the role is successfully created, false when the role already exists. - * @throws AuthException f the given rolename is iIllegal. - */ - boolean createRole(String rolename) throws AuthException; - - /** - * Delete a role. - * - * @param rolename the rolename of the role. - * @return boolean, true means we have the role in roleManager. - */ - boolean deleteRole(String rolename); - - /** - * Grant a privilege on a seriesPath to a role. - * - * @param rolename The rolename of the role to which the privilege should be added. - * @param path The seriesPath on which the privilege takes effect. If the privilege is a - * seriesPath-free privilege, this should be "root". - * @param privilegeId An integer that represents a privilege. - * @param grantOpt Whether the privilege can be granted to other role/user. - * @throws AuthException If the role does not exist or the privilege or the seriesPath is illegal. - */ - void grantPrivilegeToRole(String rolename, PartialPath path, int privilegeId, boolean grantOpt) - throws AuthException; - - /** - * Revoke a privilege on seriesPath from a role. - * - * @param rolename The rolename of the role from which the privilege should be removed. - * @param path The seriesPath on which the privilege takes effect. If the privilege is a - * seriesPath-free privilege like 'CREATE_USER', this should be "null". - * @param privilegeId An integer that represents a privilege. - * @return True if the permission is successfully revoked, false if the permission does not - * exists. - * @throws AuthException If the role does not exist or the privilege or the seriesPath is illegal. - */ - boolean revokePrivilegeFromRole(String rolename, PartialPath path, int privilegeId) - throws AuthException; - - /** Re-initialize this object. */ - void reset() throws AuthException; - - /** - * List all roles in the database. - * - * @return A list that contains names of all roles. - */ - List listAllRoles(); - - /** - * clear all old roles info, replace the old roles with the new one. The caller should guarantee - * that no other methods of this interface are invoked concurrently when this method is called. - * - * @param roles new roles info - * @throws AuthException - */ - void replaceAllRoles(Map roles) throws AuthException; - - void setPreVersion(boolean preVersion); - - boolean preVersion(); - - void checkAndRefreshPathPri(); -} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java index 3b77888604c2..809d2397f3be 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleAccessor.java @@ -18,17 +18,19 @@ */ package org.apache.iotdb.commons.auth.role; +import org.apache.iotdb.commons.auth.entity.DatabasePrivilege; +import org.apache.iotdb.commons.auth.entity.IEntityAccessor; import org.apache.iotdb.commons.auth.entity.PathPrivilege; import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.conf.IoTDBConstant; +import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.file.SystemFileFactory; import org.apache.iotdb.commons.utils.FileUtils; import org.apache.iotdb.commons.utils.IOUtils; -import org.apache.iotdb.commons.utils.TestOnly; import org.apache.thrift.TException; -import org.apache.tsfile.utils.Pair; +import org.apache.tsfile.utils.ReadWriteIOUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -40,28 +42,29 @@ import java.io.FileOutputStream; import java.io.IOException; import java.nio.ByteBuffer; -import java.nio.file.Files; import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Set; import java.util.UUID; /** * This class store role info in a separate sequential file and every role will have one role file. - * Role file schema: rolename-length int32| rolename-bytes-utf8| system-perivileges int32| + * Role file schema: roleName-length int32| roleName-bytes-utf8| system-privileges int32| * path[1]-length int32| path[1]-bytes-utf8 | privilege-int32| path[2]-length int32| * path[2]-bytes-utf8 | privilege-int32| path[3]-length int32| path[3]-bytes-utf8 | privilege-int32| * ..... * *

system-privileges is an int32 mask code. Low 16 bits for privileges and high 16 bits mark that - * whether the corresponding position 's privilege has grant option.So we can use a int32 to mark 16 - * kinds of privileges. Here are the permissions corresponding to the bit location identifier: + * whether the corresponding position 's privilege has grant option.So we can use an int32 to mark + * 16 kinds of privileges. Here are the permissions corresponding to the bit location identifier: * Manage_database : 0 MANAGE_USER : 1 MANAGE_ROLE : 2 USE_TRIGGER : 3 USE_UDF : 4 USE_CQ : 5 - * USE_PIPE : 6 EXTEDN_TEMPLATE : 7 AUDIT : 8 MAINTAIN : 9 |0000-0000-0000-0001|0000-0000-0000-0001| + * USE_PIPE : 6 EXTEND_TEMPLATE : 7 AUDIT : 8 MAINTAIN : 9 |0000-0000-0000-0001|0000-0000-0000-0001| * means this role has Manage_database's privilege and be able to grant it to others. * - *

path-privileges is a pair contains paths and privileges mask. Path privilege is a int32 mask + *

path-privileges is a pair contains paths and privileges mask. Path privilege is an int32 mask * code which has the same structure as system-privileges. For path privilege, the low 16 bits stand * for four privileges, they are: READ_DATA : 0 WRITE_DATA : 1 READ_SCHEMA : 2 WRITE_SCHEMA : 3 And * the high 16 bits stand for grant option. @@ -73,67 +76,146 @@ * All user/role file store in config node's user/role folder. when our system start, we load all * user/role from folder. If we did some alter query, we just store raft log. */ -public class LocalFileRoleAccessor implements IRoleAccessor { +public class LocalFileRoleAccessor implements IEntityAccessor { private static final Logger LOGGER = LoggerFactory.getLogger(LocalFileRoleAccessor.class); - private static final String TEMP_SUFFIX = ".temp"; - private static final String STRING_ENCODING = "utf-8"; - private static final String ROLE_SNAPSHOT_FILE_NAME = "system" + File.separator + "roles"; + protected static final String TEMP_SUFFIX = ".temp"; + protected static final String STRING_ENCODING = "utf-8"; + protected final String entityDirPath; - private final String roleDirPath; + // It might be a good idea to use a Version number to control upgrade compatibility. + // Now it's version 1 + protected static final int VERSION = 1; /** * Reused buffer for primitive types encoding/decoding, which aim to reduce memory fragments. Use * ThreadLocal for thread safety. */ - private final ThreadLocal encodingBufferLocal = new ThreadLocal<>(); + protected final ThreadLocal encodingBufferLocal = new ThreadLocal<>(); - private final ThreadLocal strBufferLocal = new ThreadLocal<>(); + protected final ThreadLocal strBufferLocal = new ThreadLocal<>(); public LocalFileRoleAccessor(String roleDirPath) { - this.roleDirPath = roleDirPath; + this.entityDirPath = roleDirPath; } - /** - * @return role struct - * @throws IOException - */ - @Override - public Role loadRole(String rolename) throws IOException { - File roleProfile = + protected String getEntitySnapshotFileName() { + return "system" + File.separator + "roles"; + } + + protected void saveEntityVersion(BufferedOutputStream outputStream) throws IOException { + IOUtils.writeInt(outputStream, VERSION, encodingBufferLocal); + } + + protected void saveEntityName(BufferedOutputStream outputStream, Role role) throws IOException { + IOUtils.writeString(outputStream, role.getName(), STRING_ENCODING, encodingBufferLocal); + } + + protected void savePrivileges(BufferedOutputStream outputStream, Role role) throws IOException { + IOUtils.writeInt(outputStream, role.getAllSysPrivileges(), encodingBufferLocal); + int privilegeNum = role.getPathPrivilegeList().size(); + IOUtils.writeInt(outputStream, privilegeNum, encodingBufferLocal); + for (int i = 0; i < privilegeNum; i++) { + PathPrivilege pathPrivilege = role.getPathPrivilegeList().get(i); + IOUtils.writePathPrivilege(outputStream, pathPrivilege, STRING_ENCODING, encodingBufferLocal); + } + IOUtils.writeInt(outputStream, role.getAnyScopePrivileges(), encodingBufferLocal); + privilegeNum = role.getDBScopePrivilegeMap().size(); + IOUtils.writeInt(outputStream, privilegeNum, encodingBufferLocal); + for (Map.Entry objectPrivilegeMap : + role.getDBScopePrivilegeMap().entrySet()) { + IOUtils.writeObjectPrivilege( + outputStream, objectPrivilegeMap.getValue(), STRING_ENCODING, encodingBufferLocal); + } + } + + protected void loadPrivileges(DataInputStream dataInputStream, Role role) + throws IOException, IllegalPathException { + role.setSysPrivilegesWithMask(dataInputStream.readInt()); + int num = ReadWriteIOUtils.readInt(dataInputStream); + List pathPrivilegeList = new ArrayList<>(); + for (int i = 0; i < num; i++) { + pathPrivilegeList.add( + IOUtils.readPathPrivilege(dataInputStream, STRING_ENCODING, strBufferLocal)); + } + role.setPrivilegeList(pathPrivilegeList); + role.setAnyScopePrivilegeSetWithMask(ReadWriteIOUtils.readInt(dataInputStream)); + Map objectPrivilegeMap = new HashMap<>(); + num = ReadWriteIOUtils.readInt(dataInputStream); + for (int i = 0; i < num; i++) { + DatabasePrivilege databasePrivilege = + IOUtils.readRelationalPrivilege(dataInputStream, STRING_ENCODING, strBufferLocal); + objectPrivilegeMap.put(databasePrivilege.getDatabaseName(), databasePrivilege); + } + role.setObjectPrivilegeMap(objectPrivilegeMap); + } + + protected void saveRoles(Role role) throws IOException { + // Just used in LocalFileUserAccessor.java. + // Do nothing. + } + + protected File checkFileAvailable(String entityName, String suffix) { + File userProfile = SystemFileFactory.INSTANCE.getFile( - roleDirPath + File.separator + rolename + IoTDBConstant.PROFILE_SUFFIX); - if (!roleProfile.exists() || !roleProfile.isFile()) { - // System may crush before a newer file is written, so search for back-up file. - File backProfile = + entityDirPath + File.separator + entityName + suffix + IoTDBConstant.PROFILE_SUFFIX); + if (!userProfile.exists() || !userProfile.isFile()) { + // System may crush before a newer file is renamed. + File newProfile = SystemFileFactory.INSTANCE.getFile( - roleDirPath + File.separator + rolename + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX); - if (backProfile.exists() && backProfile.isFile()) { - roleProfile = backProfile; + entityDirPath + + File.separator + + entityName + + suffix + + IoTDBConstant.PROFILE_SUFFIX + + TEMP_SUFFIX); + if (newProfile.exists() && newProfile.isFile()) { + if (!newProfile.renameTo(userProfile)) { + LOGGER.error("New profile renaming not succeed."); + } + userProfile = newProfile; } else { return null; } } - FileInputStream inputStream = new FileInputStream(roleProfile); + return userProfile; + } + + @Override + public Role loadEntity(String entityName) throws IOException { + File entityFile = checkFileAvailable(entityName, ""); + if (entityFile == null) { + return null; + } + + FileInputStream inputStream = new FileInputStream(entityFile); try (DataInputStream dataInputStream = new DataInputStream(new BufferedInputStream(inputStream))) { - Role role = new Role(); - Pair result = - IOUtils.readAuthString(dataInputStream, STRING_ENCODING, strBufferLocal); - role.setName(result.getLeft()); - boolean oldVersion = result.getRight(); - if (oldVersion) { - IOUtils.loadRolePrivilege(role, dataInputStream, STRING_ENCODING, strBufferLocal); - return role; - } else { - role.setSysPrivilegeSet(dataInputStream.readInt()); + boolean fromOldVersion = false; + int tag = dataInputStream.readInt(); + if (tag < 0) { + fromOldVersion = true; + } + + if (fromOldVersion) { + String name = + IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal, -1 * tag); + Role role = new Role(name); + role.setSysPrivilegesWithMask(dataInputStream.readInt()); List pathPrivilegeList = new ArrayList<>(); for (int i = 0; dataInputStream.available() != 0; i++) { pathPrivilegeList.add( - IOUtils.readPathPrivilege(dataInputStream, STRING_ENCODING, strBufferLocal, false)); + IOUtils.readPathPrivilege(dataInputStream, STRING_ENCODING, strBufferLocal)); } role.setPrivilegeList(pathPrivilegeList); return role; + } else { + assert tag == VERSION; + entityName = IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal); + Role role = new Role(entityName); + loadPrivileges(dataInputStream, role); + return role; } + } catch (Exception e) { throw new IOException(e); } finally { @@ -142,36 +224,28 @@ public Role loadRole(String rolename) throws IOException { } @Override - public void saveRole(Role role) throws IOException { + public void saveEntity(Role entity) throws IOException { File roleProfile = SystemFileFactory.INSTANCE.getFile( - roleDirPath + entityDirPath + File.separator - + role.getName() + + entity.getName() + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX); - File roleDir = new File(roleDirPath); + File roleDir = new File(entityDirPath); if (!roleDir.exists() && !roleDir.mkdirs()) { - LOGGER.error("Failed to create role dir {}", roleDirPath); + LOGGER.error("Failed to create role dir {}", entityDirPath); } try (FileOutputStream fileOutputStream = new FileOutputStream(roleProfile); BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream)) { - byte[] strBuffer = role.getName().getBytes(STRING_ENCODING); - IOUtils.writeInt(outputStream, -1 * strBuffer.length, encodingBufferLocal); - outputStream.write(strBuffer); - IOUtils.writeInt(outputStream, role.getAllSysPrivileges(), encodingBufferLocal); - int privilegeNum = role.getPathPrivilegeList().size(); - for (int i = 0; i < privilegeNum; i++) { - PathPrivilege pathPrivilege = role.getPathPrivilegeList().get(i); - IOUtils.writePathPrivilege( - outputStream, pathPrivilege, STRING_ENCODING, encodingBufferLocal); - } - // handle outputstream's exception in saveRole locally. + saveEntityVersion(outputStream); + saveEntityName(outputStream, entity); + savePrivileges(outputStream, entity); outputStream.flush(); fileOutputStream.getFD().sync(); } catch (Exception e) { - LOGGER.warn("meet error when save role: {}", role.getName()); + LOGGER.warn("meet error when save role: {}", entity.getName()); throw new IOException(e); } finally { encodingBufferLocal.remove(); @@ -179,35 +253,44 @@ public void saveRole(Role role) throws IOException { File oldFile = SystemFileFactory.INSTANCE.getFile( - roleDirPath + File.separator + role.getName() + IoTDBConstant.PROFILE_SUFFIX); + entityDirPath + File.separator + entity.getName() + IoTDBConstant.PROFILE_SUFFIX); IOUtils.replaceFile(roleProfile, oldFile); + saveRoles(entity); } @Override - public boolean deleteRole(String rolename) throws IOException { + public boolean deleteEntity(String entityName) throws IOException { File roleProfile = SystemFileFactory.INSTANCE.getFile( - roleDirPath + File.separator + rolename + IoTDBConstant.PROFILE_SUFFIX); + entityDirPath + File.separator + entityName + IoTDBConstant.PROFILE_SUFFIX); File backFile = SystemFileFactory.INSTANCE.getFile( - roleDirPath + File.separator + rolename + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX); + entityDirPath + + File.separator + + entityName + + IoTDBConstant.PROFILE_SUFFIX + + TEMP_SUFFIX); if (!roleProfile.exists() && !backFile.exists()) { return false; } if ((roleProfile.exists() && !roleProfile.delete()) || (backFile.exists() && !backFile.delete())) { - throw new IOException(String.format("Cannot delete role file of %s", rolename)); + throw new IOException(String.format("Cannot delete role file of %s", entityName)); } return true; } @Override - public List listAllRoles() { - File roleDir = SystemFileFactory.INSTANCE.getFile(roleDirPath); + public List listAllEntities() { + File roleDir = SystemFileFactory.INSTANCE.getFile(entityDirPath); String[] names = roleDir.list( (dir, name) -> name.endsWith(IoTDBConstant.PROFILE_SUFFIX) || name.endsWith(TEMP_SUFFIX)); + return getEntityStrings(names); + } + + public static List getEntityStrings(String[] names) { List retList = new ArrayList<>(); if (names != null) { // in very rare situations, normal file and backup file may exist at the same time @@ -224,12 +307,12 @@ public List listAllRoles() { @Override public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE; - File roleFolder = systemFileFactory.getFile(roleDirPath); - File roleSnapshotDir = systemFileFactory.getFile(snapshotDir, ROLE_SNAPSHOT_FILE_NAME); + File roleFolder = systemFileFactory.getFile(entityDirPath); + File roleSnapshotDir = systemFileFactory.getFile(snapshotDir, getEntitySnapshotFileName()); File roleTmpSnapshotDir = systemFileFactory.getFile(roleSnapshotDir.getAbsolutePath() + "-" + UUID.randomUUID()); - boolean result = true; + boolean result; try { result = FileUtils.copyDir(roleFolder, roleTmpSnapshotDir); result &= roleTmpSnapshotDir.renameTo(roleSnapshotDir); @@ -244,10 +327,10 @@ public boolean processTakeSnapshot(File snapshotDir) throws TException, IOExcept @Override public void processLoadSnapshot(File snapshotDir) throws TException, IOException { SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE; - File roleFolder = systemFileFactory.getFile(roleDirPath); + File roleFolder = systemFileFactory.getFile(entityDirPath); File roleTmpFolder = systemFileFactory.getFile(roleFolder.getAbsolutePath() + "-" + UUID.randomUUID()); - File roleSnapshotDir = systemFileFactory.getFile(snapshotDir, ROLE_SNAPSHOT_FILE_NAME); + File roleSnapshotDir = systemFileFactory.getFile(snapshotDir, getEntitySnapshotFileName()); if (roleSnapshotDir.exists()) { try { org.apache.commons.io.FileUtils.moveDirectory(roleFolder, roleTmpFolder); @@ -267,15 +350,15 @@ public void processLoadSnapshot(File snapshotDir) throws TException, IOException @Override public void reset() { - checkOldRoleDir(SystemFileFactory.INSTANCE.getFile(roleDirPath)); - if (SystemFileFactory.INSTANCE.getFile(roleDirPath).mkdirs()) { - LOGGER.info("role info dir {} is created", roleDirPath); - } else if (!SystemFileFactory.INSTANCE.getFile(roleDirPath).exists()) { - LOGGER.error("role info dir {} can not be created", roleDirPath); + checkOldEntityDir(SystemFileFactory.INSTANCE.getFile(entityDirPath)); + if (SystemFileFactory.INSTANCE.getFile(entityDirPath).mkdirs()) { + LOGGER.info("role info dir {} is created", entityDirPath); + } else if (!SystemFileFactory.INSTANCE.getFile(entityDirPath).exists()) { + LOGGER.error("role info dir {} can not be created", entityDirPath); } } - private void checkOldRoleDir(File newDir) { + private void checkOldEntityDir(File newDir) { File oldDir = new File(CommonDescriptor.getInstance().getConfig().getOldRoleFolder()); if (oldDir.exists()) { if (!FileUtils.moveFileSafe(oldDir, newDir)) { @@ -285,8 +368,8 @@ private void checkOldRoleDir(File newDir) { } @Override - public void cleanRoleFolder() { - File[] files = SystemFileFactory.INSTANCE.getFile(roleDirPath).listFiles(); + public void cleanEntityFolder() { + File[] files = SystemFileFactory.INSTANCE.getFile(entityDirPath).listFiles(); if (files != null) { for (File file : files) { FileUtils.deleteFileIfExist(file); @@ -295,51 +378,4 @@ public void cleanRoleFolder() { LOGGER.warn("Role folder not exists"); } } - - @TestOnly - public void saveRoleOldVer(Role role) throws IOException { - File roleProfile = - SystemFileFactory.INSTANCE.getFile( - roleDirPath - + File.separator - + role.getName() - + IoTDBConstant.PROFILE_SUFFIX - + TEMP_SUFFIX); - File roleDir = new File(roleDirPath); - if (!roleDir.exists()) { - if (!roleDir.mkdirs()) { - LOGGER.error("Failed to create role dir {}", roleDirPath); - } - } - try (BufferedOutputStream outputStream = - new BufferedOutputStream(Files.newOutputStream(roleProfile.toPath()))) { - try { - IOUtils.writeString(outputStream, role.getName(), STRING_ENCODING, encodingBufferLocal); - int privilegeNum = role.getPathPrivilegeList().size(); - IOUtils.writeInt(outputStream, privilegeNum, encodingBufferLocal); - for (int i = 0; i < privilegeNum; i++) { - PathPrivilege pathPrivilege = role.getPathPrivilegeList().get(i); - IOUtils.writeString( - outputStream, - pathPrivilege.getPath().getFullPath(), - STRING_ENCODING, - encodingBufferLocal); - IOUtils.writeInt(outputStream, pathPrivilege.getPrivileges().size(), encodingBufferLocal); - for (Integer item : pathPrivilege.getPrivileges()) { - IOUtils.writeInt(outputStream, item, encodingBufferLocal); - } - } - outputStream.flush(); - } catch (Exception e) { - throw new IOException(e); - } - } finally { - encodingBufferLocal.remove(); - } - - File oldFile = - SystemFileFactory.INSTANCE.getFile( - roleDirPath + File.separator + role.getName() + IoTDBConstant.PROFILE_SUFFIX); - IOUtils.replaceFile(roleProfile, oldFile); - } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleManager.java index a466f9029899..8499f064207c 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleManager.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/role/LocalFileRoleManager.java @@ -36,9 +36,9 @@ public LocalFileRoleManager(String roleDirPath) { @Override public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { - accessor.cleanRoleFolder(); - for (Map.Entry entry : roleMap.entrySet()) { - accessor.saveRole(entry.getValue()); + accessor.cleanEntityFolder(); + for (Map.Entry entry : entityMap.entrySet()) { + accessor.saveEntity(entry.getValue()); } return accessor.processTakeSnapshot(snapshotDir); } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java index bf7d3a642b45..f47e0c453a46 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/BasicUserManager.java @@ -19,11 +19,11 @@ package org.apache.iotdb.commons.auth.user; import org.apache.iotdb.commons.auth.AuthException; +import org.apache.iotdb.commons.auth.entity.IEntityAccessor; import org.apache.iotdb.commons.auth.entity.PathPrivilege; -import org.apache.iotdb.commons.auth.entity.PriPrivilegeType; import org.apache.iotdb.commons.auth.entity.PrivilegeType; import org.apache.iotdb.commons.auth.entity.User; -import org.apache.iotdb.commons.concurrent.HashLock; +import org.apache.iotdb.commons.auth.role.BasicRoleManager; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.IllegalPathException; @@ -35,54 +35,30 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; - /** This class stores information of each user. */ -public abstract class BasicUserManager implements IUserManager { +public abstract class BasicUserManager extends BasicRoleManager { private static final Logger LOGGER = LoggerFactory.getLogger(BasicUserManager.class); - private static final String NO_SUCH_USER_ERROR = "No such user %s"; - - protected Map userMap; - protected IUserAccessor accessor; - protected HashLock lock; - - /** - * This filed only for pre version. When we do a major version upgrade, it can be removed - * directly. - */ - // FOR PRE VERSION BEGIN ----- - private boolean preVersion = false; @Override - public void setPreVersion(boolean preVersion) { - this.preVersion = preVersion; + protected TSStatusCode getEntityNotExistErrorCode() { + return TSStatusCode.USER_NOT_EXIST; } @Override - @TestOnly - public boolean preVersion() { - return this.preVersion; + protected String getNoSuchEntityError() { + return "No such user %s"; } - // FOR PRE VERSION DONE ------ - /** * BasicUserManager Constructor. * * @param accessor user accessor * @throws AuthException Authentication Exception */ - protected BasicUserManager(IUserAccessor accessor) throws AuthException { - this.userMap = new HashMap<>(); + protected BasicUserManager(IEntityAccessor accessor) throws AuthException { + super(accessor); this.accessor = accessor; - this.lock = new HashLock(); - init(); } @@ -92,13 +68,7 @@ protected BasicUserManager(IUserAccessor accessor) throws AuthException { * @throws AuthException if an exception is raised when interacting with the lower storage. */ private void initAdmin() throws AuthException { - User admin; - try { - admin = getUser(CommonDescriptor.getInstance().getConfig().getAdminName()); - } catch (AuthException e) { - LOGGER.warn("Cannot load admin, Creating a new one", e); - admin = null; - } + User admin = this.getEntity(CommonDescriptor.getInstance().getConfig().getAdminName()); if (admin == null) { createUser( @@ -106,24 +76,22 @@ private void initAdmin() throws AuthException { CommonDescriptor.getInstance().getConfig().getAdminPassword(), true, true); - setUserUseWaterMark(CommonDescriptor.getInstance().getConfig().getAdminName(), false); } - // admin has all privileges. - admin = getUser(CommonDescriptor.getInstance().getConfig().getAdminName()); + admin = this.getEntity(CommonDescriptor.getInstance().getConfig().getAdminName()); try { PartialPath rootPath = new PartialPath(IoTDBConstant.PATH_ROOT + ".**"); PathPrivilege pathPri = new PathPrivilege(rootPath); for (PrivilegeType item : PrivilegeType.values()) { - if (!item.isPathRelevant()) { - admin.getSysPrivilege().add(item.ordinal()); - admin.getSysPriGrantOpt().add(item.ordinal()); - } else { - pathPri.grantPrivilege(item.ordinal(), true); + if (item.isSystemPrivilege()) { + admin.grantSysPrivilege(item, true); + } else if (item.isRelationalPrivilege()) { + admin.grantAnyScopePrivilege(item, true); + } else if (item.isPathPrivilege()) { + pathPri.grantPrivilege(item, true); } } admin.getPathPrivilegeList().clear(); admin.getPathPrivilegeList().add(pathPri); - admin.setServiceReady(true); } catch (IllegalPathException e) { // This error only leads to a lack of permissions for list. LOGGER.warn("Got a wrong path for root to init"); @@ -132,14 +100,10 @@ private void initAdmin() throws AuthException { } @Override - public User getUser(String username) throws AuthException { - lock.readLock(username); - User user = userMap.get(username); - lock.readUnlock(username); - return user; + public User getEntity(String entityName) { + return (User) super.getEntity(entityName); } - @Override public boolean createUser( String username, String password, boolean validCheck, boolean enableEncrypt) throws AuthException { @@ -150,117 +114,23 @@ public boolean createUser( } } - User user = getUser(username); + User user = this.getEntity(username); if (user != null) { return false; } lock.writeLock(username); try { user = new User(username, enableEncrypt ? AuthUtils.encryptPassword(password) : password); - userMap.put(username, user); + entityMap.put(username, user); return true; } finally { lock.writeUnlock(username); } } - @Override - public boolean deleteUser(String username) { - lock.writeLock(username); - try { - return userMap.remove(username) != null; - } finally { - lock.writeUnlock(username); - } - } - - @Override - public boolean grantPrivilegeToUser( - String username, PartialPath path, int privilegeId, boolean grantOpt) throws AuthException { - lock.writeLock(username); - try { - User user = getUser(username); - if (user == null) { - throw new AuthException( - TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_ERROR, username)); - } - // Pre version's operation: - // all privileges are stored in path privileges. - // global privileges will come with root.** - // need to handle privileges ALL there. - if (preVersion) { - AuthUtils.validatePath(path); - if (privilegeId == PriPrivilegeType.ALL.ordinal()) { - for (PriPrivilegeType type : PriPrivilegeType.values()) { - user.addPathPrivilege(path, type.ordinal(), false); - } - } else { - user.addPathPrivilege(path, privilegeId, false); - } - // mark that the user has pre Version's privilege. - if (user.getServiceReady()) { - user.setServiceReady(false); - } - return true; - } - if (path != null) { - AuthUtils.validatePatternPath(path); - user.addPathPrivilege(path, privilegeId, grantOpt); - } else { - user.addSysPrivilege(privilegeId); - if (grantOpt) { - user.getSysPriGrantOpt().add(privilegeId); - } - } - return true; - } finally { - lock.writeUnlock(username); - } - } - - @Override - public boolean revokePrivilegeFromUser(String username, PartialPath path, int privilegeId) - throws AuthException { - lock.writeLock(username); - try { - User user = getUser(username); - if (user == null) { - throw new AuthException( - TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_ERROR, username)); - } - if (preVersion) { - if (!AuthUtils.hasPrivilege(path, privilegeId, user.getPathPrivilegeList())) { - return false; - } - AuthUtils.removePrivilegePre(path, privilegeId, user.getPathPrivilegeList()); - return true; - } - - if (!user.hasPrivilegeToRevoke(path, privilegeId)) { - return false; - } - if (path != null) { - AuthUtils.validatePatternPath(path); - user.removePathPrivilege(path, privilegeId); - } else { - user.getSysPrivilege().remove(privilegeId); - user.getSysPriGrantOpt().remove(privilegeId); - } - - return true; - } finally { - lock.writeUnlock(username); - } - } - - @Override public boolean updateUserPassword(String username, String newPassword) throws AuthException { try { - if (preVersion) { - AuthUtils.validatePasswordPre(newPassword); - } else { - AuthUtils.validatePassword(newPassword); - } + AuthUtils.validatePassword(newPassword); } catch (AuthException e) { LOGGER.debug("An illegal password detected ", e); return false; @@ -268,10 +138,10 @@ public boolean updateUserPassword(String username, String newPassword) throws Au lock.writeLock(username); try { - User user = getUser(username); + User user = this.getEntity(username); if (user == null) { throw new AuthException( - TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_ERROR, username)); + getEntityNotExistErrorCode(), String.format(getNoSuchEntityError(), username)); } user.setPassword(AuthUtils.encryptPassword(newPassword)); return true; @@ -280,38 +150,36 @@ public boolean updateUserPassword(String username, String newPassword) throws Au } } - @Override public boolean grantRoleToUser(String roleName, String username) throws AuthException { lock.writeLock(username); try { - User user = getUser(username); + User user = this.getEntity(username); if (user == null) { throw new AuthException( - TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_ERROR, username)); + getEntityNotExistErrorCode(), String.format(getNoSuchEntityError(), username)); } if (user.hasRole(roleName)) { return false; } - user.getRoleList().add(roleName); + user.getRoleSet().add(roleName); return true; } finally { lock.writeUnlock(username); } } - @Override public boolean revokeRoleFromUser(String roleName, String username) throws AuthException { lock.writeLock(username); try { - User user = getUser(username); + User user = this.getEntity(username); if (user == null) { throw new AuthException( - TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_ERROR, username)); + getEntityNotExistErrorCode(), String.format(getNoSuchEntityError(), username)); } if (!user.hasRole(roleName)) { return false; } - user.getRoleList().remove(roleName); + user.getRoleSet().remove(roleName); return true; } finally { lock.writeUnlock(username); @@ -325,76 +193,10 @@ private void init() throws AuthException { @Override public void reset() throws AuthException { - accessor.reset(); - userMap.clear(); - for (String name : accessor.listAllUsers()) { - try { - userMap.put(name, accessor.loadUser(name)); - } catch (IOException e) { - LOGGER.warn("Get exception when load user {}", name); - throw new AuthException(TSStatusCode.AUTH_IO_EXCEPTION, e); - } - } + super.reset(); initAdmin(); } - @Override - public List listAllUsers() { - List rtlist = new ArrayList<>(); - userMap.forEach((name, item) -> rtlist.add(name)); - rtlist.sort(null); - return rtlist; - } - - @Override - public boolean isUserUseWaterMark(String username) throws AuthException { - User user = getUser(username); - if (user == null) { - throw new AuthException( - TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_ERROR, username)); - } - return user.isUseWaterMark(); - } - - @Override - public void setUserUseWaterMark(String username, boolean useWaterMark) throws AuthException { - User user = getUser(username); - if (user == null) { - throw new AuthException( - TSStatusCode.USER_NOT_EXIST, String.format(NO_SUCH_USER_ERROR, username)); - } - boolean oldFlag = user.isUseWaterMark(); - if (oldFlag == useWaterMark) { - return; - } - user.setUseWaterMark(useWaterMark); - } - - @Override - public void replaceAllUsers(Map users) throws AuthException { - synchronized (this) { - reset(); - userMap = users; - - for (Entry entry : userMap.entrySet()) { - User user = entry.getValue(); - try { - accessor.saveUser(user); - } catch (IOException e) { - throw new AuthException(TSStatusCode.AUTH_IO_EXCEPTION, e); - } - } - } - } - - @Override - public void checkAndRefreshPathPri() { - userMap.forEach( - (rolename, user) -> { - AuthUtils.checkAndRefreshPri(user); - }); - } - @TestOnly public boolean createUser(String username, String password, boolean validCheck) throws AuthException { diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserManager.java deleted file mode 100644 index d33f320a4064..000000000000 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/IUserManager.java +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ -package org.apache.iotdb.commons.auth.user; - -import org.apache.iotdb.commons.auth.AuthException; -import org.apache.iotdb.commons.auth.entity.User; -import org.apache.iotdb.commons.path.PartialPath; -import org.apache.iotdb.commons.snapshot.SnapshotProcessor; - -import java.util.List; -import java.util.Map; - -/** This interface provides accesses to users. */ -public interface IUserManager extends SnapshotProcessor { - - /** - * Get a user object. - * - * @param username The name of the user. - * @return A user object whose name is username or null if such user does not exist. - * @throws AuthException if an exception is raised when interacting with the lower storage. - */ - User getUser(String username) throws AuthException; - - /** - * Create a user with given username and password. New users will only be granted no privileges. - * - * @param username is not null or empty - * @param password is not null or empty - * @param validCheck is whether check user's name and password - * @return True if the user is successfully created, false when the user already exists. - * @throws AuthException if the given username or password is illegal. - */ - boolean createUser(String username, String password, boolean validCheck, boolean enableEncrypt) - throws AuthException; - - /** - * Delete a user. - * - * @param username the username of the user. - * @return True if the user is successfully deleted, false if the user does not exists. - * @throws AuthException . - */ - boolean deleteUser(String username) throws AuthException; - - /** - * Grant a privilege on a seriesPath to a user. - * - * @param username The username of the user to which the privilege should be added. - * @param path The seriesPath on which the privilege takes effect. If the privilege is a - * seriesPath-free privilege, this should be "root". - * @param privilegeId An integer that represents a privilege. - * @param grantOpt Whether the privilege can be granted to other role/user. - * @return True if the permission is successfully added, false if the permission already exists. - * @throws AuthException If the user does not exist or the privilege or the seriesPath is illegal. - */ - boolean grantPrivilegeToUser(String username, PartialPath path, int privilegeId, boolean grantOpt) - throws AuthException; - - /** - * Revoke a privilege on seriesPath from a user. - * - * @param username The username of the user from which the privilege should be removed. - * @param path The seriesPath on which the privilege takes effect. If the privilege is a - * seriesPath-free privilege, this should be "root". - * @param privilegeId An integer that represents a privilege. - * @return True if the permission is successfully revoked, false if the permission does not - * exists. - * @throws AuthException If the user does not exist or the privilege or the seriesPath is illegal. - */ - boolean revokePrivilegeFromUser(String username, PartialPath path, int privilegeId) - throws AuthException; - - /** - * Modify the password of a user. - * - * @param username The user whose password is to be modified. - * @param newPassword The new password. - * @return True if the password is successfully modified, false if the new password is illegal. - * @throws AuthException If the user does not exists. - */ - boolean updateUserPassword(String username, String newPassword) throws AuthException; - - /** - * Add a role to a user. - * - * @param roleName The name of the role to be added. - * @param username The name of the user to which the role is added. - * @return True if the role is successfully added, false if the role already exists. - * @throws AuthException If the user does not exist. - */ - boolean grantRoleToUser(String roleName, String username) throws AuthException; - - /** - * Revoke a role from a user. - * - * @param roleName The name of the role to be removed. - * @param username The name of the user from which the role is removed. - * @return True if the role is successfully removed, false if the role does not exist. - * @throws AuthException If the user does not exist. - */ - boolean revokeRoleFromUser(String roleName, String username) throws AuthException; - - /** Re-initialize this object. */ - void reset() throws AuthException; - - /** - * List all users in the database. - * - * @return A list that contains all users'name. - */ - List listAllUsers(); - - /** - * Whether data water-mark is enabled for user 'userName'. - * - * @param userName - * @return - * @throws AuthException if the user does not exist - */ - boolean isUserUseWaterMark(String userName) throws AuthException; - - /** - * Enable or disable data water-mark for user 'userName'. - * - * @param userName - * @param useWaterMark - * @throws AuthException if the user does not exist. - */ - void setUserUseWaterMark(String userName, boolean useWaterMark) throws AuthException; - - /** - * clear all old users info, replace the old users with the new one. The caller should guarantee - * that no other methods of this interface are invoked concurrently when this method is called. - * - * @param users new users info - * @throws AuthException - */ - void replaceAllUsers(Map users) throws AuthException; - - void setPreVersion(boolean perVersion); - - boolean preVersion(); - - void checkAndRefreshPathPri(); -} diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java index 03a075936778..8a50a87ad967 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserAccessor.java @@ -19,8 +19,9 @@ package org.apache.iotdb.commons.auth.user; import org.apache.iotdb.commons.auth.entity.PathPrivilege; +import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; -import org.apache.iotdb.commons.conf.CommonDescriptor; +import org.apache.iotdb.commons.auth.role.LocalFileRoleAccessor; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.file.SystemFileFactory; import org.apache.iotdb.commons.utils.FileUtils; @@ -28,20 +29,16 @@ import org.apache.iotdb.commons.utils.TestOnly; import org.apache.thrift.TException; -import org.apache.tsfile.utils.Pair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataInputStream; -import java.io.EOFException; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.file.Files; import java.util.ArrayList; import java.util.HashSet; import java.util.List; @@ -77,174 +74,32 @@ * All user/role file store in config node's user/role folder. when our system start, we load all * user/role from folder. If we did some alter query, we just store raft log. */ -public class LocalFileUserAccessor implements IUserAccessor { +public class LocalFileUserAccessor extends LocalFileRoleAccessor { private static final Logger LOGGER = LoggerFactory.getLogger(LocalFileUserAccessor.class); - private static final String TEMP_SUFFIX = ".temp"; - private static final String STRING_ENCODING = "utf-8"; - private static final String USER_SNAPSHOT_FILE_NAME = "system" + File.separator + "users"; - public static final String ROLE_SUFFIX = "_role"; - private final String userDirPath; - - /** - * Reused buffer for primitive types encoding/decoding, which aim to reduce memory fragments. Use - * ThreadLocal for thread safety. - */ - private final ThreadLocal encodingBufferLocal = new ThreadLocal<>(); - - private final ThreadLocal strBufferLocal = new ThreadLocal<>(); - public LocalFileUserAccessor(String userDirPath) { - this.userDirPath = userDirPath; + super(userDirPath); } - /** - * Deserialize a user from its user file. - * - * @param username The name of the user to be deserialized. - * @return The user object or null if no such user. - */ @Override - public User loadUser(String username) throws IOException { - File userProfile = - SystemFileFactory.INSTANCE.getFile( - userDirPath + File.separator + username + IoTDBConstant.PROFILE_SUFFIX); - if (!userProfile.exists() || !userProfile.isFile()) { - // System may crush before a newer file is renamed. - File newProfile = - SystemFileFactory.INSTANCE.getFile( - userDirPath + File.separator + username + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX); - if (newProfile.exists() && newProfile.isFile()) { - if (!newProfile.renameTo(userProfile)) { - LOGGER.error("New profile renaming not succeed."); - } - userProfile = newProfile; - } else { - return null; - } - } - - FileInputStream inputStream = new FileInputStream(userProfile); - try (DataInputStream dataInputStream = - new DataInputStream(new BufferedInputStream(inputStream))) { - User user = new User(); - Pair result = - IOUtils.readAuthString(dataInputStream, STRING_ENCODING, strBufferLocal); - boolean oldVersion = result.getRight(); - user.setName(result.getLeft()); - user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal)); - if (oldVersion) { - IOUtils.loadRolePrivilege(user, dataInputStream, STRING_ENCODING, strBufferLocal); - int roleNum = dataInputStream.readInt(); - List roleList = new ArrayList<>(); - for (int i = 0; i < roleNum; i++) { - String roleName = IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal); - roleList.add(roleName); - } - user.setRoleList(roleList); - try { - user.setUseWaterMark(dataInputStream.readInt() != 0); - } catch (EOFException e1) { - user.setUseWaterMark(false); - } - return user; - } else { - user.setSysPrivilegeSet(dataInputStream.readInt()); - List pathPrivilegeList = new ArrayList<>(); - for (int i = 0; dataInputStream.available() != 0; i++) { - pathPrivilegeList.add( - IOUtils.readPathPrivilege(dataInputStream, STRING_ENCODING, strBufferLocal, false)); - } - user.setPrivilegeList(pathPrivilegeList); - - File roleOfUser = - SystemFileFactory.INSTANCE.getFile( - userDirPath, - File.separator + username + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX); - - if (!roleOfUser.isFile() || !roleOfUser.exists()) { - // System may crush before a newer file is renamed. - File newRoleProfile = - SystemFileFactory.INSTANCE.getFile( - userDirPath - + File.separator - + username - + "_role" - + IoTDBConstant.PROFILE_SUFFIX - + TEMP_SUFFIX); - if (newRoleProfile.exists() && newRoleProfile.isFile()) { - if (!newRoleProfile.renameTo(roleOfUser)) { - LOGGER.warn(" New role profile renaming not succeed."); - } - roleOfUser = newRoleProfile; - } - } - - List roleList = new ArrayList<>(); - if (roleOfUser.exists()) { - inputStream = new FileInputStream(roleOfUser); - try (DataInputStream roleInpuStream = - new DataInputStream(new BufferedInputStream(inputStream))) { - - for (int i = 0; roleInpuStream.available() != 0; i++) { - String rolename = IOUtils.readString(roleInpuStream, STRING_ENCODING, strBufferLocal); - roleList.add(rolename); - } - } - } - user.setRoleList(roleList); - return user; - } - } catch (Exception e) { - throw new IOException(e); - } finally { - strBufferLocal.remove(); - } + protected String getEntitySnapshotFileName() { + return "system" + File.separator + "users"; } - /** - * Serialize the user object to a temp file, then replace the old user file with the new file. - * - * @param user The user object that is to be saved. - */ @Override - public void saveUser(User user) throws IOException { - File userProfile = - SystemFileFactory.INSTANCE.getFile( - userDirPath - + File.separator - + user.getName() - + IoTDBConstant.PROFILE_SUFFIX - + TEMP_SUFFIX); - - try (FileOutputStream fileOutputStream = new FileOutputStream(userProfile); - BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream)) { - // for IOTDB 1.2, the username's length will be stored as a negative number. - byte[] strBuffer = user.getName().getBytes(STRING_ENCODING); - IOUtils.writeInt(outputStream, -1 * strBuffer.length, encodingBufferLocal); - outputStream.write(strBuffer); - IOUtils.writeString(outputStream, user.getPassword(), STRING_ENCODING, encodingBufferLocal); - IOUtils.writeInt(outputStream, user.getAllSysPrivileges(), encodingBufferLocal); - - int privilegeNum = user.getPathPrivilegeList().size(); - for (int i = 0; i < privilegeNum; i++) { - PathPrivilege pathPrivilege = user.getPathPrivilegeList().get(i); - IOUtils.writePathPrivilege( - outputStream, pathPrivilege, STRING_ENCODING, encodingBufferLocal); - } - outputStream.flush(); - fileOutputStream.getFD().sync(); - } catch (Exception e) { - LOGGER.warn("Get exception when save user {}'s privileges", user.getName(), e); - throw new IOException(e); - } finally { - encodingBufferLocal.remove(); - } + protected void saveEntityName(BufferedOutputStream outputStream, Role role) throws IOException { + super.saveEntityName(outputStream, role); + IOUtils.writeString( + outputStream, ((User) role).getPassword(), STRING_ENCODING, encodingBufferLocal); + } + @Override + protected void saveRoles(Role role) throws IOException { + User user = (User) role; File roleProfile = SystemFileFactory.INSTANCE.getFile( - userDirPath + entityDirPath + File.separator + user.getName() + ROLE_SUFFIX @@ -252,10 +107,8 @@ public void saveUser(User user) throws IOException { + TEMP_SUFFIX); try (FileOutputStream fileOutputStream = new FileOutputStream(roleProfile); BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream)) { - int userNum = user.getRoleList().size(); - for (int i = 0; i < userNum; i++) { - IOUtils.writeString( - outputStream, user.getRoleList().get(i), STRING_ENCODING, encodingBufferLocal); + for (String roleName : user.getRoleSet()) { + IOUtils.writeString(outputStream, roleName, STRING_ENCODING, encodingBufferLocal); } outputStream.flush(); fileOutputStream.getFD().sync(); @@ -266,13 +119,9 @@ public void saveUser(User user) throws IOException { encodingBufferLocal.remove(); } - File oldUserFile = - SystemFileFactory.INSTANCE.getFile( - userDirPath + File.separator + user.getName() + IoTDBConstant.PROFILE_SUFFIX); - IOUtils.replaceFile(userProfile, oldUserFile); File oldURoleFile = SystemFileFactory.INSTANCE.getFile( - userDirPath + entityDirPath + File.separator + user.getName() + ROLE_SUFFIX @@ -281,47 +130,92 @@ public void saveUser(User user) throws IOException { } /** - * Delete a user's user file. + * Deserialize a user from its user file. * - * @param username The name of the user to be deleted. - * @return True if the file is successfully deleted, false if the file does not exists. - * @throws IOException when the file cannot be deleted. + * @param entityName The name of the user to be deserialized. + * @return The user object or null if no such user. */ @Override - public boolean deleteUser(String username) throws IOException { - File userProfile = - SystemFileFactory.INSTANCE.getFile( - userDirPath + File.separator + username + IoTDBConstant.PROFILE_SUFFIX); - File backFile = - SystemFileFactory.INSTANCE.getFile( - userDirPath + File.separator + username + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX); - // The user may be not flush. So if not find the file, just return true; - if (!userProfile.exists() && !backFile.exists()) { - return true; + public User loadEntity(String entityName) throws IOException { + File entityFile = checkFileAvailable(entityName, ""); + if (entityFile == null) { + return null; } - if ((userProfile.exists() && !userProfile.delete()) - || (backFile.exists() && !backFile.delete())) { - throw new IOException(String.format("Cannot delete user file of %s", username)); - } - if (!deleteURole(username)) { - return false; + FileInputStream inputStream = new FileInputStream(entityFile); + try (DataInputStream dataInputStream = + new DataInputStream(new BufferedInputStream(inputStream))) { + boolean fromOldVersion = false; + int tag = dataInputStream.readInt(); + if (tag < 0) { + fromOldVersion = true; + } + User user = new User(); + + if (fromOldVersion) { + String name = + IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal, -1 * tag); + user.setName(name); + user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal)); + user.setSysPrivilegesWithMask(dataInputStream.readInt()); + List pathPrivilegeList = new ArrayList<>(); + for (int i = 0; dataInputStream.available() != 0; i++) { + pathPrivilegeList.add( + IOUtils.readPathPrivilege(dataInputStream, STRING_ENCODING, strBufferLocal)); + } + user.setPrivilegeList(pathPrivilegeList); + } else { + assert (tag == VERSION); + user.setName(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal)); + user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal)); + loadPrivileges(dataInputStream, user); + } + + File roleOfUser = checkFileAvailable(entityName, "_role"); + Set roleSet = new HashSet<>(); + if (roleOfUser != null && roleOfUser.exists()) { + inputStream = new FileInputStream(roleOfUser); + try (DataInputStream roleInputStream = + new DataInputStream(new BufferedInputStream(inputStream))) { + for (int i = 0; roleInputStream.available() != 0; i++) { + String roleName = IOUtils.readString(roleInputStream, STRING_ENCODING, strBufferLocal); + roleSet.add(roleName); + } + } + } + user.setRoleSet(roleSet); + return user; + + } catch (Exception e) { + throw new IOException(e); + } finally { + strBufferLocal.remove(); } - return true; } - public boolean deleteURole(String username) throws IOException { + /** + * Delete a user's user file. + * + * @param entityName The name of the user to be deleted. + * @return True if the file is successfully deleted, false if the file does not exist. + * @throws IOException when the file cannot be deleted. + */ + @Override + public boolean deleteEntity(String entityName) throws IOException { + return super.deleteEntity(entityName) && deleteURole(entityName); + } + + private boolean deleteURole(String username) throws IOException { File uRoleProfile = SystemFileFactory.INSTANCE.getFile( - userDirPath + File.separator + username + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX); + entityDirPath + File.separator + username + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX); File backProfile = SystemFileFactory.INSTANCE.getFile( - userDirPath + entityDirPath + File.separator + username + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX); - // This role don't have any role. if (!uRoleProfile.exists() && !backProfile.exists()) { return true; } @@ -333,54 +227,26 @@ public boolean deleteURole(String username) throws IOException { } @Override - public List listAllUsers() { - File userDir = SystemFileFactory.INSTANCE.getFile(userDirPath); + public List listAllEntities() { + File userDir = SystemFileFactory.INSTANCE.getFile(entityDirPath); String[] names = userDir.list( (dir, name) -> (name.endsWith(IoTDBConstant.PROFILE_SUFFIX) && !name.endsWith(ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX)) - || (name.endsWith(TEMP_SUFFIX) && !name.endsWith(ROLE_SUFFIX + TEMP_SUFFIX))); - List retList = new ArrayList<>(); - if (names != null) { - // in very rare situations, normal file and backup file may exist at the same time - // so a set is used to deduplicate - Set set = new HashSet<>(); - for (String fileName : names) { - set.add(fileName.replace(IoTDBConstant.PROFILE_SUFFIX, "").replace(TEMP_SUFFIX, "")); - } - retList.addAll(set); - } - return retList; - } - - @Override - public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { - SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE; - File userFolder = systemFileFactory.getFile(userDirPath); - File userSnapshotDir = systemFileFactory.getFile(snapshotDir, USER_SNAPSHOT_FILE_NAME); - File userTmpSnapshotDir = - systemFileFactory.getFile(userSnapshotDir.getAbsolutePath() + "-" + UUID.randomUUID()); - - boolean result = true; - try { - result = FileUtils.copyDir(userFolder, userTmpSnapshotDir); - result &= userTmpSnapshotDir.renameTo(userSnapshotDir); - } finally { - if (userTmpSnapshotDir.exists() && !userTmpSnapshotDir.delete()) { - FileUtils.deleteFileOrDirectory(userTmpSnapshotDir); - } - } - return result; + || (name.endsWith(TEMP_SUFFIX) + && !name.endsWith( + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX))); + return getEntityStrings(names); } @Override public void processLoadSnapshot(File snapshotDir) throws TException, IOException { SystemFileFactory systemFileFactory = SystemFileFactory.INSTANCE; - File userFolder = systemFileFactory.getFile(userDirPath); + File userFolder = systemFileFactory.getFile(entityDirPath); File userTmpFolder = systemFileFactory.getFile(userFolder.getAbsolutePath() + "-" + UUID.randomUUID()); - File userSnapshotDir = systemFileFactory.getFile(snapshotDir, USER_SNAPSHOT_FILE_NAME); + File userSnapshotDir = systemFileFactory.getFile(snapshotDir, getEntitySnapshotFileName()); try { org.apache.commons.io.FileUtils.moveDirectory(userFolder, userTmpFolder); @@ -395,91 +261,43 @@ public void processLoadSnapshot(File snapshotDir) throws TException, IOException } } - @Override - public void reset() { - checkOldUserDir(SystemFileFactory.INSTANCE.getFile(userDirPath)); - if (SystemFileFactory.INSTANCE.getFile(userDirPath).mkdirs()) { - LOGGER.info("user info dir {} is created", userDirPath); - } else if (!SystemFileFactory.INSTANCE.getFile(userDirPath).exists()) { - LOGGER.error("user info dir {} can not be created", userDirPath); - } - } - - private void checkOldUserDir(File newDir) { - File oldDir = new File(CommonDescriptor.getInstance().getConfig().getOldUserFolder()); - if (oldDir.exists()) { - if (!FileUtils.moveFileSafe(oldDir, newDir)) { - LOGGER.error("move old user dir fail: {}", oldDir.getAbsolutePath()); - } - } - } - - @Override - public String getDirPath() { - return userDirPath; - } - - @Override - public void cleanUserFolder() { - File[] files = SystemFileFactory.INSTANCE.getFile(userDirPath).listFiles(); - if (files != null) { - for (File file : files) { - FileUtils.deleteFileIfExist(file); - } - } else { - LOGGER.warn("User folder not exists"); - } - } - @TestOnly public void saveUserOldVersion(User user) throws IOException { File userProfile = SystemFileFactory.INSTANCE.getFile( - userDirPath + entityDirPath + File.separator + user.getName() + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX); - try (BufferedOutputStream outputStream = - new BufferedOutputStream(Files.newOutputStream(userProfile.toPath()))) { - try { - IOUtils.writeString(outputStream, user.getName(), STRING_ENCODING, encodingBufferLocal); - IOUtils.writeString(outputStream, user.getPassword(), STRING_ENCODING, encodingBufferLocal); - - int privilegeNum = user.getPathPrivilegeList().size(); - IOUtils.writeInt(outputStream, privilegeNum, encodingBufferLocal); - for (int i = 0; i < privilegeNum; i++) { - PathPrivilege pathPrivilege = user.getPathPrivilegeList().get(i); - IOUtils.writeString( - outputStream, - pathPrivilege.getPath().getFullPath(), - STRING_ENCODING, - encodingBufferLocal); - IOUtils.writeInt(outputStream, pathPrivilege.getPrivileges().size(), encodingBufferLocal); - for (Integer item : pathPrivilege.getPrivileges()) { - IOUtils.writeInt(outputStream, item, encodingBufferLocal); - } - } - int userNum = user.getRoleList().size(); - IOUtils.writeInt(outputStream, userNum, encodingBufferLocal); - for (int i = 0; i < userNum; i++) { - IOUtils.writeString( - outputStream, user.getRoleList().get(i), STRING_ENCODING, encodingBufferLocal); - } - IOUtils.writeInt(outputStream, user.isUseWaterMark() ? 1 : 0, encodingBufferLocal); - outputStream.flush(); + try (FileOutputStream fileOutputStream = new FileOutputStream(userProfile); + BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream)) { + // for IoTDB 1.3, the username's length will be stored as a negative number. + byte[] strBuffer = user.getName().getBytes(STRING_ENCODING); + IOUtils.writeInt(outputStream, -1 * strBuffer.length, encodingBufferLocal); + outputStream.write(strBuffer); + IOUtils.writeString(outputStream, user.getPassword(), STRING_ENCODING, encodingBufferLocal); + IOUtils.writeInt(outputStream, user.getAllSysPrivileges(), encodingBufferLocal); - } catch (Exception e) { - throw new IOException(e); + int privilegeNum = user.getPathPrivilegeList().size(); + for (int i = 0; i < privilegeNum; i++) { + PathPrivilege pathPrivilege = user.getPathPrivilegeList().get(i); + IOUtils.writePathPrivilege( + outputStream, pathPrivilege, STRING_ENCODING, encodingBufferLocal); } + outputStream.flush(); + fileOutputStream.getFD().sync(); + + } catch (Exception e) { + throw new IOException(e); } finally { encodingBufferLocal.remove(); } File oldFile = SystemFileFactory.INSTANCE.getFile( - userDirPath + File.separator + user.getName() + IoTDBConstant.PROFILE_SUFFIX); + entityDirPath + File.separator + user.getName() + IoTDBConstant.PROFILE_SUFFIX); IOUtils.replaceFile(userProfile, oldFile); } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java index fa6536d572b3..e2c8a33fee01 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/user/LocalFileUserManager.java @@ -19,7 +19,7 @@ package org.apache.iotdb.commons.auth.user; import org.apache.iotdb.commons.auth.AuthException; -import org.apache.iotdb.commons.auth.entity.User; +import org.apache.iotdb.commons.auth.entity.Role; import org.apache.thrift.TException; @@ -35,9 +35,9 @@ public LocalFileUserManager(String userDirPath) throws AuthException { @Override public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { - accessor.cleanUserFolder(); - for (Map.Entry entry : userMap.entrySet()) { - accessor.saveUser(entry.getValue()); + accessor.cleanEntityFolder(); + for (Map.Entry entry : entityMap.entrySet()) { + accessor.saveEntity(entry.getValue()); } return accessor.processTakeSnapshot(snapshotDir); } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java index e2858e8dc44a..2bafd0e5678e 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/schema/column/ColumnHeaderConstant.java @@ -72,6 +72,7 @@ private ColumnHeaderConstant() { public static final String PATHS = "Paths"; public static final String PATH = "Path"; public static final String VARIABLE = "Variable"; + public static final String SCOPE = "Scope"; // column names for count statement public static final String COLUMN = "Column"; @@ -601,10 +602,10 @@ private ColumnHeaderConstant() { new ColumnHeader(COLUMN_TTL, TSDataType.TEXT), new ColumnHeader(STATUS, TSDataType.TEXT)); - public static final List LIST_USER_PRIVILEGES_Column_HEADERS = + public static final List LIST_USER_OR_ROLE_PRIVILEGES_COLUMN_HEADERS = ImmutableList.of( new ColumnHeader(ROLE, TSDataType.TEXT), - new ColumnHeader(PATH, TSDataType.TEXT), + new ColumnHeader(SCOPE, TSDataType.TEXT), new ColumnHeader(PRIVILEGES, TSDataType.TEXT), new ColumnHeader(GRANT_OPTION, TSDataType.BOOLEAN)); diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java index 9c90d09ea59b..64e1420e081d 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/AuthUtils.java @@ -20,12 +20,9 @@ import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.entity.PathPrivilege; -import org.apache.iotdb.commons.auth.entity.PriPrivilegeType; import org.apache.iotdb.commons.auth.entity.PrivilegeType; -import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.conf.IoTDBConstant; -import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.path.PathDeserializeUtil; import org.apache.iotdb.commons.path.PathPatternUtil; @@ -64,49 +61,6 @@ private AuthUtils() { // Empty constructor } - /** - * This filed only for pre version. When we do a major version upgrade, it can be removed - * directly. - */ - // FOR PRE VERSION BEGIN ----- - private static final int MAX_LENGTH_PRE = 64; - - private static final String REX_PATTERN_PRE = "^[-\\w]*$"; - - public static void validatePasswordPre(String password) throws AuthException { - validateNameOrPasswordPre(password); - } - - public static void validateUsernamePre(String username) throws AuthException { - validateNameOrPasswordPre(username); - } - - public static void validateRolenamePre(String rolename) throws AuthException { - validateNameOrPasswordPre(rolename); - } - - public static void validateNameOrPasswordPre(String str) throws AuthException { - int length = str.length(); - if (length < MIN_LENGTH) { - throw new AuthException( - TSStatusCode.ILLEGAL_PARAMETER, - "The length of name or password must be greater than or equal to " + MIN_LENGTH); - } else if (length > MAX_LENGTH_PRE) { - throw new AuthException( - TSStatusCode.ILLEGAL_PARAMETER, - "The length of name or password must be less than or equal to " + MAX_LENGTH); - } else if (str.contains(" ")) { - throw new AuthException( - TSStatusCode.ILLEGAL_PARAMETER, "The name or password cannot contain spaces"); - } else if (!str.matches(REX_PATTERN_PRE)) { - throw new AuthException( - TSStatusCode.ILLEGAL_PARAMETER, - "The name or password can only contain letters, numbers, and underscores"); - } - } - - // FOR PRE VERSION DONE --- - /** * Validate password * @@ -143,11 +97,11 @@ public static void validateUsername(String username) throws AuthException { /** * Validate role name * - * @param rolename role name - * @throws AuthException contains message why rolename is invalid + * @param roleName role name + * @throws AuthException contains message why roleName is invalid */ - public static void validateRolename(String rolename) throws AuthException { - validateNameOrPassword(rolename); + public static void validateRolename(String roleName) throws AuthException { + validateNameOrPassword(roleName); } public static void validateNameOrPassword(String str) throws AuthException { @@ -207,20 +161,6 @@ public static void validatePatternPath(PartialPath path) throws AuthException { } } - public static PartialPath convertPatternPath(PartialPath path) throws IllegalPathException { - String pathStr = new String(); - int i = 0; - for (; i < path.getNodeLength(); i++) { - if (!PathPatternUtil.hasWildcard(path.getNodes()[i])) { - pathStr = pathStr.concat(path.getNodes()[i] + "."); - } else { - break; - } - } - pathStr = pathStr.concat("**"); - return new PartialPath(pathStr); - } - /** * Encrypt password * @@ -238,34 +178,40 @@ public static String encryptPassword(String password) { * Check path privilege * * @param path series path - * @param privilegeId privilege Id + * @param priv privilege type * @param privilegeList privileges in List structure - * @exception AuthException throw if path is invalid or path in privilege is invalid * @return True if privilege-check passed */ public static boolean checkPathPrivilege( - PartialPath path, int privilegeId, List privilegeList) { + PartialPath path, PrivilegeType priv, List privilegeList) { if (privilegeList == null) { return false; } for (PathPrivilege pathPrivilege : privilegeList) { - if (pathPrivilege.getPath().matchFullPath(path) - && pathPrivilege.checkPrivilege(privilegeId)) { + if (pathPrivilege.getPath().matchFullPath(path) && pathPrivilege.checkPrivilege(priv)) { return true; } } return false; } + /** + * Check path privilege grant option + * + * @param path series path + * @param priv privilege type + * @param privilegeList privileges in List structure + * @return True if privilege-check passed + */ public static boolean checkPathPrivilegeGrantOpt( - PartialPath path, int privilegeId, List privilegeList) { + PartialPath path, PrivilegeType priv, List privilegeList) { if (privilegeList == null) { return false; } for (PathPrivilege pathPrivilege : privilegeList) { if (pathPrivilege.getPath().matchFullPath(path) - && pathPrivilege.getPrivileges().contains(privilegeId) - && pathPrivilege.getGrantOpt().contains(privilegeId)) { + && pathPrivilege.getPrivileges().contains(priv) + && pathPrivilege.getGrantOpt().contains(priv)) { return true; } } @@ -276,17 +222,19 @@ public static boolean checkPathPrivilegeGrantOpt( * Get privileges * * @param path The seriesPath on which the privileges take effect. - * @exception AuthException throw if path is invalid or path in privilege is invalid * @return The privileges granted to the role */ - public static Set getPrivileges(PartialPath path, List privilegeList) { + public static Set getPrivileges( + PartialPath path, List privilegeList) { if (privilegeList == null) { return new HashSet<>(); } - Set privileges = new HashSet<>(); + Set privileges = new HashSet<>(); for (PathPrivilege pathPrivilege : privilegeList) { if (pathPrivilege.getPath().matchFullPath(path)) { - privileges.addAll(pathPrivilege.getPrivileges()); + for (Integer item : pathPrivilege.getPrivilegeIntSet()) { + privileges.add(PrivilegeType.values()[item]); + } } } return privileges; @@ -296,26 +244,15 @@ public static Set getPrivileges(PartialPath path, List p * Check if series path has this privilege to revoke * * @param path series path - * @param privilegeId privilege Id + * @param priv privilege type * @param privilegeList privileges in List structure * @return True if series path has this privilege */ - public static boolean hasPrivilegeToReovke( - PartialPath path, int privilegeId, List privilegeList) { + public static boolean hasPrivilegeToRevoke( + PartialPath path, PrivilegeType priv, List privilegeList) { for (PathPrivilege pathPrivilege : privilegeList) { if (path.matchFullPath(pathPrivilege.getPath()) - && pathPrivilege.getPrivileges().contains(privilegeId)) { - return true; - } - } - return false; - } - - public static boolean hasPrivilege( - PartialPath path, int privilegeId, List privilegeList) { - for (PathPrivilege pathPrivilege : privilegeList) { - if (pathPrivilege.getPath().equals(path) - && pathPrivilege.getPrivileges().contains(privilegeId)) { + && pathPrivilege.getPrivileges().contains(priv)) { return true; } } @@ -326,11 +263,14 @@ public static boolean hasPrivilege( * Add privilege * * @param path series path - * @param privilegeId privilege Id + * @param priv privilege type * @param privilegeList privileges in List structure of user or role */ public static void addPrivilege( - PartialPath path, int privilegeId, List privilegeList, boolean grantOption) { + PartialPath path, + PrivilegeType priv, + List privilegeList, + boolean grantOption) { PathPrivilege targetPathPrivilege = null; // check PathPrivilege of target path is already existed for (PathPrivilege pathPrivilege : privilegeList) { @@ -345,45 +285,42 @@ public static void addPrivilege( privilegeList.add(targetPathPrivilege); } // add privilegeId into targetPathPrivilege - targetPathPrivilege.grantPrivilege(privilegeId, grantOption); + targetPathPrivilege.grantPrivilege(priv, grantOption); } /** * Remove privilege * * @param path series path - * @param privilegeId privilege Id + * @param priv privilege type * @param privilegeList privileges in List structure of user or role */ public static void removePrivilege( - PartialPath path, int privilegeId, List privilegeList) { + PartialPath path, PrivilegeType priv, List privilegeList) { Iterator it = privilegeList.iterator(); while (it.hasNext()) { PathPrivilege pathPri = it.next(); if (path.matchFullPath(pathPri.getPath())) { - pathPri.revokePrivilege(privilegeId); - if (pathPri.getPrivileges().isEmpty()) { + pathPri.revokePrivilege(priv); + if (pathPri.getPrivilegeIntSet().isEmpty()) { it.remove(); } } } } - public static void removePrivilegePre( - PartialPath path, int privilegeId, List privilegeList) { - Iterator it = privilegeList.iterator(); - while (it.hasNext()) { - PathPrivilege pathPri = it.next(); - if (pathPri.getPath().equals(path)) { - if (privilegeId != PriPrivilegeType.ALL.ordinal()) { - pathPri.revokePrivilege(privilegeId); - } else { - it.remove(); - return; - } - if (pathPri.getPrivileges().isEmpty()) { - it.remove(); - } + /** + * Remove privilege grant option + * + * @param path series path + * @param priv privilege type + * @param privilegeList privileges in List structure of user or role + */ + public static void removePrivilegeGrantOption( + PartialPath path, PrivilegeType priv, List privilegeList) { + for (PathPrivilege pathPri : privilegeList) { + if (path.matchFullPath(pathPri.getPath())) { + pathPri.revokeGrantOpt(priv); } } } @@ -393,9 +330,28 @@ public static TPermissionInfoResp generateEmptyPermissionInfoResp() { TPermissionInfoResp permissionInfoResp = new TPermissionInfoResp(); permissionInfoResp.setUserInfo( new TUserResp( - "", "", new ArrayList<>(), new HashSet<>(), new HashSet<>(), new ArrayList<>(), false)); + new TRoleResp( + "", + new ArrayList<>(), + new HashSet<>(), + new HashSet<>(), + new HashMap<>(), + new HashSet<>(), + new HashSet<>()), + "", + new HashSet<>(), + false)); Map roleInfo = new HashMap<>(); - roleInfo.put("", new TRoleResp("", new ArrayList<>(), new HashSet<>(), new HashSet<>())); + roleInfo.put( + "", + new TRoleResp( + "", + new ArrayList<>(), + new HashSet<>(), + new HashSet<>(), + new HashMap<>(), + new HashSet<>(), + new HashSet<>())); permissionInfoResp.setRoleInfo(roleInfo); return permissionInfoResp; } @@ -404,7 +360,7 @@ public static TPermissionInfoResp generateEmptyPermissionInfoResp() { * Transform permission from name to privilegeId * * @param authorizationList the list of privilege name - * @return the list of privilege Ids + * @return the set of privilege type * @throws AuthException throws if there are no privilege matched */ public static Set strToPermissions(String[] authorizationList) throws AuthException { @@ -454,75 +410,38 @@ public static List deserializePartialPathList(ByteBuffer buffer) { return paths; } - public static void checkAndRefreshPri(Role role) { - if (role.getServiceReady()) { - return; - } - Set sysPriCopy = role.getSysPrivilege(); - List priCopy = role.getPathPrivilegeList(); - role.setSysPrivilegeSet(new HashSet<>()); - role.setPrivilegeList(new ArrayList<>()); - - // Pre version's privileges were stored in path list; - for (PathPrivilege pathPri : priCopy) { - PartialPath path = pathPri.getPath(); - for (int prePri : pathPri.getPrivileges()) { - PriPrivilegeType type = PriPrivilegeType.values()[prePri]; - if (type.isAccept()) { - for (PrivilegeType curType : type.getSubPri()) { - if (curType.isPathRelevant()) { - try { - AuthUtils.validatePatternPath(path); - } catch (AuthException e) { - try { - path = AuthUtils.convertPatternPath(path); - } catch (IllegalPathException illegalE) { - // will never get here - String[] str = {"root", "**"}; - path = new PartialPath(str); - } - } - role.addPathPrivilege(path, curType.ordinal(), false); - } else { - role.addSysPrivilege(curType.ordinal()); - } - } - } - } - } - role.setServiceReady(true); - } - - public static int posToSysPri(int pos) { + // deserialize privilege type from an int mask. + public static PrivilegeType posToSysPri(int pos) { switch (pos) { case 0: - return PrivilegeType.MANAGE_DATABASE.ordinal(); + return PrivilegeType.MANAGE_DATABASE; case 1: - return PrivilegeType.MANAGE_USER.ordinal(); + return PrivilegeType.MANAGE_USER; case 2: - return PrivilegeType.MANAGE_ROLE.ordinal(); + return PrivilegeType.MANAGE_ROLE; case 3: - return PrivilegeType.USE_TRIGGER.ordinal(); + return PrivilegeType.USE_TRIGGER; case 4: - return PrivilegeType.USE_UDF.ordinal(); + return PrivilegeType.USE_UDF; case 5: - return PrivilegeType.USE_CQ.ordinal(); + return PrivilegeType.USE_CQ; case 6: - return PrivilegeType.USE_PIPE.ordinal(); + return PrivilegeType.USE_PIPE; case 7: - return PrivilegeType.EXTEND_TEMPLATE.ordinal(); + return PrivilegeType.EXTEND_TEMPLATE; case 8: - return PrivilegeType.MAINTAIN.ordinal(); + return PrivilegeType.MAINTAIN; case 9: - return PrivilegeType.USE_MODEL.ordinal(); + return PrivilegeType.USE_MODEL; default: - return -1; + // Not reach here. + LOGGER.warn("Not support position"); + throw new RuntimeException("Not support position"); } } - public static int sysPriTopos(int privilegeId) { - PrivilegeType type = PrivilegeType.values()[privilegeId]; - switch (type) { + public static int sysPriToPos(PrivilegeType priv) { + switch (priv) { case MANAGE_DATABASE: return 0; case MANAGE_USER: @@ -574,7 +493,45 @@ public static int pathPriToPos(PrivilegeType pri) { case WRITE_SCHEMA: return 3; default: - return -1; + throw new RuntimeException("Not support PrivilegeType " + pri); + } + } + + public static PrivilegeType posToObjPri(int pos) { + switch (pos) { + case 0: + return PrivilegeType.CREATE; + case 1: + return PrivilegeType.DROP; + case 2: + return PrivilegeType.ALTER; + case 3: + return PrivilegeType.SELECT; + case 4: + return PrivilegeType.INSERT; + case 5: + return PrivilegeType.DELETE; + default: + throw new RuntimeException("Not support position"); + } + } + + public static int objPriToPos(PrivilegeType pri) { + switch (pri) { + case CREATE: + return 0; + case DROP: + return 1; + case ALTER: + return 2; + case SELECT: + return 3; + case INSERT: + return 4; + case DELETE: + return 5; + default: + throw new RuntimeException("Not support position"); } } } diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java index f96ae8443b98..d1c049e86d75 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/IOUtils.java @@ -18,22 +18,20 @@ */ package org.apache.iotdb.commons.utils; +import org.apache.iotdb.commons.auth.entity.DatabasePrivilege; import org.apache.iotdb.commons.auth.entity.PathPrivilege; -import org.apache.iotdb.commons.auth.entity.PriPrivilegeType; -import org.apache.iotdb.commons.auth.entity.Role; +import org.apache.iotdb.commons.auth.entity.TablePrivilege; import org.apache.iotdb.commons.exception.IllegalPathException; import org.apache.iotdb.commons.path.PartialPath; import com.google.common.base.Supplier; -import org.apache.tsfile.utils.Pair; import java.io.DataInputStream; import java.io.File; import java.io.IOException; import java.io.OutputStream; import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.function.Function; @@ -107,8 +105,14 @@ public static void writeInt( public static String readString( DataInputStream inputStream, String encoding, ThreadLocal strBufferLocal) throws IOException { - byte[] strBuffer; int length = inputStream.readInt(); + return readString(inputStream, encoding, strBufferLocal, length); + } + + public static String readString( + DataInputStream inputStream, String encoding, ThreadLocal strBufferLocal, int length) + throws IOException { + byte[] strBuffer; if (length > 0) { if (strBufferLocal != null) { strBuffer = strBufferLocal.get(); @@ -126,46 +130,6 @@ public static String readString( return null; } - /** - * To distinguish between permissions files of the new and old versions, in files generated by the - * new version, the length of the first string will be stored as a negative number. - * - *

Only for LocalFileUserAccessor/LocalFileRoleAccessor. We save our roles/users' name string - * in a different format. - * - * @param inputStream the source to read. - * @param encoding string encoding like "utf-8" - * @param strBufferLocal a ThreadLocal buffer may be passed to avoid frequently memory. - * allocations. A null may also be passed to use a local buffer. - * @return a pair contains one string and boolean. The string is the auth string we get from input - * stream. The boolean is true means length of the auth string is greater than 0. - * @throws IOException when an exception raised during operating the stream. - */ - public static Pair readAuthString( - DataInputStream inputStream, String encoding, ThreadLocal strBufferLocal) - throws IOException { - byte[] strBuffer; - int length = inputStream.readInt(); - int absLength = Math.abs(length); - if (absLength != 0) { - if (strBufferLocal != null) { - strBuffer = strBufferLocal.get(); - if (strBuffer == null || absLength > strBuffer.length) { - strBuffer = new byte[absLength]; - strBufferLocal.set(strBuffer); - } - } else { - strBuffer = new byte[absLength]; - } - - inputStream.read(strBuffer, 0, absLength); - Pair result = - new Pair<>(new String(strBuffer, 0, absLength, encoding), length > 0); - return result; - } - return null; - } - /** * Read a PathPrivilege from the given stream. * @@ -173,59 +137,17 @@ public static Pair readAuthString( * @param encoding string encoding like 'utf-8'. * @param strBufferLocal a ThreadLocal buffer may be passed to avoid frequently memory * allocations. A null may also be passed to use a local buffer. - * @param compatible A boolean value used to indicate whether the old version of permission - * deserialization method should be employed. * @return a PathPrivilege read from the stream. * @throws IOException when an exception raised during operating the stream. */ public static PathPrivilege readPathPrivilege( - DataInputStream inputStream, - String encoding, - ThreadLocal strBufferLocal, - boolean compatible) + DataInputStream inputStream, String encoding, ThreadLocal strBufferLocal) throws IOException, IllegalPathException { String path = IOUtils.readString(inputStream, encoding, strBufferLocal); - if (compatible) { - int privilegeNum = inputStream.readInt(); - PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath(path)); - for (int i = 0; i < privilegeNum; i++) { - // Need to check the corresponding relationship of permissions - pathPrivilege.getPrivileges().add(inputStream.readInt()); - } - return pathPrivilege; - } else { - PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath(path)); - int privileges = inputStream.readInt(); - pathPrivilege.setAllPrivileges(privileges); - return pathPrivilege; - } - } - - // Because pre version's privilege will stored by path + privilege - // This func will turn the privilege info into role's path privileges. - // for the global privilege, they were stored by root + privilege. - - public static void loadRolePrivilege( - Role role, DataInputStream inputStream, String encoding, ThreadLocal strBufferLocal) - throws IOException, IllegalPathException { - role.setSysPrivilegeSet(0); - int pathPriNum = inputStream.readInt(); - List pathPrivilegeList = new ArrayList<>(); - for (int i = 0; i < pathPriNum; i++) { - String path = IOUtils.readString(inputStream, encoding, strBufferLocal); - PartialPath ppath = new PartialPath(path); - PathPrivilege pathPriv = new PathPrivilege(ppath); - int priNum = inputStream.readInt(); - for (int j = 0; j < priNum; j++) { - PriPrivilegeType priType = PriPrivilegeType.values()[inputStream.readInt()]; - if (priType.isAccept()) { - pathPriv.grantPrivilege(priType.ordinal(), false); - } - } - pathPrivilegeList.add(pathPriv); - } - role.setPrivilegeList(pathPrivilegeList); - role.setServiceReady(false); + PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath(path)); + int privileges = inputStream.readInt(); + pathPrivilege.setAllPrivileges(privileges); + return pathPrivilege; } /** @@ -248,6 +170,38 @@ public static void writePathPrivilege( writeInt(outputStream, pathPrivilege.getAllPrivileges(), encodingBufferLocal); } + public static DatabasePrivilege readRelationalPrivilege( + DataInputStream inputStream, String encoding, ThreadLocal strBufferLocal) + throws IOException { + String databaseName = IOUtils.readString(inputStream, encoding, strBufferLocal); + DatabasePrivilege databasePrivilege = new DatabasePrivilege(databaseName); + databasePrivilege.setPrivileges(inputStream.readInt()); + int tableNum = inputStream.readInt(); + for (int i = 0; i < tableNum; i++) { + String tableName = IOUtils.readString(inputStream, encoding, strBufferLocal); + TablePrivilege tablePrivilege = new TablePrivilege(tableName); + tablePrivilege.setPrivileges(inputStream.readInt()); + databasePrivilege.getTablePrivilegeMap().put(tableName, tablePrivilege); + } + return databasePrivilege; + } + + public static void writeObjectPrivilege( + OutputStream outputStream, + DatabasePrivilege databasePrivilege, + String encoding, + ThreadLocal encodingBufferLocal) + throws IOException { + writeString(outputStream, databasePrivilege.getDatabaseName(), encoding, encodingBufferLocal); + writeInt(outputStream, databasePrivilege.getAllPrivileges(), encodingBufferLocal); + writeInt(outputStream, databasePrivilege.getTablePrivilegeMap().size(), encodingBufferLocal); + for (Map.Entry entry : + databasePrivilege.getTablePrivilegeMap().entrySet()) { + writeString(outputStream, entry.getValue().getTableName(), encoding, encodingBufferLocal); + writeInt(outputStream, entry.getValue().getAllPrivileges(), encodingBufferLocal); + } + } + /** * Replace newFile with oldFile. If the file system does not support atomic file replacement then * delete the old file first. diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/SerializeUtils.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/SerializeUtils.java index 8e5893edd05c..7b34111aa1fb 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/SerializeUtils.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/utils/SerializeUtils.java @@ -19,6 +19,8 @@ package org.apache.iotdb.commons.utils; +import org.apache.iotdb.commons.auth.entity.PrivilegeType; + import org.apache.tsfile.common.conf.TSFileConfig; import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.read.TimeValuePair; @@ -587,4 +589,23 @@ public static long[] deserializeLongs(ByteBuffer buffer) { } return ret; } + + public static void serializePrivilegeTypeSet( + Set types, DataOutputStream dataOutputStream) { + try { + dataOutputStream.writeInt(types.size()); + for (PrivilegeType type : types) { + dataOutputStream.writeInt(type.ordinal()); + } + } catch (IOException e) { + // + } + } + + public static void deserializePrivilegeTypeSet(Set types, ByteBuffer buffer) { + int length = buffer.getInt(); + for (int i = 0; i < length; i++) { + types.add(PrivilegeType.values()[buffer.getInt()]); + } + } } diff --git a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java index cda689255609..9fde20b53561 100644 --- a/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java +++ b/iotdb-core/node-commons/src/test/java/org/apache/iotdb/commons/utils/AuthUtilsTest.java @@ -63,49 +63,45 @@ public void authUtilsTest_ParameterCheck() throws AuthException, IllegalPathExce public void authUtilsTest_PrivilegeGrantRevokeCheck() throws IllegalPathException { PartialPath path = new PartialPath(new String("root.t1")); PathPrivilege pathWithPri = new PathPrivilege(path); - pathWithPri.grantPrivilege(PrivilegeType.READ_SCHEMA.ordinal(), false); - pathWithPri.grantPrivilege(PrivilegeType.READ_DATA.ordinal(), false); + pathWithPri.grantPrivilege(PrivilegeType.READ_SCHEMA, false); + pathWithPri.grantPrivilege(PrivilegeType.READ_DATA, false); PartialPath path2 = new PartialPath(new String("root.t2")); PathPrivilege pathWithPri2 = new PathPrivilege(path2); - pathWithPri2.grantPrivilege(PrivilegeType.WRITE_SCHEMA.ordinal(), false); + pathWithPri2.grantPrivilege(PrivilegeType.WRITE_SCHEMA, false); PartialPath path3 = new PartialPath(new String("root.**")); PathPrivilege pathWithPri3 = new PathPrivilege(path3); - pathWithPri3.grantPrivilege(PrivilegeType.READ_DATA.ordinal(), false); + pathWithPri3.grantPrivilege(PrivilegeType.READ_DATA, false); /** root.t1 : read schema, read data; root.t2 : write schema; root.** : read data */ // Privilege list is empty. - Assert.assertFalse( - AuthUtils.checkPathPrivilege(path2, PrivilegeType.READ_SCHEMA.ordinal(), null)); + Assert.assertFalse(AuthUtils.checkPathPrivilege(path2, PrivilegeType.READ_SCHEMA, null)); List privilegeList = new ArrayList<>(); privilegeList.add(pathWithPri); privilegeList.add(pathWithPri2); privilegeList.add(pathWithPri3); Assert.assertTrue( - AuthUtils.checkPathPrivilege(path2, PrivilegeType.READ_SCHEMA.ordinal(), privilegeList)); - Assert.assertTrue( - AuthUtils.checkPathPrivilege(path, PrivilegeType.READ_SCHEMA.ordinal(), privilegeList)); + AuthUtils.checkPathPrivilege(path2, PrivilegeType.READ_SCHEMA, privilegeList)); + Assert.assertTrue(AuthUtils.checkPathPrivilege(path, PrivilegeType.READ_SCHEMA, privilegeList)); - pathWithPri.revokePrivilege(PrivilegeType.READ_SCHEMA.ordinal()); + pathWithPri.revokePrivilege(PrivilegeType.READ_SCHEMA); /** root.t1 : read data; root.t2 : write schema ; root.** : read data */ Assert.assertFalse( - AuthUtils.checkPathPrivilege(path, PrivilegeType.READ_SCHEMA.ordinal(), privilegeList)); - Assert.assertTrue( - AuthUtils.checkPathPrivilege(path, PrivilegeType.READ_DATA.ordinal(), privilegeList)); + AuthUtils.checkPathPrivilege(path, PrivilegeType.READ_SCHEMA, privilegeList)); + Assert.assertTrue(AuthUtils.checkPathPrivilege(path, PrivilegeType.READ_DATA, privilegeList)); // root.t2 have read data privilege because root.** - Assert.assertTrue( - AuthUtils.checkPathPrivilege(path2, PrivilegeType.READ_DATA.ordinal(), privilegeList)); + Assert.assertTrue(AuthUtils.checkPathPrivilege(path2, PrivilegeType.READ_DATA, privilegeList)); Assert.assertFalse( - AuthUtils.hasPrivilegeToReovke(path2, PrivilegeType.READ_DATA.ordinal(), privilegeList)); + AuthUtils.hasPrivilegeToRevoke(path2, PrivilegeType.READ_DATA, privilegeList)); Assert.assertEquals(AuthUtils.getPrivileges(path, privilegeList).size(), 1); Assert.assertEquals(AuthUtils.getPrivileges(path, null).size(), 0); - pathWithPri.grantPrivilege(PrivilegeType.WRITE_DATA.ordinal(), false); + pathWithPri.grantPrivilege(PrivilegeType.WRITE_DATA, false); Assert.assertTrue( - AuthUtils.getPrivileges(path, privilegeList).contains(PrivilegeType.WRITE_DATA.ordinal())); + AuthUtils.getPrivileges(path, privilegeList).contains(PrivilegeType.WRITE_DATA)); } @Test @@ -113,32 +109,32 @@ public void authUtilsTest_PathPrivilegeAddRemove() throws IllegalPathException, List privs = new ArrayList<>(); PartialPath path1 = new PartialPath("root.t1"); - AuthUtils.addPrivilege(path1, PrivilegeType.READ_SCHEMA.ordinal(), privs, false); - AuthUtils.addPrivilege(path1, PrivilegeType.READ_DATA.ordinal(), privs, false); - AuthUtils.addPrivilege(path1, PrivilegeType.WRITE_SCHEMA.ordinal(), privs, true); + AuthUtils.addPrivilege(path1, PrivilegeType.READ_SCHEMA, privs, false); + AuthUtils.addPrivilege(path1, PrivilegeType.READ_DATA, privs, false); + AuthUtils.addPrivilege(path1, PrivilegeType.WRITE_SCHEMA, privs, true); Assert.assertEquals(privs.size(), 1); - Assert.assertEquals(privs.get(0).getPrivileges().size(), 3); + Assert.assertEquals(privs.get(0).getPrivilegeIntSet().size(), 3); Assert.assertEquals(privs.get(0).getGrantOpt().size(), 1); PartialPath path2 = new PartialPath("root.t2"); - AuthUtils.addPrivilege(path2, PrivilegeType.READ_SCHEMA.ordinal(), privs, false); + AuthUtils.addPrivilege(path2, PrivilegeType.READ_SCHEMA, privs, false); Assert.assertEquals(privs.size(), 2); - AuthUtils.removePrivilege(path2, PrivilegeType.READ_SCHEMA.ordinal(), privs); + AuthUtils.removePrivilege(path2, PrivilegeType.READ_SCHEMA, privs); Assert.assertEquals(privs.size(), 1); // if we revoke privileges on root.**, privileges on root.t1 and root.t2 will also be removed. PartialPath rootPath = new PartialPath("root.**"); - AuthUtils.removePrivilege(rootPath, PrivilegeType.READ_DATA.ordinal(), privs); - Assert.assertEquals(privs.get(0).getPrivileges().size(), 2); - Assert.assertFalse(privs.get(0).getPrivileges().contains(PrivilegeType.READ_DATA.ordinal())); - - AuthUtils.addPrivilege(path2, PrivilegeType.WRITE_SCHEMA.ordinal(), privs, true); - AuthUtils.removePrivilege(rootPath, PrivilegeType.WRITE_SCHEMA.ordinal(), privs); - Assert.assertEquals(privs.size(), 1); - Assert.assertEquals(privs.get(0).getPrivileges().size(), 1); + AuthUtils.removePrivilege(rootPath, PrivilegeType.READ_DATA, privs); + Assert.assertEquals(2, privs.get(0).getPrivilegeIntSet().size()); + Assert.assertFalse(privs.get(0).getPrivileges().contains(PrivilegeType.READ_DATA)); + + AuthUtils.addPrivilege(path2, PrivilegeType.WRITE_SCHEMA, privs, true); + AuthUtils.removePrivilege(rootPath, PrivilegeType.WRITE_SCHEMA, privs); + Assert.assertEquals(1, privs.size()); + Assert.assertEquals(1, privs.get(0).getPrivilegeIntSet().size()); } @Test @@ -188,16 +184,4 @@ public void authUtilsTest_PatternPathCheck() throws AuthException, IllegalPathEx AuthException.class, () -> AuthUtils.validatePatternPath(new PartialPath(new String("*a.data.t1.**.**")))); } - - @Test - public void authUtilsTest_ConvertPattern() throws IllegalPathException { - PartialPath path = AuthUtils.convertPatternPath(new PartialPath("root.*.t1.t2")); - Assert.assertTrue(path.equals(new PartialPath("root.**"))); - path = AuthUtils.convertPatternPath(new PartialPath("root.*t1.t1.t2")); - Assert.assertTrue(path.equals(new PartialPath("root.**"))); - path = AuthUtils.convertPatternPath(new PartialPath("root.*")); - Assert.assertTrue(path.equals(new PartialPath("root.**"))); - path = AuthUtils.convertPatternPath(new PartialPath("root.t2.*.t1.**")); - Assert.assertTrue(path.equals(new PartialPath("root.t2.**"))); - } } diff --git a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 index 16f0ab9956c9..20180597005e 100644 --- a/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 +++ b/iotdb-core/relational-grammar/src/main/antlr4/org/apache/iotdb/db/relational/grammar/sql/RelationalSql.g4 @@ -128,6 +128,19 @@ statement | showCurrentTimestampStatement // auth Statement + | grantStatement + | revokeStatement + | createUserStatement + | createRoleStatement + | dropUserStatement + | dropRoleStatement + | grantUserRoleStatement + | revokeUserRoleStatement + | alterUserStatement + | listUserPrivilegeStatement + | listRolePrivilegeStatement + | listUserStatement + | listRoleStatement // View, Trigger, pipe, CQ, Quota are not supported yet ; @@ -547,9 +560,114 @@ showCurrentTimestampStatement ; +// ------------------------------------------- Authority Statement ----------------------------------------------------- +createUserStatement + : CREATE USER userName=identifier password=string + ; + +createRoleStatement + : CREATE ROLE roleName=identifier + ; + +dropUserStatement + : DROP USER userName=identifier + ; + +dropRoleStatement + : DROP ROLE roleName=identifier + ; + +alterUserStatement + : ALTER USER userName=identifier SET PASSWORD password=identifier + ; + +grantUserRoleStatement + : GRANT ROLE roleName=identifier TO userName=identifier + ; + +revokeUserRoleStatement + : REVOKE ROLE roleName=identifier FROM userName=identifier + ; + + +grantStatement + : GRANT privilegeObjectScope TO holderType holderName=identifier (grantOpt)? + ; + +listUserPrivilegeStatement + : LIST PRIVILEGES OF USER userName=identifier + ; + +listRolePrivilegeStatement + : LIST PRIVILEGES OF ROLE roleName=identifier + ; + +listUserStatement + : LIST USER + ; + +listRoleStatement + : LIST ROLE + ; +revokeStatement + : REVOKE (revokeGrantOpt)? privilegeObjectScope FROM holderType holderName=identifier + ; + +privilegeObjectScope + : systemPrivileges + | objectPrivileges ON objectType objectName=identifier + | objectPrivileges ON objectScope + | objectPrivileges ON ANY + | ALL + ; + +systemPrivileges + : systemPrivilege (',' systemPrivilege)* + ; + +objectPrivileges + : objectPrivilege (',' objectPrivilege)* + ; + +objectScope + : dbname=identifier '.' tbname=identifier; + +systemPrivilege + : MANAGE_USER + | MANAGE_ROLE + | MAINTAIN + ; + +objectPrivilege + : CREATE + | DROP + | ALTER + | SELECT + | INSERT + | DELETE + ; + +objectType + : TABLE + | DATABASE + ; + +holderType + : USER + | ROLE + ; + +grantOpt + : WITH GRANT OPTION + ; + +revokeGrantOpt + : GRANT OPTION FOR + ; + // ------------------------------------------- Query Statement --------------------------------------------------------- queryStatement : query #statementDefault @@ -1139,6 +1257,7 @@ LEVEL: 'LEVEL'; LIKE: 'LIKE'; LIMIT: 'LIMIT'; LINEAR: 'LINEAR'; +LIST: 'LIST'; LISTAGG: 'LISTAGG'; LOAD: 'LOAD'; LOCAL: 'LOCAL'; @@ -1146,6 +1265,9 @@ LOCALTIME: 'LOCALTIME'; LOCALTIMESTAMP: 'LOCALTIMESTAMP'; LOGICAL: 'LOGICAL'; LOOP: 'LOOP'; +MAINTAIN: 'MAINTAIN'; +MANAGE_ROLE: 'MANAGE_ROLE'; +MANAGE_USER: 'MANAGE_USER'; MAP: 'MAP'; MATCH: 'MATCH'; MATCHED: 'MATCHED'; @@ -1196,6 +1318,7 @@ OVERFLOW: 'OVERFLOW'; PARTITION: 'PARTITION'; PARTITIONS: 'PARTITIONS'; PASSING: 'PASSING'; +PASSWORD: 'PASSWORD'; PAST: 'PAST'; PATH: 'PATH'; PATTERN: 'PATTERN'; @@ -1355,6 +1478,7 @@ CONCAT: '||'; QUESTION_MARK: '?'; SEMICOLON: ';'; + STRING : '\'' ( ~'\'' | '\'\'' )* '\'' ; diff --git a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift index ea0918ce83d3..7385fafd2e08 100644 --- a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift +++ b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift @@ -339,6 +339,17 @@ struct TAuthorizerReq { 8: required binary nodeNameList } +struct TAuthorizerRelationalReq { + 1: required i32 authorType + 2: required string userName + 3: required string roleName + 4: required string password + 5: required string database + 6: required string table + 7: required set permissions + 8: required bool grantOpt +} + struct TAuthorizerResp { 1: required common.TSStatus status 2: optional string tag @@ -347,20 +358,20 @@ struct TAuthorizerResp { } struct TUserResp { - 1: required string username + 1: required TRoleResp permissionInfo 2: required string password - 3: required list privilegeList - 4: required set sysPriSet - 5: required set sysPriSetGrantOpt - 6: required list roleList - 7: required bool isOpenIdUser + 3: required set roleSet + 4: required bool isOpenIdUser } struct TRoleResp { - 1: required string roleName + 1: required string name 2: required list privilegeList 3: required set sysPriSet 4: required set sysPriSetGrantOpt + 5: required map dbPrivilegeMap + 6: required set anyScopeSet + 7: required set anyScopeGrantSet } struct TPathPrivilege { @@ -369,6 +380,19 @@ struct TPathPrivilege { 3: required set priGrantOpt } +struct TTablePrivilege { + 1: required string tableName + 2: required set privileges + 3: required set grantOption +} + +struct TDBPrivilege { + 1: required string databaseName + 2: required set privileges + 3: required set grantOpt + 4: required map tablePrivilegeMap +} + struct TPermissionInfoResp { 1: required common.TSStatus status 2: optional list failPos @@ -389,11 +413,22 @@ struct TLoginReq { 2: required string password } +// reqtype : tree, relational, system +// to check tree privilege, paths is required +// to check relational privilege, database or table is required +// to check system privilege, just spec permission + +// if grant opt is true, check grant option of spec permission. +// if permission is -1, means check visible. + struct TCheckUserPrivilegesReq { 1: required string username - 2: required binary paths - 3: required i32 permission - 4: optional bool grantOpt + 2: required i32 reqtype + 3: optional binary paths + 4: optional string database + 5: optional string table + 6: required i32 permission + 7: required bool grantOpt } // ConfigNode @@ -1359,6 +1394,7 @@ service IConfigNodeRPCService { * INTERNAL_SERVER_ERROR if the permission type does not exist */ common.TSStatus operatePermission(TAuthorizerReq req) + common.TSStatus operateRPermission(TAuthorizerRelationalReq req) /** * Execute permission read operations such as list user @@ -1369,6 +1405,7 @@ service IConfigNodeRPCService { * INTERNAL_SERVER_ERROR if the permission type does not exist */ TAuthorizerResp queryPermission(TAuthorizerReq req) + TAuthorizerResp queryRPermission(TAuthorizerRelationalReq req) /** * Authenticate user login @@ -1389,8 +1426,6 @@ service IConfigNodeRPCService { TAuthizedPatternTreeResp fetchAuthizedPatternTree(TCheckUserPrivilegesReq req) - TPermissionInfoResp checkUserPrivilegeGrantOpt(TCheckUserPrivilegesReq req) - TPermissionInfoResp checkRoleOfUser(TAuthorizerReq req) From f2d24ffdc7d877be37a9819ef6b5def295d96509 Mon Sep 17 00:00:00 2001 From: Zhihao Shen Date: Fri, 24 Jan 2025 22:35:34 +0800 Subject: [PATCH 2/3] Introduce operator of window function. --- .../process/window/TableWindowOperator.java | 411 ++++++ .../window/function/WindowFunction.java | 45 + .../aggregate/AggregationWindowFunction.java | 113 ++ .../function/aggregate/WindowAggregator.java | 126 ++ .../function/rank/CumeDistFunction.java | 52 + .../function/rank/DenseRankFunction.java | 52 + .../window/function/rank/NTileFunction.java | 62 + .../function/rank/PercentRankFunction.java | 63 + .../window/function/rank/RankFunction.java | 57 + .../function/rank/RankWindowFunction.java | 66 + .../function/rank/RowNumberFunction.java | 41 + .../function/value/FirstValueFunction.java | 64 + .../window/function/value/LagFunction.java | 77 ++ .../function/value/LastValueFunction.java | 64 + .../window/function/value/LeadFunction.java | 79 ++ .../function/value/NthValueFunction.java | 81 ++ .../function/value/ValueWindowFunction.java | 52 + .../process/window/partition/Partition.java | 226 ++++ .../window/partition/PartitionExecutor.java | 197 +++ .../process/window/partition/frame/Frame.java | 26 + .../window/partition/frame/FrameInfo.java | 145 +++ .../window/partition/frame/GroupsFrame.java | 238 ++++ .../window/partition/frame/RangeFrame.java | 617 +++++++++ .../window/partition/frame/RowsFrame.java | 108 ++ .../process/window/utils/ColumnList.java | 141 +++ .../operator/process/window/utils/Range.java | 38 + .../process/window/utils/RowComparator.java | 231 ++++ .../aggregation/AvgAccumulator.java | 2 +- .../window/TableWindowOperatorTest.java | 320 +++++ .../window/TableWindowOperatorTestUtils.java | 131 ++ .../window/function/FunctionTestUtils.java | 113 ++ .../AggregationWindowFunctionTest.java | 195 +++ .../function/rank/CumeDistFunctionTest.java | 72 ++ .../function/rank/DenseRankFunctionTest.java | 71 ++ .../function/rank/NTileFunctionTest.java | 128 ++ .../rank/PercentRankFunctionTest.java | 72 ++ .../function/rank/RankFunctionTest.java | 71 ++ .../function/rank/RowNumberFunctionTest.java | 70 + .../value/FirstValueFunctionTest.java | 120 ++ .../function/value/LagFunctionTest.java | 159 +++ .../function/value/LastValueFunctionTest.java | 120 ++ .../function/value/LeadFunctionTest.java | 159 +++ .../function/value/NthValueFunctionTest.java | 150 +++ .../partition/frame/FrameTestUtils.java | 139 ++ .../partition/frame/GroupsFrameTest.java | 359 ++++++ .../partition/frame/RangeFrameTest.java | 1127 +++++++++++++++++ .../window/partition/frame/RowsFrameTest.java | 359 ++++++ 47 files changed, 7408 insertions(+), 1 deletion(-) create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperator.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/WindowFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/AggregationWindowFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/WindowAggregator.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/CumeDistFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/DenseRankFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/NTileFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/PercentRankFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankWindowFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RowNumberFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/FirstValueFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LagFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LastValueFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LeadFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/NthValueFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/ValueWindowFunction.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/Partition.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/PartitionExecutor.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/Frame.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/FrameInfo.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/GroupsFrame.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RangeFrame.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RowsFrame.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/ColumnList.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/Range.java create mode 100644 iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/RowComparator.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTestUtils.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/FunctionTestUtils.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/AggregationWindowFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/CumeDistFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/DenseRankFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/NTileFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/PercentRankFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RowNumberFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/FirstValueFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LagFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LastValueFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LeadFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/NthValueFunctionTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/FrameTestUtils.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/GroupsFrameTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RangeFrameTest.java create mode 100644 iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RowsFrameTest.java diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperator.java new file mode 100644 index 000000000000..62bf71eb585d --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperator.java @@ -0,0 +1,411 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window; + +import org.apache.iotdb.db.queryengine.execution.MemoryEstimationHelper; +import org.apache.iotdb.db.queryengine.execution.operator.Operator; +import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.db.queryengine.execution.operator.process.ProcessOperator; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.WindowFunction; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.RowComparator; +import org.apache.iotdb.db.queryengine.plan.planner.memory.MemoryReservationManager; + +import com.google.common.collect.ImmutableList; +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.common.conf.TSFileDescriptor; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.utils.RamUsageEstimator; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.concurrent.TimeUnit; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; +import static org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanGraphPrinter.MAX_RESERVED_MEMORY; + +public class TableWindowOperator implements ProcessOperator { + private static final long INSTANCE_SIZE = + RamUsageEstimator.shallowSizeOfInstance(TableWindowOperator.class); + + // Common fields + private final OperatorContext operatorContext; + private final Operator inputOperator; + private final List inputDataTypes; + private final List outputChannels; + private final TsBlockBuilder tsBlockBuilder; + private final MemoryReservationManager memoryReservationManager; + + // Basic information about window operator + private final List windowFunctions; + private final List frameInfoList; + + // Partition + private final List partitionChannels; + private final RowComparator partitionComparator; + private final List cachedTsBlocks; + private int startIndexInFirstBlock; + + // Sort + private final List sortChannels; + + // Transformation + private LinkedList cachedPartitionExecutors; + + // Misc + private long totalMemorySize; + private long maxUsedMemory; + private final long maxRuntime; + + public TableWindowOperator( + OperatorContext operatorContext, + Operator inputOperator, + List inputDataTypes, + List outputDataTypes, + List outputChannels, + List windowFunctions, + List frameInfoList, + List partitionChannels, + List sortChannels) { + // Common part(among all other operators) + this.operatorContext = operatorContext; + this.inputOperator = inputOperator; + this.inputDataTypes = ImmutableList.copyOf(inputDataTypes); + this.outputChannels = ImmutableList.copyOf(outputChannels); + this.tsBlockBuilder = new TsBlockBuilder(outputDataTypes); + + // Basic information part + this.windowFunctions = windowFunctions; + this.frameInfoList = frameInfoList; + + // Partition Part + this.partitionChannels = ImmutableList.copyOf(partitionChannels); + // Acquire partition channels' data types + List partitionDataTypes = new ArrayList<>(); + for (Integer channel : partitionChannels) { + partitionDataTypes.add(inputDataTypes.get(channel)); + } + this.partitionComparator = new RowComparator(partitionDataTypes); + + // Ordering part + this.sortChannels = ImmutableList.copyOf(sortChannels); + + // Transformation part + this.cachedPartitionExecutors = new LinkedList<>(); + + // Misc + this.cachedTsBlocks = new ArrayList<>(); + this.startIndexInFirstBlock = -1; + this.maxRuntime = this.operatorContext.getMaxRunTime().roundTo(TimeUnit.NANOSECONDS); + this.totalMemorySize = 0; + this.maxUsedMemory = 0; + this.memoryReservationManager = + operatorContext + .getDriverContext() + .getFragmentInstanceContext() + .getMemoryReservationContext(); + } + + @Override + public OperatorContext getOperatorContext() { + return operatorContext; + } + + @Override + public TsBlock next() throws Exception { + long startTime = System.nanoTime(); + + // Transform is not finished + if (!cachedPartitionExecutors.isEmpty()) { + TsBlock tsBlock = transform(startTime); + if (tsBlock != null) { + return tsBlock; + } + // Receive more data when result TsBlock builder is not full + // In this case, all partition executors are done + } + + if (inputOperator.hasNextWithTimer()) { + // This TsBlock is pre-sorted with PARTITION BY and ORDER BY channels + TsBlock preSortedBlock = inputOperator.next(); + // StreamSort Operator sometimes returns null + if (preSortedBlock == null || preSortedBlock.isEmpty()) { + return null; + } + + cachedPartitionExecutors = partition(preSortedBlock); + if (cachedPartitionExecutors.isEmpty()) { + // No partition found + // i.e., partition crosses multiple TsBlocks + return null; + } + + // May return null if builder is not full + return transform(startTime); + } else if (!cachedTsBlocks.isEmpty()) { + // Form last partition + TsBlock lastTsBlock = cachedTsBlocks.get(cachedTsBlocks.size() - 1); + int endIndexOfLastTsBlock = lastTsBlock.getPositionCount(); + PartitionExecutor partitionExecutor = + new PartitionExecutor( + cachedTsBlocks, + inputDataTypes, + startIndexInFirstBlock, + endIndexOfLastTsBlock, + outputChannels, + windowFunctions, + frameInfoList, + sortChannels); + cachedPartitionExecutors.addLast(partitionExecutor); + cachedTsBlocks.clear(); + releaseAllCachedTsBlockMemory(); + + TsBlock tsBlock = transform(startTime); + if (tsBlock == null) { + // TsBlockBuilder is not full + // Force build since this is the last partition + tsBlock = + tsBlockBuilder.build( + new RunLengthEncodedColumn( + TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + tsBlockBuilder.reset(); + } + + return tsBlock; + } else if (!tsBlockBuilder.isEmpty()) { + // Return remaining data in result TsBlockBuilder + // This happens when last partition is too large + // And TsBlockBuilder is not full at the end of transform + return getTsBlockFromTsBlockBuilder(); + } + + return null; + } + + private LinkedList partition(TsBlock tsBlock) { + LinkedList partitionExecutors = new LinkedList<>(); + + int partitionStartInCurrentBlock = 0; + int partitionEndInCurrentBlock = partitionStartInCurrentBlock + 1; + + // In this stage, we only consider partition channels + List partitionColumns = extractPartitionColumns(tsBlock); + + // Previous TsBlocks forms a partition + if (!cachedTsBlocks.isEmpty()) { + TsBlock lastTsBlock = cachedTsBlocks.get(cachedTsBlocks.size() - 1); + int endIndexOfLastTsBlock = lastTsBlock.getPositionCount(); + + // Whether the first row of current TsBlock is not equal to + // last row of previous cached TsBlocks + List lastPartitionColumns = extractPartitionColumns(lastTsBlock); + if (!partitionComparator.equal( + partitionColumns, 0, lastPartitionColumns, endIndexOfLastTsBlock - 1)) { + PartitionExecutor partitionExecutor = + new PartitionExecutor( + cachedTsBlocks, + inputDataTypes, + startIndexInFirstBlock, + endIndexOfLastTsBlock, + outputChannels, + windowFunctions, + frameInfoList, + sortChannels); + + partitionExecutors.addLast(partitionExecutor); + cachedTsBlocks.clear(); + releaseAllCachedTsBlockMemory(); + startIndexInFirstBlock = -1; + } + } + + // Try to find all partitions + int count = tsBlock.getPositionCount(); + while (count == 1 || partitionEndInCurrentBlock < count) { + // Try to find one partition + while (partitionEndInCurrentBlock < count + && partitionComparator.equalColumns( + partitionColumns, partitionStartInCurrentBlock, partitionEndInCurrentBlock)) { + partitionEndInCurrentBlock++; + } + + if (partitionEndInCurrentBlock != count) { + // Find partition + PartitionExecutor partitionExecutor; + if (partitionStartInCurrentBlock != 0 || startIndexInFirstBlock == -1) { + // Small partition within this TsBlock + partitionExecutor = + new PartitionExecutor( + Collections.singletonList(tsBlock), + inputDataTypes, + partitionStartInCurrentBlock, + partitionEndInCurrentBlock, + outputChannels, + windowFunctions, + frameInfoList, + sortChannels); + } else { + // Large partition crosses multiple TsBlocks + reserveOneTsBlockMemory(tsBlock); + cachedTsBlocks.add(tsBlock); + partitionExecutor = + new PartitionExecutor( + cachedTsBlocks, + inputDataTypes, + startIndexInFirstBlock, + partitionEndInCurrentBlock, + outputChannels, + windowFunctions, + frameInfoList, + sortChannels); + // Clear TsBlock of last partition + cachedTsBlocks.clear(); + releaseAllCachedTsBlockMemory(); + } + partitionExecutors.addLast(partitionExecutor); + + partitionStartInCurrentBlock = partitionEndInCurrentBlock; + partitionEndInCurrentBlock = partitionStartInCurrentBlock + 1; + } else { + // Last partition of TsBlock + // The beginning of next TsBlock may have rows in this partition + if (startIndexInFirstBlock == -1) { + startIndexInFirstBlock = partitionStartInCurrentBlock; + } + reserveOneTsBlockMemory(tsBlock); + cachedTsBlocks.add(tsBlock); + // For count == 1 + break; + } + } + + return partitionExecutors; + } + + private TsBlock transform(long startTime) { + while (!cachedPartitionExecutors.isEmpty()) { + PartitionExecutor partitionExecutor = cachedPartitionExecutors.getFirst(); + + while (System.nanoTime() - startTime < maxRuntime + && !tsBlockBuilder.isFull() + && partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + if (!partitionExecutor.hasNext()) { + cachedPartitionExecutors.removeFirst(); + } + + if (System.nanoTime() - startTime >= maxRuntime || tsBlockBuilder.isFull()) { + return getTsBlockFromTsBlockBuilder(); + } + } + + // Reach partition end, but builder is not full yet + return null; + } + + private List extractPartitionColumns(TsBlock tsBlock) { + List partitionColumns = new ArrayList<>(partitionChannels.size()); + for (int channel : partitionChannels) { + Column partitionColumn = tsBlock.getColumn(channel); + partitionColumns.add(partitionColumn); + } + return partitionColumns; + } + + private TsBlock getTsBlockFromTsBlockBuilder() { + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + tsBlockBuilder.reset(); + return result; + } + + @Override + public boolean hasNext() throws Exception { + return !cachedPartitionExecutors.isEmpty() + || inputOperator.hasNext() + || !cachedTsBlocks.isEmpty() + || !tsBlockBuilder.isEmpty(); + } + + @Override + public void close() throws Exception { + inputOperator.close(); + if (totalMemorySize != 0) { + memoryReservationManager.releaseMemoryCumulatively(totalMemorySize); + } + } + + @Override + public boolean isFinished() throws Exception { + return !this.hasNextWithTimer(); + } + + private void reserveOneTsBlockMemory(TsBlock tsBlock) { + long reserved = tsBlock.getTotalInstanceSize(); + memoryReservationManager.reserveMemoryCumulatively(reserved); + totalMemorySize += reserved; + maxUsedMemory = Math.max(maxUsedMemory, totalMemorySize); + operatorContext.recordSpecifiedInfo(MAX_RESERVED_MEMORY, Long.toString(maxUsedMemory)); + } + + private void releaseAllCachedTsBlockMemory() { + long released = cachedTsBlocks.stream().mapToInt(TsBlock::getTotalInstanceSize).sum(); + memoryReservationManager.releaseMemoryCumulatively(released); + totalMemorySize -= released; + // No need to update maxUsedMemory + operatorContext.recordSpecifiedInfo(MAX_RESERVED_MEMORY, Long.toString(maxUsedMemory)); + } + + @Override + public long calculateMaxPeekMemory() { + long maxPeekMemoryFromInput = inputOperator.calculateMaxPeekMemoryWithCounter(); + long maxPeekMemoryFromCurrent = + TSFileDescriptor.getInstance().getConfig().getMaxTsBlockSizeInBytes(); + return Math.max(maxPeekMemoryFromInput, maxPeekMemoryFromCurrent) + + inputOperator.calculateRetainedSizeAfterCallingNext(); + } + + @Override + public long calculateMaxReturnSize() { + return TSFileDescriptor.getInstance().getConfig().getMaxTsBlockSizeInBytes(); + } + + @Override + public long calculateRetainedSizeAfterCallingNext() { + return inputOperator.calculateRetainedSizeAfterCallingNext(); + } + + @Override + public long ramBytesUsed() { + return INSTANCE_SIZE + + MemoryEstimationHelper.getEstimatedSizeOfAccountableObject(inputOperator) + + MemoryEstimationHelper.getEstimatedSizeOfAccountableObject(operatorContext) + + tsBlockBuilder.getRetainedSizeInBytes(); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/WindowFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/WindowFunction.java new file mode 100644 index 000000000000..acaf72da8565 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/WindowFunction.java @@ -0,0 +1,45 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public interface WindowFunction { + void reset(); + + void transform( + Partition partition, + ColumnBuilder builder, + int index, + int frameStart, + int frameEnd, + int peerGroupStart, + int peerGroupEnd); + + default boolean needPeerGroup() { + return true; + } + + default boolean needFrame() { + return true; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/AggregationWindowFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/AggregationWindowFunction.java new file mode 100644 index 000000000000..b8f5a95f3fa3 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/AggregationWindowFunction.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.aggregate; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.WindowFunction; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class AggregationWindowFunction implements WindowFunction { + private final WindowAggregator aggregator; + private int currentStart; + private int currentEnd; + + public AggregationWindowFunction(WindowAggregator aggregator) { + this.aggregator = aggregator; + reset(); + } + + @Override + public void reset() { + aggregator.reset(); + currentStart = -1; + currentEnd = -1; + } + + @Override + public void transform( + Partition partition, + ColumnBuilder builder, + int index, + int frameStart, + int frameEnd, + int peerGroupStart, + int peerGroupEnd) { + if (frameStart < 0) { + // Empty frame + reset(); + } else if (frameStart == currentStart && frameEnd >= currentEnd) { + // Frame expansion + if (frameEnd != currentEnd) { + Partition region = partition.getRegion(currentEnd + 1, frameEnd); + aggregator.addInput(region); + currentEnd = frameEnd; + } + } else { + buildNewFrame(partition, frameStart, frameEnd); + } + + aggregator.evaluate(builder); + } + + private void buildNewFrame(Partition partition, int frameStart, int frameEnd) { + if (aggregator.removable()) { + int prefix = Math.abs(currentStart - frameStart); + int suffix = Math.abs(currentEnd - frameEnd); + int frameLength = frameEnd - frameStart + 1; + + // Compare remove && add cost with re-computation + if (frameLength > prefix + suffix) { + if (currentStart < frameStart) { + Partition region = partition.getRegion(currentStart, frameStart - 1); + aggregator.removeInput(region); + } else if (currentStart > frameStart) { + Partition region = partition.getRegion(frameStart, currentStart - 1); + aggregator.addInput(region); + } // Do nothing when currentStart == frameStart + + if (frameEnd < currentEnd) { + Partition region = partition.getRegion(frameEnd + 1, currentEnd); + aggregator.removeInput(region); + } else if (frameEnd > currentEnd) { + Partition region = partition.getRegion(currentEnd + 1, frameEnd); + aggregator.addInput(region); + } // Do nothing when frameEnd == currentEnd + + currentStart = frameStart; + currentEnd = frameEnd; + return; + } + } + + // Re-compute + aggregator.reset(); + Partition region = partition.getRegion(frameStart, frameEnd); + aggregator.addInput(region); + + currentStart = frameStart; + currentEnd = frameEnd; + } + + @Override + public boolean needPeerGroup() { + return false; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/WindowAggregator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/WindowAggregator.java new file mode 100644 index 000000000000..5d540db57442 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/WindowAggregator.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.aggregate; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; +import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.AggregationMask; +import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.TableAccumulator; + +import com.google.common.primitives.Ints; +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.file.metadata.statistics.Statistics; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.List; + +import static java.util.Objects.requireNonNull; +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class WindowAggregator { + private final TableAccumulator accumulator; + private final TSDataType outputType; + private final int[] inputChannels; + + public WindowAggregator( + TableAccumulator accumulator, TSDataType outputType, List inputChannels) { + this.accumulator = requireNonNull(accumulator, "accumulator is null"); + this.outputType = requireNonNull(outputType, "intermediateType is null"); + this.inputChannels = Ints.toArray(requireNonNull(inputChannels, "inputChannels is null")); + } + + public TSDataType getType() { + return outputType; + } + + public void addInput(Partition partition) { + List allColumns = partition.getAllColumns(); + for (Column[] columns : allColumns) { + addInput(columns); + } + } + + public void addInput(Column[] columns) { + Column[] arguments = new Column[inputChannels.length]; + for (int i = 0; i < inputChannels.length; i++) { + arguments[i] = columns[inputChannels[i]]; + } + + // Process count(*) + int count = columns[0].getPositionCount(); + if (arguments.length == 0) { + arguments = new Column[] {new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, count)}; + } + + AggregationMask mask = AggregationMask.createSelectAll(count); + accumulator.addInput(arguments, mask); + } + + public void removeInput(Partition partition) { + List allColumns = partition.getAllColumns(); + for (Column[] columns : allColumns) { + removeInput(columns); + } + } + + private void removeInput(Column[] columns) { + Column[] arguments = new Column[inputChannels.length]; + for (int i = 0; i < inputChannels.length; i++) { + arguments[i] = columns[inputChannels[i]]; + } + + // Process count(*) + int count = columns[0].getPositionCount(); + if (arguments.length == 0) { + arguments = new Column[] {new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, count)}; + } + + accumulator.removeInput(arguments); + } + + public void evaluate(ColumnBuilder columnBuilder) { + accumulator.evaluateFinal(columnBuilder); + } + + public void processStatistics(Statistics[] statistics) { + accumulator.addStatistics(statistics); + } + + public boolean hasFinalResult() { + return accumulator.hasFinalResult(); + } + + public void reset() { + accumulator.reset(); + } + + public boolean removable() { + return accumulator.removable(); + } + + public long getEstimatedSize() { + return accumulator.getEstimatedSize(); + } + + public int getChannelCount() { + return this.inputChannels.length; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/CumeDistFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/CumeDistFunction.java new file mode 100644 index 000000000000..e4745bd0ad54 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/CumeDistFunction.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class CumeDistFunction extends RankWindowFunction { + private long count; + + public CumeDistFunction() { + reset(); + } + + @Override + public void reset() { + super.reset(); + count = 0; + } + + @Override + public void transform( + Partition partition, + ColumnBuilder builder, + int index, + boolean isNewPeerGroup, + int peerGroupCount) { + if (isNewPeerGroup) { + count += peerGroupCount; + } + + builder.writeDouble(((double) count) / partition.getPositionCount()); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/DenseRankFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/DenseRankFunction.java new file mode 100644 index 000000000000..ac16fcf66f08 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/DenseRankFunction.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class DenseRankFunction extends RankWindowFunction { + private long rank; + + public DenseRankFunction() { + reset(); + } + + @Override + public void reset() { + super.reset(); + rank = 0; + } + + @Override + public void transform( + Partition partition, + ColumnBuilder builder, + int index, + boolean isNewPeerGroup, + int peerGroupCount) { + if (isNewPeerGroup) { + rank++; + } + + builder.writeLong(rank); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/NTileFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/NTileFunction.java new file mode 100644 index 000000000000..48b70cc8ba69 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/NTileFunction.java @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class NTileFunction extends RankWindowFunction { + private final int n; + + public NTileFunction(int n) { + this.n = n; + } + + @Override + public void transform( + Partition partition, + ColumnBuilder builder, + int index, + boolean isNewPeerGroup, + int peerGroupCount) { + builder.writeLong(bucket(n, index, partition.getPositionCount()) + 1); + } + + private long bucket(long buckets, int index, int count) { + if (count < buckets) { + return index; + } + + long remainderRows = count % buckets; + long rowsPerBucket = count / buckets; + + if (index < ((rowsPerBucket + 1) * remainderRows)) { + return index / (rowsPerBucket + 1); + } + + return (index - remainderRows) / rowsPerBucket; + } + + @Override + public boolean needPeerGroup() { + return false; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/PercentRankFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/PercentRankFunction.java new file mode 100644 index 000000000000..35741eb4078b --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/PercentRankFunction.java @@ -0,0 +1,63 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class PercentRankFunction extends RankWindowFunction { + private long rank; + private long count; + + public PercentRankFunction() { + reset(); + } + + @Override + public void reset() { + super.reset(); + rank = 0; + count = 1; + } + + @Override + public void transform( + Partition partition, + ColumnBuilder builder, + int index, + boolean isNewPeerGroup, + int peerGroupCount) { + int total = partition.getPositionCount(); + if (total == 1) { + builder.writeDouble(0); + return; + } + + if (isNewPeerGroup) { + rank += count; + count = 1; + } else { + count++; + } + + builder.writeDouble(((double) (rank - 1)) / (total - 1)); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankFunction.java new file mode 100644 index 000000000000..c6fbf710e725 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankFunction.java @@ -0,0 +1,57 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class RankFunction extends RankWindowFunction { + private long rank; + private long count; + + public RankFunction() { + reset(); + } + + @Override + public void reset() { + super.reset(); + rank = 0; + count = 1; + } + + @Override + public void transform( + Partition partition, + ColumnBuilder builder, + int index, + boolean isNewPeerGroup, + int peerGroupCount) { + if (isNewPeerGroup) { + rank += count; + count = 1; + } else { + count++; + } + + builder.writeLong(rank); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankWindowFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankWindowFunction.java new file mode 100644 index 000000000000..b2c672afbea6 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankWindowFunction.java @@ -0,0 +1,66 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.WindowFunction; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public abstract class RankWindowFunction implements WindowFunction { + private int currentPeerGroupStart; + + @Override + public void reset() { + currentPeerGroupStart = -1; + } + + @Override + public void transform( + Partition partition, + ColumnBuilder builder, + int index, + int frameStart, + int frameEnd, + int peerGroupStart, + int peerGroupEnd) { + boolean isNewPeerGroup = false; + if (peerGroupStart != currentPeerGroupStart) { + currentPeerGroupStart = peerGroupStart; + isNewPeerGroup = true; + } + + int peerGroupCount = (peerGroupEnd - peerGroupStart) + 1; + + transform(partition, builder, index, isNewPeerGroup, peerGroupCount); + } + + public abstract void transform( + Partition partition, + ColumnBuilder builder, + int index, + boolean isNewPeerGroup, + int peerGroupCount); + + @Override + public boolean needFrame() { + return false; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RowNumberFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RowNumberFunction.java new file mode 100644 index 000000000000..67a790557bcb --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RowNumberFunction.java @@ -0,0 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class RowNumberFunction extends RankWindowFunction { + @Override + public void transform( + Partition partition, + ColumnBuilder builder, + int index, + boolean isNewPeerGroup, + int peerGroupCount) { + builder.writeLong((long) index + 1); + } + + @Override + public boolean needPeerGroup() { + return false; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/FirstValueFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/FirstValueFunction.java new file mode 100644 index 000000000000..465ec2920fc2 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/FirstValueFunction.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class FirstValueFunction extends ValueWindowFunction { + private final int channel; + private final boolean ignoreNull; + + public FirstValueFunction(int channel, boolean ignoreNull) { + this.channel = channel; + this.ignoreNull = ignoreNull; + } + + @Override + public void transform( + Partition partition, ColumnBuilder builder, int index, int frameStart, int frameEnd) { + // Empty frame + if (frameStart < 0) { + builder.appendNull(); + return; + } + + if (ignoreNull) { + // Handle nulls + int pos = frameStart; + while (pos <= frameEnd && partition.isNull(channel, pos)) { + pos++; + } + + if (pos > frameEnd) { + builder.appendNull(); + } else { + partition.writeTo(builder, channel, pos); + } + } else { + if (partition.isNull(channel, frameStart)) { + builder.appendNull(); + } else { + partition.writeTo(builder, channel, frameStart); + } + } + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LagFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LagFunction.java new file mode 100644 index 000000000000..cfa48742f924 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LagFunction.java @@ -0,0 +1,77 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class LagFunction extends ValueWindowFunction { + private final int channel; + private final Integer offset; + private final Object defaultVal; + private final boolean ignoreNull; + + public LagFunction(int channel, Integer offset, Object defaultVal, boolean ignoreNull) { + this.channel = channel; + this.offset = offset == null ? 1 : offset; + this.defaultVal = defaultVal; + this.ignoreNull = ignoreNull; + } + + @Override + public void transform( + Partition partition, ColumnBuilder builder, int index, int frameStart, int frameEnd) { + int pos; + if (ignoreNull) { + int nonNullCount = 0; + pos = index - 1; + while (pos >= 0) { + if (!partition.isNull(channel, pos)) { + nonNullCount++; + if (nonNullCount == offset) { + break; + } + } + + pos--; + } + } else { + pos = index - offset; + } + + if (pos >= 0) { + if (!partition.isNull(channel, pos)) { + partition.writeTo(builder, channel, pos); + } else { + builder.appendNull(); + } + } else if (defaultVal != null) { + builder.writeObject(defaultVal); + } else { + builder.appendNull(); + } + } + + @Override + public boolean needFrame() { + return false; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LastValueFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LastValueFunction.java new file mode 100644 index 000000000000..1c929d035be3 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LastValueFunction.java @@ -0,0 +1,64 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class LastValueFunction extends ValueWindowFunction { + private final int channel; + private final boolean ignoreNull; + + public LastValueFunction(int channel, boolean ignoreNull) { + this.channel = channel; + this.ignoreNull = ignoreNull; + } + + @Override + public void transform( + Partition partition, ColumnBuilder builder, int index, int frameStart, int frameEnd) { + // Empty frame + if (frameStart < 0) { + builder.appendNull(); + return; + } + + if (ignoreNull) { + // Handle nulls + int pos = frameEnd; + while (pos >= frameStart && partition.isNull(channel, pos)) { + pos--; + } + + if (pos < frameStart) { + builder.appendNull(); + } else { + partition.writeTo(builder, channel, pos); + } + } else { + if (partition.isNull(channel, frameEnd)) { + builder.appendNull(); + } else { + partition.writeTo(builder, channel, frameEnd); + } + } + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LeadFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LeadFunction.java new file mode 100644 index 000000000000..514357df57a9 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LeadFunction.java @@ -0,0 +1,79 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class LeadFunction extends ValueWindowFunction { + private final int channel; + private final Integer offset; + private final Integer defaultVal; + private final boolean ignoreNull; + + public LeadFunction(int channel, Integer offset, Integer defaultVal, boolean ignoreNull) { + this.channel = channel; + this.offset = offset == null ? 1 : offset; + this.defaultVal = defaultVal; + this.ignoreNull = ignoreNull; + } + + @Override + public void transform( + Partition partition, ColumnBuilder builder, int index, int frameStart, int frameEnd) { + int length = partition.getPositionCount(); + + int pos; + if (ignoreNull) { + int nonNullCount = 0; + pos = index + 1; + while (pos < length) { + if (!partition.isNull(channel, pos)) { + nonNullCount++; + if (nonNullCount == offset) { + break; + } + } + + pos++; + } + } else { + pos = index + offset; + } + + if (pos < length) { + if (!partition.isNull(channel, pos)) { + partition.writeTo(builder, channel, pos); + } else { + builder.appendNull(); + } + } else if (defaultVal != null) { + builder.writeObject(defaultVal); + } else { + builder.appendNull(); + } + } + + @Override + public boolean needFrame() { + return false; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/NthValueFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/NthValueFunction.java new file mode 100644 index 000000000000..8978b5f13b61 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/NthValueFunction.java @@ -0,0 +1,81 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public class NthValueFunction extends ValueWindowFunction { + private final int n; + private final int channel; + private final boolean ignoreNull; + + public NthValueFunction(int n, int channel, boolean ignoreNull) { + this.n = n; + this.channel = channel; + this.ignoreNull = ignoreNull; + } + + @Override + public void transform( + Partition partition, ColumnBuilder builder, int index, int frameStart, int frameEnd) { + // Empty frame + if (frameStart < 0) { + builder.appendNull(); + return; + } + + int pos; + if (ignoreNull) { + // Handle nulls + pos = frameStart; + int nonNullCount = 0; + while (pos <= frameEnd) { + if (!partition.isNull(channel, pos)) { + nonNullCount++; + if (nonNullCount == n) { + break; + } + } + pos++; + } + + if (pos <= frameEnd) { + partition.writeTo(builder, channel, pos); + } else { + builder.appendNull(); + } + return; + } + + // n starts with 1 + pos = frameStart + n - 1; + if (pos <= frameEnd) { + if (!partition.isNull(channel, pos)) { + partition.writeTo(builder, channel, pos); + } else { + builder.appendNull(); + } + } else { + builder.appendNull(); + } + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/ValueWindowFunction.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/ValueWindowFunction.java new file mode 100644 index 000000000000..45e5b9def92b --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/ValueWindowFunction.java @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.WindowFunction; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; + +import org.apache.tsfile.block.column.ColumnBuilder; + +public abstract class ValueWindowFunction implements WindowFunction { + @Override + public void reset() { + // do nothing, value functions are stateless + } + + @Override + public void transform( + Partition partition, + ColumnBuilder builder, + int index, + int frameStart, + int frameEnd, + int peerGroupStart, + int peerGroupEnd) { + transform(partition, builder, index, frameStart, frameEnd); + } + + public abstract void transform( + Partition partition, ColumnBuilder builder, int index, int frameStart, int frameEnd); + + @Override + public boolean needPeerGroup() { + return false; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/Partition.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/Partition.java new file mode 100644 index 000000000000..b8acd7fae11f --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/Partition.java @@ -0,0 +1,226 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.ColumnList; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.utils.Binary; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Partition { + private final List tsBlocks; + private int cachedPositionCount = -1; + + public Partition(List tsBlocks, int startIndexInFirstBlock, int endIndexInLastBlock) { + if (tsBlocks.size() == 1) { + int length = endIndexInLastBlock - startIndexInFirstBlock; + this.tsBlocks = + Collections.singletonList(tsBlocks.get(0).getRegion(startIndexInFirstBlock, length)); + return; + } + + this.tsBlocks = new ArrayList<>(tsBlocks.size()); + // First TsBlock + TsBlock firstBlock = tsBlocks.get(0).subTsBlock(startIndexInFirstBlock); + this.tsBlocks.add(firstBlock); + // Middle TsBlock + for (int i = 1; i < tsBlocks.size() - 1; i++) { + this.tsBlocks.add(tsBlocks.get(i)); + } + // Last TsBlock + TsBlock lastBlock = tsBlocks.get(tsBlocks.size() - 1).getRegion(0, endIndexInLastBlock); + this.tsBlocks.add(lastBlock); + } + + public int getPositionCount() { + if (cachedPositionCount == -1) { + // Lazy initialized + cachedPositionCount = 0; + for (TsBlock block : tsBlocks) { + cachedPositionCount += block.getPositionCount(); + } + } + + return cachedPositionCount; + } + + public int getValueColumnCount() { + return tsBlocks.get(0).getValueColumnCount(); + } + + public TsBlock getTsBlock(int tsBlockIndex) { + return tsBlocks.get(tsBlockIndex); + } + + public List getAllColumns() { + List allColumns = new ArrayList<>(); + for (TsBlock block : tsBlocks) { + allColumns.add(block.getAllColumns()); + } + + return allColumns; + } + + public boolean getBoolean(int channel, int rowIndex) { + PartitionIndex partitionIndex = getPartitionIndex(rowIndex); + int tsBlockIndex = partitionIndex.getTsBlockIndex(); + int offsetInTsBlock = partitionIndex.getOffsetInTsBlock(); + + TsBlock tsBlock = tsBlocks.get(tsBlockIndex); + return tsBlock.getColumn(channel).getBoolean(offsetInTsBlock); + } + + public int getInt(int channel, int rowIndex) { + PartitionIndex partitionIndex = getPartitionIndex(rowIndex); + int tsBlockIndex = partitionIndex.getTsBlockIndex(); + int offsetInTsBlock = partitionIndex.getOffsetInTsBlock(); + + TsBlock tsBlock = tsBlocks.get(tsBlockIndex); + return tsBlock.getColumn(channel).getInt(offsetInTsBlock); + } + + public long getLong(int channel, int rowIndex) { + PartitionIndex partitionIndex = getPartitionIndex(rowIndex); + int tsBlockIndex = partitionIndex.getTsBlockIndex(); + int offsetInTsBlock = partitionIndex.getOffsetInTsBlock(); + + TsBlock tsBlock = tsBlocks.get(tsBlockIndex); + return tsBlock.getColumn(channel).getLong(offsetInTsBlock); + } + + public float getFloat(int channel, int rowIndex) { + PartitionIndex partitionIndex = getPartitionIndex(rowIndex); + int tsBlockIndex = partitionIndex.getTsBlockIndex(); + int offsetInTsBlock = partitionIndex.getOffsetInTsBlock(); + + TsBlock tsBlock = tsBlocks.get(tsBlockIndex); + return tsBlock.getColumn(channel).getFloat(offsetInTsBlock); + } + + public double getDouble(int channel, int rowIndex) { + PartitionIndex partitionIndex = getPartitionIndex(rowIndex); + int tsBlockIndex = partitionIndex.getTsBlockIndex(); + int offsetInTsBlock = partitionIndex.getOffsetInTsBlock(); + + TsBlock tsBlock = tsBlocks.get(tsBlockIndex); + return tsBlock.getColumn(channel).getDouble(offsetInTsBlock); + } + + public Binary getBinary(int channel, int rowIndex) { + PartitionIndex partitionIndex = getPartitionIndex(rowIndex); + int tsBlockIndex = partitionIndex.getTsBlockIndex(); + int offsetInTsBlock = partitionIndex.getOffsetInTsBlock(); + + TsBlock tsBlock = tsBlocks.get(tsBlockIndex); + return tsBlock.getColumn(channel).getBinary(offsetInTsBlock); + } + + public boolean isNull(int channel, int rowIndex) { + PartitionIndex partitionIndex = getPartitionIndex(rowIndex); + int tsBlockIndex = partitionIndex.getTsBlockIndex(); + int offsetInTsBlock = partitionIndex.getOffsetInTsBlock(); + + TsBlock tsBlock = tsBlocks.get(tsBlockIndex); + return tsBlock.getColumn(channel).isNull(offsetInTsBlock); + } + + public void writeTo(ColumnBuilder builder, int channel, int rowIndex) { + PartitionIndex partitionIndex = getPartitionIndex(rowIndex); + int tsBlockIndex = partitionIndex.getTsBlockIndex(); + int offsetInTsBlock = partitionIndex.getOffsetInTsBlock(); + + Column column = tsBlocks.get(tsBlockIndex).getColumn(channel); + builder.write(column, offsetInTsBlock); + } + + public static class PartitionIndex { + private final int tsBlockIndex; + private final int offsetInTsBlock; + + PartitionIndex(int tsBlockIndex, int offsetInTsBlock) { + this.tsBlockIndex = tsBlockIndex; + this.offsetInTsBlock = offsetInTsBlock; + } + + public int getTsBlockIndex() { + return tsBlockIndex; + } + + public int getOffsetInTsBlock() { + return offsetInTsBlock; + } + } + + // start and end are indexes within partition + // Both of them are inclusive, i.e. [start, end] + public Partition getRegion(int start, int end) { + PartitionIndex startPartitionIndex = getPartitionIndex(start); + PartitionIndex endPartitionIndex = getPartitionIndex(end); + + List tsBlockList = new ArrayList<>(); + int startTsBlockIndex = startPartitionIndex.getTsBlockIndex(); + int endTsBlockIndex = endPartitionIndex.getTsBlockIndex(); + for (int i = startTsBlockIndex; i <= endTsBlockIndex; i++) { + tsBlockList.add(tsBlocks.get(i)); + } + + int startIndexInFirstBlock = startPartitionIndex.getOffsetInTsBlock(); + int endIndexInLastBlock = endPartitionIndex.getOffsetInTsBlock(); + return new Partition(tsBlockList, startIndexInFirstBlock, endIndexInLastBlock + 1); + } + + // rowIndex is index within partition + public PartitionIndex getPartitionIndex(int rowIndex) { + int tsBlockIndex = 0; + while (tsBlockIndex < tsBlocks.size() + && rowIndex >= tsBlocks.get(tsBlockIndex).getPositionCount()) { + rowIndex -= tsBlocks.get(tsBlockIndex).getPositionCount(); + // Enter next TsBlock + tsBlockIndex++; + } + + if (tsBlockIndex != tsBlocks.size()) { + return new PartitionIndex(tsBlockIndex, rowIndex); + } else { + // Unlikely + throw new IndexOutOfBoundsException("Index out of Partition's bounds!"); + } + } + + public List getSortedColumnList(List sortedChannels) { + List columnLists = new ArrayList<>(); + + for (Integer sortedChannel : sortedChannels) { + List columns = new ArrayList<>(); + for (TsBlock tsBlock : tsBlocks) { + columns.add(tsBlock.getColumn(sortedChannel)); + } + columnLists.add(new ColumnList(columns)); + } + + return columnLists; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/PartitionExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/PartitionExecutor.java new file mode 100644 index 000000000000..b7a4239a314b --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/PartitionExecutor.java @@ -0,0 +1,197 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.WindowFunction; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.Frame; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.GroupsFrame; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.RangeFrame; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.RowsFrame; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.ColumnList; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.Range; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.RowComparator; + +import com.google.common.collect.ImmutableList; +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; + +import java.util.ArrayList; +import java.util.List; + +public final class PartitionExecutor { + private final int partitionStart; + private final int partitionEnd; + private final Partition partition; + + private final List windowFunctions; + + private final List sortedColumns; + private final RowComparator peerGroupComparator; + private int peerGroupStart; + private int peerGroupEnd; + + private final List outputChannels; + + private int currentGroupIndex = -1; + private int currentPosition; + + private final List frames; + + private final boolean needPeerGroup; + + public PartitionExecutor( + List tsBlocks, + List dataTypes, + int startIndexInFirstBlock, + int endIndexInLastBlock, + List outputChannels, + List windowFunctions, + List frameInfoList, + List sortChannels) { + // Partition + this.partition = new Partition(tsBlocks, startIndexInFirstBlock, endIndexInLastBlock); + this.partitionStart = startIndexInFirstBlock; + this.partitionEnd = startIndexInFirstBlock + this.partition.getPositionCount(); + // Window functions and frames + this.windowFunctions = ImmutableList.copyOf(windowFunctions); + this.frames = new ArrayList<>(); + + this.outputChannels = ImmutableList.copyOf(outputChannels); + + // Prepare for peer group comparing + List sortDataTypes = new ArrayList<>(); + for (int channel : sortChannels) { + TSDataType dataType = dataTypes.get(channel); + sortDataTypes.add(dataType); + } + peerGroupComparator = new RowComparator(sortDataTypes); + sortedColumns = partition.getSortedColumnList(sortChannels); + + // Reset functions for new partition + for (WindowFunction windowFunction : windowFunctions) { + windowFunction.reset(); + } + + currentPosition = partitionStart; + needPeerGroup = + windowFunctions.stream().anyMatch(WindowFunction::needPeerGroup) + || frameInfoList.stream() + .anyMatch(frameInfo -> frameInfo.getFrameType() != FrameInfo.FrameType.ROWS); + if (needPeerGroup) { + updatePeerGroup(); + } + + for (int i = 0; i < frameInfoList.size(); i++) { + Frame frame = null; + if (windowFunctions.get(i).needFrame()) { + FrameInfo frameInfo = frameInfoList.get(i); + switch (frameInfo.getFrameType()) { + case RANGE: + frame = new RangeFrame(partition, frameInfo, sortedColumns, peerGroupComparator); + break; + case ROWS: + frame = new RowsFrame(partition, frameInfo, partitionStart, partitionEnd); + break; + case GROUPS: + frame = + new GroupsFrame( + partition, + frameInfo, + sortedColumns, + peerGroupComparator, + peerGroupEnd - partitionStart - 1); + break; + default: + // Unreachable + throw new UnsupportedOperationException("Unreachable!"); + } + } + frames.add(frame); + } + } + + public boolean hasNext() { + return currentPosition < partitionEnd; + } + + public void processNextRow(TsBlockBuilder builder) { + // Copy origin data + int index = currentPosition - partitionStart; + Partition.PartitionIndex partitionIndex = partition.getPartitionIndex(index); + int tsBlockIndex = partitionIndex.getTsBlockIndex(); + int offsetInTsBlock = partitionIndex.getOffsetInTsBlock(); + TsBlock tsBlock = partition.getTsBlock(tsBlockIndex); + + int channel = 0; + for (int i = 0; i < outputChannels.size(); i++) { + Column column = tsBlock.getColumn(outputChannels.get(i)); + ColumnBuilder columnBuilder = builder.getColumnBuilder(i); + columnBuilder.write(column, offsetInTsBlock); + channel++; + } + + if (needPeerGroup && currentPosition == peerGroupEnd) { + updatePeerGroup(); + } + + for (int i = 0; i < windowFunctions.size(); i++) { + Frame frame = frames.get(i); + WindowFunction windowFunction = windowFunctions.get(i); + + Range frameRange = + windowFunction.needFrame() + ? frame.getRange( + index, + currentGroupIndex, + peerGroupStart - partitionStart, + peerGroupEnd - partitionStart) + : new Range(-1, -1); + windowFunction.transform( + partition, + builder.getColumnBuilder(channel), + currentPosition - partitionStart, + frameRange.getStart(), + frameRange.getEnd(), + peerGroupStart - partitionStart, + peerGroupEnd - partitionStart - 1); + + channel++; + } + + currentPosition++; + builder.declarePosition(); + } + + private void updatePeerGroup() { + currentGroupIndex++; + peerGroupStart = currentPosition; + // Find end of peer group + peerGroupEnd = peerGroupStart + 1; + while (peerGroupEnd < partitionEnd + && peerGroupComparator.equalColumnLists( + sortedColumns, peerGroupStart - partitionStart, peerGroupEnd - partitionStart)) { + peerGroupEnd++; + } + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/Frame.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/Frame.java new file mode 100644 index 000000000000..b697420443c5 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/Frame.java @@ -0,0 +1,26 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.Range; + +public interface Frame { + Range getRange(int currentPosition, int currentGroup, int peerGroupStart, int peerGroupEnd); +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/FrameInfo.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/FrameInfo.java new file mode 100644 index 000000000000..eac6126e4981 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/FrameInfo.java @@ -0,0 +1,145 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame; + +import org.apache.iotdb.db.queryengine.plan.relational.planner.SortOrder; + +public class FrameInfo { + public enum FrameType { + RANGE, + ROWS, + GROUPS + } + + public enum FrameBoundType { + UNBOUNDED_PRECEDING, + PRECEDING, + CURRENT_ROW, + FOLLOWING, + UNBOUNDED_FOLLOWING; + } + + private final FrameType frameType; + private final FrameBoundType startType; + private final int startOffsetChannel; // For PRECEDING and FOLLOWING use + private final FrameBoundType endType; + private final int endOffsetChannel; // Same as startOffset + // For RANGE type frame + private final int sortChannel; + private final SortOrder sortOrder; + + public FrameInfo(FrameType frameType, FrameBoundType startType, FrameBoundType endType) { + this(frameType, startType, -1, endType, -1); + } + + public FrameInfo( + FrameType frameType, + FrameBoundType startType, + int startOffsetChannel, + FrameBoundType endType) { + this(frameType, startType, startOffsetChannel, endType, -1); + } + + public FrameInfo( + FrameType frameType, + FrameBoundType startType, + int startOffsetChannel, + FrameBoundType endType, + int sortChannel, + SortOrder sortOrder) { + this(frameType, startType, startOffsetChannel, endType, -1, sortChannel, sortOrder); + } + + public FrameInfo( + FrameType frameType, FrameBoundType startType, FrameBoundType endType, int endOffsetChannel) { + this(frameType, startType, -1, endType, endOffsetChannel); + } + + public FrameInfo( + FrameType frameType, + FrameBoundType startType, + FrameBoundType endType, + int endOffsetChannel, + int sortChannel, + SortOrder sortOrder) { + this(frameType, startType, -1, endType, endOffsetChannel, sortChannel, sortOrder); + } + + public FrameInfo( + FrameType frameType, + FrameBoundType startType, + int startOffsetChannel, + FrameBoundType endType, + int endOffsetChannel) { + this( + frameType, + startType, + startOffsetChannel, + endType, + endOffsetChannel, + -1, + SortOrder.ASC_NULLS_FIRST); + } + + public FrameInfo( + FrameType frameType, + FrameBoundType startType, + int startOffsetChannel, + FrameBoundType endType, + int endOffsetChannel, + int sortChannel, + SortOrder sortOrder) { + this.frameType = frameType; + this.startType = startType; + this.startOffsetChannel = startOffsetChannel; + this.endType = endType; + this.endOffsetChannel = endOffsetChannel; + this.sortChannel = sortChannel; + this.sortOrder = sortOrder; + } + + public FrameType getFrameType() { + return frameType; + } + + public FrameBoundType getStartType() { + return startType; + } + + public FrameBoundType getEndType() { + return endType; + } + + public int getStartOffsetChannel() { + return startOffsetChannel; + } + + public int getEndOffsetChannel() { + return endOffsetChannel; + } + + public int getSortChannel() { + return sortChannel; + } + + public SortOrder getSortOrder() { + return sortOrder; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/GroupsFrame.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/GroupsFrame.java new file mode 100644 index 000000000000..3e9b111301da --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/GroupsFrame.java @@ -0,0 +1,238 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame; + +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.ColumnList; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.Range; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.RowComparator; + +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; + +public class GroupsFrame implements Frame { + private final Partition partition; + private final FrameInfo frameInfo; + private final int partitionSize; + + private final List columns; + private final RowComparator peerGroupComparator; + + private Range recentRange; + private int recentStartPeerGroup; + private int recentEndPeerGroup; + private boolean frameStartFollowingReachEnd = false; + + public GroupsFrame( + Partition partition, + FrameInfo frameInfo, + List sortedColumns, + RowComparator peerGroupComparator, + int initialEnd) { + this.partition = partition; + this.frameInfo = frameInfo; + this.partitionSize = partition.getPositionCount(); + this.columns = sortedColumns; + this.peerGroupComparator = peerGroupComparator; + + this.recentRange = new Range(0, initialEnd); + this.recentStartPeerGroup = 0; + this.recentEndPeerGroup = 0; + } + + @Override + public Range getRange( + int currentPosition, int currentGroup, int peerGroupStart, int peerGroupEnd) { + int frameStart; + switch (frameInfo.getStartType()) { + case UNBOUNDED_PRECEDING: + frameStart = 0; + break; + case PRECEDING: + frameStart = getStartPrecedingOffset(currentPosition, currentGroup); + break; + case CURRENT_ROW: + frameStart = peerGroupStart; + break; + case FOLLOWING: + frameStart = getStartFollowingOffset(currentPosition, currentGroup); + break; + default: + // UNBOUND_FOLLOWING is not allowed in frame start + throw new SemanticException("UNBOUND FOLLOWING is not allowed in frame start!"); + } + + int frameEnd; + switch (frameInfo.getEndType()) { + case PRECEDING: + frameEnd = getEndPrecedingOffset(currentPosition, currentGroup); + break; + case CURRENT_ROW: + frameEnd = peerGroupEnd - 1; + break; + case FOLLOWING: + frameEnd = getEndFollowingOffset(currentPosition, currentGroup); + break; + case UNBOUNDED_FOLLOWING: + frameEnd = partitionSize - 1; + break; + default: + // UNBOUND_PRECEDING is not allowed in frame end + throw new SemanticException("UNBOUND PRECEDING is not allowed in frame end!"); + } + + // Empty frame + if (frameEnd < frameStart || frameEnd < 0 || frameStart >= partitionSize) { + return new Range(-1, -1); + } + + frameStart = Math.max(frameStart, 0); + frameEnd = Math.min(frameEnd, partitionSize - 1); + recentRange = new Range(frameStart, frameEnd); + return recentRange; + } + + private int getStartPrecedingOffset(int currentPosition, int currentGroup) { + int start = recentRange.getStart(); + int offset = (int) getOffset(frameInfo.getStartOffsetChannel(), currentPosition); + + // We may encounter empty frame + if (currentGroup - offset < 0) { + return -1; + } + + if (currentGroup - offset > recentStartPeerGroup) { + int count = currentGroup - offset - recentStartPeerGroup; + for (int i = 0; i < count; i++) { + // Scan over current peer group + start = scanPeerGroup(start); + // Enter next peer group(won't reach partition end) + start++; + } + recentStartPeerGroup = currentGroup - offset; + } + + return start; + } + + private int getEndPrecedingOffset(int currentPosition, int currentGroup) { + int end = recentRange.getEnd(); + int offset = (int) getOffset(frameInfo.getEndOffsetChannel(), currentPosition); + + // We may encounter empty frame + if (currentGroup - offset < 0) { + return -1; + } + + if (currentGroup - offset > recentEndPeerGroup) { + int count = currentGroup - offset - recentEndPeerGroup; + for (int i = 0; i < count; i++) { + // Enter next peer group + end++; + // Scan over current peer group(won't reach partition end) + end = scanPeerGroup(end); + } + recentEndPeerGroup = currentGroup - offset; + } + + return end; + } + + private int getStartFollowingOffset(int currentPosition, int currentGroup) { + // Shortcut if we have reached last peer group already + if (frameStartFollowingReachEnd) { + return partitionSize; + } + + int start = recentRange.getStart(); + + int offset = (int) getOffset(frameInfo.getStartOffsetChannel(), currentPosition); + if (currentGroup + offset > recentStartPeerGroup) { + int count = currentGroup + offset - recentStartPeerGroup; + for (int i = 0; i < count; i++) { + // Scan over current peer group + start = scanPeerGroup(start); + // Enter next peer group + if (start == partitionSize - 1) { + // Reach partition end + // We may encounter empty frame here + recentStartPeerGroup = currentGroup + i; + frameStartFollowingReachEnd = true; + return partitionSize; + } else { + start++; + } + } + recentStartPeerGroup = currentGroup + offset; + } + + return start; + } + + private int getEndFollowingOffset(int currentPosition, int currentGroup) { + int end = recentRange.getEnd(); + // Shortcut if we have reached partition end already + if (end == partitionSize - 1) { + return end; + } + + int offset = (int) getOffset(frameInfo.getEndOffsetChannel(), currentPosition); + if (currentGroup + offset > recentEndPeerGroup) { + int count = currentGroup + offset - recentEndPeerGroup; + for (int i = 0; i < count; i++) { + // Enter next peer group + if (end == partitionSize - 1) { + if (i != count - 1) { + // Too early, we may encounter empty frame + return partitionSize; + } + + // Reach partition end + recentEndPeerGroup = currentGroup + i; + return end; + } + end++; + // Scan over current peer group + end = scanPeerGroup(end); + } + recentEndPeerGroup = currentGroup + offset; + } + + return end; + } + + private int scanPeerGroup(int currentPosition) { + while (currentPosition < partitionSize - 1 + && peerGroupComparator.equalColumnLists(columns, currentPosition, currentPosition + 1)) { + currentPosition++; + } + return currentPosition; + } + + public long getOffset(int channel, int index) { + checkArgument(!partition.isNull(channel, index)); + long offset = partition.getLong(channel, index); + + checkArgument(offset >= 0); + return offset; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RangeFrame.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RangeFrame.java new file mode 100644 index 000000000000..59e9de731483 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RangeFrame.java @@ -0,0 +1,617 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame; + +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.ColumnList; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.Range; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.RowComparator; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.write.UnSupportedDataTypeException; + +import java.util.List; + +import static com.google.common.base.Preconditions.checkArgument; +import static org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo.FrameBoundType.CURRENT_ROW; +import static org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING; +import static org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING; + +public class RangeFrame implements Frame { + private final Partition partition; + private final FrameInfo frameInfo; + private final ColumnList column; + private final TSDataType dataType; + + private final int partitionSize; + private final RowComparator peerGroupComparator; + private Range recentRange; + + public RangeFrame( + Partition partition, + FrameInfo frameInfo, + List sortedColumns, + RowComparator comparator) { + this.partition = partition; + this.frameInfo = frameInfo; + // Only one sort key is allowed in range frame + checkArgument(sortedColumns.size() == 1); + this.column = sortedColumns.get(0); + this.dataType = column.getDataType(); + this.partitionSize = partition.getPositionCount(); + this.peerGroupComparator = comparator; + this.recentRange = new Range(0, 0); + } + + @Override + public Range getRange( + int currentPosition, int currentGroup, int peerGroupStart, int peerGroupEnd) { + // Full partition + if (frameInfo.getStartType() == UNBOUNDED_PRECEDING + && frameInfo.getEndType() == UNBOUNDED_FOLLOWING) { + return new Range(0, partitionSize - 1); + } + + // Peer group + if (frameInfo.getStartType() == CURRENT_ROW && frameInfo.getEndType() == CURRENT_ROW + || frameInfo.getStartType() == CURRENT_ROW && frameInfo.getEndType() == UNBOUNDED_FOLLOWING + || frameInfo.getStartType() == UNBOUNDED_PRECEDING + && frameInfo.getEndType() == CURRENT_ROW) { + if (currentPosition == 0 + || !peerGroupComparator.equal(column, currentPosition - 1, currentPosition)) { + // New peer group + int frameStart = frameInfo.getStartType() == CURRENT_ROW ? peerGroupStart : 0; + int frameEnd = frameInfo.getEndType() == CURRENT_ROW ? peerGroupEnd - 1 : partitionSize - 1; + + recentRange = new Range(frameStart, frameEnd); + } + // Old peer group is considered as well + return recentRange; + } + + // Current row is NULL + // According to Spec, behavior of "X PRECEDING", "X FOLLOWING" frame boundaries is similar to + // "CURRENT ROW" for null values. + if (column.isNull(currentPosition)) { + recentRange = + new Range( + frameInfo.getStartType() == UNBOUNDED_PRECEDING ? 0 : peerGroupStart, + frameInfo.getEndType() == UNBOUNDED_FOLLOWING ? partitionSize - 1 : peerGroupEnd - 1); + return recentRange; + } + + // Current row is not NULL + // Frame definition has at least one of: X PRECEDING, Y FOLLOWING + int frameStart; + switch (frameInfo.getStartType()) { + case UNBOUNDED_PRECEDING: + frameStart = 0; + break; + case PRECEDING: + frameStart = getPrecedingOffset(currentPosition, peerGroupStart, peerGroupEnd, true); + break; + case CURRENT_ROW: + frameStart = peerGroupStart; + break; + case FOLLOWING: + frameStart = getFollowingOffset(currentPosition, peerGroupStart, peerGroupEnd, true); + break; + default: + // UNBOUND_FOLLOWING is not allowed in frame start + throw new SemanticException("UNBOUND PRECEDING is not allowed in frame start!"); + } + + int frameEnd; + switch (frameInfo.getEndType()) { + case PRECEDING: + frameEnd = getPrecedingOffset(currentPosition, peerGroupStart, peerGroupEnd, false); + break; + case CURRENT_ROW: + frameEnd = peerGroupEnd - 1; + break; + case FOLLOWING: + frameEnd = getFollowingOffset(currentPosition, peerGroupStart, peerGroupEnd, false); + break; + case UNBOUNDED_FOLLOWING: + frameEnd = partitionSize - 1; + break; + default: + // UNBOUND_PRECEDING is not allowed in frame start + throw new SemanticException("UNBOUND PRECEDING is not allowed in frame end!"); + } + + if (frameEnd < frameStart || frameEnd < 0 || frameStart >= partitionSize) { + recentRange = new Range(Math.min(partitionSize - 1, frameStart), Math.max(0, frameEnd)); + return new Range(-1, -1); + } + + frameStart = Math.max(frameStart, 0); + frameEnd = Math.min(frameEnd, partitionSize - 1); + recentRange = new Range(frameStart, frameEnd); + return recentRange; + } + + private int getPrecedingOffset(int index, int peerGroupStart, int peerGroupEnd, boolean isStart) { + int offset; + if (isStart) { + if (!dataType.isNumeric() + && dataType != TSDataType.DATE + && dataType != TSDataType.TIMESTAMP) { + return peerGroupStart; + } + + int recentStart = recentRange.getStart(); + + // Recent start from NULL + // Which means current row is the first non-null row + if (frameInfo.getSortOrder().isNullsFirst() && column.isNull(recentStart)) { + // Then the frame starts with current row + return index; + } + + if (frameInfo.getSortOrder().isAscending()) { + offset = getAscFrameStartPreceding(index, recentStart); + } else { + offset = getDescFrameStartPreceding(index, recentStart); + } + } else { + if (!dataType.isNumeric() + && dataType != TSDataType.DATE + && dataType != TSDataType.TIMESTAMP) { + return peerGroupEnd; + } + + int recentEnd = recentRange.getEnd(); + + // Leave section of leading nulls + if (frameInfo.getSortOrder().isNullsFirst()) { + while (recentEnd < partitionSize && column.isNull(recentEnd)) { + recentEnd++; + } + } + + if (frameInfo.getSortOrder().isAscending()) { + offset = getAscFrameEndPreceding(index, recentEnd); + } else { + offset = getDescFrameEndPreceding(index, recentEnd); + } + } + + return offset; + } + + private int getFollowingOffset(int index, int peerGroupStart, int peerGroupEnd, boolean isStart) { + int offset; + if (isStart) { + if (!dataType.isNumeric() + && dataType != TSDataType.DATE + && dataType != TSDataType.TIMESTAMP) { + return peerGroupStart; + } + + int recentStart = recentRange.getStart(); + + // Leave section of leading nulls + if (recentStart == 0 && frameInfo.getSortOrder().isNullsFirst() && column.isNull(0)) { + // Then the frame starts with current row + recentStart = index; + } + + // Leave section of tailing nulls + if (!frameInfo.getSortOrder().isNullsFirst()) { + while (recentStart >= 0 && column.isNull(recentStart)) { + recentStart--; + } + if (recentStart < 0) { + return recentStart; + } + } + + if (frameInfo.getSortOrder().isAscending()) { + offset = getAscFrameStartFollowing(index, recentStart); + } else { + offset = getDescFrameStartFollowing(index, recentStart); + } + } else { + if (!dataType.isNumeric() + && dataType != TSDataType.DATE + && dataType != TSDataType.TIMESTAMP) { + return peerGroupEnd; + } + + int recentEnd = recentRange.getEnd(); + + // Leave section of leading nulls + if (frameInfo.getSortOrder().isNullsFirst() && column.isNull(recentEnd)) { + // Then the frame starts with current row + recentEnd = index; + } + + if (frameInfo.getSortOrder().isAscending()) { + offset = getAscFrameEndFollowing(index, recentEnd); + } else { + offset = getDescFrameEndFollowing(index, recentEnd); + } + } + + return offset; + } + + // Find first row which satisfy: + // follow >= current + offset + // And stop right there + private int getAscFrameStartFollowing(int currentIndex, int recentIndex) { + while (recentIndex < partitionSize && !column.isNull(recentIndex)) { + if (compareInAscFrameStartFollowing( + currentIndex, recentIndex, frameInfo.getStartOffsetChannel())) { + return recentIndex; + } + recentIndex++; + } + return recentIndex; + } + + private boolean compareInAscFrameStartFollowing(int currentIndex, int recentIndex, int channel) { + checkArgument(!partition.isNull(channel, currentIndex)); + switch (column.getDataType()) { + case INT32: + case DATE: + int currentInt = column.getInt(currentIndex); + int followInt = column.getInt(recentIndex); + int deltaInt = partition.getInt(channel, currentIndex); + return followInt >= currentInt + deltaInt; + case INT64: + case TIMESTAMP: + long currentLong = column.getLong(currentIndex); + long followLong = column.getLong(recentIndex); + long deltaLong = partition.getLong(channel, currentIndex); + return followLong >= currentLong + deltaLong; + case FLOAT: + float currentFloat = column.getFloat(currentIndex); + float followFloat = column.getFloat(recentIndex); + float deltaFloat = partition.getFloat(channel, currentIndex); + return followFloat >= currentFloat + deltaFloat; + case DOUBLE: + double currentDouble = column.getDouble(currentIndex); + double followDouble = column.getDouble(recentIndex); + double deltaDouble = partition.getDouble(channel, currentIndex); + return followDouble >= currentDouble + deltaDouble; + default: + // Unreachable + throw new UnSupportedDataTypeException("Unsupported data type: " + column.getDataType()); + } + } + + // Find first row which satisfy: + // follow > current + offset + // And return its previous index + private int getAscFrameEndFollowing(int currentIndex, int recentIndex) { + while (recentIndex < partitionSize && !column.isNull(recentIndex)) { + if (compareInAscFrameEndFollowing( + currentIndex, recentIndex, frameInfo.getEndOffsetChannel())) { + return recentIndex - 1; + } + recentIndex++; + } + return recentIndex - 1; + } + + private boolean compareInAscFrameEndFollowing(int currentIndex, int recentIndex, int channel) { + checkArgument(!partition.isNull(channel, currentIndex)); + switch (column.getDataType()) { + case INT32: + case DATE: + int currentInt = column.getInt(currentIndex); + int followInt = column.getInt(recentIndex); + int deltaInt = partition.getInt(channel, currentIndex); + return followInt > currentInt + deltaInt; + case INT64: + case TIMESTAMP: + long currentLong = column.getLong(currentIndex); + long followLong = column.getLong(recentIndex); + long deltaLong = partition.getLong(channel, currentIndex); + return followLong > currentLong + deltaLong; + case FLOAT: + float currentFloat = column.getFloat(currentIndex); + float followFloat = column.getFloat(recentIndex); + float deltaFloat = partition.getFloat(channel, currentIndex); + return followFloat > currentFloat + deltaFloat; + case DOUBLE: + double currentDouble = column.getDouble(currentIndex); + double followDouble = column.getDouble(recentIndex); + double deltaDouble = partition.getDouble(channel, currentIndex); + return followDouble > currentDouble + deltaDouble; + default: + // Unreachable + throw new UnSupportedDataTypeException("Unsupported data type: " + column.getDataType()); + } + } + + // Find first row which satisfy: + // precede >= current - offset + // And stop right there + private int getAscFrameStartPreceding(int currentIndex, int recentIndex) { + while (recentIndex < currentIndex) { + if (compareInAscFrameStartPreceding( + currentIndex, recentIndex, frameInfo.getStartOffsetChannel())) { + return recentIndex; + } + recentIndex++; + } + return recentIndex; + } + + private boolean compareInAscFrameStartPreceding(int currentIndex, int recentIndex, int channel) { + checkArgument(!partition.isNull(channel, currentIndex)); + switch (column.getDataType()) { + case INT32: + case DATE: + int currentInt = column.getInt(currentIndex); + int precedeInt = column.getInt(recentIndex); + int deltaInt = partition.getInt(channel, currentIndex); + return precedeInt >= currentInt - deltaInt; + case INT64: + case TIMESTAMP: + long currentLong = column.getLong(currentIndex); + long precedeLong = column.getLong(recentIndex); + long deltaLong = partition.getLong(channel, currentIndex); + return precedeLong >= currentLong - deltaLong; + case FLOAT: + float currentFloat = column.getFloat(currentIndex); + float precedeFollow = column.getFloat(recentIndex); + float deltaFloat = partition.getFloat(channel, currentIndex); + return precedeFollow >= currentFloat - deltaFloat; + case DOUBLE: + double currentDouble = column.getDouble(currentIndex); + double precedeDouble = column.getDouble(recentIndex); + double deltaDouble = partition.getDouble(channel, currentIndex); + return precedeDouble >= currentDouble - deltaDouble; + default: + // Unreachable + throw new UnSupportedDataTypeException("Unsupported data type: " + column.getDataType()); + } + } + + // Find first row which satisfy: + // precede > current - offset + // And return its previous index + private int getAscFrameEndPreceding(int currentIndex, int recentIndex) { + while (recentIndex < partitionSize) { + if (compareInAscFrameEndPreceding( + currentIndex, recentIndex, frameInfo.getEndOffsetChannel())) { + return recentIndex - 1; + } + recentIndex++; + } + return recentIndex - 1; + } + + private boolean compareInAscFrameEndPreceding(int currentIndex, int recentIndex, int channel) { + checkArgument(!partition.isNull(channel, currentIndex)); + switch (column.getDataType()) { + case INT32: + case DATE: + int currentInt = column.getInt(currentIndex); + int precedeInt = column.getInt(recentIndex); + int deltaInt = partition.getInt(channel, currentIndex); + return precedeInt > currentInt - deltaInt; + case INT64: + case TIMESTAMP: + long currentLong = column.getLong(currentIndex); + long precedeLong = column.getLong(recentIndex); + long deltaLong = partition.getLong(channel, currentIndex); + return precedeLong > currentLong - deltaLong; + case FLOAT: + float currentFloat = column.getFloat(currentIndex); + float precedeFollow = column.getFloat(recentIndex); + float deltaFloat = partition.getFloat(channel, currentIndex); + return precedeFollow > currentFloat - deltaFloat; + case DOUBLE: + double currentDouble = column.getDouble(currentIndex); + double precedeDouble = column.getDouble(recentIndex); + double deltaDouble = partition.getDouble(channel, currentIndex); + return precedeDouble > currentDouble - deltaDouble; + default: + // Unreachable + throw new UnSupportedDataTypeException("Unsupported data type: " + column.getDataType()); + } + } + + // Find first row which satisfy: + // follow <= current - offset + // And stop right there + private int getDescFrameStartFollowing(int currentIndex, int recentIndex) { + while (recentIndex < partitionSize && !column.isNull(recentIndex)) { + if (compareInDescFrameStartFollowing( + currentIndex, recentIndex, frameInfo.getStartOffsetChannel())) { + return recentIndex; + } + recentIndex++; + } + return recentIndex; + } + + private boolean compareInDescFrameStartFollowing(int currentIndex, int recentIndex, int channel) { + checkArgument(!partition.isNull(channel, currentIndex)); + switch (column.getDataType()) { + case INT32: + case DATE: + int currentInt = column.getInt(currentIndex); + int followInt = column.getInt(recentIndex); + int deltaInt = partition.getInt(channel, currentIndex); + return followInt <= currentInt - deltaInt; + case INT64: + case TIMESTAMP: + long currentLong = column.getLong(currentIndex); + long followLong = column.getLong(recentIndex); + long deltaLong = partition.getLong(channel, currentIndex); + return followLong <= currentLong - deltaLong; + case FLOAT: + float currentFloat = column.getFloat(currentIndex); + float followFloat = column.getFloat(recentIndex); + float deltaFloat = partition.getFloat(channel, currentIndex); + return followFloat <= currentFloat - deltaFloat; + case DOUBLE: + double currentDouble = column.getDouble(currentIndex); + double followDouble = column.getDouble(recentIndex); + double deltaDouble = partition.getDouble(channel, currentIndex); + return followDouble <= currentDouble - deltaDouble; + default: + // Unreachable + throw new UnSupportedDataTypeException("Unsupported data type: " + column.getDataType()); + } + } + + // Find first row which satisfy: + // follow < current - offset + // And return its previous index + private int getDescFrameEndFollowing(int currentIndex, int recentIndex) { + while (recentIndex < partitionSize && !column.isNull(recentIndex)) { + if (compareInDescFrameEndFollowing( + currentIndex, recentIndex, frameInfo.getEndOffsetChannel())) { + return recentIndex - 1; + } + recentIndex++; + } + return recentIndex - 1; + } + + private boolean compareInDescFrameEndFollowing(int currentIndex, int recentIndex, int channel) { + checkArgument(!partition.isNull(channel, currentIndex)); + switch (column.getDataType()) { + case INT32: + case DATE: + int currentInt = column.getInt(currentIndex); + int followInt = column.getInt(recentIndex); + int deltaInt = partition.getInt(channel, currentIndex); + return followInt < currentInt - deltaInt; + case INT64: + case TIMESTAMP: + long currentLong = column.getLong(currentIndex); + long followLong = column.getLong(recentIndex); + long deltaLong = partition.getLong(channel, currentIndex); + return followLong < currentLong - deltaLong; + case FLOAT: + float currentFloat = column.getFloat(currentIndex); + float followFloat = column.getFloat(recentIndex); + float deltaFloat = partition.getFloat(channel, currentIndex); + return followFloat < currentFloat - deltaFloat; + case DOUBLE: + double currentDouble = column.getDouble(currentIndex); + double followDouble = column.getDouble(recentIndex); + double deltaDouble = partition.getDouble(channel, currentIndex); + return followDouble < currentDouble - deltaDouble; + default: + // Unreachable + throw new UnSupportedDataTypeException("Unsupported data type: " + column.getDataType()); + } + } + + // Find first row which satisfy: + // precede <= current + offset + // And stop right there + private int getDescFrameStartPreceding(int currentIndex, int recentIndex) { + while (recentIndex < currentIndex) { + if (compareInDescFrameStartPreceding( + currentIndex, recentIndex, frameInfo.getStartOffsetChannel())) { + return recentIndex; + } + recentIndex++; + } + return recentIndex; + } + + private boolean compareInDescFrameStartPreceding(int currentIndex, int recentIndex, int channel) { + checkArgument(!partition.isNull(channel, currentIndex)); + switch (column.getDataType()) { + case INT32: + case DATE: + int currentInt = column.getInt(currentIndex); + int precedeInt = column.getInt(recentIndex); + int deltaInt = partition.getInt(channel, currentIndex); + return precedeInt <= currentInt + deltaInt; + case INT64: + case TIMESTAMP: + long currentLong = column.getLong(currentIndex); + long precedeLong = column.getLong(recentIndex); + long deltaLong = partition.getLong(channel, currentIndex); + return precedeLong <= currentLong + deltaLong; + case FLOAT: + float currentFloat = column.getFloat(currentIndex); + float precedeFollow = column.getFloat(recentIndex); + float deltaFloat = partition.getFloat(channel, currentIndex); + return precedeFollow <= currentFloat + deltaFloat; + case DOUBLE: + double currentDouble = column.getDouble(currentIndex); + double precedeDouble = column.getDouble(recentIndex); + double deltaDouble = partition.getDouble(channel, currentIndex); + return precedeDouble <= currentDouble + deltaDouble; + default: + // Unreachable + throw new UnSupportedDataTypeException("Unsupported data type: " + column.getDataType()); + } + } + + // Find first row which satisfy: + // precede < current + offset + // And return its previous index + private int getDescFrameEndPreceding(int currentIndex, int recentIndex) { + while (recentIndex < partitionSize) { + if (compareInDescFrameEndPreceding( + currentIndex, recentIndex, frameInfo.getEndOffsetChannel())) { + return recentIndex - 1; + } + recentIndex++; + } + return recentIndex - 1; + } + + private boolean compareInDescFrameEndPreceding(int currentIndex, int recentIndex, int channel) { + checkArgument(!partition.isNull(channel, currentIndex)); + switch (column.getDataType()) { + case INT32: + case DATE: + int currentInt = column.getInt(currentIndex); + int precedeInt = column.getInt(recentIndex); + int deltaInt = partition.getInt(channel, currentIndex); + return precedeInt < currentInt + deltaInt; + case INT64: + case TIMESTAMP: + long currentLong = column.getLong(currentIndex); + long precedeLong = column.getLong(recentIndex); + long deltaLong = partition.getLong(channel, currentIndex); + return precedeLong < currentLong + deltaLong; + case FLOAT: + float currentFloat = column.getFloat(currentIndex); + float precedeFollow = column.getFloat(recentIndex); + float deltaFloat = partition.getFloat(channel, currentIndex); + return precedeFollow < currentFloat + deltaFloat; + case DOUBLE: + double currentDouble = column.getDouble(currentIndex); + double precedeDouble = column.getDouble(recentIndex); + double deltaDouble = partition.getDouble(channel, currentIndex); + return precedeDouble < currentDouble + deltaDouble; + default: + // Unreachable + throw new UnSupportedDataTypeException("Unsupported data type: " + column.getDataType()); + } + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RowsFrame.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RowsFrame.java new file mode 100644 index 000000000000..2177bb68c8e3 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RowsFrame.java @@ -0,0 +1,108 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame; + +import org.apache.iotdb.db.exception.sql.SemanticException; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.Range; + +import static com.google.common.base.Preconditions.checkArgument; + +public class RowsFrame implements Frame { + private final Partition partition; + private final FrameInfo frameInfo; + private final int partitionStart; + private final int partitionSize; + + public RowsFrame(Partition partition, FrameInfo frameInfo, int partitionStart, int partitionEnd) { + checkArgument(frameInfo.getFrameType() == FrameInfo.FrameType.ROWS); + + this.partition = partition; + this.frameInfo = frameInfo; + this.partitionStart = partitionStart; + this.partitionSize = partitionEnd - partitionStart; + } + + @Override + public Range getRange( + int currentPosition, int currentGroup, int peerGroupStart, int peerGroupEnd) { + int posInPartition = currentPosition - partitionStart; + + int offset; + int frameStart; + switch (frameInfo.getStartType()) { + case UNBOUNDED_PRECEDING: + frameStart = 0; + break; + case PRECEDING: + offset = (int) getOffset(frameInfo.getStartOffsetChannel(), currentPosition); + frameStart = posInPartition - offset; + break; + case CURRENT_ROW: + frameStart = posInPartition; + break; + case FOLLOWING: + offset = (int) getOffset(frameInfo.getStartOffsetChannel(), currentPosition); + frameStart = posInPartition + offset; + break; + default: + // UNBOUND_FOLLOWING is not allowed in frame start + throw new SemanticException("UNBOUND PRECEDING is not allowed in frame start!"); + } + + int frameEnd; + switch (frameInfo.getEndType()) { + case PRECEDING: + offset = (int) getOffset(frameInfo.getEndOffsetChannel(), currentPosition); + frameEnd = posInPartition - offset; + break; + case CURRENT_ROW: + frameEnd = posInPartition; + break; + case FOLLOWING: + offset = (int) getOffset(frameInfo.getEndOffsetChannel(), currentPosition); + frameEnd = posInPartition + offset; + break; + case UNBOUNDED_FOLLOWING: + frameEnd = partitionSize - 1; + break; + default: + // UNBOUND_PRECEDING is not allowed in frame end + throw new SemanticException("UNBOUND PRECEDING is not allowed in frame end!"); + } + + // Empty frame + if (frameEnd < frameStart || frameEnd < 0 || frameStart >= partitionSize) { + return new Range(-1, -1); + } + + frameStart = Math.max(frameStart, 0); + frameEnd = Math.min(frameEnd, partitionSize - 1); + return new Range(frameStart, frameEnd); + } + + public long getOffset(int channel, int index) { + checkArgument(!partition.isNull(channel, index)); + long offset = partition.getLong(channel, index); + + checkArgument(offset >= 0); + return offset; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/ColumnList.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/ColumnList.java new file mode 100644 index 000000000000..97152b715d80 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/ColumnList.java @@ -0,0 +1,141 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.utils; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.block.column.ColumnEncoding; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.utils.Binary; + +import java.util.ArrayList; +import java.util.List; + +public class ColumnList { + private final List columns; + private final List positionCounts; + + public ColumnList(List columns) { + this.columns = columns; + + positionCounts = new ArrayList<>(columns.size()); + for (Column column : columns) { + positionCounts.add(column.getPositionCount()); + } + } + + public TSDataType getDataType() { + return columns.get(0).getDataType(); + } + + public ColumnEncoding getEncoding() { + return columns.get(0).getEncoding(); + } + + public static class ColumnListIndex { + private final int columnIndex; + private final int offsetInColumn; + + ColumnListIndex(int columnIndex, int offsetInColumn) { + this.columnIndex = columnIndex; + this.offsetInColumn = offsetInColumn; + } + + public int getColumnIndex() { + return columnIndex; + } + + public int getOffsetInColumn() { + return offsetInColumn; + } + } + + public ColumnListIndex getColumnListIndex(int rowIndex) { + int columnIndex = 0; + while (columnIndex < columns.size() && rowIndex >= positionCounts.get(columnIndex)) { + rowIndex -= positionCounts.get(columnIndex); + // Enter next Column + columnIndex++; + } + + if (columnIndex != columns.size()) { + return new ColumnListIndex(columnIndex, rowIndex); + } else { + // Unlikely + throw new IndexOutOfBoundsException("Index out of Partition's bounds!"); + } + } + + public boolean getBoolean(int position) { + ColumnListIndex columnListIndex = getColumnListIndex(position); + int columnIndex = columnListIndex.getColumnIndex(); + int offsetInColumn = columnListIndex.getOffsetInColumn(); + + return columns.get(columnIndex).getBoolean(offsetInColumn); + } + + public int getInt(int position) { + ColumnListIndex columnListIndex = getColumnListIndex(position); + int columnIndex = columnListIndex.getColumnIndex(); + int offsetInColumn = columnListIndex.getOffsetInColumn(); + return columns.get(columnIndex).getInt(offsetInColumn); + } + + public long getLong(int position) { + ColumnListIndex columnListIndex = getColumnListIndex(position); + int columnIndex = columnListIndex.getColumnIndex(); + int offsetInColumn = columnListIndex.getOffsetInColumn(); + return columns.get(columnIndex).getLong(offsetInColumn); + } + + public float getFloat(int position) { + ColumnListIndex columnListIndex = getColumnListIndex(position); + int columnIndex = columnListIndex.getColumnIndex(); + int offsetInColumn = columnListIndex.getOffsetInColumn(); + return columns.get(columnIndex).getFloat(offsetInColumn); + } + + public double getDouble(int position) { + ColumnListIndex columnListIndex = getColumnListIndex(position); + int columnIndex = columnListIndex.getColumnIndex(); + int offsetInColumn = columnListIndex.getOffsetInColumn(); + return columns.get(columnIndex).getDouble(offsetInColumn); + } + + public Binary getBinary(int position) { + ColumnListIndex columnListIndex = getColumnListIndex(position); + int columnIndex = columnListIndex.getColumnIndex(); + int offsetInColumn = columnListIndex.getOffsetInColumn(); + return columns.get(columnIndex).getBinary(offsetInColumn); + } + + public Object getObject(int position) { + ColumnListIndex columnListIndex = getColumnListIndex(position); + int columnIndex = columnListIndex.getColumnIndex(); + int offsetInColumn = columnListIndex.getOffsetInColumn(); + return columns.get(columnIndex).getObject(offsetInColumn); + } + + public boolean isNull(int position) { + ColumnListIndex columnListIndex = getColumnListIndex(position); + int columnIndex = columnListIndex.getColumnIndex(); + int offsetInColumn = columnListIndex.getOffsetInColumn(); + return columns.get(columnIndex).isNull(offsetInColumn); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/Range.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/Range.java new file mode 100644 index 000000000000..9994742bbfee --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/Range.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.utils; + +public class Range { + private final int start; + private final int end; + + public Range(int start, int end) { + this.start = start; + this.end = end; + } + + public int getStart() { + return start; + } + + public int getEnd() { + return end; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/RowComparator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/RowComparator.java new file mode 100644 index 000000000000..768eeaed8542 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/utils/RowComparator.java @@ -0,0 +1,231 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.utils; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.utils.Binary; +import org.apache.tsfile.write.UnSupportedDataTypeException; + +import java.util.List; + +public class RowComparator { + private final List dataTypes; + + public RowComparator(List dataTypes) { + this.dataTypes = dataTypes; + } + + public boolean equalColumns(List columns, int offset1, int offset2) { + for (int i = 0; i < dataTypes.size(); i++) { + Column column = columns.get(i); + TSDataType dataType = dataTypes.get(i); + if (!equal(column, dataType, offset1, offset2)) { + return false; + } + } + return true; + } + + public boolean equal(Column column, int offset1, int offset2) { + assert dataTypes.size() == 1; + return equal(column, dataTypes.get(0), offset1, offset2); + } + + private boolean equal(Column column, TSDataType dataType, int offset1, int offset2) { + switch (dataType) { + case BOOLEAN: + boolean bool1 = column.getBoolean(offset1); + boolean bool2 = column.getBoolean(offset2); + if (bool1 != bool2) { + return false; + } + break; + case INT32: + int int1 = column.getInt(offset1); + int int2 = column.getInt(offset2); + if (int1 != int2) { + return false; + } + break; + case INT64: + long long1 = column.getLong(offset1); + long long2 = column.getLong(offset2); + if (long1 != long2) { + return false; + } + break; + case FLOAT: + float float1 = column.getFloat(offset1); + float float2 = column.getFloat(offset2); + if (float1 != float2) { + return false; + } + break; + case DOUBLE: + double double1 = column.getDouble(offset1); + double double2 = column.getDouble(offset2); + if (double1 != double2) { + return false; + } + break; + case TEXT: + Binary bin1 = column.getBinary(offset1); + Binary bin2 = column.getBinary(offset2); + if (!bin1.equals(bin2)) { + return false; + } + break; + default: + // Would throw at the first run + throw new UnSupportedDataTypeException(dataType.toString()); + } + return true; + } + + public boolean equalColumnLists(List columns, int offset1, int offset2) { + for (int i = 0; i < dataTypes.size(); i++) { + ColumnList column = columns.get(i); + TSDataType dataType = dataTypes.get(i); + if (!equal(column, dataType, offset1, offset2)) { + return false; + } + } + return true; + } + + public boolean equal(ColumnList column, int offset1, int offset2) { + assert dataTypes.size() == 1; + return equal(column, dataTypes.get(0), offset1, offset2); + } + + private boolean equal(ColumnList column, TSDataType dataType, int offset1, int offset2) { + switch (dataType) { + case BOOLEAN: + boolean bool1 = column.getBoolean(offset1); + boolean bool2 = column.getBoolean(offset2); + if (bool1 != bool2) { + return false; + } + break; + case INT32: + int int1 = column.getInt(offset1); + int int2 = column.getInt(offset2); + if (int1 != int2) { + return false; + } + break; + case INT64: + long long1 = column.getLong(offset1); + long long2 = column.getLong(offset2); + if (long1 != long2) { + return false; + } + break; + case FLOAT: + float float1 = column.getFloat(offset1); + float float2 = column.getFloat(offset2); + if (float1 != float2) { + return false; + } + break; + case DOUBLE: + double double1 = column.getDouble(offset1); + double double2 = column.getDouble(offset2); + if (double1 != double2) { + return false; + } + break; + case TEXT: + Binary bin1 = column.getBinary(offset1); + Binary bin2 = column.getBinary(offset2); + if (!bin1.equals(bin2)) { + return false; + } + break; + default: + // Would throw at the first run + throw new UnSupportedDataTypeException(dataType.toString()); + } + return true; + } + + public boolean equal(List columns1, int offset1, List columns2, int offset2) { + for (int i = 0; i < dataTypes.size(); i++) { + TSDataType dataType = dataTypes.get(i); + Column column1 = columns1.get(i); + Column column2 = columns2.get(i); + + switch (dataType) { + case BOOLEAN: + boolean bool1 = column1.getBoolean(offset1); + boolean bool2 = column2.getBoolean(offset2); + if (bool1 != bool2) { + return false; + } + break; + case INT32: + int int1 = column1.getInt(offset1); + int int2 = column2.getInt(offset2); + if (int1 != int2) { + return false; + } + break; + case INT64: + long long1 = column1.getLong(offset1); + long long2 = column2.getLong(offset2); + if (long1 != long2) { + return false; + } + break; + case FLOAT: + float float1 = column1.getFloat(offset1); + float float2 = column2.getFloat(offset2); + if (float1 != float2) { + return false; + } + break; + case DOUBLE: + double double1 = column1.getDouble(offset1); + double double2 = column2.getDouble(offset2); + if (double1 != double2) { + return false; + } + break; + case TEXT: + Binary bin1 = column1.getBinary(offset1); + Binary bin2 = column2.getBinary(offset2); + if (!bin1.equals(bin2)) { + return false; + } + break; + default: + // Would throw at the first run + throw new UnSupportedDataTypeException(dataType.toString()); + } + } + + return true; + } + + public List getDataTypes() { + return dataTypes; + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/AvgAccumulator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/AvgAccumulator.java index b4a97b19e0d1..935a114cbdfd 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/AvgAccumulator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/operator/source/relational/aggregation/AvgAccumulator.java @@ -319,7 +319,7 @@ private void removeFloatInput(Column column) { for (int i = 0; i < count; i++) { if (!column.isNull(i)) { countValue--; - sumValue += column.getFloat(i); + sumValue -= column.getFloat(i); } } } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTest.java new file mode 100644 index 000000000000..ed0ec405a9c4 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTest.java @@ -0,0 +1,320 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window; + +import org.apache.iotdb.commons.concurrent.IoTDBThreadPoolFactory; +import org.apache.iotdb.db.queryengine.common.FragmentInstanceId; +import org.apache.iotdb.db.queryengine.common.PlanFragmentId; +import org.apache.iotdb.db.queryengine.common.QueryId; +import org.apache.iotdb.db.queryengine.execution.driver.DriverContext; +import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext; +import org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceStateMachine; +import org.apache.iotdb.db.queryengine.execution.operator.Operator; +import org.apache.iotdb.db.queryengine.execution.operator.OperatorContext; +import org.apache.iotdb.db.queryengine.execution.operator.process.TreeLinearFillOperator; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.WindowFunction; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank.RankFunction; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo; +import org.apache.iotdb.db.queryengine.plan.planner.plan.node.PlanNodeId; + +import com.google.common.util.concurrent.ListenableFuture; +import org.apache.tsfile.common.conf.TSFileConfig; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.apache.tsfile.utils.Binary; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.ExecutorService; + +import static org.apache.iotdb.db.queryengine.execution.fragment.FragmentInstanceContext.createFragmentInstanceContext; +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; + +public class TableWindowOperatorTest { + private static final ExecutorService instanceNotificationExecutor = + IoTDBThreadPoolFactory.newFixedThreadPool(1, "windowOperator-test-instance-notification"); + + private final long[] column1 = new long[] {1, 2, 3, 4, 5, 6, 7}; + private final String[] column2 = new String[] {"d1", "d1", "d2", "d2", "d2", "d2", "d2"}; + private final int[] column3 = new int[] {1, 2, 3, 4, 5, 6, 7}; + private final long[] column4 = new long[] {1, 2, 1, 2, 3, 4, 5}; + + @Test + public void testOneTsBlockWithMultiPartition() { + long[][] timeArray = + new long[][] { + {1, 2, 3, 4, 5, 6, 7}, + }; + String[][] deviceIdArray = + new String[][] { + {"d1", "d1", "d2", "d2", "d2", "d2", "d2"}, + }; + int[][] valueArray = + new int[][] { + {1, 2, 3, 4, 5, 6, 7}, + }; + + int count = 0; + try (TableWindowOperator windowOperator = + genWindowOperator(timeArray, deviceIdArray, valueArray)) { + ListenableFuture listenableFuture = windowOperator.isBlocked(); + listenableFuture.get(); + while (!windowOperator.isFinished() && windowOperator.hasNext()) { + TsBlock tsBlock = windowOperator.next(); + if (tsBlock != null && !tsBlock.isEmpty()) { + for (int i = 0, size = tsBlock.getPositionCount(); i < size; i++, count++) { + assertEquals(column1[count], tsBlock.getColumn(0).getLong(i)); + assertEquals( + column2[count], + tsBlock.getColumn(1).getBinary(i).getStringValue(TSFileConfig.STRING_CHARSET)); + assertEquals(column3[count], tsBlock.getColumn(2).getInt(i)); + assertEquals(column4[count], tsBlock.getColumn(3).getLong(i)); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + @Test + public void testPartitionCrossMultiTsBlock() { + long[][] timeArray = + new long[][] { + {1, 2}, + {3, 4}, + {5}, + {6, 7}, + }; + String[][] deviceIdArray = + new String[][] { + {"d1", "d1"}, + {"d2", "d2"}, + {"d2"}, + {"d2", "d2"}, + }; + int[][] valueArray = + new int[][] { + {1, 2}, + {3, 4}, + {5}, + {6, 7}, + }; + + int count = 0; + try (TableWindowOperator windowOperator = + genWindowOperator(timeArray, deviceIdArray, valueArray)) { + ListenableFuture listenableFuture = windowOperator.isBlocked(); + listenableFuture.get(); + while (!windowOperator.isFinished() && windowOperator.hasNext()) { + TsBlock tsBlock = windowOperator.next(); + if (tsBlock != null && !tsBlock.isEmpty()) { + for (int i = 0, size = tsBlock.getPositionCount(); i < size; i++, count++) { + assertEquals(column1[count], tsBlock.getColumn(0).getLong(i)); + assertEquals( + column2[count], + tsBlock.getColumn(1).getBinary(i).getStringValue(TSFileConfig.STRING_CHARSET)); + assertEquals(column3[count], tsBlock.getColumn(2).getInt(i)); + assertEquals(column4[count], tsBlock.getColumn(3).getLong(i)); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + @Test + public void testMixedPartition() { + long[][] timeArray = + new long[][] { + {1, 2, 3, 4}, + {5, 6, 7}, + }; + String[][] deviceIdArray = + new String[][] { + {"d1", "d1", "d2", "d2"}, + {"d2", "d2", "d2"}, + }; + int[][] valueArray = + new int[][] { + {1, 2, 3, 4}, + {5, 6, 7}, + }; + + int count = 0; + try (TableWindowOperator windowOperator = + genWindowOperator(timeArray, deviceIdArray, valueArray)) { + ListenableFuture listenableFuture = windowOperator.isBlocked(); + listenableFuture.get(); + while (!windowOperator.isFinished() && windowOperator.hasNext()) { + TsBlock tsBlock = windowOperator.next(); + if (tsBlock != null && !tsBlock.isEmpty()) { + for (int i = 0, size = tsBlock.getPositionCount(); i < size; i++, count++) { + assertEquals(column1[count], tsBlock.getColumn(0).getLong(i)); + assertEquals( + column2[count], + tsBlock.getColumn(1).getBinary(i).getStringValue(TSFileConfig.STRING_CHARSET)); + assertEquals(column3[count], tsBlock.getColumn(2).getInt(i)); + assertEquals(column4[count], tsBlock.getColumn(3).getLong(i)); + } + } + } + } catch (Exception e) { + e.printStackTrace(); + fail(e.getMessage()); + } + } + + static class ChildOperator implements Operator { + private int index; + + private final long[][] timeArray; + private final String[][] deviceIdArray; + private final int[][] valueArray; + private final DriverContext driverContext; + + ChildOperator( + long[][] timeArray, + String[][] deviceIdArray, + int[][] valueArray, + DriverContext driverContext) { + this.timeArray = timeArray; + this.deviceIdArray = deviceIdArray; + this.valueArray = valueArray; + this.driverContext = driverContext; + + this.index = 0; + } + + @Override + public OperatorContext getOperatorContext() { + return driverContext.getOperatorContexts().get(0); + } + + @Override + public TsBlock next() throws Exception { + if (timeArray[index] == null) { + index++; + return null; + } + TsBlockBuilder builder = + new TsBlockBuilder( + timeArray[index].length, + Arrays.asList(TSDataType.TIMESTAMP, TSDataType.TEXT, TSDataType.INT32)); + for (int i = 0, size = timeArray[index].length; i < size; i++) { + builder.getColumnBuilder(0).writeLong(timeArray[index][i]); + builder + .getColumnBuilder(1) + .writeBinary(new Binary(deviceIdArray[index][i], TSFileConfig.STRING_CHARSET)); + builder.getColumnBuilder(2).writeInt(valueArray[index][i]); + } + builder.declarePositions(timeArray[index].length); + index++; + return builder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, builder.getPositionCount())); + } + + @Override + public boolean hasNext() { + return index < timeArray.length; + } + + @Override + public boolean isFinished() { + return index >= timeArray.length; + } + + @Override + public void close() { + // do nothing + } + + @Override + public long calculateMaxPeekMemory() { + return 0; + } + + @Override + public long calculateMaxReturnSize() { + return 0; + } + + @Override + public long calculateRetainedSizeAfterCallingNext() { + return 0; + } + + @Override + public long ramBytesUsed() { + return 0; + } + } + + private TableWindowOperator genWindowOperator( + long[][] timeArray, String[][] deviceIdArray, int[][] valueArray) { + QueryId queryId = new QueryId("stub_query"); + FragmentInstanceId instanceId = + new FragmentInstanceId(new PlanFragmentId(queryId, 0), "stub-instance"); + FragmentInstanceStateMachine stateMachine = + new FragmentInstanceStateMachine(instanceId, instanceNotificationExecutor); + FragmentInstanceContext fragmentInstanceContext = + createFragmentInstanceContext(instanceId, stateMachine); + DriverContext driverContext = new DriverContext(fragmentInstanceContext, 0); + PlanNodeId planNode = new PlanNodeId("1"); + driverContext.addOperatorContext(1, planNode, TreeLinearFillOperator.class.getSimpleName()); + + List inputDataTypes = + Arrays.asList(TSDataType.TIMESTAMP, TSDataType.TEXT, TSDataType.INT32); + List outputDataTypes = + Arrays.asList(TSDataType.TIMESTAMP, TSDataType.TEXT, TSDataType.INT32, TSDataType.INT64); + ArrayList outputChannels = new ArrayList<>(); + for (int i = 0; i < inputDataTypes.size(); i++) { + outputChannels.add(i); + } + WindowFunction windowFunction = new RankFunction(); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.CURRENT_ROW); + + Operator childOperator = new ChildOperator(timeArray, deviceIdArray, valueArray, driverContext); + return new TableWindowOperator( + driverContext.getOperatorContexts().get(0), + childOperator, + inputDataTypes, + outputDataTypes, + outputChannels, + Collections.singletonList(windowFunction), + Collections.singletonList(frameInfo), + Collections.singletonList(1), + Collections.singletonList(2)); + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTestUtils.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTestUtils.java new file mode 100644 index 000000000000..3cdaa0fdb1f6 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/TableWindowOperatorTestUtils.java @@ -0,0 +1,131 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window; + +import org.apache.tsfile.block.column.ColumnBuilder; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; + +import java.util.Arrays; +import java.util.Collections; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class TableWindowOperatorTestUtils { + public static TsBlock createIntsTsBlockWithoutNulls(int[] inputs) { + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(Collections.singletonList(TSDataType.INT32)); + ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders(); + for (int input : inputs) { + columnBuilders[0].writeInt(input); + tsBlockBuilder.declarePosition(); + } + + return tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + } + + public static TsBlock createIntsTsBlockWithoutNulls(int[] inputs, int offset) { + TsBlockBuilder tsBlockBuilder = + new TsBlockBuilder(Arrays.asList(TSDataType.INT32, TSDataType.INT32)); + ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders(); + for (int input : inputs) { + columnBuilders[0].writeInt(input); + columnBuilders[1].writeInt(offset); + tsBlockBuilder.declarePosition(); + } + + return tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + } + + public static TsBlock createIntsTsBlockWithoutNulls( + int[] inputs, int startOffset, int endOffset) { + TsBlockBuilder tsBlockBuilder = + new TsBlockBuilder(Arrays.asList(TSDataType.INT32, TSDataType.INT32, TSDataType.INT32)); + ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders(); + for (int input : inputs) { + columnBuilders[0].writeInt(input); + columnBuilders[1].writeInt(startOffset); + columnBuilders[2].writeInt(endOffset); + tsBlockBuilder.declarePosition(); + } + + return tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + } + + public static TsBlock createIntsTsBlockWithNulls(int[] inputs) { + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(Collections.singletonList(TSDataType.INT32)); + ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders(); + for (int input : inputs) { + if (input >= 0) { + columnBuilders[0].writeInt(input); + } else { + // Mimic null value + columnBuilders[0].appendNull(); + } + tsBlockBuilder.declarePosition(); + } + + return tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + } + + public static TsBlock createIntsTsBlockWithNulls(int[] inputs, int offset) { + TsBlockBuilder tsBlockBuilder = + new TsBlockBuilder(Arrays.asList(TSDataType.INT32, TSDataType.INT32)); + ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders(); + for (int input : inputs) { + if (input >= 0) { + columnBuilders[0].writeInt(input); + } else { + // Mimic null value + columnBuilders[0].appendNull(); + } + columnBuilders[1].writeInt(offset); + tsBlockBuilder.declarePosition(); + } + + return tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + } + + public static TsBlock createIntsTsBlockWithNulls(int[] inputs, int startOffset, int endOffset) { + TsBlockBuilder tsBlockBuilder = + new TsBlockBuilder(Arrays.asList(TSDataType.INT32, TSDataType.INT32, TSDataType.INT32)); + ColumnBuilder[] columnBuilders = tsBlockBuilder.getValueColumnBuilders(); + for (int input : inputs) { + if (input >= 0) { + columnBuilders[0].writeInt(input); + } else { + // Mimic null value + columnBuilders[0].appendNull(); + } + columnBuilders[1].writeInt(startOffset); + columnBuilders[2].writeInt(endOffset); + tsBlockBuilder.declarePosition(); + } + + return tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/FunctionTestUtils.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/FunctionTestUtils.java new file mode 100644 index 000000000000..ccec0f68563b --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/FunctionTestUtils.java @@ -0,0 +1,113 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function; + +import org.apache.iotdb.common.rpc.thrift.TAggregationType; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.aggregate.AggregationWindowFunction; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.aggregate.WindowAggregator; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo; +import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.AccumulatorFactory; +import org.apache.iotdb.db.queryengine.execution.operator.source.relational.aggregation.TableAccumulator; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; + +public class FunctionTestUtils { + public static PartitionExecutor createPartitionExecutor( + TsBlock tsBlock, List dataTypes, WindowFunction windowFunction) { + return createPartitionExecutor(tsBlock, dataTypes, windowFunction, new ArrayList<>()); + } + + public static PartitionExecutor createPartitionExecutor( + TsBlock tsBlock, + List dataTypes, + WindowFunction windowFunction, + List sortChannels) { + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.CURRENT_ROW); + return createPartitionExecutor(tsBlock, dataTypes, windowFunction, frameInfo, sortChannels); + } + + public static PartitionExecutor createPartitionExecutor( + TsBlock tsBlock, + List dataTypes, + WindowFunction windowFunction, + FrameInfo frameInfo) { + return createPartitionExecutor( + tsBlock, dataTypes, windowFunction, frameInfo, new ArrayList<>()); + } + + public static PartitionExecutor createPartitionExecutor( + TsBlock tsBlock, + List dataTypes, + WindowFunction windowFunction, + FrameInfo frameInfo, + List sortChannels) { + List tsBlocks = Collections.singletonList(tsBlock); + int startIndex = 0, endIndex = tsBlock.getPositionCount(); + List windowFunctions = Collections.singletonList(windowFunction); + List frameInfoList = Collections.singletonList(frameInfo); + + // Output channels are contiguous + ArrayList outputChannels = new ArrayList<>(); + for (int i = 0; i < dataTypes.size(); i++) { + outputChannels.add(i); + } + + return new PartitionExecutor( + tsBlocks, + dataTypes, + startIndex, + endIndex, + outputChannels, + windowFunctions, + frameInfoList, + sortChannels); + } + + // Assume input TsBlock has only one column + // And only output one column + public static AggregationWindowFunction createAggregationWindowFunction( + TAggregationType aggregationType, + TSDataType inputDataType, + TSDataType outputDataType, + boolean ascending) { + // inputExpressions and inputAttributes are not used in this method + TableAccumulator accumulator = + AccumulatorFactory.createBuiltinAccumulator( + aggregationType, + Collections.singletonList(inputDataType), + new ArrayList<>(), + new HashMap<>(), + ascending); + WindowAggregator aggregator = + new WindowAggregator(accumulator, outputDataType, Collections.singletonList(0)); + return new AggregationWindowFunction(aggregator); + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/AggregationWindowFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/AggregationWindowFunctionTest.java new file mode 100644 index 000000000000..91a60124cbdf --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/aggregate/AggregationWindowFunctionTest.java @@ -0,0 +1,195 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.aggregate; + +import org.apache.iotdb.common.rpc.thrift.TAggregationType; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo; +import org.apache.iotdb.db.queryengine.plan.relational.planner.SortOrder; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +// For aggregator that supports removeInputs, only SUM is tested, others are similar +public class AggregationWindowFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + private final int[] inputs = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + @Test + public void testFrameExpansion() { + List outputDataTypes = Arrays.asList(TSDataType.INT32, TSDataType.DOUBLE); + double[] expected = {0, 1, 3, 6, 10, 15, 21, 28, 36, 45}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + AggregationWindowFunction function = + FunctionTestUtils.createAggregationWindowFunction( + TAggregationType.SUM, TSDataType.INT32, TSDataType.DOUBLE, true); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.CURRENT_ROW); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + // This floating point are integers, no delta is needed + Assert.assertEquals(column.getDouble(i), expected[i], 0); + } + } + + @Test + public void testNotRemovableAggregationReComputation() { + List outputDataTypes = Arrays.asList(TSDataType.INT32, TSDataType.INT32); + int[] expected = {0, 0, 0, 1, 2, 3, 4, 5, 6, 7}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 2, 2); + AggregationWindowFunction function = + FunctionTestUtils.createAggregationWindowFunction( + TAggregationType.MIN, TSDataType.INT32, TSDataType.INT32, true); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + // This floating point are integers, no delta is needed + Assert.assertEquals(column.getInt(i), expected[i], 0); + } + } + + @Test + public void testAggregationNoReComputation() { + List outputDataTypes = Arrays.asList(TSDataType.INT32, TSDataType.DOUBLE); + double[] expected = {3, 6, 10, 15, 20, 25, 30, 35, 30, 24}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 2, 2); + AggregationWindowFunction function = + FunctionTestUtils.createAggregationWindowFunction( + TAggregationType.SUM, TSDataType.INT32, TSDataType.DOUBLE, true); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + // This floating point are integers, no delta is needed + Assert.assertEquals(column.getDouble(i), expected[i], 0); + } + } + + @Test + public void testAggregationReComputation() { + List inputDataTypes = Collections.singletonList(TSDataType.INT32); + int[] inputs = {1, 1, 1, 1, 3, 3, 3, 3, 5, 5}; + + List outputDataTypes = Arrays.asList(TSDataType.INT32, TSDataType.DOUBLE); + double[] expected = {4, 4, 4, 4, 12, 12, 12, 12, 10, 10}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + AggregationWindowFunction function = + FunctionTestUtils.createAggregationWindowFunction( + TAggregationType.SUM, TSDataType.INT32, TSDataType.DOUBLE, true); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.CURRENT_ROW, + -1, + FrameInfo.FrameBoundType.CURRENT_ROW, + -1, + 0, + SortOrder.ASC_NULLS_FIRST); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor( + tsBlock, inputDataTypes, function, frameInfo, Collections.singletonList(0)); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + // This floating point are integers, no delta is needed + Assert.assertEquals(column.getDouble(i), expected[i], 0); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/CumeDistFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/CumeDistFunctionTest.java new file mode 100644 index 000000000000..5962c44a4b3d --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/CumeDistFunctionTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class CumeDistFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + private final int[] inputs = {1, 1, 2, 2, 3}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.DOUBLE); + private final double[] expected = {0.4, 0.4, 0.8, 0.8, 1}; + + @Test + public void testCumeDistFunction() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + CumeDistFunction function = new CumeDistFunction(); + List sortedColumns = Collections.singletonList(0); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, sortedColumns); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + // 0.4, 0.8 and 1 do not need delta + Assert.assertEquals(column.getDouble(i), expected[i], 0); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/DenseRankFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/DenseRankFunctionTest.java new file mode 100644 index 000000000000..576ea56e931c --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/DenseRankFunctionTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class DenseRankFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + private final int[] inputs = {0, 1, 1, 1, 2, 2, 3, 4, 4, 5}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.INT64); + private final int[] expected = {1, 2, 2, 2, 3, 3, 4, 5, 5, 6}; + + @Test + public void testDenseRankFunction() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + DenseRankFunction function = new DenseRankFunction(); + List sortedColumns = Collections.singletonList(0); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, sortedColumns); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(column.getLong(i), expected[i]); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/NTileFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/NTileFunctionTest.java new file mode 100644 index 000000000000..92916a258a3b --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/NTileFunctionTest.java @@ -0,0 +1,128 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class NTileFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.INT64); + + @Test + public void testNTileFunctionWhenNIsLarge() { + int n = 5; + int[] inputs = {1, 2, 3}; + int[] expected = {1, 2, 3}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + NTileFunction function = new NTileFunction(n); + List sortedColumns = Collections.singletonList(0); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, sortedColumns); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(column.getLong(i), expected[i]); + } + } + + @Test + public void testNTileFunctionUniform() { + int n = 3; + int[] inputs = {1, 2, 3, 4, 5, 6}; + int[] expected = {1, 1, 2, 2, 3, 3}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + NTileFunction function = new NTileFunction(n); + List sortedColumns = Collections.singletonList(0); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, sortedColumns); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(column.getLong(i), expected[i]); + } + } + + @Test + public void testNTileFunctionNonUniform() { + int n = 3; + int[] inputs = {1, 2, 3, 4, 5, 6, 7}; + int[] expected = {1, 1, 1, 2, 2, 3, 3}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + NTileFunction function = new NTileFunction(n); + List sortedColumns = Collections.singletonList(0); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, sortedColumns); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(column.getLong(i), expected[i]); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/PercentRankFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/PercentRankFunctionTest.java new file mode 100644 index 000000000000..7883047e3bdc --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/PercentRankFunctionTest.java @@ -0,0 +1,72 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class PercentRankFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + private final int[] inputs = {1, 1, 2, 2, 3}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.DOUBLE); + private final double[] expected = {0, 0, 0.5, 0.5, 1}; + + @Test + public void testPercentRankFunction() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + PercentRankFunction function = new PercentRankFunction(); + List sortedColumns = Collections.singletonList(0); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, sortedColumns); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + // 0, 0.5 and 1 do not need delta + Assert.assertEquals(column.getDouble(i), expected[i], 0); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankFunctionTest.java new file mode 100644 index 000000000000..75c805a90bb7 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RankFunctionTest.java @@ -0,0 +1,71 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class RankFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + private final int[] inputs = {0, 1, 1, 1, 2, 2, 3, 4, 4, 5}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.INT64); + private final int[] expected = {1, 2, 2, 2, 5, 5, 7, 8, 8, 10}; + + @Test + public void testRankFunction() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + RankFunction function = new RankFunction(); + List sortedColumns = Collections.singletonList(0); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, sortedColumns); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(column.getLong(i), expected[i]); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RowNumberFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RowNumberFunctionTest.java new file mode 100644 index 000000000000..ce960505bf5b --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/rank/RowNumberFunctionTest.java @@ -0,0 +1,70 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.rank; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class RowNumberFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + private final int[] inputs = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.INT64); + private final int[] expected = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; + + @Test + public void testRowNumberFunction() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + RowNumberFunction function = new RowNumberFunction(); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(column.getLong(i), expected[i]); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/FirstValueFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/FirstValueFunctionTest.java new file mode 100644 index 000000000000..52a6011e3efc --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/FirstValueFunctionTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class FirstValueFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + // Inputs element less than 0 means this pos is null + private final int[] inputs = {0, -1, -1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1, -1, -1}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.INT32); + + @Test + public void testFirstValueFunctionIgnoreNull() { + int[] expected = {0, 0, 0, 1, 1, 1, 2, 3, 3, 4, 5, 5, 6, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs, 2, 2); + FirstValueFunction function = new FirstValueFunction(0, true); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(column.getInt(i), expected[i]); + } + } + } + + @Test + public void testFirstValueFunctionNotIgnoreNull() { + int[] expected = {0, 0, 0, -1, -1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs, 2, 2); + FirstValueFunction function = new FirstValueFunction(0, false); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(column.getInt(i), expected[i]); + } + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LagFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LagFunctionTest.java new file mode 100644 index 000000000000..367eb15bb6c5 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LagFunctionTest.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class LagFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + // Inputs element less than 0 means this pos is null + private final int[] inputs = {0, -1, -1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1, -1, -1}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.INT32); + + @Test + public void testLagFunctionIgnoreNullWithoutDefault() { + int[] expected = {-1, -1, -1, -1, 0, 1, 1, 2, 3, 3, 4, 5, 5, 5, 5, 5}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs); + LagFunction function = new LagFunction(0, 2, null, true); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(expected[i], column.getInt(i)); + } + } + } + + @Test + public void testLagFunctionIgnoreNullWithDefault() { + int[] expected = {10, 10, 10, 10, 0, 1, 1, 2, 3, 3, 4, 5, 5, 5, 5, 5}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs); + LagFunction function = new LagFunction(0, 2, 10, true); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(expected[i], column.getInt(i)); + } + } + + @Test + public void testLagFunctionNotIgnoreNullWithoutDefault() { + int[] expected = {-1, -1, 0, -1, -1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs); + LagFunction function = new LagFunction(0, 2, null, false); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(expected[i], column.getInt(i)); + } + } + } + + @Test + public void testLagFunctionNotIgnoreNullWithDefault() { + int[] expected = {10, 10, 0, -1, -1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs); + LagFunction function = new LagFunction(0, 2, 10, false); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(expected[i], column.getInt(i)); + } + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LastValueFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LastValueFunctionTest.java new file mode 100644 index 000000000000..64f1269917b8 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LastValueFunctionTest.java @@ -0,0 +1,120 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class LastValueFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + // Inputs element less than 0 means this pos is null + private final int[] inputs = {0, -1, -1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1, -1, -1}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.INT32); + + @Test + public void testLastValueFunctionIgnoreNull() { + int[] expected = {0, 1, 2, 2, 3, 4, 4, 5, 6, 6, 6, 6, 6, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs, 2, 2); + LastValueFunction function = new LastValueFunction(0, true); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(column.getInt(i), expected[i]); + } + } + } + + @Test + public void testLastValueFunctionNotIgnoreNull() { + int[] expected = {-1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1, -1, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs, 2, 2); + LastValueFunction function = new LastValueFunction(0, false); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(column.getInt(i), expected[i]); + } + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LeadFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LeadFunctionTest.java new file mode 100644 index 000000000000..90deb02b4151 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/LeadFunctionTest.java @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class LeadFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + // Inputs element less than 0 means this pos is null + private final int[] inputs = {0, -1, -1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1, -1, -1}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.INT32); + + @Test + public void testLeadFunctionIgnoreNullWithoutDefault() { + int[] expected = {2, 2, 2, 3, 4, 4, 5, 6, 6, -1, -1, -1, -1, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs); + LeadFunction function = new LeadFunction(0, 2, null, true); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(expected[i], column.getInt(i)); + } + } + } + + @Test + public void testLeadFunctionIgnoreNullWithDefault() { + int[] expected = {2, 2, 2, 3, 4, 4, 5, 6, 6, 10, 10, 10, 10, 10, 10, 10}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs); + LeadFunction function = new LeadFunction(0, 2, 10, true); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + Assert.assertEquals(expected[i], column.getInt(i)); + } + } + + @Test + public void testLeadFunctionNotIgnoreNullWithoutDefault() { + int[] expected = {-1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1, -1, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs); + LeadFunction function = new LeadFunction(0, 2, null, false); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(expected[i], column.getInt(i)); + } + } + } + + @Test + public void testLeadFunctionNotIgnoreNullWithDefault() { + int[] expected = {-1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1, -1, -1, 10, 10}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs); + LeadFunction function = new LeadFunction(0, 2, 10, false); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(expected[i], column.getInt(i)); + } + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/NthValueFunctionTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/NthValueFunctionTest.java new file mode 100644 index 000000000000..b350a2a7aa39 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/function/value/NthValueFunctionTest.java @@ -0,0 +1,150 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.function.value; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.function.FunctionTestUtils; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.PartitionExecutor; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame.FrameInfo; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.apache.tsfile.read.common.block.TsBlockBuilder; +import org.apache.tsfile.read.common.block.column.RunLengthEncodedColumn; +import org.junit.Assert; +import org.junit.Test; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + +import static org.apache.iotdb.db.queryengine.execution.operator.source.relational.TableScanOperator.TIME_COLUMN_TEMPLATE; + +public class NthValueFunctionTest { + private final List inputDataTypes = Collections.singletonList(TSDataType.INT32); + // Inputs element less than 0 means this pos is null + private final int[] inputs = {0, -1, -1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1, -1, -1}; + + private final List outputDataTypes = + Arrays.asList(TSDataType.INT32, TSDataType.INT32); + + @Test + public void testNthValueFunctionIgnoreNull() { + int[] expected = {-1, -1, 2, -1, 3, 3, 4, 5, 5, 6, -1, -1, -1, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs, 2, 2); + NthValueFunction function = new NthValueFunction(3, 0, true); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(expected[i], column.getInt(i)); + } + } + } + + @Test + public void testNthValueFunctionNotIgnoreNull() { + int[] expected = {-1, -1, -1, 1, 2, -1, 3, 4, -1, 5, 6, -1, -1, -1, -1, -1}; + + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs, 2, 2); + NthValueFunction function = new NthValueFunction(3, 0, false); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(expected.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), expected.length); + for (int i = 0; i < expected.length; i++) { + if (expected[i] < 0) { + Assert.assertTrue(column.isNull(i)); + } else { + Assert.assertEquals(column.getInt(i), expected[i]); + } + } + } + + @Test + public void testNthValueFunctionNotIgnoreNullOutOfBounds() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(inputs, 2, 2); + NthValueFunction function = new NthValueFunction(10, 0, false); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + PartitionExecutor partitionExecutor = + FunctionTestUtils.createPartitionExecutor(tsBlock, inputDataTypes, function, frameInfo); + + TsBlockBuilder tsBlockBuilder = new TsBlockBuilder(inputs.length, outputDataTypes); + while (partitionExecutor.hasNext()) { + partitionExecutor.processNextRow(tsBlockBuilder); + } + + TsBlock result = + tsBlockBuilder.build( + new RunLengthEncodedColumn(TIME_COLUMN_TEMPLATE, tsBlockBuilder.getPositionCount())); + Column column = result.getColumn(1); + + Assert.assertEquals(column.getPositionCount(), inputs.length); + for (int i = 0; i < inputs.length; i++) { + Assert.assertTrue(column.isNull(i)); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/FrameTestUtils.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/FrameTestUtils.java new file mode 100644 index 000000000000..d8a55d45aa29 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/FrameTestUtils.java @@ -0,0 +1,139 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.Partition; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.ColumnList; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.Range; +import org.apache.iotdb.db.queryengine.execution.operator.process.window.utils.RowComparator; + +import org.apache.tsfile.block.column.Column; +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class FrameTestUtils { + private final Partition partition; + private final List sortedColumns; + + private final int partitionStart; + private final int partitionEnd; + + private int currentGroupIndex = -1; + private int peerGroupStart; + private int peerGroupEnd; + + private final RowComparator peerGroupComparator; + private final Frame frame; + + private final List frameStarts; + private final List frameEnds; + + public FrameTestUtils(TsBlock tsBlock, TSDataType inputDataType, FrameInfo frameInfo) { + this.partition = tsBlockToPartition(tsBlock); + this.sortedColumns = this.partition.getSortedColumnList(Collections.singletonList(0)); + this.partitionStart = 0; + this.partitionEnd = tsBlock.getPositionCount(); + + this.peerGroupComparator = new RowComparator(Collections.singletonList(inputDataType)); + + updatePeerGroup(0); + this.frame = createFrame(frameInfo); + + this.frameStarts = new ArrayList<>(); + this.frameEnds = new ArrayList<>(); + } + + public void processAllRows() { + for (int i = partitionStart; i < partitionEnd; i++) { + if (i == peerGroupEnd) { + updatePeerGroup(i); + } + + Range range = frame.getRange(i, currentGroupIndex, peerGroupStart, peerGroupEnd); + this.frameStarts.add(range.getStart()); + this.frameEnds.add(range.getEnd()); + } + } + + public List getFrameStarts() { + return frameStarts; + } + + public List getFrameEnds() { + return frameEnds; + } + + private void updatePeerGroup(int index) { + currentGroupIndex++; + peerGroupStart = index; + // Find end of peer group + peerGroupEnd = peerGroupStart + 1; + while (peerGroupEnd < partitionEnd + && peerGroupComparator.equalColumnLists(sortedColumns, peerGroupStart, peerGroupEnd)) { + peerGroupEnd++; + } + } + + private Partition tsBlockToPartition(TsBlock tsBlock) { + return new Partition(Collections.singletonList(tsBlock), 0, tsBlock.getPositionCount()); + } + + private List tsBlockToColumnLists(TsBlock tsBlock) { + Column[] allColumns = tsBlock.getValueColumns(); + + List columnLists = new ArrayList<>(); + for (Column column : allColumns) { + ColumnList columnList = new ColumnList(Collections.singletonList(column)); + columnLists.add(columnList); + } + + return columnLists; + } + + private Frame createFrame(FrameInfo frameInfo) { + Frame frame; + switch (frameInfo.getFrameType()) { + case RANGE: + frame = new RangeFrame(partition, frameInfo, sortedColumns, peerGroupComparator); + break; + case ROWS: + frame = new RowsFrame(partition, frameInfo, partitionStart, partitionEnd); + break; + case GROUPS: + frame = + new GroupsFrame( + partition, + frameInfo, + sortedColumns, + peerGroupComparator, + peerGroupEnd - partitionStart - 1); + break; + default: + // Unreachable + throw new UnsupportedOperationException("Unreachable!"); + } + + return frame; + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/GroupsFrameTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/GroupsFrameTest.java new file mode 100644 index 000000000000..e937f147c58d --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/GroupsFrameTest.java @@ -0,0 +1,359 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class GroupsFrameTest { + private final int[] inputs = {1, 1, 2, 3, 3, 3}; + private final TSDataType dataType = TSDataType.INT32; + + @Test + public void testUnboundPrecedingAndPreceding() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.PRECEDING, + 1); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {-1, -1, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {-1, -1, 1, 2, 2, 2}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndCurrentRow() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.CURRENT_ROW); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.FOLLOWING, + 1); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {2, 2, 5, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {5, 5, 5, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndPreceding() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 2, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.PRECEDING, + 2); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {-1, -1, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {-1, -1, 1, 2, 2, 2}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndCurrentRow() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.CURRENT_ROW); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 2, 2, 2}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 2, 2, 2}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {2, 2, 5, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 2, 2, 2}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {5, 5, 5, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndCurrentRow() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.CURRENT_ROW); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 3}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.FOLLOWING, + 1); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 3}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {2, 2, 5, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 3}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {5, 5, 5, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {2, 2, 3, -1, -1, -1}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {5, 5, 5, -1, -1, -1}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.GROUPS, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {2, 2, 3, -1, -1, -1}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {5, 5, 5, -1, -1, -1}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RangeFrameTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RangeFrameTest.java new file mode 100644 index 000000000000..4c2885fccc13 --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RangeFrameTest.java @@ -0,0 +1,1127 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; +import org.apache.iotdb.db.queryengine.plan.relational.planner.SortOrder; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class RangeFrameTest { + // No PRECEDING and FOLLOWING involved + private final int[] inputs = {1, 1, 2, 3, 3, 3, 5, 5}; + + // For PRECEDING or FOLLOWING + private final int[] ascNullsFirst = {-1, -1, 1, 4, 4, 5, 7, 7}; + private final int[] ascNullsLast = {1, 4, 4, 5, 7, 7, -1, -1}; + private final int[] descNullsFirst = {-1, -1, 7, 7, 5, 4, 4, 1}; + private final int[] descNullsLast = {7, 7, 5, 4, 4, 1, -1, -1}; + + private final TSDataType dataType = TSDataType.INT32; + + @Test + public void testUnboundPrecedingAndPrecedingAscNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsFirst, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.PRECEDING, + 1, + 0, + SortOrder.ASC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 1, 2, 2, 4, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndPrecedingAscNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsLast, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.PRECEDING, + 1, + 0, + SortOrder.ASC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {-1, 0, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {-1, 0, 0, 2, 3, 3, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndPrecedingDescNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsFirst, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.PRECEDING, + 1, + 0, + SortOrder.DESC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 1, 1, 3, 4, 4, 6}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndPrecedingDescNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsLast, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.PRECEDING, + 1, + 0, + SortOrder.DESC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {-1, -1, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {-1, -1, 1, 2, 2, 4, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndCurrentRow() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.CURRENT_ROW); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 5, 5, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndFollowingAscNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsFirst, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + 0, + SortOrder.ASC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 5, 5, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndFollowingAscNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsLast, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + 0, + SortOrder.ASC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {0, 3, 3, 3, 5, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndFollowingDescNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsFirst, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + 0, + SortOrder.DESC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 3, 3, 6, 6, 6, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndFollowingDescNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsLast, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + 0, + SortOrder.DESC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 4, 4, 4, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndPrecedingAscNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsFirst, 2, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.PRECEDING, + 2, + 0, + SortOrder.ASC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, -1, -1, -1, 3, 5, 5}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, -1, -1, -1, 4, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndPrecedingAscNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsLast, 2, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.PRECEDING, + 2, + 0, + SortOrder.ASC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {-1, -1, -1, 1, 3, 3, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {-1, -1, -1, 2, 3, 3, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndPrecedingDescNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsFirst, 2, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.PRECEDING, + 2, + 0, + SortOrder.DESC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, -1, -1, 2, 4, 4, -1}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, -1, -1, 3, 4, 4, -1}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndPrecedingDescNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsLast, 2, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.PRECEDING, + 2, + 0, + SortOrder.DESC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {-1, -1, 0, 2, 2, -1, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {-1, -1, 1, 2, 2, -1, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndCurrentRowAscNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsFirst, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.CURRENT_ROW, + 0, + SortOrder.ASC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 3, 5, 5}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 4, 4, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndCurrentRowAscNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsLast, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.CURRENT_ROW, + 0, + SortOrder.ASC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 1, 1, 1, 3, 3, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {0, 2, 2, 3, 5, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndCurrentRowDescNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsFirst, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.CURRENT_ROW, + 0, + SortOrder.DESC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 2, 2, 4, 4, 7}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 3, 3, 4, 6, 6, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndCurrentRowDescNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsLast, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.CURRENT_ROW, + 0, + SortOrder.DESC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 2, 2, 5, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 4, 4, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndFollowingAscNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsFirst, 1, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2, + 0, + SortOrder.ASC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 3, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 5, 5, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndFollowingAscNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsLast, 1, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2, + 0, + SortOrder.ASC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 1, 1, 1, 4, 4, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {0, 3, 3, 3, 5, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndFollowingDescNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsFirst, 1, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2, + 0, + SortOrder.DESC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 2, 4, 4, 4, 7}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 3, 3, 6, 6, 6, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndFollowingDescNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsLast, 1, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2, + 0, + SortOrder.DESC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 2, 2, 5, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 4, 4, 4, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndUnboundFollowingAscNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsFirst, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING, + 0, + SortOrder.ASC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 3, 5, 5}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndUnboundFollowingAscNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsLast, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING, + 0, + SortOrder.ASC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 1, 1, 1, 3, 3, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndUnboundFollowingDescNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsFirst, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING, + 0, + SortOrder.DESC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 2, 2, 4, 4, 7}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndUnboundFollowingDescNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsLast, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING, + 0, + SortOrder.DESC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 2, 2, 5, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndCurrentRow() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.CURRENT_ROW); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 3, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 5, 5, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndFollowingAscNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsFirst, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + 0, + SortOrder.ASC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 5, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 2, 5, 5, 7, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndFollowingAscNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsLast, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + 0, + SortOrder.ASC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 1, 1, 3, 4, 4, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {0, 3, 3, 5, 5, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndFollowingDescNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsFirst, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + 0, + SortOrder.DESC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 2, 4, 5, 5, 7}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 4, 4, 6, 6, 6, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndFollowingDescNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsLast, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + 0, + SortOrder.DESC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 5, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {2, 2, 4, 4, 4, 5, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 2, 3, 3, 3, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndFollowingAscNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsFirst, 1, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2, + 0, + SortOrder.ASC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, -1, 5, 5, 6, -1, -1}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, -1, 5, 5, 7, -1, -1}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndFollowingAscNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsLast, 1, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2, + 0, + SortOrder.ASC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {-1, 3, 3, 4, -1, -1, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {-1, 3, 3, 5, -1, -1, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndFollowingDescNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsFirst, 1, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2, + 0, + SortOrder.DESC_NULLS_FIRST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 4, 4, 5, -1, -1, -1}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 1, 4, 4, 6, -1, -1, -1}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndFollowingDescNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsLast, 1, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2, + 0, + SortOrder.DESC_NULLS_LAST); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {2, 2, 3, -1, -1, -1, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {2, 2, 4, -1, -1, -1, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndUnboundFollowingAscNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsFirst, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING, + 0, + SortOrder.ASC_NULLS_FIRST); + + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 3, 6, 6, 6, -1, -1}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, -1, -1}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndUnboundFollowingAscNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(ascNullsLast, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING, + 0, + SortOrder.ASC_NULLS_LAST); + + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {1, 4, 4, 4, 6, 6, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndUnboundFollowingDescNullsFirst() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsFirst, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING, + 0, + SortOrder.DESC_NULLS_FIRST); + + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 4, 4, 7, 7, 7, -1}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, 7, -1}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndUnboundFollowingDescNullsLast() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithNulls(descNullsLast, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.RANGE, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING, + 0, + SortOrder.DESC_NULLS_LAST); + + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {2, 2, 5, 5, 5, 6, 6, 6}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {7, 7, 7, 7, 7, 7, 7, 7}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RowsFrameTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RowsFrameTest.java new file mode 100644 index 000000000000..19b0daeee49f --- /dev/null +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/execution/operator/process/window/partition/frame/RowsFrameTest.java @@ -0,0 +1,359 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.execution.operator.process.window.partition.frame; + +import org.apache.iotdb.db.queryengine.execution.operator.process.window.TableWindowOperatorTestUtils; + +import org.apache.tsfile.enums.TSDataType; +import org.apache.tsfile.read.common.block.TsBlock; +import org.junit.Assert; +import org.junit.Test; + +import java.util.List; + +public class RowsFrameTest { + private final int[] inputs = {0, 1, 2, 3, 4, 5}; + private final TSDataType dataType = TSDataType.INT32; + + @Test + public void testUnboundPrecedingAndPreceding() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.PRECEDING, + 1); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {-1, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {-1, 0, 1, 2, 3, 4}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndCurrentRow() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.CURRENT_ROW); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {0, 1, 2, 3, 4, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.FOLLOWING, + 1); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 2, 3, 4, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testUnboundPrecedingAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.UNBOUNDED_PRECEDING, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 0, 0, 0, 0}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {5, 5, 5, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndPreceding() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 2, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.PRECEDING, + 2); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {-1, 0, 0, 1, 2, 3}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {-1, 0, 1, 2, 3, 4}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndCurrentRow() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.CURRENT_ROW); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 1, 2, 3, 4}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {0, 1, 2, 3, 4, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 1, 2, 3, 4}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 2, 3, 4, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testPrecedingAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.PRECEDING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 0, 1, 2, 3, 4}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {5, 5, 5, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndCurrentRow() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.CURRENT_ROW); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 1, 2, 3, 4, 5}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {0, 1, 2, 3, 4, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.FOLLOWING, + 1); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 1, 2, 3, 4, 5}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {1, 2, 3, 4, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testCurrentRowAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.CURRENT_ROW, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {0, 1, 2, 3, 4, 5}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {5, 5, 5, 5, 5, 5}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1, 2); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.FOLLOWING, + 2); + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {1, 2, 3, 4, 5, -1}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {2, 3, 4, 5, 5, -1}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } + + @Test + public void testFollowingAndUnboundFollowing() { + TsBlock tsBlock = TableWindowOperatorTestUtils.createIntsTsBlockWithoutNulls(inputs, 1); + FrameInfo frameInfo = + new FrameInfo( + FrameInfo.FrameType.ROWS, + FrameInfo.FrameBoundType.FOLLOWING, + 1, + FrameInfo.FrameBoundType.UNBOUNDED_FOLLOWING); + + FrameTestUtils utils = new FrameTestUtils(tsBlock, dataType, frameInfo); + utils.processAllRows(); + + int[] expectedStarts = {1, 2, 3, 4, 5, -1}; + List actualStarts = utils.getFrameStarts(); + for (int i = 0; i < expectedStarts.length; i++) { + Assert.assertEquals(expectedStarts[i], (int) actualStarts.get(i)); + } + + int[] expectedEnds = {5, 5, 5, 5, 5, -1}; + List actualEnds = utils.getFrameEnds(); + for (int i = 0; i < expectedEnds.length; i++) { + Assert.assertEquals(expectedEnds[i], (int) actualEnds.get(i)); + } + } +} From 25128fcc1a75cae79afb8b2008540cb787ab9a38 Mon Sep 17 00:00:00 2001 From: Liao Lanyu <1435078631@qq.com> Date: Sat, 25 Jan 2025 12:31:52 +0800 Subject: [PATCH 3/3] Remove duplicate code in column transformer --- .../relational/ColumnTransformerBuilder.java | 234 ++++++------------ 1 file changed, 80 insertions(+), 154 deletions(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java index 55de53919385..ce69b23c3bcf 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/execution/relational/ColumnTransformerBuilder.java @@ -237,16 +237,8 @@ protected ColumnTransformer visitArithmeticBinary( throw new UnsupportedOperationException( String.format(UNSUPPORTED_EXPRESSION, node.getOperator())); } - TSDataType tsDataType = InternalTypeManager.getTSDataType(type); - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - type, context.originSize + context.commonTransformerList.size()); - ColumnTransformer columnTransformer = context.hasSeen.get(node); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(tsDataType); - context.cache.put(node, identity); + appendIdentityColumnTransformer( + node, type, InternalTypeManager.getTSDataType(type), context); } else { ZoneId zoneId = context.sessionInfo.getZoneId(); ColumnTransformer left = process(node.getLeft(), context); @@ -276,9 +268,7 @@ protected ColumnTransformer visitArithmeticBinary( context.cache.put(node, child); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } @Override @@ -290,15 +280,7 @@ protected ColumnTransformer visitArithmeticUnary( case MINUS: if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - DOUBLE, context.originSize + context.commonTransformerList.size()); - ColumnTransformer columnTransformer = context.hasSeen.get(node); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(TSDataType.DOUBLE); - context.cache.put(node, identity); + appendIdentityColumnTransformer(node, DOUBLE, TSDataType.DOUBLE, context); } else { ColumnTransformer childColumnTransformer = process(node.getValue(), context); context.cache.put( @@ -306,9 +288,7 @@ protected ColumnTransformer visitArithmeticUnary( ArithmeticColumnTransformerApi.getNegationTransformer(childColumnTransformer)); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); default: throw new UnsupportedOperationException("Unknown sign: " + node.getSign()); } @@ -318,15 +298,7 @@ protected ColumnTransformer visitArithmeticUnary( protected ColumnTransformer visitBetweenPredicate(BetweenPredicate node, Context context) { if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - BOOLEAN, context.originSize + context.commonTransformerList.size()); - ColumnTransformer columnTransformer = context.hasSeen.get(node); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(TSDataType.BOOLEAN); - context.cache.put(node, identity); + appendIdentityColumnTransformer(node, BOOLEAN, TSDataType.BOOLEAN, context); } else { ColumnTransformer value = this.process(node.getValue(), context); ColumnTransformer min = this.process(node.getMin(), context); @@ -334,9 +306,7 @@ protected ColumnTransformer visitBetweenPredicate(BetweenPredicate node, Context context.cache.put(node, new BetweenColumnTransformer(BOOLEAN, value, min, max, false)); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } @Override @@ -345,15 +315,12 @@ protected ColumnTransformer visitCast(Cast node, Context context) { if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { ColumnTransformer columnTransformer = context.hasSeen.get(node); - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - columnTransformer.getType(), - context.originSize + context.commonTransformerList.size()); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(getTSDataType(columnTransformer.getType())); - context.cache.put(node, identity); + appendIdentityColumnTransformer( + node, + columnTransformer.getType(), + getTSDataType(columnTransformer.getType()), + context, + columnTransformer); } else { ColumnTransformer child = this.process(node.getExpression(), context); Type type; @@ -369,9 +336,7 @@ protected ColumnTransformer visitCast(Cast node, Context context) { : new CastFunctionColumnTransformer(type, child, context.sessionInfo.getZoneId())); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } @Override @@ -642,24 +607,19 @@ protected ColumnTransformer visitFunctionCall(FunctionCall node, Context context if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { ColumnTransformer columnTransformer = context.hasSeen.get(node); - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - columnTransformer.getType(), - context.originSize + context.commonTransformerList.size()); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(getTSDataType(columnTransformer.getType())); - context.cache.put(node, identity); + appendIdentityColumnTransformer( + node, + columnTransformer.getType(), + getTSDataType(columnTransformer.getType()), + context, + columnTransformer); } else { context.cache.put( node, getFunctionColumnTransformer(node.getName().getSuffix(), node.getArguments(), context)); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } private ColumnTransformer getFunctionColumnTransformer( @@ -1047,15 +1007,7 @@ private ColumnTransformer getFunctionColumnTransformer( protected ColumnTransformer visitInPredicate(InPredicate node, Context context) { if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - BOOLEAN, context.originSize + context.commonTransformerList.size()); - ColumnTransformer columnTransformer = context.hasSeen.get(node); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(TSDataType.BOOLEAN); - context.cache.put(node, identity); + appendIdentityColumnTransformer(node, BOOLEAN, TSDataType.BOOLEAN, context); } else { ColumnTransformer childColumnTransformer = process(node.getValue(), context); TypeEnum childTypeEnum = childColumnTransformer.getType().getTypeEnum(); @@ -1076,9 +1028,7 @@ protected ColumnTransformer visitInPredicate(InPredicate node, Context context) } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } private static InMultiColumnTransformer constructInColumnTransformer( @@ -1181,38 +1131,20 @@ private static InMultiColumnTransformer constructInColumnTransformer( protected ColumnTransformer visitNotExpression(NotExpression node, Context context) { if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - BOOLEAN, context.originSize + context.commonTransformerList.size()); - ColumnTransformer columnTransformer = context.hasSeen.get(node); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(TSDataType.BOOLEAN); - context.cache.put(node, identity); + appendIdentityColumnTransformer(node, BOOLEAN, TSDataType.BOOLEAN, context); } else { ColumnTransformer childColumnTransformer = process(node.getValue(), context); context.cache.put(node, new LogicNotColumnTransformer(BOOLEAN, childColumnTransformer)); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } @Override protected ColumnTransformer visitLikePredicate(LikePredicate node, Context context) { if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - BOOLEAN, context.originSize + context.commonTransformerList.size()); - ColumnTransformer columnTransformer = context.hasSeen.get(node); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(TSDataType.BOOLEAN); - context.cache.put(node, identity); + appendIdentityColumnTransformer(node, BOOLEAN, TSDataType.BOOLEAN, context); } else { ColumnTransformer likeColumnTransformer = null; ColumnTransformer childColumnTransformer = process(node.getValue(), context); @@ -1247,56 +1179,34 @@ protected ColumnTransformer visitLikePredicate(LikePredicate node, Context conte context.cache.put(node, likeColumnTransformer); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } @Override protected ColumnTransformer visitIsNotNullPredicate(IsNotNullPredicate node, Context context) { if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - BOOLEAN, context.originSize + context.commonTransformerList.size()); - ColumnTransformer columnTransformer = context.hasSeen.get(node); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(TSDataType.BOOLEAN); - context.cache.put(node, identity); + appendIdentityColumnTransformer(node, BOOLEAN, TSDataType.BOOLEAN, context); } else { ColumnTransformer childColumnTransformer = process(node.getValue(), context); context.cache.put(node, new IsNullColumnTransformer(BOOLEAN, childColumnTransformer, true)); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } @Override protected ColumnTransformer visitIsNullPredicate(IsNullPredicate node, Context context) { if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - BOOLEAN, context.originSize + context.commonTransformerList.size()); - ColumnTransformer columnTransformer = context.hasSeen.get(node); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(TSDataType.BOOLEAN); - context.cache.put(node, identity); + appendIdentityColumnTransformer(node, BOOLEAN, TSDataType.BOOLEAN, context); } else { ColumnTransformer childColumnTransformer = process(node.getValue(), context); context.cache.put( node, new IsNullColumnTransformer(BOOLEAN, childColumnTransformer, false)); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } @Override @@ -1366,15 +1276,12 @@ protected ColumnTransformer visitCoalesceExpression(CoalesceExpression node, Con if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { ColumnTransformer columnTransformer = context.hasSeen.get(node); - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - columnTransformer.getType(), - context.originSize + context.commonTransformerList.size()); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(getTSDataType(columnTransformer.getType())); - context.cache.put(node, identity); + appendIdentityColumnTransformer( + node, + columnTransformer.getType(), + getTSDataType(columnTransformer.getType()), + context, + columnTransformer); } else { List children = node.getChildren().stream().map(c -> process(c, context)).collect(Collectors.toList()); @@ -1392,15 +1299,12 @@ protected ColumnTransformer visitSimpleCaseExpression( if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { ColumnTransformer columnTransformer = context.hasSeen.get(node); - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - columnTransformer.getType(), - context.originSize + context.commonTransformerList.size()); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(InternalTypeManager.getTSDataType(columnTransformer.getType())); - context.cache.put(node, identity); + appendIdentityColumnTransformer( + node, + columnTransformer.getType(), + getTSDataType(columnTransformer.getType()), + context, + columnTransformer); } else { List whenList = new ArrayList<>(); List thenList = new ArrayList<>(); @@ -1423,9 +1327,7 @@ protected ColumnTransformer visitSimpleCaseExpression( thenList.get(0).getType(), whenList, thenList, elseColumnTransformer)); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } @Override @@ -1434,15 +1336,12 @@ protected ColumnTransformer visitSearchedCaseExpression( if (!context.cache.containsKey(node)) { if (context.hasSeen.containsKey(node)) { ColumnTransformer columnTransformer = context.hasSeen.get(node); - IdentityColumnTransformer identity = - new IdentityColumnTransformer( - columnTransformer.getType(), - context.originSize + context.commonTransformerList.size()); - columnTransformer.addReferenceCount(); - context.commonTransformerList.add(columnTransformer); - context.leafList.add(identity); - context.inputDataTypes.add(InternalTypeManager.getTSDataType(columnTransformer.getType())); - context.cache.put(node, identity); + appendIdentityColumnTransformer( + node, + columnTransformer.getType(), + InternalTypeManager.getTSDataType(columnTransformer.getType()), + context, + columnTransformer); } else { List whenList = new ArrayList<>(); List thenList = new ArrayList<>(); @@ -1460,9 +1359,7 @@ protected ColumnTransformer visitSearchedCaseExpression( thenList.get(0).getType(), whenList, thenList, elseColumnTransformer)); } } - ColumnTransformer res = context.cache.get(node); - res.addReferenceCount(); - return res; + return getColumnTransformerFromCacheAndAddReferenceCount(node, context); } @Override @@ -1480,6 +1377,35 @@ protected ColumnTransformer visitNullIfExpression(NullIfExpression node, Context throw new UnsupportedOperationException(String.format(UNSUPPORTED_EXPRESSION, node)); } + private void appendIdentityColumnTransformer( + Expression expression, Type identityReturnType, TSDataType inputType, Context context) { + appendIdentityColumnTransformer( + expression, identityReturnType, inputType, context, context.hasSeen.get(expression)); + } + + private void appendIdentityColumnTransformer( + Expression expression, + Type identityReturnType, + TSDataType inputType, + Context context, + ColumnTransformer columnTransformer) { + IdentityColumnTransformer identity = + new IdentityColumnTransformer( + identityReturnType, context.originSize + context.commonTransformerList.size()); + columnTransformer.addReferenceCount(); + context.commonTransformerList.add(columnTransformer); + context.leafList.add(identity); + context.inputDataTypes.add(inputType); + context.cache.put(expression, identity); + } + + private ColumnTransformer getColumnTransformerFromCacheAndAddReferenceCount( + Expression expression, Context context) { + ColumnTransformer columnTransformer = context.cache.get(expression); + columnTransformer.addReferenceCount(); + return columnTransformer; + } + public static boolean isLongLiteral(Expression expression) { return expression instanceof LongLiteral; }