From d31ab894f7eba58a28abbef7aa949c90eb0a9e63 Mon Sep 17 00:00:00 2001 From: zhangdong <493738387@qq.com> Date: Sat, 3 Aug 2024 06:23:38 +0800 Subject: [PATCH] [fix](auth)fix create table like need create_priv of existed table #37879 #25711 (#38569) --- .../doris/datasource/InternalCatalog.java | 24 ++++-- .../privilege/AccessControllerManager.java | 3 + .../org/apache/doris/qe/ConnectContext.java | 14 ++++ .../org/apache/doris/qe/StmtExecutor.java | 21 +++-- .../insert_into_table/insert_auth.groovy | 79 +++++++++++++++++++ 5 files changed, 128 insertions(+), 13 deletions(-) create mode 100644 regression-test/suites/nereids_p0/insert_into_table/insert_auth.groovy diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java index cfe94a1dded9ab..78d9d4f3d48e74 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/InternalCatalog.java @@ -186,6 +186,7 @@ import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; @@ -1136,6 +1137,8 @@ public boolean createTable(CreateTableStmt stmt) throws UserException { } public void createTableLike(CreateTableLikeStmt stmt) throws DdlException { + ConnectContext ctx = ConnectContext.get(); + Objects.requireNonNull(ctx, "ConnectContext.get() can not be null."); try { DatabaseIf db = getDbOrDdlException(stmt.getExistedDbName()); TableIf table = db.getTableOrDdlException(stmt.getExistedTableName()); @@ -1169,14 +1172,23 @@ public void createTableLike(CreateTableLikeStmt stmt) throws DdlException { } finally { table.readUnlock(); } - CreateTableStmt parsedCreateTableStmt = (CreateTableStmt) SqlParserUtils.parseAndAnalyzeStmt( - createTableStmt.get(0), ConnectContext.get()); - parsedCreateTableStmt.setTableName(stmt.getTableName()); - parsedCreateTableStmt.setIfNotExists(stmt.isIfNotExists()); - createTable(parsedCreateTableStmt); + boolean originalSkipAuth = ctx.isSkipAuth(); + try { + // analyze CreateTableStmt will check create_priv of existedTable, create table like only need + // create_priv of newTable, and select_priv of existedTable, and priv check has done in + // CreateTableStmt/CreateTableCommand, so we skip it + ctx.setSkipAuth(true); + CreateTableStmt parsedCreateTableStmt = (CreateTableStmt) SqlParserUtils.parseAndAnalyzeStmt( + createTableStmt.get(0), ctx); + parsedCreateTableStmt.setTableName(stmt.getTableName()); + parsedCreateTableStmt.setIfNotExists(stmt.isIfNotExists()); + createTable(parsedCreateTableStmt); + } finally { + ctx.setSkipAuth(originalSkipAuth); + } } catch (UserException e) { throw new DdlException("Failed to execute CREATE TABLE LIKE " + stmt.getExistedTableName() + ". Reason: " - + e.getMessage()); + + e.getMessage(), e); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java index 257a6e88bc5a4d..db88ee34b7d7cb 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/AccessControllerManager.java @@ -170,6 +170,9 @@ public boolean checkTblPriv(ConnectContext ctx, TableName tableName, PrivPredica public boolean checkTblPriv(ConnectContext ctx, String qualifiedCtl, String qualifiedDb, String tbl, PrivPredicate wanted) { + if (ctx.isSkipAuth()) { + return true; + } return checkTblPriv(ctx.getCurrentUserIdentity(), qualifiedCtl, qualifiedDb, tbl, wanted); } diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java index c0e0bd52bc5710..c73bd2915bfb03 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ConnectContext.java @@ -194,6 +194,12 @@ public class ConnectContext { // it's default thread-safe private boolean isProxy = false; + //internal call like `insert overwrite` need skipAuth + // For example, `insert overwrite` only requires load permission, + // but the internal implementation will call the logic of `AlterTable`. + // In this case, `skipAuth` needs to be set to `true` to skip the permission check of `AlterTable` + private boolean skipAuth = false; + public void setUserQueryTimeout(int queryTimeout) { if (queryTimeout > 0) { sessionVariable.setQueryTimeoutS(queryTimeout); @@ -979,5 +985,13 @@ public void setUserVars(Map userVars) { public boolean isProxy() { return isProxy; } + + public boolean isSkipAuth() { + return skipAuth; + } + + public void setSkipAuth(boolean skipAuth) { + this.skipAuth = skipAuth; + } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java index 19dc2bd012964a..060b629c3e02f9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/StmtExecutor.java @@ -1069,6 +1069,7 @@ public void analyze(TQueryOptions tQueryOptions) throws UserException, Interrupt queryStmt.getTables(analyzer, false, tableMap, parentViewNameSet); } else if (parsedStmt instanceof InsertOverwriteTableStmt) { InsertOverwriteTableStmt parsedStmt = (InsertOverwriteTableStmt) this.parsedStmt; + parsedStmt.analyze(analyzer); queryStmt = parsedStmt.getQueryStmt(); queryStmt.getTables(analyzer, false, tableMap, parentViewNameSet); } else if (parsedStmt instanceof CreateTableAsSelectStmt) { @@ -2507,13 +2508,19 @@ private void handleCtasRollback(TableName table) { } private void handleIotStmt() { - InsertOverwriteTableStmt iotStmt = (InsertOverwriteTableStmt) this.parsedStmt; - if (iotStmt.getPartitionNames().size() == 0) { - // insert overwrite table - handleOverwriteTable(iotStmt); - } else { - // insert overwrite table with partition - handleOverwritePartition(iotStmt); + boolean originalSkipAuth = context.isSkipAuth(); + context.setSkipAuth(true); + try { + InsertOverwriteTableStmt iotStmt = (InsertOverwriteTableStmt) this.parsedStmt; + if (iotStmt.getPartitionNames().size() == 0) { + // insert overwrite table + handleOverwriteTable(iotStmt); + } else { + // insert overwrite table with partition + handleOverwritePartition(iotStmt); + } + } finally { + context.setSkipAuth(originalSkipAuth); } } diff --git a/regression-test/suites/nereids_p0/insert_into_table/insert_auth.groovy b/regression-test/suites/nereids_p0/insert_into_table/insert_auth.groovy new file mode 100644 index 00000000000000..1a333d41d3269e --- /dev/null +++ b/regression-test/suites/nereids_p0/insert_into_table/insert_auth.groovy @@ -0,0 +1,79 @@ +// Licensed to the Apache Software Foundation (ASF) under one +// or more contributor license agreements. See the NOTICE file +// distributed with this work for additional information +// regarding copyright ownership. The ASF licenses this file +// to you under the Apache License, Version 2.0 (the +// "License"); you may not use this file except in compliance +// with the License. You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, +// software distributed under the License is distributed on an +// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. See the License for the +// specific language governing permissions and limitations +// under the License. + +suite('nereids_insert_auth') { + sql 'set enable_nereids_planner=true' + sql 'set enable_fallback_to_original_planner=false' + sql 'set enable_nereids_dml=true' + sql 'set enable_strict_consistency_dml=true' + + def db = 'nereids_insert_auth_db' + sql "drop database if exists ${db}" + sql "create database ${db}" + sql "use ${db}" + + def t1 = 't1' + + sql "drop table if exists ${t1}" + + sql """ + create table ${t1} ( + id int, + c1 bigint + ) + distributed by hash(id) buckets 2 + properties( + 'replication_num'='1' + ); + """ + + String user = "nereids_insert_auth_user"; + String pwd = '123456'; + def tokens = context.config.jdbcUrl.split('/') + def url = tokens[0] + "//" + tokens[2] + "/" + "information_schema" + "?" + try_sql("DROP USER ${user}") + sql """CREATE USER '${user}' IDENTIFIED BY '${pwd}'""" + + connect(user=user, password="${pwd}", url=url) { + try { + sql """ insert into ${db}.${t1} values (1, 1) """ + fail() + } catch (Exception e) { + log.info(e.getMessage()) + } + } + + sql """GRANT LOAD_PRIV ON ${db}.${t1} TO ${user}""" + + connect(user=user, password="${pwd}", url=url) { + try { + sql """ insert into ${db}.${t1} values (1, 1) """ + } catch (Exception e) { + log.info(e.getMessage()) + fail() + } + } + + connect(user=user, password="${pwd}", url=url) { + try { + sql """ insert overwrite table ${db}.${t1} values (2, 2) """ + } catch (Exception e) { + log.info(e.getMessage()) + fail() + } + } +} \ No newline at end of file