You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@iotdb.apache.org by ch...@apache.org on 2022/04/18 05:22:02 UTC
[iotdb] branch master updated: [IOTDB-2892] add PermissionInfoDataSet and test (#5492)
This is an automated email from the ASF dual-hosted git repository.
chaow pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/iotdb.git
The following commit(s) were added to refs/heads/master by this push:
new 83ffc19622 [IOTDB-2892] add PermissionInfoDataSet and test (#5492)
83ffc19622 is described below
commit 83ffc19622e9748cea1c4eb19228d7e295b25aee
Author: 任宇华 <79...@users.noreply.github.com>
AuthorDate: Mon Apr 18 13:21:56 2022 +0800
[IOTDB-2892] add PermissionInfoDataSet and test (#5492)
* add PermissionInfoDataSet and PermissionQuery
Co-authored-by: renyuhua <ry...@163.com>
---
.../consensus/response/PermissionInfoDataSet.java | 32 +-
.../iotdb/confignode/manager/ConfigManager.java | 19 +-
.../apache/iotdb/confignode/manager/Manager.java | 14 +-
.../confignode/manager/PermissionManager.java | 5 +
.../persistence/AuthorInfoPersistence.java | 129 ++++++-
.../confignode/service/executor/PlanExecutor.java | 4 +-
.../server/ConfigNodeRPCServerProcessor.java | 36 +-
.../apache/iotdb/confignode/auth/AuthorTest.java | 44 ---
.../server/ConfigNodeRPCServerProcessorTest.java | 375 +++++++++++++++++++++
.../mpp/execution/config/AuthorizerConfigTask.java | 95 ++++++
.../db/mpp/execution/config/ConfigExecution.java | 3 +
.../plan/node/metedata/write/AuthorNode.java | 2 +-
.../src/main/thrift/confignode.thrift | 7 +
13 files changed, 699 insertions(+), 66 deletions(-)
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/PermissionInfoDataSet.java b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/PermissionInfoDataSet.java
index a60a2377fb..093f959c5f 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/PermissionInfoDataSet.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/consensus/response/PermissionInfoDataSet.java
@@ -19,8 +19,38 @@
package org.apache.iotdb.confignode.consensus.response;
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.consensus.common.DataSet;
+import java.util.List;
+import java.util.Map;
+
public class PermissionInfoDataSet implements DataSet {
- // TODO: Store the returned result
+
+ private TSStatus status;
+
+ private Map<String, List<String>> permissionInfo;
+
+ public PermissionInfoDataSet() {}
+
+ public PermissionInfoDataSet(TSStatus status, Map<String, List<String>> permissionInfo) {
+ this.status = status;
+ this.permissionInfo = permissionInfo;
+ }
+
+ public Map<String, List<String>> getPermissionInfo() {
+ return permissionInfo;
+ }
+
+ public void setPermissionInfo(Map<String, List<String>> permissionInfo) {
+ this.permissionInfo = permissionInfo;
+ }
+
+ public TSStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(TSStatus status) {
+ this.status = status;
+ }
}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
index c605d418b5..43db8e07eb 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/ConfigManager.java
@@ -24,6 +24,7 @@ import org.apache.iotdb.commons.partition.SeriesPartitionSlot;
import org.apache.iotdb.confignode.consensus.response.DataNodeConfigurationDataSet;
import org.apache.iotdb.confignode.consensus.response.DataNodesInfoDataSet;
import org.apache.iotdb.confignode.consensus.response.DataPartitionDataSet;
+import org.apache.iotdb.confignode.consensus.response.PermissionInfoDataSet;
import org.apache.iotdb.confignode.consensus.response.SchemaPartitionDataSet;
import org.apache.iotdb.confignode.consensus.response.StorageGroupSchemaDataSet;
import org.apache.iotdb.confignode.physical.PhysicalPlan;
@@ -276,9 +277,23 @@ public class ConfigManager implements Manager {
@Override
public TSStatus operatePermission(PhysicalPlan physicalPlan) {
- if (physicalPlan instanceof AuthorPlan) {
+ TSStatus status = confirmLeader();
+ if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
return permissionManager.operatePermission((AuthorPlan) physicalPlan);
+ } else {
+ return status;
+ }
+ }
+
+ @Override
+ public DataSet queryPermission(PhysicalPlan physicalPlan) {
+ TSStatus status = confirmLeader();
+ if (status.getCode() == TSStatusCode.SUCCESS_STATUS.getStatusCode()) {
+ return permissionManager.queryPermission((AuthorPlan) physicalPlan);
+ } else {
+ PermissionInfoDataSet dataSet = new PermissionInfoDataSet();
+ dataSet.setStatus(status);
+ return dataSet;
}
- return ERROR_TSSTATUS;
}
}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java
index 291713002a..cae13c395e 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/Manager.java
@@ -126,10 +126,18 @@ public interface Manager {
DataSet getOrCreateDataPartition(PhysicalPlan physicalPlan);
/**
- * operate permission
+ * Operate Permission
*
- * @param physicalPlan
- * @return
+ * @param physicalPlan AuthorPlan
+ * @return status
*/
TSStatus operatePermission(PhysicalPlan physicalPlan);
+
+ /**
+ * Query Permission
+ *
+ * @param physicalPlan AuthorPlan
+ * @return PermissionInfoDataSet
+ */
+ DataSet queryPermission(PhysicalPlan physicalPlan);
}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java
index 9a9f8185e9..5e8f37f55a 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/manager/PermissionManager.java
@@ -20,6 +20,7 @@
package org.apache.iotdb.confignode.manager;
import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.confignode.consensus.response.PermissionInfoDataSet;
import org.apache.iotdb.confignode.physical.sys.AuthorPlan;
public class PermissionManager {
@@ -34,6 +35,10 @@ public class PermissionManager {
return getConsensusManager().write(authorPlan).getStatus();
}
+ public PermissionInfoDataSet queryPermission(AuthorPlan authorPlan) {
+ return (PermissionInfoDataSet) getConsensusManager().read(authorPlan).getDataset();
+ }
+
private ConsensusManager getConsensusManager() {
return configNodeManager.getConsensusManager();
}
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfoPersistence.java b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfoPersistence.java
index 27677ec68e..77d4e2a926 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfoPersistence.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/persistence/AuthorInfoPersistence.java
@@ -19,17 +19,27 @@
package org.apache.iotdb.confignode.persistence;
import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.confignode.consensus.response.PermissionInfoDataSet;
import org.apache.iotdb.confignode.physical.PhysicalPlanType;
import org.apache.iotdb.confignode.physical.sys.AuthorPlan;
import org.apache.iotdb.db.auth.AuthException;
import org.apache.iotdb.db.auth.authorizer.BasicAuthorizer;
import org.apache.iotdb.db.auth.authorizer.IAuthorizer;
+import org.apache.iotdb.db.auth.entity.PathPrivilege;
+import org.apache.iotdb.db.auth.entity.PrivilegeType;
+import org.apache.iotdb.db.auth.entity.Role;
+import org.apache.iotdb.db.auth.entity.User;
+import org.apache.iotdb.db.utils.AuthUtils;
import org.apache.iotdb.rpc.TSStatusCode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
import java.util.Set;
public class AuthorInfoPersistence {
@@ -42,7 +52,7 @@ public class AuthorInfoPersistence {
try {
authorizer = BasicAuthorizer.getInstance();
} catch (AuthException e) {
- logger.error("get user or role info failed", e);
+ logger.error("get user or role permissionInfo failed", e);
}
}
@@ -106,29 +116,128 @@ public class AuthorInfoPersistence {
return new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode());
}
- /** TODO: Construct the query result as a DataSet and return it */
- public PermissionInfoDataSet executeListRole(AuthorPlan plan) throws AuthException {
- return null;
+ public PermissionInfoDataSet executeListRole() throws AuthException {
+ PermissionInfoDataSet result = new PermissionInfoDataSet();
+ List<String> roleList = authorizer.listAllRoles();
+ Map<String, List<String>> permissionInfo = new HashMap<>();
+ permissionInfo.put(IoTDBConstant.COLUMN_ROLE, roleList);
+ result.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
+ result.setPermissionInfo(permissionInfo);
+ return result;
}
- public PermissionInfoDataSet executeListUser(AuthorPlan plan) throws AuthException {
- return null;
+ public PermissionInfoDataSet executeListUser() throws AuthException {
+ PermissionInfoDataSet result = new PermissionInfoDataSet();
+ List<String> userList = authorizer.listAllUsers();
+ Map<String, List<String>> permissionInfo = new HashMap<>();
+ permissionInfo.put(IoTDBConstant.COLUMN_USER, userList);
+ result.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
+ result.setPermissionInfo(permissionInfo);
+ return result;
}
public PermissionInfoDataSet executeListRoleUsers(AuthorPlan plan) throws AuthException {
- return null;
+ PermissionInfoDataSet result = new PermissionInfoDataSet();
+ Role role = authorizer.getRole(plan.getRoleName());
+ if (role == null) {
+ throw new AuthException("No such role : " + plan.getRoleName());
+ }
+ List<String> roleUsersList = new ArrayList<>();
+ List<String> userList = authorizer.listAllUsers();
+ for (String userN : userList) {
+ User userObj = authorizer.getUser(userN);
+ if (userObj != null && userObj.hasRole(plan.getRoleName())) {
+ roleUsersList.add(userN);
+ }
+ }
+ Map<String, List<String>> permissionInfo = new HashMap<>();
+ permissionInfo.put(IoTDBConstant.COLUMN_USER, roleUsersList);
+ result.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
+ result.setPermissionInfo(permissionInfo);
+ return result;
}
public PermissionInfoDataSet executeListUserRoles(AuthorPlan plan) throws AuthException {
- return null;
+ PermissionInfoDataSet result = new PermissionInfoDataSet();
+ User user = authorizer.getUser(plan.getUserName());
+ if (user == null) {
+ throw new AuthException("No such user : " + plan.getUserName());
+ }
+ List<String> userRoleList = new ArrayList<>();
+ for (String roleN : user.getRoleList()) {
+ userRoleList.add(roleN);
+ }
+ Map<String, List<String>> permissionInfo = new HashMap<>();
+ permissionInfo.put(IoTDBConstant.COLUMN_ROLE, userRoleList);
+ result.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
+ result.setPermissionInfo(permissionInfo);
+ return result;
}
public PermissionInfoDataSet executeListRolePrivileges(AuthorPlan plan) throws AuthException {
- return null;
+ PermissionInfoDataSet result = new PermissionInfoDataSet();
+ Role role = authorizer.getRole(plan.getRoleName());
+ if (role == null) {
+ throw new AuthException("No such role : " + plan.getRoleName());
+ }
+ List<String> rolePrivilegesList = new ArrayList<>();
+ for (PathPrivilege pathPrivilege : role.getPrivilegeList()) {
+ if (plan.getNodeName().equals("")
+ || AuthUtils.pathBelongsTo(plan.getNodeName(), pathPrivilege.getPath())) {
+ rolePrivilegesList.add(pathPrivilege.toString());
+ }
+ }
+ Map<String, List<String>> permissionInfo = new HashMap<>();
+ permissionInfo.put(IoTDBConstant.COLUMN_PRIVILEGE, rolePrivilegesList);
+ result.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
+ result.setPermissionInfo(permissionInfo);
+ return result;
}
public PermissionInfoDataSet executeListUserPrivileges(AuthorPlan plan) throws AuthException {
- return null;
+ PermissionInfoDataSet result = new PermissionInfoDataSet();
+ User user = authorizer.getUser(plan.getUserName());
+ if (user == null) {
+ throw new AuthException("No such user : " + plan.getUserName());
+ }
+ List<String> userPrivilegesList = new ArrayList<>();
+ Map<String, List<String>> permissionInfo = new HashMap<>();
+ if (IoTDBConstant.PATH_ROOT.equals(plan.getUserName())) {
+ for (PrivilegeType privilegeType : PrivilegeType.values()) {
+ userPrivilegesList.add(privilegeType.toString());
+ }
+ permissionInfo.put(IoTDBConstant.COLUMN_PRIVILEGE, userPrivilegesList);
+ result.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
+ result.setPermissionInfo(permissionInfo);
+ return result;
+ } else {
+ List<String> rolePrivileges = new ArrayList<>();
+ for (PathPrivilege pathPrivilege : user.getPrivilegeList()) {
+ if (plan.getNodeName().equals("")
+ || AuthUtils.pathBelongsTo(plan.getNodeName(), pathPrivilege.getPath())) {
+ rolePrivileges.add("");
+ userPrivilegesList.add(pathPrivilege.toString());
+ }
+ }
+ for (String roleN : user.getRoleList()) {
+ Role role = authorizer.getRole(roleN);
+ if (roleN == null) {
+ continue;
+ }
+ for (PathPrivilege pathPrivilege : role.getPrivilegeList()) {
+ if (plan.getNodeName().equals("")
+ || AuthUtils.pathBelongsTo(plan.getNodeName(), pathPrivilege.getPath())) {
+ rolePrivileges.add(roleN);
+ userPrivilegesList.add(pathPrivilege.toString());
+ }
+ }
+ }
+ permissionInfo.put(IoTDBConstant.COLUMN_ROLE, rolePrivileges);
+ permissionInfo.put(IoTDBConstant.COLUMN_PRIVILEGE, userPrivilegesList);
+ result.setStatus(new TSStatus(TSStatusCode.SUCCESS_STATUS.getStatusCode()));
+ result.setPermissionInfo(permissionInfo);
+ return result;
+ }
}
private static class AuthorInfoPersistenceHolder {
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/executor/PlanExecutor.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/executor/PlanExecutor.java
index 85d11bfa14..8e5a34d47c 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/service/executor/PlanExecutor.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/executor/PlanExecutor.java
@@ -68,9 +68,9 @@ public class PlanExecutor {
case GetOrCreateSchemaPartition:
return partitionInfoPersistence.getSchemaPartition((GetOrCreateSchemaPartitionPlan) plan);
case LIST_USER:
- return authorInfoPersistence.executeListUser((AuthorPlan) plan);
+ return authorInfoPersistence.executeListUser();
case LIST_ROLE:
- return authorInfoPersistence.executeListRole((AuthorPlan) plan);
+ return authorInfoPersistence.executeListRole();
case LIST_USER_PRIVILEGE:
return authorInfoPersistence.executeListUserPrivileges((AuthorPlan) plan);
case LIST_ROLE_PRIVILEGE:
diff --git a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/server/ConfigNodeRPCServerProcessor.java b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/server/ConfigNodeRPCServerProcessor.java
index 15ef51437f..af6647c3b7 100644
--- a/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/server/ConfigNodeRPCServerProcessor.java
+++ b/confignode/src/main/java/org/apache/iotdb/confignode/service/thrift/server/ConfigNodeRPCServerProcessor.java
@@ -25,6 +25,7 @@ import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
import org.apache.iotdb.confignode.consensus.response.DataNodeConfigurationDataSet;
import org.apache.iotdb.confignode.consensus.response.DataNodesInfoDataSet;
import org.apache.iotdb.confignode.consensus.response.DataPartitionDataSet;
+import org.apache.iotdb.confignode.consensus.response.PermissionInfoDataSet;
import org.apache.iotdb.confignode.consensus.response.SchemaPartitionDataSet;
import org.apache.iotdb.confignode.consensus.response.StorageGroupSchemaDataSet;
import org.apache.iotdb.confignode.manager.ConfigManager;
@@ -37,6 +38,7 @@ import org.apache.iotdb.confignode.physical.sys.RegisterDataNodePlan;
import org.apache.iotdb.confignode.physical.sys.SetStorageGroupPlan;
import org.apache.iotdb.confignode.rpc.thrift.ConfigIService;
import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq;
+import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp;
import org.apache.iotdb.confignode.rpc.thrift.TDataNodeMessageResp;
import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq;
import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterResp;
@@ -50,6 +52,7 @@ import org.apache.iotdb.confignode.rpc.thrift.TSetTTLReq;
import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchemaResp;
import org.apache.iotdb.db.auth.AuthException;
import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree;
+import org.apache.iotdb.db.qp.logical.sys.AuthorOperator;
import org.apache.thrift.TException;
import org.slf4j.Logger;
@@ -184,14 +187,16 @@ public class ConfigNodeRPCServerProcessor implements ConfigIService.Iface {
@Override
public TSStatus operatePermission(TAuthorizerReq req) throws TException {
- if (req.getAuthorType() < 0 || req.getAuthorType() >= PhysicalPlanType.values().length) {
- throw new IndexOutOfBoundsException("Invalid ordinal");
+ if (req.getAuthorType() < 0
+ || req.getAuthorType() >= AuthorOperator.AuthorType.values().length) {
+ throw new IndexOutOfBoundsException("Invalid Author Type ordinal");
}
AuthorPlan plan = null;
try {
plan =
new AuthorPlan(
- PhysicalPlanType.values()[req.getAuthorType()],
+ PhysicalPlanType.values()[
+ req.getAuthorType() + PhysicalPlanType.AUTHOR.ordinal() + 1],
req.getUserName(),
req.getRoleName(),
req.getPassword(),
@@ -204,6 +209,31 @@ public class ConfigNodeRPCServerProcessor implements ConfigIService.Iface {
return configManager.operatePermission(plan);
}
+ @Override
+ public TAuthorizerResp queryPermission(TAuthorizerReq req) throws TException {
+ if (req.getAuthorType() < 0
+ || req.getAuthorType() >= AuthorOperator.AuthorType.values().length) {
+ throw new IndexOutOfBoundsException("Invalid Author Type ordinal");
+ }
+ AuthorPlan plan = null;
+ try {
+ plan =
+ new AuthorPlan(
+ PhysicalPlanType.values()[
+ req.getAuthorType() + PhysicalPlanType.AUTHOR.ordinal() + 1],
+ req.getUserName(),
+ req.getRoleName(),
+ req.getPassword(),
+ req.getNewPassword(),
+ req.getPermissions(),
+ req.getNodeName());
+ } catch (AuthException e) {
+ LOGGER.error(e.getMessage());
+ }
+ PermissionInfoDataSet dataSet = (PermissionInfoDataSet) configManager.queryPermission(plan);
+ return new TAuthorizerResp(dataSet.getStatus(), dataSet.getPermissionInfo());
+ }
+
public void handleClientExit() {}
// TODO: Interfaces for data operations
diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/auth/AuthorTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/auth/AuthorTest.java
deleted file mode 100644
index 7d86edfabd..0000000000
--- a/confignode/src/test/java/org/apache/iotdb/confignode/auth/AuthorTest.java
+++ /dev/null
@@ -1,44 +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.auth;
-
-import org.apache.iotdb.confignode.utils.ConfigNodeEnvironmentUtils;
-import org.apache.iotdb.db.auth.authorizer.BasicAuthorizer;
-import org.apache.iotdb.db.auth.authorizer.IAuthorizer;
-
-import org.junit.After;
-import org.junit.Before;
-
-public class AuthorTest {
-
- IAuthorizer authorizer;
-
- @Before
- public void setUp() throws Exception {
- ConfigNodeEnvironmentUtils.envSetUp();
- authorizer = BasicAuthorizer.getInstance();
- }
-
- @After
- public void tearDown() throws Exception {
- ConfigNodeEnvironmentUtils.cleanEnv();
- }
-
- // TODO: ADD TEST
-}
diff --git a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/server/ConfigNodeRPCServerProcessorTest.java b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/server/ConfigNodeRPCServerProcessorTest.java
index 0aa164ccd0..939458a6fe 100644
--- a/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/server/ConfigNodeRPCServerProcessorTest.java
+++ b/confignode/src/test/java/org/apache/iotdb/confignode/service/thrift/server/ConfigNodeRPCServerProcessorTest.java
@@ -23,6 +23,7 @@ import org.apache.iotdb.common.rpc.thrift.TRegionReplicaSet;
import org.apache.iotdb.common.rpc.thrift.TSStatus;
import org.apache.iotdb.common.rpc.thrift.TSeriesPartitionSlot;
import org.apache.iotdb.common.rpc.thrift.TTimePartitionSlot;
+import org.apache.iotdb.commons.conf.IoTDBConstant;
import org.apache.iotdb.commons.consensus.ConsensusGroupId;
import org.apache.iotdb.commons.consensus.DataRegionId;
import org.apache.iotdb.commons.consensus.SchemaRegionId;
@@ -30,6 +31,8 @@ import org.apache.iotdb.confignode.conf.ConfigNodeDescriptor;
import org.apache.iotdb.confignode.persistence.DataNodeInfoPersistence;
import org.apache.iotdb.confignode.persistence.PartitionInfoPersistence;
import org.apache.iotdb.confignode.persistence.RegionInfoPersistence;
+import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq;
+import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerResp;
import org.apache.iotdb.confignode.rpc.thrift.TDataNodeMessage;
import org.apache.iotdb.confignode.rpc.thrift.TDataNodeMessageResp;
import org.apache.iotdb.confignode.rpc.thrift.TDataNodeRegisterReq;
@@ -42,9 +45,11 @@ import org.apache.iotdb.confignode.rpc.thrift.TSchemaPartitionResp;
import org.apache.iotdb.confignode.rpc.thrift.TSetStorageGroupReq;
import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchema;
import org.apache.iotdb.confignode.rpc.thrift.TStorageGroupSchemaResp;
+import org.apache.iotdb.db.auth.entity.PrivilegeType;
import org.apache.iotdb.db.exception.metadata.IllegalPathException;
import org.apache.iotdb.db.metadata.path.PartialPath;
import org.apache.iotdb.db.mpp.common.schematree.PathPatternTree;
+import org.apache.iotdb.db.qp.logical.sys.AuthorOperator;
import org.apache.iotdb.rpc.TSStatusCode;
import org.apache.iotdb.tsfile.utils.PublicBAOS;
@@ -61,8 +66,10 @@ import java.nio.ByteBuffer;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
import java.util.concurrent.TimeUnit;
public class ConfigNodeRPCServerProcessorTest {
@@ -489,4 +496,372 @@ public class ConfigNodeRPCServerProcessorTest {
timePartitionSlotNum,
dataPartitionResp.getDataPartitionMap());
}
+
+ @Test
+ public void permissionTest() throws TException {
+ TSStatus status;
+
+ List<String> userList = new ArrayList<>();
+ userList.add("root");
+ userList.add("tempuser0");
+ userList.add("tempuser1");
+ List<String> roleList = new ArrayList<>();
+ roleList.add("temprole0");
+ roleList.add("temprole1");
+ TAuthorizerReq authorizerReq;
+ TAuthorizerResp authorizerResp;
+ Set<Integer> privilegeList = new HashSet<>();
+ privilegeList.add(PrivilegeType.DELETE_USER.ordinal());
+ privilegeList.add(PrivilegeType.CREATE_USER.ordinal());
+ Set<Integer> revokePrivilege = new HashSet<>();
+ revokePrivilege.add(PrivilegeType.DELETE_USER.ordinal());
+ Map<String, List<String>> permissionInfo;
+ List<String> privilege = new ArrayList<>();
+ privilege.add("root : CREATE_USER");
+ privilege.add("root : CREATE_USER");
+
+ cleanUserAndRole();
+
+ // create user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.CREATE_USER.ordinal(),
+ "tempuser0",
+ "",
+ "passwd",
+ "",
+ new HashSet<>(),
+ "");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ authorizerReq.setUserName("tempuser1");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // drop user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.DROP_USER.ordinal(),
+ "tempuser1",
+ "",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // list user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_USER.ordinal(), "", "", "", "", new HashSet<>(), "");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ userList.remove("tempuser1");
+ Assert.assertEquals(
+ userList, authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_USER));
+
+ // create role
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.CREATE_ROLE.ordinal(),
+ "",
+ "temprole0",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ authorizerReq.setRoleName("temprole1");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // drop role
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.DROP_ROLE.ordinal(),
+ "",
+ "temprole1",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // list role
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_ROLE.ordinal(), "", "", "", "", new HashSet<>(), "");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ roleList.remove("temprole1");
+ Assert.assertEquals(
+ roleList, authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_ROLE));
+
+ // alter user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.UPDATE_USER.ordinal(),
+ "tempuser0",
+ "",
+ "",
+ "newpwd",
+ new HashSet<>(),
+ "");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // grant user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.GRANT_USER.ordinal(),
+ "tempuser0",
+ "",
+ "",
+ "",
+ privilegeList,
+ "root.ln");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // grant role
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.GRANT_ROLE.ordinal(),
+ "",
+ "temprole0",
+ "",
+ "",
+ privilegeList,
+ "root.ln");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // grant role to user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.GRANT_ROLE_TO_USER.ordinal(),
+ "tempuser0",
+ "temprole0",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // revoke user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.REVOKE_USER.ordinal(),
+ "tempuser0",
+ "",
+ "",
+ "",
+ revokePrivilege,
+ "root.ln");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // revoke role
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.REVOKE_ROLE.ordinal(),
+ "",
+ "temprole0",
+ "",
+ "",
+ revokePrivilege,
+ "root.ln");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // list privileges user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_USER_PRIVILEGE.ordinal(),
+ "tempuser0",
+ "",
+ "",
+ "",
+ new HashSet<>(),
+ "root.ln");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ privilege, authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_PRIVILEGE));
+
+ // list user privileges
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_USER_PRIVILEGE.ordinal(),
+ "tempuser0",
+ "",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ privilege, authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_PRIVILEGE));
+
+ // list privileges role
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_ROLE_PRIVILEGE.ordinal(),
+ "",
+ "temprole0",
+ "",
+ "",
+ new HashSet<>(),
+ "root.ln");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ privilege.remove(0);
+ Assert.assertEquals(
+ privilege, authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_PRIVILEGE));
+
+ // list role privileges
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_ROLE_PRIVILEGE.ordinal(),
+ "",
+ "temprole0",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ Assert.assertEquals(
+ privilege, authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_PRIVILEGE));
+
+ // list all role of user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_USER_ROLES.ordinal(),
+ "tempuser0",
+ "",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ roleList.remove("temprole1");
+ Assert.assertEquals(
+ roleList, authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_ROLE));
+
+ // list all user of role
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_ROLE_USERS.ordinal(),
+ "",
+ "temprole0",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ userList.remove("tempuser1");
+ userList.remove("root");
+ Assert.assertEquals(
+ userList, authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_USER));
+
+ // revoke role from user
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.REVOKE_ROLE_FROM_USER.ordinal(),
+ "tempuser0",
+ "temprole0",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ // list root privileges
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_USER_PRIVILEGE.ordinal(),
+ "root",
+ "",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ for (int i = 0; i < PrivilegeType.values().length; i++) {
+ Assert.assertEquals(
+ PrivilegeType.values()[i].toString(),
+ authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_PRIVILEGE).get(i));
+ }
+ }
+
+ private void cleanUserAndRole() throws TException {
+ TSStatus status;
+
+ // clean user
+ TAuthorizerReq authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_USER.ordinal(), "", "", "", "", new HashSet<>(), "");
+ TAuthorizerResp authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ List<String> allUsers = authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_USER);
+ for (String user : allUsers) {
+ if (!user.equals("root")) {
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.DROP_USER.ordinal(),
+ user,
+ "",
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ }
+ }
+
+ // clean role
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.LIST_ROLE.ordinal(), "", "", "", "", new HashSet<>(), "");
+ authorizerResp = processor.queryPermission(authorizerReq);
+ status = authorizerResp.getStatus();
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+
+ List<String> roleList = authorizerResp.getAuthorizerInfo().get(IoTDBConstant.COLUMN_ROLE);
+ for (String roleN : roleList) {
+ authorizerReq =
+ new TAuthorizerReq(
+ AuthorOperator.AuthorType.DROP_ROLE.ordinal(),
+ "",
+ roleN,
+ "",
+ "",
+ new HashSet<>(),
+ "");
+ status = processor.operatePermission(authorizerReq);
+ Assert.assertEquals(TSStatusCode.SUCCESS_STATUS.getStatusCode(), status.getCode());
+ }
+ }
}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/config/AuthorizerConfigTask.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/config/AuthorizerConfigTask.java
new file mode 100644
index 0000000000..9b27ad3b9d
--- /dev/null
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/config/AuthorizerConfigTask.java
@@ -0,0 +1,95 @@
+/*
+ * 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.mpp.execution.config;
+
+import org.apache.iotdb.common.rpc.thrift.TSStatus;
+import org.apache.iotdb.commons.exception.BadNodeUrlException;
+import org.apache.iotdb.confignode.rpc.thrift.TAuthorizerReq;
+import org.apache.iotdb.db.auth.AuthException;
+import org.apache.iotdb.db.client.ConfigNodeClient;
+import org.apache.iotdb.db.mpp.sql.planner.plan.node.metedata.write.AuthorNode;
+import org.apache.iotdb.db.mpp.sql.statement.sys.AuthorStatement;
+import org.apache.iotdb.rpc.IoTDBConnectionException;
+import org.apache.iotdb.rpc.StatementExecutionException;
+import org.apache.iotdb.rpc.TSStatusCode;
+
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.SettableFuture;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Locale;
+
+public class AuthorizerConfigTask implements IConfigTask {
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(AuthorizerConfigTask.class);
+
+ private AuthorStatement authorStatement;
+
+ public AuthorizerConfigTask(AuthorStatement authorStatement) {
+ this.authorStatement = authorStatement;
+ }
+
+ @Override
+ public ListenableFuture<Void> execute() {
+ SettableFuture<Void> future = SettableFuture.create();
+ ConfigNodeClient configNodeClient = null;
+ try {
+ // Construct request using statement
+ TAuthorizerReq req =
+ new TAuthorizerReq(
+ authorStatement.getAuthorType().ordinal(),
+ authorStatement.getUserName() == null ? "" : authorStatement.getUserName(),
+ authorStatement.getRoleName() == null ? "" : authorStatement.getRoleName(),
+ authorStatement.getPassWord() == null ? "" : authorStatement.getPassWord(),
+ authorStatement.getNewPassword() == null ? "" : authorStatement.getNewPassword(),
+ AuthorNode.strToPermissions(authorStatement.getPrivilegeList()),
+ authorStatement.getNodeName() == null
+ ? ""
+ : authorStatement.getNodeName().getFullPath());
+ configNodeClient = new ConfigNodeClient();
+ // Send request to some API server
+ TSStatus tsStatus = configNodeClient.operatePermission(req);
+ // Get response or throw exception
+ if (TSStatusCode.SUCCESS_STATUS.getStatusCode() != tsStatus.getCode()) {
+ LOGGER.error(
+ "Failed to execute {} in config node, status is {}.",
+ authorStatement.getAuthorType().toString().toLowerCase(Locale.ROOT),
+ tsStatus);
+ future.setException(new StatementExecutionException(tsStatus));
+ } else {
+ future.set(null);
+ }
+ } catch (IoTDBConnectionException | BadNodeUrlException e) {
+ LOGGER.error("Failed to connect to config node.");
+ future.setException(e);
+ } catch (AuthException e) {
+ LOGGER.error("No such privilege {}.", authorStatement.getAuthorType());
+ future.setException(e);
+ } finally {
+ if (configNodeClient != null) {
+ configNodeClient.close();
+ }
+ }
+ // If the action is executed successfully, return the Future.
+ // If your operation is async, you can return the corresponding future directly.
+ return future;
+ }
+}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/execution/config/ConfigExecution.java b/server/src/main/java/org/apache/iotdb/db/mpp/execution/config/ConfigExecution.java
index 21afde5d29..7ceb542cef 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/execution/config/ConfigExecution.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/execution/config/ConfigExecution.java
@@ -27,6 +27,7 @@ import org.apache.iotdb.db.mpp.execution.QueryStateMachine;
import org.apache.iotdb.db.mpp.sql.analyze.QueryType;
import org.apache.iotdb.db.mpp.sql.statement.Statement;
import org.apache.iotdb.db.mpp.sql.statement.metadata.SetStorageGroupStatement;
+import org.apache.iotdb.db.mpp.sql.statement.sys.AuthorStatement;
import org.apache.iotdb.rpc.RpcUtils;
import org.apache.iotdb.rpc.TSStatusCode;
import org.apache.iotdb.tsfile.exception.NotImplementedException;
@@ -149,6 +150,8 @@ public class ConfigExecution implements IQueryExecution {
switch (statement.getType()) {
case SET_STORAGE_GROUP:
return new SetStorageGroupTask((SetStorageGroupStatement) statement);
+ case AUTHOR:
+ return new AuthorizerConfigTask((AuthorStatement) statement);
default:
throw new NotImplementedException();
}
diff --git a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/metedata/write/AuthorNode.java b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/metedata/write/AuthorNode.java
index e50454c4b2..adcd0cfc06 100644
--- a/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/metedata/write/AuthorNode.java
+++ b/server/src/main/java/org/apache/iotdb/db/mpp/sql/planner/plan/node/metedata/write/AuthorNode.java
@@ -248,7 +248,7 @@ public class AuthorNode extends PlanNode {
throw new NotImplementedException("serializeAttributes of AuthorNode is not implemented");
}
- public Set<Integer> strToPermissions(String[] privilegeList) throws AuthException {
+ public static Set<Integer> strToPermissions(String[] privilegeList) throws AuthException {
Set<Integer> result = new HashSet<>();
if (privilegeList == null) {
return result;
diff --git a/thrift-confignode/src/main/thrift/confignode.thrift b/thrift-confignode/src/main/thrift/confignode.thrift
index a10709b414..5441b7f571 100644
--- a/thrift-confignode/src/main/thrift/confignode.thrift
+++ b/thrift-confignode/src/main/thrift/confignode.thrift
@@ -117,6 +117,11 @@ struct TAuthorizerReq {
7: required string nodeName
}
+struct TAuthorizerResp {
+ 1: required common.TSStatus status
+ 2: required map<string, list<string>> authorizerInfo
+}
+
service ConfigIService {
/* DataNode */
@@ -148,6 +153,8 @@ service ConfigIService {
TDataPartitionResp getOrCreateDataPartition(TDataPartitionReq req)
/* Authorize */
+
common.TSStatus operatePermission(TAuthorizerReq req)
+ TAuthorizerResp queryPermission(TAuthorizerReq req)
}
\ No newline at end of file