This is an automated email from the ASF dual-hosted git repository. jackietien pushed a commit to branch QueryAccessControl in repository https://gitbox.apache.org/repos/asf/iotdb.git
commit ac6dd6a3f3eafb4a905f4ce791a5a81867e7119a Author: JackieTien97 <[email protected]> AuthorDate: Mon Dec 2 20:19:06 2024 +0800 Add AccessControl support for query and database related --- .../iotdb/db/queryengine/plan/Coordinator.java | 13 ++- .../execution/config/TableConfigTaskVisitor.java | 41 +++++++- .../config/executor/ClusterConfigTaskExecutor.java | 6 +- .../config/executor/IConfigTaskExecutor.java | 4 +- .../config/metadata/relational/ShowDBTask.java | 28 +++-- .../plan/relational/analyzer/Analyzer.java | 11 -- .../relational/analyzer/StatementAnalyzer.java | 33 ++---- .../plan/relational/planner/TableModelPlanner.java | 9 +- .../plan/relational/security/AccessControl.java | 113 ++++++++++++++++++++- .../relational/security/AccessControlImpl.java | 91 +++++++++++++++++ .../relational/security/AllowAllAccessControl.java | 64 +++++++++++- .../relational/security/ITableAuthChecker.java | 75 ++++++++++++++ ...AccessControl.java => TableModelPrivilege.java} | 15 ++- .../plan/relational/analyzer/AnalyzerTest.java | 5 +- .../plan/relational/planner/PlanTester.java | 6 +- .../exception/auth/AccessDeniedException.java} | 14 ++- 16 files changed, 456 insertions(+), 72 deletions(-) diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java index 61be1471b15..22c9a7a282b 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/Coordinator.java @@ -52,6 +52,8 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.TableModelPlanner import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.DistributedOptimizeFactory; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.LogicalOptimizeFactory; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanOptimizer; +import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; +import org.apache.iotdb.db.queryengine.plan.relational.security.AllowAllAccessControl; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AddColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.ClearCache; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.CreateDB; @@ -139,6 +141,7 @@ public class Coordinator { private final List<PlanOptimizer> logicalPlanOptimizers; private final List<PlanOptimizer> distributionPlanOptimizers; + private final AccessControl accessControl; private Coordinator() { this.queryExecutionMap = new ConcurrentHashMap<>(); @@ -155,6 +158,7 @@ public class Coordinator { new PlannerContext( LocalExecutionPlanner.getInstance().metadata, new InternalTypeManager())) .getPlanOptimizers(); + this.accessControl = new AllowAllAccessControl(); } private ExecutionResult execution( @@ -333,7 +337,8 @@ public class Coordinator { SYNC_INTERNAL_SERVICE_CLIENT_MANAGER, ASYNC_INTERNAL_SERVICE_CLIENT_MANAGER, logicalPlanOptimizers, - distributionPlanOptimizers); + distributionPlanOptimizers, + accessControl); return new QueryExecution(tableModelPlanner, queryContext, executor); } @@ -380,7 +385,8 @@ public class Coordinator { queryContext, null, executor, - statement.accept(new TableConfigTaskVisitor(clientSession, metadata), queryContext)); + statement.accept( + new TableConfigTaskVisitor(clientSession, metadata, accessControl), queryContext)); } if (statement instanceof WrappedInsertStatement) { ((WrappedInsertStatement) statement).setContext(queryContext); @@ -396,7 +402,8 @@ public class Coordinator { SYNC_INTERNAL_SERVICE_CLIENT_MANAGER, ASYNC_INTERNAL_SERVICE_CLIENT_MANAGER, logicalPlanOptimizers, - distributionPlanOptimizers); + distributionPlanOptimizers, + accessControl); return new QueryExecution(tableModelPlanner, queryContext, executor); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java index ab93ad531cd..569e82bb2f4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/TableConfigTaskVisitor.java @@ -74,7 +74,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.analyzer.Analyzer; import org.apache.iotdb.db.queryengine.plan.relational.analyzer.StatementAnalyzerFactory; import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.metadata.fetcher.TableHeaderSchemaValidator; -import org.apache.iotdb.db.queryengine.plan.relational.planner.TableModelPlanner; +import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AddColumn; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AlterPipe; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.AstVisitor; @@ -133,6 +133,7 @@ import org.apache.tsfile.enums.TSDataType; import org.apache.tsfile.utils.Binary; import org.apache.tsfile.utils.Pair; +import java.security.AccessControlException; import java.util.Collections; import java.util.HashMap; import java.util.List; @@ -163,9 +164,15 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont private final Metadata metadata; - public TableConfigTaskVisitor(final IClientSession clientSession, final Metadata metadata) { + private final AccessControl accessControl; + + public TableConfigTaskVisitor( + final IClientSession clientSession, + final Metadata metadata, + final AccessControl accessControl) { this.clientSession = clientSession; this.metadata = metadata; + this.accessControl = accessControl; } @Override @@ -184,6 +191,8 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont final String dbName = node.getDbName(); validateDatabaseName(dbName); + accessControl.checkCanCreateDatabase(context.getSession().getUserName(), dbName); + schema.setName(ROOT + PATH_SEPARATOR_CHAR + dbName); for (final Property property : node.getProperties()) { @@ -235,25 +244,40 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont @Override protected IConfigTask visitUse(final Use node, final MPPQueryContext context) { context.setQueryType(QueryType.WRITE); + accessControl.checkCanShowOrUseDatabase( + context.getSession().getUserName(), node.getDatabaseId().getValue()); return new UseDBTask(node, clientSession); } @Override protected IConfigTask visitDropDB(final DropDB node, final MPPQueryContext context) { context.setQueryType(QueryType.WRITE); + accessControl.checkCanDropDatabase( + context.getSession().getUserName(), node.getDbName().getValue()); return new DropDBTask(node); } @Override protected IConfigTask visitShowDB(final ShowDB node, final MPPQueryContext context) { context.setQueryType(QueryType.READ); - return new ShowDBTask(node); + return new ShowDBTask( + node, + databaseName -> { + try { + accessControl.checkCanShowOrUseDatabase( + context.getSession().getUserName(), databaseName); + return true; + } catch (AccessControlException e) { + return false; + } + }); } @Override protected IConfigTask visitShowCluster( final ShowCluster showCluster, final MPPQueryContext context) { context.setQueryType(QueryType.READ); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); // As the implementation is identical, we'll simply translate to the // corresponding tree-model variant and execute that. ShowClusterStatement treeStatement = new ShowClusterStatement(); @@ -265,6 +289,7 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont protected IConfigTask visitShowRegions( final ShowRegions showRegions, final MPPQueryContext context) { context.setQueryType(QueryType.READ); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); // As the implementation is identical, we'll simply translate to the // corresponding tree-model variant and execute that. final ShowRegionStatement treeStatement = new ShowRegionStatement(); @@ -278,6 +303,7 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont protected IConfigTask visitShowDataNodes( final ShowDataNodes showDataNodesStatement, final MPPQueryContext context) { context.setQueryType(QueryType.READ); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); return new ShowDataNodesTask(); } @@ -285,6 +311,7 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont protected IConfigTask visitShowConfigNodes( final ShowConfigNodes showConfigNodesStatement, final MPPQueryContext context) { context.setQueryType(QueryType.READ); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); return new ShowConfigNodesTask(); } @@ -292,6 +319,7 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont protected IConfigTask visitShowAINodes( final ShowAINodes showAINodesStatement, final MPPQueryContext context) { context.setQueryType(QueryType.READ); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); return new ShowAINodesTask(); } @@ -299,6 +327,7 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont protected IConfigTask visitClearCache( final ClearCache clearCacheStatement, final MPPQueryContext context) { context.setQueryType(QueryType.WRITE); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); return new ClearCacheTask(clearCacheStatement); } @@ -515,7 +544,7 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont new Analyzer( context, context.getSession(), - new StatementAnalyzerFactory(metadata, null, new TableModelPlanner.NopAccessControl()), + new StatementAnalyzerFactory(metadata, null, accessControl), Collections.emptyList(), Collections.emptyMap(), WarningCollector.NOOP) @@ -556,12 +585,14 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont @Override protected IConfigTask visitFlush(Flush node, MPPQueryContext context) { context.setQueryType(QueryType.WRITE); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); return new FlushTask(((FlushStatement) node.getInnerTreeStatement())); } @Override protected IConfigTask visitSetConfiguration(SetConfiguration node, MPPQueryContext context) { context.setQueryType(QueryType.WRITE); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); return new SetConfigurationTask(((SetConfigurationStatement) node.getInnerTreeStatement())); } @@ -697,12 +728,14 @@ public class TableConfigTaskVisitor extends AstVisitor<IConfigTask, MPPQueryCont @Override protected IConfigTask visitShowVariables(ShowVariables node, MPPQueryContext context) { context.setQueryType(QueryType.READ); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); return new ShowVariablesTask(); } @Override protected IConfigTask visitShowClusterId(ShowClusterId node, MPPQueryContext context) { context.setQueryType(QueryType.READ); + accessControl.checkUserHasMaintainPrivilege(context.getSession().getUserName()); return new ShowClusterIdTask(); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java index bf7bc065d49..6bbb0a2238c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/ClusterConfigTaskExecutor.java @@ -306,6 +306,7 @@ import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.TreeMap; +import java.util.function.Function; import java.util.stream.Collectors; import static org.apache.iotdb.commons.conf.IoTDBConstant.MAX_DATABASE_NAME_LENGTH; @@ -3088,7 +3089,8 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor { } @Override - public SettableFuture<ConfigTaskResult> showDatabases(final ShowDB showDB) { + public SettableFuture<ConfigTaskResult> showDatabases( + final ShowDB showDB, final Function<String, Boolean> canSeenDB) { final SettableFuture<ConfigTaskResult> future = SettableFuture.create(); // Construct request using statement final List<String> databasePathPattern = Arrays.asList(ALL_RESULT_NODES); @@ -3099,7 +3101,7 @@ public class ClusterConfigTaskExecutor implements IConfigTaskExecutor { new TGetDatabaseReq(databasePathPattern, ALL_MATCH_SCOPE.serialize()); final TShowDatabaseResp resp = client.showDatabase(req); // build TSBlock - ShowDBTask.buildTSBlock(resp.getDatabaseInfoMap(), future, showDB.isDetails()); + ShowDBTask.buildTSBlock(resp.getDatabaseInfoMap(), future, showDB.isDetails(), canSeenDB); } catch (final IOException | ClientManagerException | TException e) { future.setException(e); } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java index 1773ad94d7d..4180abd3b09 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/executor/IConfigTaskExecutor.java @@ -98,6 +98,7 @@ import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; +import java.util.function.Function; public interface IConfigTaskExecutor { @@ -282,7 +283,8 @@ public interface IConfigTaskExecutor { // =============================== table syntax ========================================= - SettableFuture<ConfigTaskResult> showDatabases(ShowDB showDB); + SettableFuture<ConfigTaskResult> showDatabases( + ShowDB showDB, Function<String, Boolean> canSeenDB); SettableFuture<ConfigTaskResult> showCluster(ShowCluster showCluster); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java index d0516a14dac..976291dbdbc 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/execution/config/metadata/relational/ShowDBTask.java @@ -40,36 +40,43 @@ import org.apache.tsfile.utils.Binary; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; public class ShowDBTask implements IConfigTask { private final ShowDB node; - public ShowDBTask(final ShowDB node) { + // judge whether the specific database can be seen, dbName should be without `root.` prefix + private final Function<String, Boolean> canSeenDB; + + public ShowDBTask(final ShowDB node, final Function<String, Boolean> canSeenDB) { this.node = node; + this.canSeenDB = canSeenDB; } @Override public ListenableFuture<ConfigTaskResult> execute(final IConfigTaskExecutor configTaskExecutor) throws InterruptedException { - return configTaskExecutor.showDatabases(node); + return configTaskExecutor.showDatabases(node, canSeenDB); } public static void buildTSBlock( final Map<String, TDatabaseInfo> storageGroupInfoMap, final SettableFuture<ConfigTaskResult> future, - final boolean isDetails) { + final boolean isDetails, + final Function<String, Boolean> canSeenDB) { if (isDetails) { - buildTSBlockForDetails(storageGroupInfoMap, future); + buildTSBlockForDetails(storageGroupInfoMap, future, canSeenDB); } else { - buildTSBlockForNonDetails(storageGroupInfoMap, future); + buildTSBlockForNonDetails(storageGroupInfoMap, future, canSeenDB); } } private static void buildTSBlockForNonDetails( final Map<String, TDatabaseInfo> storageGroupInfoMap, - final SettableFuture<ConfigTaskResult> future) { + final SettableFuture<ConfigTaskResult> future, + final Function<String, Boolean> canSeenDB) { final List<TSDataType> outputDataTypes = ColumnHeaderConstant.showDBColumnHeaders.stream() .map(ColumnHeader::getColumnType) @@ -78,6 +85,9 @@ public class ShowDBTask implements IConfigTask { final TsBlockBuilder builder = new TsBlockBuilder(outputDataTypes); for (final Map.Entry<String, TDatabaseInfo> entry : storageGroupInfoMap.entrySet()) { final String dbName = entry.getKey().substring(5); + if (!canSeenDB.apply(dbName)) { + continue; + } final TDatabaseInfo storageGroupInfo = entry.getValue(); builder.getTimeColumnBuilder().writeLong(0L); builder.getColumnBuilder(0).writeBinary(new Binary(dbName, TSFileConfig.STRING_CHARSET)); @@ -104,7 +114,8 @@ public class ShowDBTask implements IConfigTask { private static void buildTSBlockForDetails( final Map<String, TDatabaseInfo> storageGroupInfoMap, - final SettableFuture<ConfigTaskResult> future) { + final SettableFuture<ConfigTaskResult> future, + final Function<String, Boolean> canSeenDB) { final List<TSDataType> outputDataTypes = ColumnHeaderConstant.showDBDetailsColumnHeaders.stream() .map(ColumnHeader::getColumnType) @@ -113,6 +124,9 @@ public class ShowDBTask implements IConfigTask { final TsBlockBuilder builder = new TsBlockBuilder(outputDataTypes); for (final Map.Entry<String, TDatabaseInfo> entry : storageGroupInfoMap.entrySet()) { final String dbName = entry.getKey().substring(5); + if (!canSeenDB.apply(dbName)) { + continue; + } final TDatabaseInfo storageGroupInfo = entry.getValue(); builder.getTimeColumnBuilder().writeLong(0L); builder.getColumnBuilder(0).writeBinary(new Binary(dbName, TSFileConfig.STRING_CHARSET)); diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analyzer.java index 75071daaa38..5ea7c492149 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/Analyzer.java @@ -96,17 +96,6 @@ public class Analyzer { context.setAnalyzeCost(analyzeCost); } - // TODO access control - // check column access permissions for each table - // analysis.getTableColumnReferences().forEach((accessControlInfo, tableColumnReferences) -> - // tableColumnReferences.forEach((tableName, columns) -> - // accessControlInfo.getAccessControl().checkCanSelectFromColumns( - // accessControlInfo.getSecurityContext(session.getRequiredTransactionId(), - // session.getQueryId(), - // session.getStart()), - // tableName, - // columns))); - return analysis; } } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java index 928354dee10..2090d58bdd4 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/StatementAnalyzer.java @@ -206,7 +206,7 @@ public class StatementAnalyzer { private final StatementAnalyzerFactory statementAnalyzerFactory; - private Analysis analysis; + private final Analysis analysis; private boolean hasFillInParentScope = false; private final MPPQueryContext queryContext; @@ -1739,6 +1739,10 @@ public class StatementAnalyzer { } QualifiedObjectName name = createQualifiedObjectName(sessionContext, table.getName()); + + // access control + accessControl.checkCanSelectFromTable(sessionContext.getUserName(), name); + analysis.setRelationName( table, QualifiedName.of(name.getDatabaseName(), name.getObjectName())); @@ -1752,37 +1756,12 @@ public class StatementAnalyzer { ImmutableList.Builder<Field> fields = ImmutableList.builder(); fields.addAll(analyzeTableOutputFields(table, name, tableSchema.get())); - // boolean addRowIdColumn = updateKind.isPresent(); - // - // if (addRowIdColumn) { - // // Add the row id field - // ColumnHandle rowIdColumnHandle = metadata.getMergeRowIdColumnHandle(session, - // tableHandle.get()); - // Type type = metadata.getColumnMetadata(session, tableHandle.get(), - // rowIdColumnHandle).getType(); - // Field field = Field.newUnqualified(Optional.empty(), type); - // fields.add(field); - // analysis.setColumn(field, rowIdColumnHandle); - // } - List<Field> outputFields = fields.build(); RelationType relationType = new RelationType(outputFields); - Scope accessControlScope = - Scope.builder().withRelationType(RelationId.anonymous(), relationType).build(); - // analyzeFiltersAndMasks(table, name, new RelationType(outputFields), - // accessControlScope); analysis.registerTable(table, tableSchema, name); - Scope tableScope = createAndAssignScope(table, scope, relationType); - - // if (addRowIdColumn) { - // FieldReference reference = new FieldReference(outputFields.size() - 1); - // analyzeExpression(reference, tableScope); - // analysis.setRowIdField(table, reference); - // } - - return tableScope; + return createAndAssignScope(table, scope, relationType); } private Scope createScopeForCommonTableExpression( diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableModelPlanner.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableModelPlanner.java index b209096732f..c8e0c948bc0 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableModelPlanner.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/planner/TableModelPlanner.java @@ -68,8 +68,7 @@ public class TableModelPlanner implements IPlanner { private final List<PlanOptimizer> distributionPlanOptimizers; private final SymbolAllocator symbolAllocator = new SymbolAllocator(); - // TODO access control - private final AccessControl accessControl = new NopAccessControl(); + private final AccessControl accessControl; private final WarningCollector warningCollector = WarningCollector.NOOP; @@ -95,7 +94,8 @@ public class TableModelPlanner implements IPlanner { final IClientManager<TEndPoint, AsyncDataNodeInternalServiceClient> asyncInternalServiceClientManager, final List<PlanOptimizer> logicalPlanOptimizers, - final List<PlanOptimizer> distributionPlanOptimizers) { + final List<PlanOptimizer> distributionPlanOptimizers, + final AccessControl accessControl) { this.statement = statement; this.sqlParser = sqlParser; this.metadata = metadata; @@ -106,6 +106,7 @@ public class TableModelPlanner implements IPlanner { this.asyncInternalServiceClientManager = asyncInternalServiceClientManager; this.logicalPlanOptimizers = logicalPlanOptimizers; this.distributionPlanOptimizers = distributionPlanOptimizers; + this.accessControl = accessControl; } @Override @@ -227,6 +228,4 @@ public class TableModelPlanner implements IPlanner { } } } - - public static class NopAccessControl implements AccessControl {} } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java index f61b67652d5..fe439a19a8c 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java @@ -19,4 +19,115 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; -public interface AccessControl {} +import org.apache.iotdb.commons.exception.auth.AccessDeniedException; +import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; + +public interface AccessControl { + + /** + * Check if user is allowed to create the specified database. + * + * @param userName name of user + * @param databaseName without `root.` prefix, like db + * @throws AccessDeniedException if not allowed + */ + void checkCanCreateDatabase(String userName, String databaseName); + + /** + * Check if user is allowed to drop the specified database. + * + * @param userName name of user + * @param databaseName without `root.` prefix, like db + * @throws AccessDeniedException if not allowed + */ + void checkCanDropDatabase(String userName, String databaseName); + + /** + * Check if user is allowed to alter the specified database. + * + * @param userName name of user + * @param databaseName without `root.` prefix, like db + * @throws AccessDeniedException if not allowed + */ + void checkCanAlterDatabase(String userName, String databaseName); + + /** + * Check if user is allowed to show or use the specified database. + * + * @param userName name of user + * @param databaseName without `root.` prefix, like db + * @throws AccessDeniedException if not allowed + */ + void checkCanShowOrUseDatabase(String userName, String databaseName); + + /** + * Check if user is allowed to create the specified table. + * + * @param userName name of user + * @param tableName qualified name of table without `root.` prefix, like db.table1 + * @throws AccessDeniedException if not allowed + */ + void checkCanCreateTable(String userName, QualifiedObjectName tableName); + + /** + * Check if user is allowed to create the specified table. + * + * @param userName name of user + * @param tableName qualified name of table without `root.` prefix, like db.table1 + * @throws AccessDeniedException if not allowed + */ + void checkCanDropTable(String userName, QualifiedObjectName tableName); + + /** + * Check if user is allowed to alter the specified table. + * + * @param userName name of user + * @param tableName qualified name of table without `root.` prefix, like db.table1 + * @throws AccessDeniedException if not allowed + */ + void checkCanAlterTable(String userName, QualifiedObjectName tableName); + + /** + * Check if user is allowed to insert into the specified table. + * + * @param userName name of user + * @param tableName qualified name of table without `root.` prefix, like db.table1 + * @throws AccessDeniedException if not allowed + */ + void checkCanInsertIntoTable(String userName, QualifiedObjectName tableName); + + /** + * Check if user is allowed to select from the specified table. + * + * @param userName name of user + * @param tableName qualified name of table without `root.` prefix, like db.table1 + * @throws AccessDeniedException if not allowed + */ + void checkCanSelectFromTable(String userName, QualifiedObjectName tableName); + + /** + * Check if user is allowed to delete from the specified table. + * + * @param userName name of user + * @param tableName qualified name of table without `root.` prefix, like db.table1 + * @throws AccessDeniedException if not allowed + */ + void checkCanDeleteFromTable(String userName, QualifiedObjectName tableName); + + /** + * Check if user is allowed to show or describe the specified table. + * + * @param userName name of user + * @param tableName qualified name of table without `root.` prefix, like db.table1 + * @throws AccessDeniedException if not allowed + */ + void checkCanShowOrDescTable(String userName, QualifiedObjectName tableName); + + /** + * Check if user has global maintain privilege + * + * @param userName name of user + * @throws AccessDeniedException if not allowed + */ + void checkUserHasMaintainPrivilege(String userName); +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java new file mode 100644 index 00000000000..e7426896699 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java @@ -0,0 +1,91 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.security; + +import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; + +public class AccessControlImpl implements AccessControl { + + private final ITableAuthChecker authChecker; + + public AccessControlImpl(ITableAuthChecker authChecker) { + this.authChecker = authChecker; + } + + @Override + public void checkCanCreateDatabase(String userName, String databaseName) { + authChecker.checkDatabasePrivilege(userName, databaseName, TableModelPrivilege.CREATE); + } + + @Override + public void checkCanDropDatabase(String userName, String databaseName) { + authChecker.checkDatabasePrivilege(userName, databaseName, TableModelPrivilege.DROP); + } + + @Override + public void checkCanAlterDatabase(String userName, String databaseName) { + authChecker.checkDatabasePrivilege(userName, databaseName, TableModelPrivilege.ALTER); + } + + @Override + public void checkCanShowOrUseDatabase(String userName, String databaseName) { + authChecker.checkDatabaseVisibility(userName, databaseName); + } + + @Override + public void checkCanCreateTable(String userName, QualifiedObjectName tableName) { + authChecker.checkTablePrivilege(userName, tableName, TableModelPrivilege.CREATE); + } + + @Override + public void checkCanDropTable(String userName, QualifiedObjectName tableName) { + authChecker.checkTablePrivilege(userName, tableName, TableModelPrivilege.DROP); + } + + @Override + public void checkCanAlterTable(String userName, QualifiedObjectName tableName) { + authChecker.checkTablePrivilege(userName, tableName, TableModelPrivilege.ALTER); + } + + @Override + public void checkCanInsertIntoTable(String userName, QualifiedObjectName tableName) { + authChecker.checkTablePrivilege(userName, tableName, TableModelPrivilege.INSERT); + } + + @Override + public void checkCanSelectFromTable(String userName, QualifiedObjectName tableName) { + authChecker.checkTablePrivilege(userName, tableName, TableModelPrivilege.SELECT); + } + + @Override + public void checkCanDeleteFromTable(String userName, QualifiedObjectName tableName) { + authChecker.checkTablePrivilege(userName, tableName, TableModelPrivilege.DELETE); + } + + @Override + public void checkCanShowOrDescTable(String userName, QualifiedObjectName tableName) { + authChecker.checkTableVisibility(userName, tableName); + } + + @Override + public void checkUserHasMaintainPrivilege(String userName) { + authChecker.checkMaintainPrivilege(userName); + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java index a6368023e18..99cb78b7a92 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AllowAllAccessControl.java @@ -19,4 +19,66 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; -public class AllowAllAccessControl implements AccessControl {} +import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; + +public class AllowAllAccessControl implements AccessControl { + @Override + public void checkCanCreateDatabase(String userName, String databaseName) { + // allow anything + } + + @Override + public void checkCanDropDatabase(String userName, String databaseName) { + // allow anything + } + + @Override + public void checkCanAlterDatabase(String userName, String databaseName) { + // allow anything + } + + @Override + public void checkCanShowOrUseDatabase(String userName, String databaseName) { + // allow anything + } + + @Override + public void checkCanCreateTable(String userName, QualifiedObjectName tableName) { + // allow anything + } + + @Override + public void checkCanDropTable(String userName, QualifiedObjectName tableName) { + // allow anything + } + + @Override + public void checkCanAlterTable(String userName, QualifiedObjectName tableName) { + // allow anything + } + + @Override + public void checkCanInsertIntoTable(String userName, QualifiedObjectName tableName) { + // allow anything + } + + @Override + public void checkCanSelectFromTable(String userName, QualifiedObjectName tableName) { + // allow anything + } + + @Override + public void checkCanDeleteFromTable(String userName, QualifiedObjectName tableName) { + // allow anything + } + + @Override + public void checkCanShowOrDescTable(String userName, QualifiedObjectName tableName) { + // allow anything + } + + @Override + public void checkUserHasMaintainPrivilege(String userName) { + // allow anything + } +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java new file mode 100644 index 00000000000..e234827bdd5 --- /dev/null +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java @@ -0,0 +1,75 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.iotdb.db.queryengine.plan.relational.security; + +import org.apache.iotdb.commons.exception.auth.AccessDeniedException; +import org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName; + +public interface ITableAuthChecker { + + /** + * Check if user has any privilege on ANY, @param{databaseName} or any tables + * in @param{databaseName} + * + * @param userName name of user + * @param databaseName without `root.` prefix, like db + * @throws AccessDeniedException if not allowed + */ + void checkDatabaseVisibility(String userName, String databaseName); + + /** + * Check if user has specified privilege on the specified database or bigger scope (like ANY). + * + * @param userName name of user + * @param databaseName without `root.` prefix, like db + * @param privilege specified privilege to be checked + * @throws AccessDeniedException if not allowed + */ + void checkDatabasePrivilege(String userName, String databaseName, TableModelPrivilege privilege); + + /** + * Check if user has specified privilege on the specified table or bigger scope (like database or + * ANY). + * + * @param userName name of user + * @param tableName qualified name of table without `root.` prefix, like db.table1 + * @param privilege specified privilege to be checked + * @throws AccessDeniedException if not allowed + */ + void checkTablePrivilege( + String userName, QualifiedObjectName tableName, TableModelPrivilege privilege); + + /** + * Check if user has any privilege on the specified table or bigger scope (like database or ANY). + * + * @param userName name of user + * @param tableName qualified name of table without `root.` prefix, like db.table1 + * @throws AccessDeniedException if not allowed + */ + void checkTableVisibility(String userName, QualifiedObjectName tableName); + + /** + * Check if user has global maintain privilege + * + * @param userName name of user + * @throws AccessDeniedException if not allowed + */ + void checkMaintainPrivilege(String userName); +} diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TableModelPrivilege.java similarity index 83% copy from iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java copy to iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TableModelPrivilege.java index f61b67652d5..1c9991aa234 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java +++ b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/TableModelPrivilege.java @@ -19,4 +19,17 @@ package org.apache.iotdb.db.queryengine.plan.relational.security; -public interface AccessControl {} +public enum TableModelPrivilege { + // global privilege + MANAGE_USER, + MANAGE_ROLE, + MAINTAIN, + + // scope privilege + CREATE, + DROP, + ALTER, + SELECT, + INSERT, + DELETE, +} diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java index b8e1ad9ae46..2463e552c9b 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AnalyzerTest.java @@ -63,6 +63,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.planner.node.OutputNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.ProjectNode; import org.apache.iotdb.db.queryengine.plan.relational.planner.node.TableScanNode; import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; +import org.apache.iotdb.db.queryengine.plan.relational.security.AllowAllAccessControl; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Expression; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.LogicalExpression; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement; @@ -112,7 +113,7 @@ import static org.mockito.ArgumentMatchers.eq; public class AnalyzerTest { - private static final NopAccessControl nopAccessControl = new NopAccessControl(); + private static final AccessControl nopAccessControl = new AllowAllAccessControl(); QueryId queryId = new QueryId("test_query"); SessionInfo sessionInfo = @@ -1264,6 +1265,4 @@ public class AnalyzerTest { NOOP); return analyzer.analyze(statement); } - - private static class NopAccessControl implements AccessControl {} } diff --git a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java index 5db55304a1f..199942012d2 100644 --- a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java +++ b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/planner/PlanTester.java @@ -37,7 +37,7 @@ import org.apache.iotdb.db.queryengine.plan.relational.execution.querystats.Plan import org.apache.iotdb.db.queryengine.plan.relational.metadata.Metadata; import org.apache.iotdb.db.queryengine.plan.relational.planner.distribute.TableDistributedPlanner; import org.apache.iotdb.db.queryengine.plan.relational.planner.optimizations.PlanOptimizer; -import org.apache.iotdb.db.queryengine.plan.relational.security.AccessControl; +import org.apache.iotdb.db.queryengine.plan.relational.security.AllowAllAccessControl; import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement; import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser; @@ -149,7 +149,7 @@ public class PlanTester { SessionInfo session) { try { StatementAnalyzerFactory statementAnalyzerFactory = - new StatementAnalyzerFactory(metadata, sqlParser, new NopAccessControl()); + new StatementAnalyzerFactory(metadata, sqlParser, new AllowAllAccessControl()); Analyzer analyzer = new Analyzer( @@ -175,6 +175,4 @@ public class PlanTester { } return distributedQueryPlan.getFragments().get(index).getPlanNodeTree().getChildren().get(0); } - - private static class NopAccessControl implements AccessControl {} } diff --git a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/auth/AccessDeniedException.java similarity index 66% copy from iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java copy to iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/auth/AccessDeniedException.java index f61b67652d5..9aca837f825 100644 --- a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControl.java +++ b/iotdb-core/node-commons/src/main/java/org/apache/iotdb/commons/exception/auth/AccessDeniedException.java @@ -17,6 +17,16 @@ * under the License. */ -package org.apache.iotdb.db.queryengine.plan.relational.security; +package org.apache.iotdb.commons.exception.auth; -public interface AccessControl {} +import org.apache.iotdb.commons.exception.IoTDBRuntimeException; +import org.apache.iotdb.rpc.TSStatusCode; + +public class AccessDeniedException extends IoTDBRuntimeException { + + public static final String PREFIX = "Access Denied: "; + + public AccessDeniedException(String message) { + super(PREFIX + message, TSStatusCode.NO_PERMISSION.getStatusCode()); + } +}
