This is an automated email from the ASF dual-hosted git repository. jackietien pushed a commit to branch rc/2.0.6 in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit bb4f5c8c9a086014f8ddad78b189fe18d06fd276 Author: wenyanshi-123 <[email protected]> AuthorDate: Sat Sep 20 22:07:58 2025 +0800 Append user index field for User (#16432) (cherry picked from commit 4ebfe9eab86d8dc56d7534bf3b24346e3692bd08) --- .../org/apache/iotdb/db/it/IoTDBRestServiceIT.java | 6 +- .../org/apache/iotdb/db/it/auth/IoTDBAuthIT.java | 109 ++++++++++++--------- .../iotdb/db/it/auth/IoTDBRelationalAuthIT.java | 22 +++-- .../manual/IoTDBPipeMetaHistoricalIT.java | 4 +- .../treemodel/manual/IoTDBPipePermissionIT.java | 4 +- .../response/auth/PermissionInfoResp.java | 11 +++ .../confignode/manager/PermissionManager.java | 4 + .../pipe/event/PipeConfigRegionSnapshotEvent.java | 35 ++++--- .../receiver/protocol/IoTDBConfigNodeReceiver.java | 3 +- .../payload/PipeTransferConfigSnapshotSealReq.java | 8 +- .../sink/protocol/IoTDBConfigRegionAirGapSink.java | 3 +- .../pipe/sink/protocol/IoTDBConfigRegionSink.java | 3 +- .../pipe/source/ConfigRegionListeningQueue.java | 25 ++++- .../pipe/source/IoTDBConfigRegionSource.java | 3 +- .../iotdb/confignode/persistence/AuthorInfo.java | 34 ++++++- .../schema/CNPhysicalPlanGenerator.java | 14 ++- .../schema/ConfigNodeSnapshotParser.java | 5 +- .../thrift/ConfigNodeRPCServiceProcessor.java | 2 + .../pipe/sink/PipeConfigNodeThriftRequestTest.java | 3 +- .../persistence/CNPhysicalPlanGeneratorTest.java | 15 +-- .../org/apache/iotdb/db/auth/AuthorityChecker.java | 22 ++++- .../org/apache/iotdb/db/auth/entity/UserTest.java | 4 +- .../db/auth/user/LocalFileUserAccessorTest.java | 19 ++-- .../commons/auth/authorizer/BasicAuthorizer.java | 11 +++ .../iotdb/commons/auth/authorizer/IAuthorizer.java | 17 ++++ .../iotdb/commons/auth/entity/IEntityAccessor.java | 15 +++ .../org/apache/iotdb/commons/auth/entity/Role.java | 18 ++++ .../org/apache/iotdb/commons/auth/entity/User.java | 36 ++++++- .../iotdb/commons/auth/role/BasicRoleManager.java | 22 +++++ .../iotdb/commons/auth/role/IEntityManager.java | 9 ++ .../commons/auth/role/LocalFileRoleAccessor.java | 64 +++++++++++- .../iotdb/commons/auth/user/BasicUserManager.java | 60 +++++++++++- .../commons/auth/user/LocalFileUserAccessor.java | 47 +++++++-- .../commons/auth/user/LocalFileUserManager.java | 1 + .../schema/column/ColumnHeaderConstant.java | 7 ++ .../org/apache/iotdb/commons/utils/IOUtils.java | 30 ++++++ .../src/main/thrift/confignode.thrift | 8 ++ 37 files changed, 585 insertions(+), 118 deletions(-) diff --git a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBRestServiceIT.java b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBRestServiceIT.java index 99d4b1560f0..7d6d8774385 100644 --- a/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBRestServiceIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/db/it/IoTDBRestServiceIT.java @@ -1399,6 +1399,7 @@ public class IoTDBRestServiceIT { List<Object> columnNames = new ArrayList<Object>() { { + add(ColumnHeaderConstant.USER_ID); add(ColumnHeaderConstant.USER); } }; @@ -1409,7 +1410,7 @@ public class IoTDBRestServiceIT { } }; Assert.assertEquals(columnNames, columnNamesResult); - Assert.assertEquals(values1, valuesResult.get(0)); + Assert.assertEquals(values1, valuesResult.get(1)); } public void selectCount(CloseableHttpClient httpClient) { @@ -2062,6 +2063,7 @@ public class IoTDBRestServiceIT { List<Object> columnNames = new ArrayList<Object>() { { + add(ColumnHeaderConstant.USER_ID); add(ColumnHeaderConstant.USER); } }; @@ -2072,7 +2074,7 @@ public class IoTDBRestServiceIT { } }; Assert.assertEquals(columnNames, columnNamesResult); - Assert.assertEquals(values1, valuesResult.get(0)); + Assert.assertEquals(values1, valuesResult.get(1)); } public void selectCountV2(CloseableHttpClient httpClient) { 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 231d9ce7215..97bccfd263a 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 @@ -94,6 +94,17 @@ public class IoTDBAuthIT { Assert.assertThrows( SQLException.class, () -> userStmt.execute("GRANT WRITE_SCHEMA ON root.a TO USER tempuser")); + Assert.assertThrows( + SQLException.class, () -> userStmt.execute("LIST PRIVILEGES OF USER root")); + + ResultSet resultSet = userStmt.executeQuery("LIST USER"); + Assert.assertTrue(resultSet.next()); + Assert.assertEquals("10000", resultSet.getString(1)); + Assert.assertEquals("tempuser", resultSet.getString(2)); + Assert.assertFalse(resultSet.next()); + + resultSet = userStmt.executeQuery("LIST PRIVILEGES OF USER tempuser"); + Assert.assertFalse(resultSet.next()); // 2. admin grant all privileges to user tempuser, So tempuser can do anything. adminStmt.execute("GRANT ALL ON root.** TO USER tempuser"); @@ -479,7 +490,7 @@ public class IoTDBAuthIT { try { ResultSet resultSet = adminStmt.executeQuery("LIST USER"); - String ans = "root,\n"; + String ans = "0,root,\n"; try { validateResultSet(resultSet, ans); @@ -488,17 +499,17 @@ public class IoTDBAuthIT { } resultSet = adminStmt.executeQuery("LIST USER"); ans = - "root,\n" - + "user0,\n" - + "user1,\n" - + "user2,\n" - + "user3,\n" - + "user4,\n" - + "user5,\n" - + "user6,\n" - + "user7,\n" - + "user8,\n" - + "user9,\n"; + "0,root,\n" + + "10000,user0,\n" + + "10001,user1,\n" + + "10002,user2,\n" + + "10003,user3,\n" + + "10004,user4,\n" + + "10005,user5,\n" + + "10006,user6,\n" + + "10007,user7,\n" + + "10008,user8,\n" + + "10009,user9,\n"; validateResultSet(resultSet, ans); for (int i = 0; i < 10; i++) { @@ -507,7 +518,13 @@ public class IoTDBAuthIT { } } resultSet = adminStmt.executeQuery("LIST USER"); - ans = "root,\n" + "user1,\n" + "user3,\n" + "user5,\n" + "user7,\n" + "user9,\n"; + ans = + "0,root,\n" + + "10001,user1,\n" + + "10003,user3,\n" + + "10005,user5,\n" + + "10007,user7,\n" + + "10009,user9,\n"; validateResultSet(resultSet, ans); } finally { resultSet.close(); @@ -591,7 +608,7 @@ public class IoTDBAuthIT { ans = "role1,\nrole2,\n"; validateResultSet(resultSet, ans); resultSet = userStmt.executeQuery("LIST USER OF ROLE role1"); - ans = "user1,\nuser2,\n"; + ans = "10000,user1,\n10001,user2,\n"; validateResultSet(resultSet, ans); } finally { userStmt.close(); @@ -782,25 +799,25 @@ public class IoTDBAuthIT { ResultSet resultSet = adminStmt.executeQuery("LIST USER OF ROLE dalao"); String ans = - "DailySecurity,\n" - + "DoubleLight,\n" - + "East,\n" - + "Eastwards,\n" - + "GoldLuck,\n" - + "GoodWoods,\n" - + "HealthHonor,\n" - + "HighFly,\n" - + "Moon,\n" - + "Persistence,\n" - + "RayBud,\n" - + "ScentEffusion,\n" - + "Smart,\n" - + "SunComparison,\n"; + "10011,DailySecurity,\n" + + "10006,DoubleLight,\n" + + "10010,East,\n" + + "10007,Eastwards,\n" + + "10005,GoldLuck,\n" + + "10003,GoodWoods,\n" + + "10004,HealthHonor,\n" + + "10000,HighFly,\n" + + "10012,Moon,\n" + + "10002,Persistence,\n" + + "10013,RayBud,\n" + + "10008,ScentEffusion,\n" + + "10009,Smart,\n" + + "10001,SunComparison,\n"; try { validateResultSet(resultSet, ans); resultSet = adminStmt.executeQuery("LIST USER OF ROLE zhazha"); - ans = "RiverSky,\n"; + ans = "10014,RiverSky,\n"; validateResultSet(resultSet, ans); adminStmt.execute("REVOKE ROLE zhazha from RiverSky"); @@ -855,23 +872,25 @@ public class IoTDBAuthIT { try (Connection userCon = EnvFactory.getEnv().getConnection("tempuser", "temppw123456"); Statement userStmt = userCon.createStatement()) { try { - Assert.assertThrows(SQLException.class, () -> userStmt.execute("LIST USER")); - // with list user privilege - adminStmt.execute("GRANT MANAGE_USER on root.** TO USER tempuser"); + String ans = "10010,tempuser,\n"; ResultSet resultSet = userStmt.executeQuery("LIST USER"); - String ans = - "root,\n" - + "tempuser,\n" - + "user0,\n" - + "user1,\n" - + "user2,\n" - + "user3,\n" - + "user4,\n" - + "user5,\n" - + "user6,\n" - + "user7,\n" - + "user8,\n" - + "user9,\n"; + validateResultSet(resultSet, ans); + // with list user privilege + adminStmt.execute("GRANT SECURITY on root.** TO USER tempuser"); + resultSet = userStmt.executeQuery("LIST USER"); + ans = + "0,root,\n" + + "10010,tempuser,\n" + + "10000,user0,\n" + + "10001,user1,\n" + + "10002,user2,\n" + + "10003,user3,\n" + + "10004,user4,\n" + + "10005,user5,\n" + + "10006,user6,\n" + + "10007,user7,\n" + + "10008,user8,\n" + + "10009,user9,\n"; validateResultSet(resultSet, ans); } finally { userStmt.close(); 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 index f2f0aef18ba..a364630906f 100644 --- 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 @@ -66,6 +66,16 @@ public class IoTDBRelationalAuthIT { Statement adminStmt = adminCon.createStatement()) { adminStmt.execute("create user testuser 'password123456'"); + try (Connection userCon = + EnvFactory.getEnv() + .getConnection("testuser", "password123456", BaseEnv.TABLE_SQL_DIALECT); + Statement userStmt = userCon.createStatement()) { + ResultSet resultSet = userStmt.executeQuery("LIST USER"); + Assert.assertTrue(resultSet.next()); + Assert.assertEquals("10000", resultSet.getString(1)); + Assert.assertEquals("testuser", resultSet.getString(2)); + Assert.assertFalse(resultSet.next()); + } adminStmt.execute("create database testdb"); adminStmt.execute("GRANT MANAGE_USER to user testuser"); Assert.assertThrows( @@ -119,7 +129,7 @@ public class IoTDBRelationalAuthIT { adminStmt.execute("create role testrole"); adminStmt.execute("GRANT ROLE testrole to testuser"); rs = adminStmt.executeQuery("LIST USER OF ROLE testrole"); - TestUtils.assertResultSetEqual(rs, "User,", Collections.singleton("testuser,")); + TestUtils.assertResultSetEqual(rs, "UserId,User,", Collections.singleton("10000,testuser,")); rs = adminStmt.executeQuery("LIST ROLE OF USER testuser"); TestUtils.assertResultSetEqual(rs, "Role,", Collections.singleton("testrole,")); } @@ -600,11 +610,11 @@ public class IoTDBRelationalAuthIT { ResultSet resultSet = adminStmt.executeQuery("List user"); Set<String> resultSetList = new HashSet<>(); - resultSetList.add("root,"); - resultSetList.add("testuser,"); - resultSetList.add("!@#$%^*()_+-=1,"); - resultSetList.add("!@#$%^*()_+-=2,"); - TestUtils.assertResultSetEqual(resultSet, "User,", resultSetList); + resultSetList.add("0,root,"); + resultSetList.add("10000,testuser,"); + resultSetList.add("10001,!@#$%^*()_+-=1,"); + resultSetList.add("10002,!@#$%^*()_+-=2,"); + TestUtils.assertResultSetEqual(resultSet, "UserId,User,", resultSetList); resultSet = adminStmt.executeQuery("List role"); TestUtils.assertResultSetEqual(resultSet, "Role,", Collections.singleton("!@#$%^*()_+-=3,")); adminStmt.execute("GRANT role \"!@#$%^*()_+-=3\" to \"!@#$%^*()_+-=1\""); diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeMetaHistoricalIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeMetaHistoricalIT.java index 4e4120e1481..acbf514e8ad 100644 --- a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeMetaHistoricalIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipeMetaHistoricalIT.java @@ -236,8 +236,8 @@ public class IoTDBPipeMetaHistoricalIT extends AbstractPipeDualTreeModelManualIT TestUtils.assertDataEventuallyOnEnv( receiverEnv, "list user of role `admin`", - ColumnHeaderConstant.USER + ",", - Collections.singleton("thulab,")); + ColumnHeaderConstant.USER_ID + "," + ColumnHeaderConstant.USER + ",", + Collections.singleton("10000,thulab,")); TestUtils.assertDataEventuallyOnEnv( receiverEnv, "list privileges of role `admin`", diff --git a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipePermissionIT.java b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipePermissionIT.java index bdd9b850c8a..fc87b74b378 100644 --- a/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipePermissionIT.java +++ b/integration-test/src/test/java/org/apache/iotdb/pipe/it/dual/treemodel/manual/IoTDBPipePermissionIT.java @@ -152,8 +152,8 @@ public class IoTDBPipePermissionIT extends AbstractPipeDualTreeModelManualIT { TestUtils.assertDataEventuallyOnEnv( receiverEnv, "list user", - "User,", - new HashSet<>(Arrays.asList("root,", "user,", "thulab,"))); + "UserId,User,", + new HashSet<>(Arrays.asList("0,root,", "10001,user,", "10000,thulab,"))); final Set<String> expectedResSet = new HashSet<>(); expectedResSet.add( "root.ln.wf02.wt01.temperature,null,root.ln,INT64,PLAIN,LZ4,null,null,null,null,BASE,"); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/auth/PermissionInfoResp.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/auth/PermissionInfoResp.java index 70413d552db..de440336753 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/auth/PermissionInfoResp.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/auth/PermissionInfoResp.java @@ -20,6 +20,7 @@ package org.apache.iotdb.confignode.consensus.response.auth; import org.apache.iotdb.common.rpc.thrift.TSStatus; +import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo; import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp; import org.apache.iotdb.consensus.common.DataSet; @@ -32,6 +33,8 @@ public class PermissionInfoResp implements DataSet { private String tag; private List<String> memberList; + private List<TListUserInfo> usersInfo; + private TPermissionInfoResp permissionInfoResp; public PermissionInfoResp() {} @@ -62,6 +65,14 @@ public class PermissionInfoResp implements DataSet { return memberList; } + public void setUsersInfo(List<TListUserInfo> usersInfo) { + this.usersInfo = usersInfo; + } + + public List<TListUserInfo> getUsersInfo() { + return usersInfo; + } + public TPermissionInfoResp getPermissionInfoResp() { return permissionInfoResp; } 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 46549c67449..605e5785e9e 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 @@ -141,4 +141,8 @@ public class PermissionManager { public TPermissionInfoResp getUser(String username) throws AuthException { return authorInfo.getUser(username); } + + public String getUserName(long userId) throws AuthException { + return authorInfo.getUserName(userId); + } } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/event/PipeConfigRegionSnapshotEvent.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/event/PipeConfigRegionSnapshotEvent.java index df76f4fe7df..5a8465e99d5 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/event/PipeConfigRegionSnapshotEvent.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/event/PipeConfigRegionSnapshotEvent.java @@ -60,6 +60,8 @@ public class PipeConfigRegionSnapshotEvent extends PipeSnapshotEvent SNAPSHOT_FILE_TYPE_2_CONFIG_PHYSICAL_PLAN_TYPE_MAP = new EnumMap<>(CNSnapshotFileType.class); private CNSnapshotFileType fileType; + private String authUserName = ""; + static { SNAPSHOT_FILE_TYPE_2_CONFIG_PHYSICAL_PLAN_TYPE_MAP.put( CNSnapshotFileType.ROLE, @@ -136,6 +138,14 @@ public class PipeConfigRegionSnapshotEvent extends PipeSnapshotEvent this.fileType = type; } + public String getAuthUserName() { + return authUserName; + } + + public void setAuthUserName(String authUserName) { + this.authUserName = authUserName; + } + public File getSnapshotFile() { return new File(snapshotPath); } @@ -195,17 +205,20 @@ public class PipeConfigRegionSnapshotEvent extends PipeSnapshotEvent final boolean skipIfNoPrivileges, final long startTime, final long endTime) { - return new PipeConfigRegionSnapshotEvent( - snapshotPath, - templateFilePath, - fileType, - pipeName, - creationTime, - pipeTaskMeta, - treePattern, - tablePattern, - userName, - skipIfNoPrivileges); + PipeConfigRegionSnapshotEvent pipeConfigRegionSnapshotEvent = + new PipeConfigRegionSnapshotEvent( + snapshotPath, + templateFilePath, + fileType, + pipeName, + creationTime, + pipeTaskMeta, + treePattern, + tablePattern, + userName, + skipIfNoPrivileges); + pipeConfigRegionSnapshotEvent.setAuthUserName(authUserName); + return pipeConfigRegionSnapshotEvent; } @Override 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 5d00996ec61..d52ad5cd7e8 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 @@ -1033,7 +1033,8 @@ public class IoTDBConfigNodeReceiver extends IoTDBFileReceiver { Paths.get(fileAbsolutePaths.get(0)), fileAbsolutePaths.size() > 1 ? Paths.get(fileAbsolutePaths.get(1)) : null, CNSnapshotFileType.deserialize( - Byte.parseByte(parameters.get(PipeTransferConfigSnapshotSealReq.FILE_TYPE)))); + Byte.parseByte(parameters.get(PipeTransferConfigSnapshotSealReq.FILE_TYPE))), + parameters.getOrDefault("authUserName", "")); if (Objects.isNull(generator)) { throw new IOException( String.format("The config region snapshots %s cannot be parsed.", fileAbsolutePaths)); diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/payload/PipeTransferConfigSnapshotSealReq.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/payload/PipeTransferConfigSnapshotSealReq.java index 1162e8ade94..90a13f32e49 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/payload/PipeTransferConfigSnapshotSealReq.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/payload/PipeTransferConfigSnapshotSealReq.java @@ -58,7 +58,8 @@ public class PipeTransferConfigSnapshotSealReq extends PipeTransferFileSealReqV2 final String templateFileName, final long templateFileLength, final CNSnapshotFileType fileType, - final String typeString) + final String typeString, + final String authUserName) throws IOException { final Map<String, String> parameters = new HashMap<>(); parameters.put(ColumnHeaderConstant.PATH_PATTERN, treePattern); @@ -72,6 +73,7 @@ public class PipeTransferConfigSnapshotSealReq extends PipeTransferFileSealReqV2 } parameters.put(FILE_TYPE, Byte.toString(fileType.getType())); parameters.put(ColumnHeaderConstant.TYPE, typeString); + parameters.put("authUserName", authUserName); return (PipeTransferConfigSnapshotSealReq) new PipeTransferConfigSnapshotSealReq() @@ -103,7 +105,8 @@ public class PipeTransferConfigSnapshotSealReq extends PipeTransferFileSealReqV2 final String templateFileName, final long templateFileLength, final CNSnapshotFileType fileType, - final String typeString) + final String typeString, + final String authUserName) throws IOException { final Map<String, String> parameters = new HashMap<>(); parameters.put(ColumnHeaderConstant.PATH_PATTERN, treePattern); @@ -117,6 +120,7 @@ public class PipeTransferConfigSnapshotSealReq extends PipeTransferFileSealReqV2 } parameters.put(FILE_TYPE, Byte.toString(fileType.getType())); parameters.put(ColumnHeaderConstant.TYPE, typeString); + parameters.put("authUserName", authUserName); return new PipeTransferConfigSnapshotSealReq() .convertToTPipeTransferSnapshotSealBytes( Objects.nonNull(templateFileName) diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionAirGapSink.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionAirGapSink.java index c9c8ded4cf0..0ca96e021e3 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionAirGapSink.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionAirGapSink.java @@ -232,7 +232,8 @@ public class IoTDBConfigRegionAirGapSink extends IoTDBAirGapSink { Objects.nonNull(templateFile) ? templateFile.getName() : null, Objects.nonNull(templateFile) ? templateFile.length() : 0, pipeConfigRegionSnapshotEvent.getFileType(), - pipeConfigRegionSnapshotEvent.toSealTypeString()))) { + pipeConfigRegionSnapshotEvent.toSealTypeString(), + pipeConfigRegionSnapshotEvent.getAuthUserName()))) { final String errorMessage = String.format("Seal config region snapshot %s error. Socket %s.", snapshot, socket); // Send handshake because we don't know whether the receiver side configNode diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionSink.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionSink.java index a1e9239ccb2..7738ddbca12 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionSink.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/sink/protocol/IoTDBConfigRegionSink.java @@ -241,7 +241,8 @@ public class IoTDBConfigRegionSink extends IoTDBSslSyncSink { Objects.nonNull(templateFile) ? templateFile.getName() : null, Objects.nonNull(templateFile) ? templateFile.length() : 0, snapshotEvent.getFileType(), - snapshotEvent.toSealTypeString())); + snapshotEvent.toSealTypeString(), + snapshotEvent.getAuthUserName())); rateLimitIfNeeded( snapshotEvent.getPipeName(), snapshotEvent.getCreationTime(), diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/ConfigRegionListeningQueue.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/ConfigRegionListeningQueue.java index 94ea7a54dde..b70a039da44 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/ConfigRegionListeningQueue.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/ConfigRegionListeningQueue.java @@ -19,6 +19,7 @@ package org.apache.iotdb.confignode.manager.pipe.source; +import org.apache.iotdb.commons.auth.AuthException; import org.apache.iotdb.commons.auth.user.LocalFileUserAccessor; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.MetadataException; @@ -135,25 +136,39 @@ public class ConfigRegionListeningQueue extends AbstractPipeListeningQueue && snapshotPath .toFile() .getName() - .equals(AuthorityChecker.SUPER_USER + IoTDBConstant.PROFILE_SUFFIX) + .equals(AuthorityChecker.SUPER_USER_ID_IN_STR + IoTDBConstant.PROFILE_SUFFIX) || type == CNSnapshotFileType.USER_ROLE && snapshotPath .toFile() .getName() .equals( - AuthorityChecker.SUPER_USER + AuthorityChecker.SUPER_USER_ID_IN_STR + LocalFileUserAccessor.ROLE_SUFFIX - + IoTDBConstant.PROFILE_SUFFIX)) { + + IoTDBConstant.PROFILE_SUFFIX) + || snapshotPath.toFile().getName().equals("user_id.profile")) { continue; } final Path templateFilePath = snapshotPathInfo.getLeft().getRight(); - events.add( + PipeConfigRegionSnapshotEvent curEvent = new PipeConfigRegionSnapshotEvent( snapshotPath.toString(), Objects.nonNull(templateFilePath) && templateFilePath.toFile().length() > 0 ? templateFilePath.toString() : null, - snapshotPathInfo.getRight())); + snapshotPathInfo.getRight()); + if (type == CNSnapshotFileType.USER_ROLE) { + long userId = Long.parseLong(snapshotPath.toFile().getName().split("_")[0]); + try { + curEvent.setAuthUserName( + ConfigNode.getInstance() + .getConfigManager() + .getPermissionManager() + .getUserName(userId)); + } catch (AuthException e) { + LOGGER.warn("Failed to collect user name for user id {}", userId, e); + } + } + events.add(curEvent); } tryListen(events); } diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/IoTDBConfigRegionSource.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/IoTDBConfigRegionSource.java index ef1e08f8536..fc965624c1d 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/IoTDBConfigRegionSource.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/manager/pipe/source/IoTDBConfigRegionSource.java @@ -203,7 +203,8 @@ public class IoTDBConfigRegionSource extends IoTDBNonDataRegionSource { Objects.nonNull(snapshotEvent.getTemplateFile()) ? Paths.get(snapshotEvent.getTemplateFile().getPath()) : null, - snapshotEvent.getFileType()); + snapshotEvent.getFileType(), + snapshotEvent.getAuthUserName()); } @Override 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 bafb17ce87e..d6a0ee2a283 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 @@ -47,6 +47,7 @@ import org.apache.iotdb.confignode.consensus.request.write.auth.AuthorTreePlan; import org.apache.iotdb.confignode.consensus.response.auth.PermissionInfoResp; import org.apache.iotdb.confignode.manager.ConfigManager; import org.apache.iotdb.confignode.rpc.thrift.TAuthizedPatternTreeResp; +import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo; import org.apache.iotdb.confignode.rpc.thrift.TPermissionInfoResp; import org.apache.iotdb.confignode.rpc.thrift.TRoleResp; import org.apache.iotdb.confignode.rpc.thrift.TUserResp; @@ -63,6 +64,7 @@ import java.io.File; 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.Iterator; @@ -502,7 +504,19 @@ public class AuthorInfo implements SnapshotProcessor { public PermissionInfoResp executeListUsers(final AuthorPlan plan) throws AuthException { final PermissionInfoResp result = new PermissionInfoResp(); - final List<String> userList = authorizer.listAllUsers(); + final List<String> userList; + final List<TListUserInfo> userInfoList; + boolean hasPermissionToListOtherUsers = plan.getUserName().isEmpty(); + if (!hasPermissionToListOtherUsers) { + // userList may be modified later + userList = new ArrayList<>(1); + userList.add(plan.getUserName()); + User user = authorizer.getUser(plan.getUserName()); + userInfoList = Collections.singletonList(user.convertToListUserInfo()); + } else { + userList = authorizer.listAllUsers(); + userInfoList = authorizer.listAllUsersInfo(); + } if (!plan.getRoleName().isEmpty()) { final Role role = authorizer.getRole(plan.getRoleName()); if (role == null) { @@ -512,8 +526,19 @@ public class AuthorInfo implements SnapshotProcessor { return result; } final Iterator<String> itr = userList.iterator(); + Set<String> toRemove = new HashSet<>(); while (itr.hasNext()) { - User userObj = authorizer.getUser(itr.next()); + String userName = itr.next(); + User userObj = authorizer.getUser(userName); + if (userObj == null || !userObj.hasRole(plan.getRoleName())) { + itr.remove(); + toRemove.add(userName); + } + } + userInfoList.removeIf(info -> toRemove.contains(info.username)); + final Iterator<TListUserInfo> userInfoitr = userInfoList.iterator(); + while (itr.hasNext()) { + User userObj = authorizer.getUser(userInfoitr.next().getUsername()); if (userObj == null || !userObj.hasRole(plan.getRoleName())) { itr.remove(); } @@ -521,6 +546,7 @@ public class AuthorInfo implements SnapshotProcessor { } result.setTag(ColumnHeaderConstant.USER); result.setMemberInfo(userList); + result.setUsersInfo(userInfoList); result.setStatus(RpcUtils.getStatus(TSStatusCode.SUCCESS_STATUS)); return result; } @@ -665,6 +691,10 @@ public class AuthorInfo implements SnapshotProcessor { return result; } + public String getUserName(long userId) throws AuthException { + return authorizer.getUser(userId).getName(); + } + @Override public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { return authorizer.processTakeSnapshot(snapshotDir); 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 33fb4b4ed6d..b81b14ef504 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 @@ -103,14 +103,15 @@ public class CNPhysicalPlanGenerator private Exception latestException = null; private String userName; - public CNPhysicalPlanGenerator(final Path snapshotFilePath, final CNSnapshotFileType fileType) + public CNPhysicalPlanGenerator( + final Path snapshotFilePath, final CNSnapshotFileType fileType, final String userName) throws IOException { if (fileType == CNSnapshotFileType.SCHEMA) { logger.warn("schema_template need two files"); return; } if (fileType == CNSnapshotFileType.USER_ROLE) { - userName = snapshotFilePath.getFileName().toString().split("_role.profile")[0]; + this.userName = userName; } snapshotFileType = fileType; inputStream = Files.newInputStream(snapshotFilePath); @@ -200,9 +201,14 @@ public class CNPhysicalPlanGenerator int tag = dataInputStream.readInt(); boolean fromOldVersion = tag < 0; String user; - if (fromOldVersion) { + if (tag < 0) { user = readString(dataInputStream, STRING_ENCODING, strBufferLocal, -1 * tag); + } else if (tag == 1) { + user = readString(dataInputStream, STRING_ENCODING, strBufferLocal); } else { + if (isUser) { + dataInputStream.readLong(); // skip userId since authorPlan do not demand it. + } user = readString(dataInputStream, STRING_ENCODING, strBufferLocal); } @@ -226,7 +232,7 @@ public class CNPhysicalPlanGenerator final int privilegeMask = dataInputStream.readInt(); generateGrantSysPlan(user, isUser, privilegeMask); - if (fromOldVersion) { + if (tag < 0) { while (dataInputStream.available() != 0) { final String path = readString(dataInputStream, STRING_ENCODING, strBufferLocal); final PartialPath priPath; diff --git a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ConfigNodeSnapshotParser.java b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ConfigNodeSnapshotParser.java index 31b1cd56519..84126441694 100644 --- a/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ConfigNodeSnapshotParser.java +++ b/iotdb-core/confignode/src/main/java/org/apache/iotdb/confignode/persistence/schema/ConfigNodeSnapshotParser.java @@ -166,7 +166,8 @@ public class ConfigNodeSnapshotParser { } public static CNPhysicalPlanGenerator translate2PhysicalPlan( - final Path path1, final Path path2, final CNSnapshotFileType type) throws IOException { + final Path path1, final Path path2, final CNSnapshotFileType type, final String userName) + throws IOException { if (path1 == null) { LOGGER.warn("Path1 should not be null"); return null; @@ -180,7 +181,7 @@ public class ConfigNodeSnapshotParser { if (type == CNSnapshotFileType.SCHEMA) { return new CNPhysicalPlanGenerator(path1, path2); } else { - return new CNPhysicalPlanGenerator(path1, type); + return new CNPhysicalPlanGenerator(path1, type, userName); } } } 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 431d0d4f952..77192cd5f34 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 @@ -687,6 +687,7 @@ public class ConfigNodeRPCServiceProcessor implements IConfigNodeRPCService.Ifac resp.setMemberInfo(dataSet.getMemberList()); resp.setPermissionInfo(dataSet.getPermissionInfoResp()); resp.setTag(dataSet.getTag()); + resp.setUsersInfo(dataSet.getUsersInfo()); return resp; } @@ -729,6 +730,7 @@ public class ConfigNodeRPCServiceProcessor implements IConfigNodeRPCService.Ifac final TAuthorizerResp resp = new TAuthorizerResp(dataSet.getStatus()); resp.setMemberInfo(dataSet.getMemberList()); resp.setPermissionInfo(dataSet.getPermissionInfoResp()); + resp.setUsersInfo(dataSet.getUsersInfo()); resp.setTag(dataSet.getTag()); return resp; } diff --git a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/sink/PipeConfigNodeThriftRequestTest.java b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/sink/PipeConfigNodeThriftRequestTest.java index 0f729c7b4b0..5c578ebead8 100644 --- a/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/sink/PipeConfigNodeThriftRequestTest.java +++ b/iotdb-core/confignode/src/test/java/org/apache/iotdb/confignode/manager/pipe/sink/PipeConfigNodeThriftRequestTest.java @@ -96,7 +96,8 @@ public class PipeConfigNodeThriftRequestTest { templateInfoName, 10, fileType, - typeString); + typeString, + ""); PipeTransferConfigSnapshotSealReq deserializeReq = PipeTransferConfigSnapshotSealReq.fromTPipeTransferReq(req); 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 647b50fdcf5..19622c80906 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 @@ -178,7 +178,7 @@ public class CNPhysicalPlanGeneratorTest { + ".profile"); final CNPhysicalPlanGenerator planGenerator = - new CNPhysicalPlanGenerator(roleProfile.toPath(), CNSnapshotFileType.ROLE); + new CNPhysicalPlanGenerator(roleProfile.toPath(), CNSnapshotFileType.ROLE, ""); int count = 0; for (ConfigPhysicalPlan authPlan : planGenerator) { Assert.assertTrue(answerSet.contains(authPlan.hashCode())); @@ -260,11 +260,11 @@ public class CNPhysicalPlanGeneratorTest { + File.separator + USER_SNAPSHOT_FILE_NAME + File.separator - + userName + + 10000 + ".profile"); CNPhysicalPlanGenerator planGenerator = - new CNPhysicalPlanGenerator(userProfile.toPath(), CNSnapshotFileType.USER); + new CNPhysicalPlanGenerator(userProfile.toPath(), CNSnapshotFileType.USER, userName); int count = 0; // plan 1-4 for (ConfigPhysicalPlan authPlan : planGenerator) { @@ -278,10 +278,11 @@ public class CNPhysicalPlanGeneratorTest { + File.separator + USER_SNAPSHOT_FILE_NAME + File.separator - + userName + + 10000 + "_role.profile"); planGenerator = - new CNPhysicalPlanGenerator(roleListProfile.toPath(), CNSnapshotFileType.USER_ROLE); + new CNPhysicalPlanGenerator( + roleListProfile.toPath(), CNSnapshotFileType.USER_ROLE, userName); count = 0; // plan 5 for (ConfigPhysicalPlan authPlan : planGenerator) { @@ -345,7 +346,7 @@ public class CNPhysicalPlanGeneratorTest { } planGenerator.checkException(); Assert.assertEquals(5, count); - planGenerator = new CNPhysicalPlanGenerator(ttlInfo.toPath(), CNSnapshotFileType.TTL); + planGenerator = new CNPhysicalPlanGenerator(ttlInfo.toPath(), CNSnapshotFileType.TTL, ""); for (ConfigPhysicalPlan plan : planGenerator) { if (plan.getType() == ConfigPhysicalPlanType.SetTTL) { if (!new PartialPath(((SetTTLPlan) plan).getPathPattern()) @@ -510,7 +511,7 @@ public class CNPhysicalPlanGeneratorTest { } Assert.assertEquals(8, count); - planGenerator = new CNPhysicalPlanGenerator(ttlInfo.toPath(), CNSnapshotFileType.TTL); + planGenerator = new CNPhysicalPlanGenerator(ttlInfo.toPath(), CNSnapshotFileType.TTL, ""); for (ConfigPhysicalPlan plan : planGenerator) { if (plan.getType() == ConfigPhysicalPlanType.SetTTL) { if (!new PartialPath(((SetTTLPlan) plan).getPathPattern()) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/auth/AuthorityChecker.java index 71f064008df..988d69d0062 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 @@ -30,6 +30,7 @@ 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.TListUserInfo; import org.apache.iotdb.confignode.rpc.thrift.TPathPrivilege; import org.apache.iotdb.confignode.rpc.thrift.TRoleResp; import org.apache.iotdb.confignode.rpc.thrift.TTablePrivilege; @@ -57,6 +58,7 @@ import java.util.Map; import java.util.Set; import java.util.stream.Collectors; +import static org.apache.iotdb.commons.schema.column.ColumnHeaderConstant.LIST_USER_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. @@ -65,6 +67,8 @@ public class AuthorityChecker { public static final String SUPER_USER = CommonDescriptor.getInstance().getConfig().getAdminName(); + public static String SUPER_USER_ID_IN_STR = "0"; + public static final TSStatus SUCCEED = new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()); public static final String ONLY_ADMIN_ALLOWED = @@ -336,7 +340,7 @@ public class AuthorityChecker { List<ColumnHeader> headerList = new ArrayList<>(); TsBlockBuilder builder; - if (listRoleUser) { + if (authResp.tag.equals(ColumnHeaderConstant.ROLE)) { headerList.add(new ColumnHeader(authResp.getTag(), TSDataType.TEXT)); types.add(TSDataType.TEXT); builder = new TsBlockBuilder(types); @@ -345,6 +349,22 @@ public class AuthorityChecker { builder.getColumnBuilder(0).writeBinary(new Binary(name, TSFileConfig.STRING_CHARSET)); builder.declarePosition(); } + } else if (authResp.tag.equals(ColumnHeaderConstant.USER)) { + headerList = LIST_USER_COLUMN_HEADERS; + types = + LIST_USER_COLUMN_HEADERS.stream() + .map(ColumnHeader::getColumnType) + .collect(Collectors.toList()); + builder = new TsBlockBuilder(types); + for (TListUserInfo userinfo : authResp.getUsersInfo()) { + builder.getTimeColumnBuilder().writeLong(0L); + builder.getColumnBuilder(0).writeLong(userinfo.getUserId()); + builder + .getColumnBuilder(1) + .writeBinary(new Binary(userinfo.getUsername(), TSFileConfig.STRING_CHARSET)); + builder.declarePosition(); + } + } else { headerList = LIST_USER_OR_ROLE_PRIVILEGES_COLUMN_HEADERS; types = 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 eee3be0bc73..82e7d8f15b1 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 @@ -39,13 +39,13 @@ public class UserTest { user.setPathPrivileges( new PartialPath("root.ln"), Collections.singleton(PrivilegeType.WRITE_DATA)); Assert.assertEquals( - "User{name='user', pathPrivilegeList=[root.ln : WRITE_DATA], " + "User{id=-1, name='user', 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', pathPrivilegeList=[root.ln : WRITE_DATA], " + "User{id=-1, name='user', pathPrivilegeList=[root.ln : WRITE_DATA], " + "sysPrivilegeSet=[], AnyScopePrivilegeMap=[], objectPrivilegeMap={}, roleList=[], isOpenIdUser=false}", user1.toString()); Assert.assertEquals(user1, user); 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 54442238891..eafb95b0824 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 @@ -66,6 +66,7 @@ public class LocalFileUserAccessorTest { @Test public void test() throws IOException, IllegalPathException { User user = new User("test", "password123456"); + user.setUserId(5); user.grantSysPrivilege(PrivilegeType.EXTEND_TEMPLATE, false); user.grantSysPrivilege(PrivilegeType.MANAGE_USER, false); PathPrivilege pathPrivilege = new PathPrivilege(new PartialPath("root.test")); @@ -80,23 +81,24 @@ public class LocalFileUserAccessorTest { user.addRole("testRole2"); accessor.saveEntity(user); accessor.reset(); - User loadUser = accessor.loadEntity("test"); + User loadUser = accessor.loadEntity("5"); assertEquals(user, loadUser); user.setName("test1"); + user.setUserId(6); accessor.saveEntity(user); // list List<String> usernames = accessor.listAllEntities(); usernames.sort(null); - assertTrue(usernames.contains("test")); - assertTrue(usernames.contains("test1")); + assertTrue(usernames.contains("5")); + assertTrue(usernames.contains("6")); // delete assertFalse(accessor.deleteEntity("not a user")); - assertTrue(accessor.deleteEntity(user.getName())); + assertTrue(accessor.deleteEntity(String.valueOf(user.getUserId()))); usernames = accessor.listAllEntities(); assertEquals(1, usernames.size()); - assertTrue(usernames.contains("test")); + assertTrue(usernames.contains("5")); User nullUser = accessor.loadEntity(user.getName()); assertNull(nullUser); } @@ -127,9 +129,14 @@ public class LocalFileUserAccessorTest { accessor.saveUserOldVersion(role); User newRole = accessor.loadEntity("root"); assertEquals(role, newRole); + newRole.setName("root1"); + accessor.saveUserOldVersion1(newRole); + User newRole1 = accessor.loadEntity("root1"); + assertEquals(newRole, newRole1); newRole.setName("root2"); + newRole.setUserId(10000); accessor.saveEntity(newRole); - User newRole2 = accessor.loadEntity("root2"); + User newRole2 = accessor.loadEntity("10000"); assertEquals(newRole, newRole2); } } 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 6f373dd49ea..2c4ac064599 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 @@ -32,6 +32,7 @@ import org.apache.iotdb.commons.security.encrypt.AsymmetricEncrypt; 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.confignode.rpc.thrift.TListUserInfo; import org.apache.iotdb.rpc.TSStatusCode; import org.apache.thrift.TException; @@ -453,6 +454,11 @@ public abstract class BasicAuthorizer implements IAuthorizer, IService { return userManager.listAllEntities(); } + @Override + public List<TListUserInfo> listAllUsersInfo() { + return userManager.listAllEntitiesInfo(); + } + @Override public List<String> listAllRoles() { return roleManager.listAllEntities(); @@ -468,6 +474,11 @@ public abstract class BasicAuthorizer implements IAuthorizer, IService { return userManager.getEntity(username); } + @Override + public User getUser(long userId) throws AuthException { + return userManager.getEntity(userId); + } + @Override public boolean processTakeSnapshot(File snapshotDir) throws TException, IOException { return userManager.processTakeSnapshot(snapshotDir) 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 82143345ba5..f00d28ccade 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 @@ -26,6 +26,7 @@ import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.path.PartialPath; import org.apache.iotdb.commons.snapshot.SnapshotProcessor; +import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo; import java.util.List; import java.util.Map; @@ -184,6 +185,14 @@ public interface IAuthorizer extends SnapshotProcessor { */ List<String> listAllUsers(); + /** + * List existing users info in the database. + * + * @return A list contains all users' baisc info including userid, username,maxSessionPerUser and + * minSessionPerUser. + */ + List<TListUserInfo> listAllUsersInfo(); + /** * List existing roles in the database. * @@ -207,6 +216,14 @@ public interface IAuthorizer extends SnapshotProcessor { */ User getUser(String username) throws AuthException; + /** + * Find a user by its userId. + * + * @param userId the index of the user. + * @return A user whose id is userId or null if such user does not exist. + */ + User getUser(long userId) throws AuthException; + /** * get all user * diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java index 3c42d1a1dec..972106467e2 100644 --- a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/IEntityAccessor.java @@ -35,6 +35,21 @@ public interface IEntityAccessor extends SnapshotProcessor { */ Role loadEntity(String entityName) throws IOException; + /** + * Deserialize userid from lower storage. + * + * @return The max userid. + * @throws IOException if an exception is raised when interacting with the lower storage. + */ + long loadUserId() throws IOException; + + /** + * save maxUserId to lower storage when snapshot. + * + * @throws IOException if an exception is raised when interacting with the lower storage. + */ + void saveUserId(long nextUserId) throws IOException; + /** * Serialize the entity object to lower storage. * diff --git a/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/auth/entity/Role.java index 21481de80c4..f54ed493af1 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 @@ -44,6 +44,8 @@ import java.util.Set; public class Role { protected String name; + protected int maxSessionPerUser = -1; + protected int minSessionPerUser = -1; protected List<PathPrivilege> pathPrivilegeList; protected Map<String, DatabasePrivilege> objectPrivilegeMap; @@ -81,6 +83,14 @@ public class Role { return name; } + public int getMaxSessionPerUser() { + return maxSessionPerUser; + } + + public int getMinSessionPerUser() { + return minSessionPerUser; + } + public List<PathPrivilege> getPathPrivilegeList() { return pathPrivilegeList; } @@ -250,6 +260,14 @@ public class Role { this.name = name; } + public void setMaxSessionPerUser(int maxSessionPerUser) { + this.maxSessionPerUser = maxSessionPerUser; + } + + public void setMinSessionPerUser(int minSessionPerUser) { + this.minSessionPerUser = minSessionPerUser; + } + public void setPrivilegeList(List<PathPrivilege> privilegeList) { this.pathPrivilegeList = privilegeList; } 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 c0098cf0d51..d7963bc3d77 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,8 @@ package org.apache.iotdb.commons.auth.entity; import org.apache.iotdb.commons.utils.SerializeUtils; +import org.apache.iotdb.commons.utils.TestOnly; +import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo; import org.apache.iotdb.confignode.rpc.thrift.TUserResp; import java.io.ByteArrayOutputStream; @@ -34,6 +36,8 @@ import java.util.Set; /** This class contains all information of a User. */ public class User extends Role { + private long userId = -1; + private String password; private Set<String> roleSet; @@ -44,15 +48,24 @@ public class User extends Role { // empty constructor } + @TestOnly + public User(String name, String password) { + super(name); + this.password = password; + this.roleSet = new HashSet<>(); + } + /** * construct function for User. * * @param name -user name * @param password -user password + * @param userId -user index */ - public User(String name, String password) { + public User(String name, String password, long userId) { super(name); this.password = password; + this.userId = userId; this.roleSet = new HashSet<>(); } @@ -73,7 +86,15 @@ public class User extends Role { roleSet.add(roleName); } + public void setUserId(long userId) { + this.userId = userId; + } + /** ------------ get func ----------------* */ + public long getUserId() { + return userId; + } + public String getPassword() { return password; } @@ -99,6 +120,15 @@ public class User extends Role { return resp; } + public TListUserInfo convertToListUserInfo() { + TListUserInfo userInfo = new TListUserInfo(); + userInfo.setUserId(userId); + userInfo.setUsername(name); + userInfo.setMaxSessionPerUser(maxSessionPerUser); + userInfo.setMinSessionPerUser(minSessionPerUser); + return userInfo; + } + /** -------------- misc ----------------* */ @Override public boolean equals(Object o) { @@ -196,7 +226,9 @@ public class User extends Role { @Override public String toString() { return "User{" - + "name='" + + "id=" + + userId + + ", name='" + super.getName() + '\'' + ", pathPrivilegeList=" 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 17cda526d34..f6804d3055c 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 @@ -23,9 +23,11 @@ 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.auth.entity.User; import org.apache.iotdb.commons.concurrent.HashLock; import org.apache.iotdb.commons.snapshot.SnapshotProcessor; import org.apache.iotdb.commons.utils.AuthUtils; +import org.apache.iotdb.confignode.rpc.thrift.TListUserInfo; import org.apache.iotdb.rpc.TSStatusCode; import org.slf4j.Logger; @@ -33,6 +35,7 @@ import org.slf4j.LoggerFactory; import java.io.IOException; import java.util.ArrayList; +import java.util.Comparator; import java.util.HashMap; import java.util.List; import java.util.Map; @@ -78,6 +81,10 @@ public abstract class BasicRoleManager implements IEntityManager, SnapshotProces return role; } + public Role getEntity(long entityId) { + return null; + } + public boolean createRole(String entityName) { Role role = getEntity(entityName); if (role != null) { @@ -229,4 +236,19 @@ public abstract class BasicRoleManager implements IEntityManager, SnapshotProces rtlist.sort(null); return rtlist; } + + public List<TListUserInfo> listAllEntitiesInfo() { + + List<TListUserInfo> rtlist = new ArrayList<>(); + for (Role r : entityMap.values()) { + TListUserInfo userInfo = new TListUserInfo(); + userInfo.userId = ((User) r).getUserId(); + userInfo.username = r.getName(); + userInfo.maxSessionPerUser = r.getMaxSessionPerUser(); + userInfo.minSessionPerUser = r.getMinSessionPerUser(); + rtlist.add(userInfo); + } + rtlist.sort(Comparator.comparingLong(TListUserInfo::getUserId)); + return rtlist; + } } 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 index d70c18273f0..c1331fa8483 100644 --- 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 @@ -37,6 +37,15 @@ public interface IEntityManager extends SnapshotProcessor { */ Role getEntity(String entityName) throws AuthException; + /** + * Get an entity object. + * + * @param entityId The id of the entity. + * @return An entity object whose index is entityId or null if such entity does not exist. + * @throws AuthException if exception is raised while getting the entity. + */ + Role getEntity(long entityId) throws AuthException; + /** * Delete an entity. * 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 809d2397f3b..07bb08ca7de 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 @@ -22,6 +22,7 @@ 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.auth.entity.User; import org.apache.iotdb.commons.conf.CommonDescriptor; import org.apache.iotdb.commons.conf.IoTDBConstant; import org.apache.iotdb.commons.exception.IllegalPathException; @@ -84,7 +85,7 @@ public class LocalFileRoleAccessor implements IEntityAccessor { // 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; + protected static final int VERSION = 2; /** * Reused buffer for primitive types encoding/decoding, which aim to reduce memory fragments. Use @@ -223,13 +224,37 @@ public class LocalFileRoleAccessor implements IEntityAccessor { } } + @Override + public long loadUserId() throws IOException { + File userIdFile = checkFileAvailable("user_id", ""); + if (userIdFile == null) { + return -1; + } + FileInputStream inputStream = new FileInputStream(userIdFile); + try (DataInputStream dataInputStream = + new DataInputStream(new BufferedInputStream(inputStream))) { + dataInputStream.readInt(); // read version + return dataInputStream.readLong(); + } catch (Exception e) { + throw new IOException(e); + } finally { + strBufferLocal.remove(); + } + } + @Override public void saveEntity(Role entity) throws IOException { + String prefixName = ""; + if (entity instanceof User) { + prefixName = String.valueOf(((User) entity).getUserId()); + } else { + prefixName = entity.getName(); + } File roleProfile = SystemFileFactory.INSTANCE.getFile( entityDirPath + File.separator - + entity.getName() + + prefixName + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX); File roleDir = new File(entityDirPath); @@ -253,7 +278,7 @@ public class LocalFileRoleAccessor implements IEntityAccessor { File oldFile = SystemFileFactory.INSTANCE.getFile( - entityDirPath + File.separator + entity.getName() + IoTDBConstant.PROFILE_SUFFIX); + entityDirPath + File.separator + prefixName + IoTDBConstant.PROFILE_SUFFIX); IOUtils.replaceFile(roleProfile, oldFile); saveRoles(entity); } @@ -301,6 +326,7 @@ public class LocalFileRoleAccessor implements IEntityAccessor { } retList.addAll(set); } + retList.remove("user_id"); // skip user_id.profile return retList; } @@ -378,4 +404,36 @@ public class LocalFileRoleAccessor implements IEntityAccessor { LOGGER.warn("Role folder not exists"); } } + + @Override + public void saveUserId(long nextUserId) throws IOException { + File userInfoProfile = + SystemFileFactory.INSTANCE.getFile( + entityDirPath + + File.separator + + "user_id" + + IoTDBConstant.PROFILE_SUFFIX + + TEMP_SUFFIX); + File userDir = new File(entityDirPath); + if (!userDir.exists() && !userDir.mkdirs()) { + LOGGER.error("Failed to create user dir {}", entityDirPath); + } + + try (FileOutputStream fileOutputStream = new FileOutputStream(userInfoProfile); + BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream)) { + IOUtils.writeInt(outputStream, VERSION, encodingBufferLocal); + IOUtils.writeLong(outputStream, nextUserId, encodingBufferLocal); + outputStream.flush(); + fileOutputStream.getFD().sync(); + } catch (Exception e) { + LOGGER.warn("meet error when save userId: {}", nextUserId); + throw new IOException(e); + } finally { + encodingBufferLocal.remove(); + } + File oldFile = + SystemFileFactory.INSTANCE.getFile( + entityDirPath + File.separator + "user_id" + IoTDBConstant.PROFILE_SUFFIX); + IOUtils.replaceFile(userInfoProfile, oldFile); + } } 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 296cd0fa6a4..0253e3dab0f 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 @@ -22,6 +22,7 @@ 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.PrivilegeType; +import org.apache.iotdb.commons.auth.entity.Role; import org.apache.iotdb.commons.auth.entity.User; import org.apache.iotdb.commons.auth.role.BasicRoleManager; import org.apache.iotdb.commons.conf.CommonDescriptor; @@ -35,6 +36,9 @@ import org.apache.iotdb.rpc.TSStatusCode; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import java.io.IOException; +import java.util.Map; + /** This class stores information of each user. */ public abstract class BasicUserManager extends BasicRoleManager { @@ -50,6 +54,8 @@ public abstract class BasicUserManager extends BasicRoleManager { return "No such user %s"; } + protected long nextUserId = 9999; + /** * BasicUserManager Constructor. * @@ -99,11 +105,42 @@ public abstract class BasicUserManager extends BasicRoleManager { LOGGER.info("Admin initialized"); } + private void initUserId() { + try { + long maxUserId = this.accessor.loadUserId(); + if (maxUserId < 9999) { + nextUserId = 9999; + } else { + nextUserId = maxUserId; + } + + for (Map.Entry<String, Role> userEntry : entityMap.entrySet()) { + User user = (User) userEntry.getValue(); + if (user.getUserId() == -1) { + user.setUserId(++nextUserId); + } + } + } catch (IOException e) { + LOGGER.warn("meet error in load max userId.", e); + throw new RuntimeException(e); + } + } + @Override public User getEntity(String entityName) { return (User) super.getEntity(entityName); } + @Override + public User getEntity(long entityId) { + for (Map.Entry<String, Role> roleEntry : entityMap.entrySet()) { + if (((User) roleEntry.getValue()).getUserId() == entityId) { + return (User) roleEntry.getValue(); + } + } + return null; + } + public boolean createUser( String username, String password, boolean validCheck, boolean enableEncrypt) throws AuthException { @@ -125,7 +162,15 @@ public abstract class BasicUserManager extends BasicRoleManager { } lock.writeLock(username); try { - user = new User(username, enableEncrypt ? AuthUtils.encryptPassword(password) : password); + long userid; + if (username.equals(CommonDescriptor.getInstance().getConfig().getAdminName())) { + userid = 0; + } else { + userid = ++nextUserId; + } + user = + new User( + username, enableEncrypt ? AuthUtils.encryptPassword(password) : password, userid); entityMap.put(username, user); return true; } finally { @@ -193,7 +238,18 @@ public abstract class BasicUserManager extends BasicRoleManager { @Override public void reset() throws AuthException { - super.reset(); + accessor.reset(); + entityMap.clear(); + for (String userId : accessor.listAllEntities()) { + try { + User user = (User) accessor.loadEntity(userId); + entityMap.put(user.getName(), user); + } catch (IOException e) { + LOGGER.warn("Get exception when load user {}", userId); + throw new AuthException(TSStatusCode.AUTH_IO_EXCEPTION, e); + } + } + initUserId(); initAdmin(); } 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 8a50a87ad96..11ff8c73e9a 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 @@ -89,6 +89,7 @@ public class LocalFileUserAccessor extends LocalFileRoleAccessor { @Override protected void saveEntityName(BufferedOutputStream outputStream, Role role) throws IOException { + IOUtils.writeLong(outputStream, ((User) role).getUserId(), encodingBufferLocal); super.saveEntityName(outputStream, role); IOUtils.writeString( outputStream, ((User) role).getPassword(), STRING_ENCODING, encodingBufferLocal); @@ -101,7 +102,7 @@ public class LocalFileUserAccessor extends LocalFileRoleAccessor { SystemFileFactory.INSTANCE.getFile( entityDirPath + File.separator - + user.getName() + + user.getUserId() + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX + TEMP_SUFFIX); @@ -123,7 +124,7 @@ public class LocalFileUserAccessor extends LocalFileRoleAccessor { SystemFileFactory.INSTANCE.getFile( entityDirPath + File.separator - + user.getName() + + user.getUserId() + ROLE_SUFFIX + IoTDBConstant.PROFILE_SUFFIX); IOUtils.replaceFile(roleProfile, oldURoleFile); @@ -144,14 +145,10 @@ public class LocalFileUserAccessor extends LocalFileRoleAccessor { 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) { + if (tag < 0) { String name = IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal, -1 * tag); user.setName(name); @@ -163,8 +160,13 @@ public class LocalFileUserAccessor extends LocalFileRoleAccessor { IOUtils.readPathPrivilege(dataInputStream, STRING_ENCODING, strBufferLocal)); } user.setPrivilegeList(pathPrivilegeList); + } else if (tag == 1) { + user.setName(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal)); + user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal)); + loadPrivileges(dataInputStream, user); } else { assert (tag == VERSION); + user.setUserId(dataInputStream.readLong()); user.setName(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal)); user.setPassword(IOUtils.readString(dataInputStream, STRING_ENCODING, strBufferLocal)); loadPrivileges(dataInputStream, user); @@ -300,4 +302,35 @@ public class LocalFileUserAccessor extends LocalFileRoleAccessor { entityDirPath + File.separator + user.getName() + IoTDBConstant.PROFILE_SUFFIX); IOUtils.replaceFile(userProfile, oldFile); } + + @TestOnly + public void saveUserOldVersion1(User user) throws IOException { + File userProfile = + SystemFileFactory.INSTANCE.getFile( + entityDirPath + + File.separator + + user.getName() + + IoTDBConstant.PROFILE_SUFFIX + + TEMP_SUFFIX); + + try (FileOutputStream fileOutputStream = new FileOutputStream(userProfile); + BufferedOutputStream outputStream = new BufferedOutputStream(fileOutputStream)) { + // test for version1 + IOUtils.writeInt(outputStream, 1, encodingBufferLocal); + IOUtils.writeString(outputStream, user.getName(), STRING_ENCODING, encodingBufferLocal); + IOUtils.writeString(outputStream, user.getPassword(), STRING_ENCODING, encodingBufferLocal); + savePrivileges(outputStream, user); + outputStream.flush(); + fileOutputStream.getFD().sync(); + } catch (Exception e) { + throw new IOException(e); + } finally { + encodingBufferLocal.remove(); + } + + File oldFile = + SystemFileFactory.INSTANCE.getFile( + 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 e2c8a33fee0..5b061e16ba2 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 @@ -39,6 +39,7 @@ public class LocalFileUserManager extends BasicUserManager { for (Map.Entry<String, Role> entry : entityMap.entrySet()) { accessor.saveEntity(entry.getValue()); } + accessor.saveUserId(nextUserId); 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 89749a491ac..145cb7774fa 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 @@ -163,6 +163,8 @@ public class ColumnHeaderConstant { public static final String COUNT_TIME_PARTITION = "count(timePartition)"; public static final String START_TIME = "StartTime"; public static final String ROLE = "Role"; + public static final String MAX_SESSION_PER_USER = "MaxSessionPerUser"; + public static final String MIN_SESSION_PER_USER = "MinSessionPerUser"; public static final String CREATE_TIME = "CreateTime"; public static final String TSFILE_SIZE = "TsFileSize"; public static final String COMPRESSION_RATIO = "CompressionRatio"; @@ -279,6 +281,7 @@ public class ColumnHeaderConstant { // column names for show throttle quota public static final String USER = "User"; + public static final String USER_ID = "UserId"; public static final String READ_WRITE = "Read/Write"; // column names for show models/trials @@ -684,6 +687,10 @@ public class ColumnHeaderConstant { new ColumnHeader(TABLE, TSDataType.TEXT), new ColumnHeader(CREATE_TABLE, TSDataType.TEXT)); + public static final List<ColumnHeader> LIST_USER_COLUMN_HEADERS = + ImmutableList.of( + new ColumnHeader(USER_ID, TSDataType.INT64), new ColumnHeader(USER, TSDataType.TEXT)); + public static final List<ColumnHeader> showTablesColumnHeaders = ImmutableList.of( new ColumnHeader(TABLE_NAME, TSDataType.TEXT), 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 d1c049e86d7..8b63d29bd78 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 @@ -92,6 +92,36 @@ public class IOUtils { outputStream.write(encodingBuffer.array(), 0, Integer.BYTES); } + /** + * Write a long (8-byte) into the given stream. + * + * @param outputStream the destination to insert. + * @param i the long value to be written. + * @param encodingBufferLocal a ThreadLocal buffer may be passed to avoid frequent memory + * allocations. A null may also be passed to use a local buffer. + * @throws IOException when an exception raised during operating the stream. + */ + public static void writeLong( + OutputStream outputStream, long i, ThreadLocal<ByteBuffer> encodingBufferLocal) + throws IOException { + + ByteBuffer encodingBuffer; + if (encodingBufferLocal != null) { + encodingBuffer = encodingBufferLocal.get(); + if (encodingBuffer == null) { + // 8 bytes is exactly what we need for a long + encodingBuffer = ByteBuffer.allocate(8); + encodingBufferLocal.set(encodingBuffer); + } + } else { + encodingBuffer = ByteBuffer.allocate(8); + } + + encodingBuffer.clear(); + encodingBuffer.putLong(i); + outputStream.write(encodingBuffer.array(), 0, Long.BYTES); + } + /** * Read a string from the given stream. * diff --git a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift index 37f60c9a206..aa3c312bdd0 100644 --- a/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift +++ b/iotdb-protocol/thrift-confignode/src/main/thrift/confignode.thrift @@ -359,6 +359,14 @@ struct TAuthorizerResp { 2: optional string tag 3: optional list<string> memberInfo 4: optional TPermissionInfoResp permissionInfo + 5: optional list<TListUserInfo> usersInfo +} + +struct TListUserInfo{ + 1: required i64 userId + 2: required string username + 3: required i32 maxSessionPerUser + 4: required i32 minSessionPerUser } struct TUserResp {
