From 8523fdeba37940a3d1659f75dc429284a555b974 Mon Sep 17 00:00:00 2001 From: zhangdong Date: Fri, 1 Aug 2025 11:33:12 +0800 Subject: [PATCH] =?UTF-8?q?branch-2.1:[fix](auth)Fix=20the=20issue=20of=20?= =?UTF-8?q?incorrectly=20checking=20base=20table=20permissio=E2=80=A6=20(#?= =?UTF-8?q?54005)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit …ns when querying external views (#53786) pick: https://github.com/apache/doris/pull/53786 --- .../java/org/apache/doris/catalog/View.java | 7 +- .../java/org/apache/doris/catalog/ViewIf.java | 23 ++ .../apache/doris/datasource/ExternalView.java | 204 ++++++++++++++++++ .../nereids/rules/analysis/BindRelation.java | 4 +- .../trees/plans/logical/LogicalView.java | 12 +- .../test_select_external_view_auth.groovy | 75 +++++++ 6 files changed, 315 insertions(+), 10 deletions(-) create mode 100644 fe/fe-core/src/main/java/org/apache/doris/catalog/ViewIf.java create mode 100644 fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalView.java create mode 100644 regression-test/suites/auth_p0/test_select_external_view_auth.groovy diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/View.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/View.java index 74cdad7107..d2eaa05dd3 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/View.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/View.java @@ -49,7 +49,7 @@ import java.util.List; * Refreshing or invalidating a view will reload the view's definition but will not * affect the metadata of the underlying tables (if any). */ -public class View extends Table { +public class View extends Table implements ViewIf { private static final Logger LOG = LogManager.getLogger(View.class); // The original SQL-string given as view definition. Set during analysis. @@ -207,6 +207,11 @@ public class View extends Table { return colLabels != null; } + @Override + public String getViewText() { + return inlineViewDef; + } + // Get the md5 of signature string of this view. // This method is used to determine whether the views have the same schema. // Contains: diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/ViewIf.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/ViewIf.java new file mode 100644 index 0000000000..e4c66fa467 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/ViewIf.java @@ -0,0 +1,23 @@ +// 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.doris.catalog; + +public interface ViewIf extends TableIf { + String getViewText(); +} + diff --git a/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalView.java b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalView.java new file mode 100644 index 0000000000..24f5dc3858 --- /dev/null +++ b/fe/fe-core/src/main/java/org/apache/doris/datasource/ExternalView.java @@ -0,0 +1,204 @@ +// 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.doris.datasource; + +import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.DatabaseIf; +import org.apache.doris.catalog.ViewIf; +import org.apache.doris.common.Pair; +import org.apache.doris.statistics.AnalysisInfo; +import org.apache.doris.statistics.BaseAnalysisTask; +import org.apache.doris.statistics.ColumnStatistic; +import org.apache.doris.statistics.TableStatsMeta; +import org.apache.doris.thrift.TTableDescriptor; + +import java.io.DataOutput; +import java.io.IOException; +import java.util.List; +import java.util.Optional; +import java.util.Set; + +public class ExternalView implements ViewIf { + private String viewText; + private ExternalTable externalTable; + + public ExternalView(ExternalTable externalTable, String viewText) { + this.viewText = viewText; + this.externalTable = externalTable; + } + + @Override + public String getViewText() { + return viewText; + } + + public ExternalTable getExternalTable() { + return externalTable; + } + + + @Override + public long getId() { + return externalTable.getId(); + } + + public String getName() { + return externalTable.getName(); + } + + @Override + public TableType getType() { + return externalTable.getType(); + } + + @Override + public List getFullSchema() { + return externalTable.getFullSchema(); + } + + @Override + public List getBaseSchema() { + return externalTable.getBaseSchema(); + } + + @Override + public List getSchemaAllIndexes(boolean full) { + return externalTable.getSchemaAllIndexes(full); + } + + @Override + public List getBaseSchema(boolean full) { + return externalTable.getBaseSchema(); + } + + @Override + public void setNewFullSchema(List newSchema) { + externalTable.setNewFullSchema(newSchema); + } + + @Override + public Column getColumn(String name) { + return externalTable.getColumn(name); + } + + @Override + public String getMysqlType() { + return externalTable.getMysqlType(); + } + + @Override + public String getEngine() { + return externalTable.getEngine(); + } + + @Override + public String getComment() { + return externalTable.getComment(); + } + + @Override + public long getCreateTime() { + return externalTable.getCreateTime(); + } + + @Override + public long getUpdateTime() { + return externalTable.getUpdateTime(); + } + + @Override + public long getRowCount() { + return externalTable.getRowCount(); + } + + @Override + public long getCachedRowCount() { + return externalTable.getCachedRowCount(); + } + + @Override + public long fetchRowCount() { + return externalTable.fetchRowCount(); + } + + @Override + public long getDataLength() { + return externalTable.getDataLength(); + } + + @Override + public long getAvgRowLength() { + return externalTable.getAvgRowLength(); + } + + @Override + public long getLastCheckTime() { + return externalTable.getLastCheckTime(); + } + + @Override + public String getComment(boolean escapeQuota) { + return externalTable.getComment(); + } + + @Override + public TTableDescriptor toThrift() { + return externalTable.toThrift(); + } + + @Override + public BaseAnalysisTask createAnalysisTask(AnalysisInfo info) { + return externalTable.createAnalysisTask(info); + } + + @Override + public DatabaseIf getDatabase() { + return externalTable.getDatabase(); + } + + @Override + public Optional getColumnStatistic(String colName) { + return externalTable.getColumnStatistic(colName); + } + + @Override + public boolean needReAnalyzeTable(TableStatsMeta tblStats) { + return false; + } + + @Override + public List> getColumnIndexPairs(Set columns) { + return externalTable.getColumnIndexPairs(columns); + } + + @Override + public List getChunkSizes() { + return externalTable.getChunkSizes(); + } + + @Override + public void write(DataOutput out) throws IOException { + externalTable.write(out); + } + + @Override + public boolean autoAnalyzeEnabled() { + return externalTable.autoAnalyzeEnabled(); + } + +} diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java index cbc2a93f18..e4dd26e167 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/rules/analysis/BindRelation.java @@ -33,6 +33,7 @@ import org.apache.doris.common.Config; import org.apache.doris.common.Pair; import org.apache.doris.common.util.Util; import org.apache.doris.datasource.ExternalTable; +import org.apache.doris.datasource.ExternalView; import org.apache.doris.datasource.hive.HMSExternalTable; import org.apache.doris.datasource.hive.HMSExternalTable.DLAType; import org.apache.doris.nereids.CTEContext; @@ -464,7 +465,8 @@ public class BindRelation extends OneAnalysisRuleFactory { ctx.changeDefaultCatalog(hiveCatalog); ctx.setDatabase(hiveDb); try { - return parseAndAnalyzeView(table, ddlSql, cascadesContext); + return new LogicalView<>(new ExternalView(table, ddlSql), + parseAndAnalyzeView(table, ddlSql, cascadesContext)); } finally { // restore catalog and db in connect context ctx.changeDefaultCatalog(previousCatalog); diff --git a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java index 4f0468da05..77fa4080b4 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java +++ b/fe/fe-core/src/main/java/org/apache/doris/nereids/trees/plans/logical/LogicalView.java @@ -17,7 +17,7 @@ package org.apache.doris.nereids.trees.plans.logical; -import org.apache.doris.catalog.View; +import org.apache.doris.catalog.ViewIf; import org.apache.doris.nereids.exceptions.AnalysisException; import org.apache.doris.nereids.memo.GroupExpression; import org.apache.doris.nereids.properties.FdItem; @@ -40,10 +40,10 @@ import java.util.Optional; /** LogicalView */ public class LogicalView extends LogicalUnary { - private final View view; + private final ViewIf view; /** LogicalView */ - public LogicalView(View view, BODY body) { + public LogicalView(ViewIf view, BODY body) { super(PlanType.LOGICAL_VIEW, Optional.empty(), Optional.empty(), body); this.view = Objects.requireNonNull(view, "catalog can not be null"); if (!(body instanceof LogicalPlan)) { @@ -73,11 +73,7 @@ public class LogicalView extends LogicalUnary { return view.getName(); } - public String getViewString() { - return view.getInlineViewDef(); - } - - public View getView() { + public ViewIf getView() { return view; } diff --git a/regression-test/suites/auth_p0/test_select_external_view_auth.groovy b/regression-test/suites/auth_p0/test_select_external_view_auth.groovy new file mode 100644 index 0000000000..ebd7a3bea9 --- /dev/null +++ b/regression-test/suites/auth_p0/test_select_external_view_auth.groovy @@ -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. + +suite("test_select_external_view_auth","p0,auth") { + String enabled = context.config.otherConfigs.get("enableHiveTest") + if (enabled == null || !enabled.equalsIgnoreCase("true")) { + logger.info("diable Hive test.") + return; + } + for (String hivePrefix : ["hive2", "hive3"]) { + try { + String hms_port = context.config.otherConfigs.get(hivePrefix + "HmsPort") + String catalogName = "${hivePrefix}_test_mtmv" + String externalEnvIp = context.config.otherConfigs.get("externalEnvIp") + + sql """drop catalog if exists ${catalogName}""" + sql """create catalog if not exists ${catalogName} properties ( + "type"="hms", + 'hive.metastore.uris' = 'thrift://${externalEnvIp}:${hms_port}' + );""" + + String suiteName = "test_select_external_view_auth" + String user = "${suiteName}_user" + String pwd = 'C123_567p' + String dbName = "`default`" + String tableName = "sale_table" + String viewName = "test_view1" + + try_sql("drop user ${user}") + sql """create user '${user}' IDENTIFIED by '${pwd}'""" + sql """grant select_priv on regression_test to ${user}""" + + //cloud-mode + if (isCloudMode()) { + def clusters = sql " SHOW CLUSTERS; " + assertTrue(!clusters.isEmpty()) + def validCluster = clusters[0][0] + sql """GRANT USAGE_PRIV ON CLUSTER `${validCluster}` TO ${user}"""; + } + + sql """grant select_priv on ${catalogName}.${dbName}.${tableName} to ${user}""" + // table column + connect(user, "${pwd}", context.config.jdbcUrl) { + try { + sql "select * from ${catalogName}.${dbName}.${viewName}" + } catch (Exception e) { + log.info(e.getMessage()) + assertTrue(e.getMessage().contains("denied")) + } + } + sql """revoke select_priv on ${catalogName}.${dbName}.${tableName} from ${user}""" + sql """grant select_priv on ${catalogName}.${dbName}.${viewName} to ${user}""" + connect(user, "${pwd}", context.config.jdbcUrl) { + sql "select * from ${catalogName}.${dbName}.${viewName}" + } + try_sql("drop user ${user}") + sql """drop catalog if exists ${catalogName}""" + } finally { + } + } +}