This is an automated email from the ASF dual-hosted git repository.
jackietien 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 06d9f3bb963 Add UT for query auth check
06d9f3bb963 is described below
commit 06d9f3bb963c43b5baa4b0fca3190eef7c7c33e5
Author: Jackie Tien <[email protected]>
AuthorDate: Thu Dec 5 08:24:24 2024 +0800
Add UT for query auth check
---
.../relational/security/AccessControlImpl.java | 2 +-
.../relational/security/ITableAuthChecker.java | 5 +-
.../plan/relational/analyzer/AuthTest.java | 252 +++++++++++++++++++++
3 files changed, 256 insertions(+), 3 deletions(-)
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
index e7426896699..009db9967a8 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/AccessControlImpl.java
@@ -86,6 +86,6 @@ public class AccessControlImpl implements AccessControl {
@Override
public void checkUserHasMaintainPrivilege(String userName) {
- authChecker.checkMaintainPrivilege(userName);
+ authChecker.checkGlobalPrivilege(userName, TableModelPrivilege.MAINTAIN);
}
}
diff --git
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java
index e234827bdd5..490c7662274 100644
---
a/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java
+++
b/iotdb-core/datanode/src/main/java/org/apache/iotdb/db/queryengine/plan/relational/security/ITableAuthChecker.java
@@ -66,10 +66,11 @@ public interface ITableAuthChecker {
void checkTableVisibility(String userName, QualifiedObjectName tableName);
/**
- * Check if user has global maintain privilege
+ * Check if user has the specified global privilege
*
* @param userName name of user
+ * @param privilege specified global privilege to be checked
* @throws AccessDeniedException if not allowed
*/
- void checkMaintainPrivilege(String userName);
+ void checkGlobalPrivilege(String userName, TableModelPrivilege privilege);
}
diff --git
a/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AuthTest.java
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AuthTest.java
new file mode 100644
index 00000000000..83f8067c59c
--- /dev/null
+++
b/iotdb-core/datanode/src/test/java/org/apache/iotdb/db/queryengine/plan/relational/analyzer/AuthTest.java
@@ -0,0 +1,252 @@
+/*
+ * 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.analyzer;
+
+import org.apache.iotdb.commons.exception.auth.AccessDeniedException;
+import org.apache.iotdb.db.protocol.session.IClientSession;
+import org.apache.iotdb.db.queryengine.common.MPPQueryContext;
+import org.apache.iotdb.db.queryengine.common.SessionInfo;
+import
org.apache.iotdb.db.queryengine.plan.execution.config.TableConfigTaskVisitor;
+import
org.apache.iotdb.db.queryengine.plan.relational.metadata.QualifiedObjectName;
+import
org.apache.iotdb.db.queryengine.plan.relational.security.AccessControlImpl;
+import
org.apache.iotdb.db.queryengine.plan.relational.security.ITableAuthChecker;
+import
org.apache.iotdb.db.queryengine.plan.relational.security.TableModelPrivilege;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.ast.Statement;
+import org.apache.iotdb.db.queryengine.plan.relational.sql.parser.SqlParser;
+
+import org.junit.Test;
+import org.mockito.Mockito;
+
+import java.time.ZoneId;
+import java.util.Collections;
+
+import static
org.apache.iotdb.db.queryengine.execution.warnings.WarningCollector.NOOP;
+import static
org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestMatadata.DB1;
+import static
org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestMatadata.TABLE1;
+import static
org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.QUERY_ID;
+import static
org.apache.iotdb.db.queryengine.plan.relational.analyzer.TestUtils.TEST_MATADATA;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
+
+public class AuthTest {
+
+ private final SqlParser sqlParser = new SqlParser();
+
+ private final ZoneId zoneId = ZoneId.systemDefault();
+
+ private final QualifiedObjectName testdbTable1 = new
QualifiedObjectName(DB1, TABLE1);
+
+ private final String userRoot = "root";
+
+ private final String user1 = "user1";
+
+ private final String user2 = "user2";
+
+ @Test
+ public void testQueryRelatedAuth() {
+ String sql = String.format("SELECT * FROM %s.%s", DB1, TABLE1);
+ ITableAuthChecker authChecker = Mockito.mock(ITableAuthChecker.class);
+ // user `root` always succeed
+ Mockito.doNothing().when(authChecker).checkTablePrivilege(eq(userRoot),
any(), any());
+ // user `user1` don't hava testdb.table1's SELECT privilege
+ String errorMsg =
+ String.format(
+ "%s doesn't have %s privilege on TABLE %s.%s",
+ user1, TableModelPrivilege.SELECT, DB1, TABLE1);
+ Mockito.doThrow(new AccessDeniedException(errorMsg))
+ .when(authChecker)
+ .checkTablePrivilege(eq(user1), eq(testdbTable1),
eq(TableModelPrivilege.SELECT));
+ // user `user2` has testdb.table1's SELECT privilege
+ Mockito.doNothing()
+ .when(authChecker)
+ .checkTablePrivilege(eq(user2), eq(testdbTable1),
eq(TableModelPrivilege.SELECT));
+
+ String userName = userRoot;
+ try {
+ analyzeSQL(sql, userName, authChecker);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+
+ userName = user1;
+ try {
+ analyzeSQL(sql, userName, authChecker);
+ fail("user1 should be denied");
+ } catch (AccessDeniedException e) {
+ assertEquals(AccessDeniedException.PREFIX + errorMsg, e.getMessage());
+ } catch (Exception e) {
+ fail("Unexpected exception : " + e.getMessage());
+ }
+
+ userName = user2;
+ try {
+ analyzeSQL(sql, userName, authChecker, DB1);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+ }
+
+ @Test
+ public void testDatabaseManagementRelatedAuth() {
+
+ // create database
+ String databaseTest1 = "test1";
+ String sql = String.format("CREATE DATABASE %s", databaseTest1);
+ ITableAuthChecker authChecker = Mockito.mock(ITableAuthChecker.class);
+ // user `root` always succeed
+ Mockito.doNothing().when(authChecker).checkDatabasePrivilege(eq(userRoot),
any(), any());
+ // user `user1` don't hava test1's CREATE privilege
+ String errorMsg =
+ String.format(
+ "%s doesn't have %s privilege on DATABASE %s",
+ user1, TableModelPrivilege.CREATE, databaseTest1);
+ Mockito.doThrow(new AccessDeniedException(errorMsg))
+ .when(authChecker)
+ .checkDatabasePrivilege(eq(user1), eq(databaseTest1),
eq(TableModelPrivilege.CREATE));
+ // user `user2` has test1's CREATE privilege
+ Mockito.doNothing()
+ .when(authChecker)
+ .checkDatabasePrivilege(eq(user2), eq(databaseTest1),
eq(TableModelPrivilege.CREATE));
+
+ String userName = userRoot;
+ try {
+ analyzeConfigTask(sql, userName, authChecker);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+
+ userName = user1;
+ try {
+ analyzeConfigTask(sql, userName, authChecker);
+ fail("user1 should be denied");
+ } catch (AccessDeniedException e) {
+ assertEquals(AccessDeniedException.PREFIX + errorMsg, e.getMessage());
+ } catch (Exception e) {
+ fail("Unexpected exception : " + e.getMessage());
+ }
+
+ userName = user2;
+ try {
+ analyzeConfigTask(sql, userName, authChecker);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+
+ // use database
+ String databaseTest2 = "test2";
+ // user `root` always succeed
+
Mockito.doNothing().when(authChecker).checkDatabaseVisibility(eq(userRoot),
any());
+ // user `user1` can't see DATABASE test1
+ errorMsg = String.format("%s has no privileges on DATABASE %s", user1,
databaseTest1);
+ Mockito.doThrow(new AccessDeniedException(errorMsg))
+ .when(authChecker)
+ .checkDatabaseVisibility(eq(user1), eq(databaseTest1));
+ // user `user1` can see DATABASE test2
+ Mockito.doNothing().when(authChecker).checkDatabaseVisibility(eq(user1),
eq(databaseTest2));
+
+ sql = String.format("USE %s", databaseTest1);
+ userName = userRoot;
+ try {
+ analyzeConfigTask(sql, userName, authChecker);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+
+ userName = user1;
+ try {
+ analyzeConfigTask(sql, userName, authChecker);
+ fail("user1 should be denied");
+ } catch (AccessDeniedException e) {
+ assertEquals(AccessDeniedException.PREFIX + errorMsg, e.getMessage());
+ } catch (Exception e) {
+ fail("Unexpected exception : " + e.getMessage());
+ }
+
+ sql = String.format("USE %s", databaseTest2);
+ try {
+ analyzeConfigTask(sql, userName, authChecker);
+ } catch (Exception e) {
+ fail(e.getMessage());
+ }
+
+ // TODO show databases
+
+ // TODO show databases
+
+ // TODO drop database
+
+ // TODO alter database
+
+ }
+
+ private void analyzeSQL(String sql, String userName, ITableAuthChecker
authChecker) {
+ analyzeSQL(sql, userName, authChecker, null);
+ }
+
+ private void analyzeSQL(
+ String sql,
+ String userName,
+ ITableAuthChecker authChecker,
+ String databaseNameInSessionInfo) {
+ Statement statement = sqlParser.createStatement(sql, zoneId);
+ SessionInfo session =
+ new SessionInfo(
+ 0, userName, zoneId, databaseNameInSessionInfo,
IClientSession.SqlDialect.TABLE);
+ StatementAnalyzerFactory statementAnalyzerFactory =
+ new StatementAnalyzerFactory(TEST_MATADATA, sqlParser, new
AccessControlImpl(authChecker));
+ MPPQueryContext context = new MPPQueryContext(sql, QUERY_ID, 0, session,
null, null);
+ Analyzer analyzer =
+ new Analyzer(
+ context,
+ session,
+ statementAnalyzerFactory,
+ Collections.emptyList(),
+ Collections.emptyMap(),
+ NOOP);
+ analyzer.analyze(statement);
+ }
+
+ private void analyzeConfigTask(String sql, String userName,
ITableAuthChecker authChecker) {
+ Statement statement = sqlParser.createStatement(sql, zoneId);
+ SessionInfo session =
+ new SessionInfo(0, userName, zoneId, null,
IClientSession.SqlDialect.TABLE);
+ MPPQueryContext context = new MPPQueryContext(sql, QUERY_ID, 0, session,
null, null);
+
+ statement.accept(
+ new TableConfigTaskVisitor(
+ Mockito.mock(IClientSession.class), TEST_MATADATA, new
AccessControlImpl(authChecker)),
+ context);
+ }
+
+ private void analyzeConfigTask(
+ String sql, String userName, ITableAuthChecker authChecker,
IClientSession clientSession) {
+ Statement statement = sqlParser.createStatement(sql, zoneId);
+ SessionInfo session =
+ new SessionInfo(0, userName, zoneId, null,
IClientSession.SqlDialect.TABLE);
+ MPPQueryContext context = new MPPQueryContext(sql, QUERY_ID, 0, session,
null, null);
+
+ statement.accept(
+ new TableConfigTaskVisitor(
+ clientSession, TEST_MATADATA, new AccessControlImpl(authChecker)),
+ context);
+ }
+}