diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowBackendsStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowBackendsStmt.java index 8e1e589477d2dc..06652d2dada50e 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowBackendsStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowBackendsStmt.java @@ -19,11 +19,13 @@ import org.apache.doris.catalog.Column; import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.InfoSchemaDb; import org.apache.doris.catalog.ScalarType; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; import org.apache.doris.common.UserException; import org.apache.doris.common.proc.BackendsProcDir; +import org.apache.doris.datasource.InternalCatalog; import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.ShowResultSetMetaData; @@ -37,10 +39,10 @@ public ShowBackendsStmt() { public void analyze(Analyzer analyzer) throws UserException { super.analyze(analyzer); - if (!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), PrivPredicate.ADMIN) - && !Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), - PrivPredicate.OPERATOR)) { - ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "ADMIN/OPERATOR"); + if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), + InternalCatalog.INTERNAL_CATALOG_NAME, InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_DB_ACCESS_DENIED_ERROR, + PrivPredicate.SELECT.getPrivs().toString(), InfoSchemaDb.DATABASE_NAME); } } diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowFrontendsStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowFrontendsStmt.java index d8b5c454ff9d08..4e968843477e14 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowFrontendsStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowFrontendsStmt.java @@ -19,11 +19,13 @@ import org.apache.doris.catalog.Column; import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.InfoSchemaDb; import org.apache.doris.catalog.ScalarType; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; import org.apache.doris.common.proc.FrontendsProcNode; +import org.apache.doris.datasource.InternalCatalog; import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.qe.ConnectContext; import org.apache.doris.qe.ShowResultSetMetaData; @@ -46,10 +48,10 @@ public String getDetailType() { @Override public void analyze(Analyzer analyzer) throws AnalysisException { - if (!Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), PrivPredicate.ADMIN) - && !Env.getCurrentEnv().getAccessManager().checkGlobalPriv(ConnectContext.get(), - PrivPredicate.OPERATOR)) { - ErrorReport.reportAnalysisException(ErrorCode.ERR_SPECIFIC_ACCESS_DENIED_ERROR, "ADMIN/OPERATOR"); + if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), + InternalCatalog.INTERNAL_CATALOG_NAME, InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT)) { + ErrorReport.reportAnalysisException(ErrorCode.ERR_DB_ACCESS_DENIED_ERROR, + PrivPredicate.SELECT.getPrivs().toString(), InfoSchemaDb.DATABASE_NAME); } if (detail != null && !detail.equalsIgnoreCase("disks")) { diff --git a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/BackendsAction.java b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/BackendsAction.java index cef898326b9acf..81e44421500b50 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/BackendsAction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/BackendsAction.java @@ -18,7 +18,10 @@ package org.apache.doris.httpv2.rest; import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.InfoSchemaDb; import org.apache.doris.httpv2.entity.ResponseEntityBuilder; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; import org.apache.doris.system.Backend; import com.fasterxml.jackson.annotation.JsonProperty; @@ -66,12 +69,8 @@ public class BackendsAction extends RestBaseController { @RequestMapping(path = "/api/backends", method = {RequestMethod.GET}) public Object getBackends(HttpServletRequest request, HttpServletResponse response) { - /** - * As required, the interface should require user have GlobalAuth-PrivPredicate.ADMIN permission. - * However, a user who uses spark-doris-connector/flink-doris-connector does not have corresponding permission. - * To ensure that the connector works properly, we do not verify the permission of the interface. - */ executeCheckPassword(request, response); + checkDbAuth(ConnectContext.get().getCurrentUserIdentity(), InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT); boolean needAlive = false; String isAlive = request.getParameter(IS_ALIVE); diff --git a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/manager/NodeAction.java b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/manager/NodeAction.java index 4658f936db5f1f..36620c36e92159 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/manager/NodeAction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/httpv2/rest/manager/NodeAction.java @@ -18,6 +18,7 @@ package org.apache.doris.httpv2.rest.manager; import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.InfoSchemaDb; import org.apache.doris.common.Config; import org.apache.doris.common.ConfigBase; import org.apache.doris.common.MarkedCountDownLatch; @@ -108,7 +109,7 @@ public class NodeAction extends RestBaseController { @RequestMapping(path = "/frontends", method = RequestMethod.GET) public Object frontends_info(HttpServletRequest request, HttpServletResponse response) throws Exception { executeCheckPassword(request, response); - checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(), PrivPredicate.ADMIN); + checkDbAuth(ConnectContext.get().getCurrentUserIdentity(), InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT); return fetchNodeInfo(request, response, "/frontends"); } @@ -117,7 +118,7 @@ public Object frontends_info(HttpServletRequest request, HttpServletResponse res @RequestMapping(path = "/backends", method = RequestMethod.GET) public Object backends_info(HttpServletRequest request, HttpServletResponse response) throws Exception { executeCheckPassword(request, response); - checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(), PrivPredicate.ADMIN); + checkDbAuth(ConnectContext.get().getCurrentUserIdentity(), InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT); return fetchNodeInfo(request, response, "/backends"); } @@ -126,7 +127,7 @@ public Object backends_info(HttpServletRequest request, HttpServletResponse resp @RequestMapping(path = "/brokers", method = RequestMethod.GET) public Object brokers_info(HttpServletRequest request, HttpServletResponse response) throws Exception { executeCheckPassword(request, response); - checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(), PrivPredicate.ADMIN); + checkDbAuth(ConnectContext.get().getCurrentUserIdentity(), InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT); return fetchNodeInfo(request, response, "/brokers"); } @@ -181,7 +182,7 @@ public NodeInfo(List columnNames, List> rows) { @RequestMapping(path = "/configuration_name", method = RequestMethod.GET) public Object configurationName(HttpServletRequest request, HttpServletResponse response) { executeCheckPassword(request, response); - checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(), PrivPredicate.ADMIN); + checkDbAuth(ConnectContext.get().getCurrentUserIdentity(), InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT); Map> result = Maps.newHashMap(); try { @@ -220,7 +221,7 @@ public Object configurationName(HttpServletRequest request, HttpServletResponse @RequestMapping(path = "/node_list", method = RequestMethod.GET) public Object nodeList(HttpServletRequest request, HttpServletResponse response) { executeCheckPassword(request, response); - checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(), PrivPredicate.ADMIN); + checkDbAuth(ConnectContext.get().getCurrentUserIdentity(), InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT); Map> result = Maps.newHashMap(); result.put("frontend", getFeList()); @@ -247,7 +248,7 @@ private static List getBeList() { @RequestMapping(path = "/config", method = RequestMethod.GET) public Object config(HttpServletRequest request, HttpServletResponse response) { executeCheckPassword(request, response); - checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(), PrivPredicate.ADMIN); + checkDbAuth(ConnectContext.get().getCurrentUserIdentity(), InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT); List> configs = ConfigBase.getConfigInfo(null); // Sort all configs by config key. @@ -308,7 +309,7 @@ public Object configurationInfo(HttpServletRequest request, HttpServletResponse @RequestParam(value = "type") String type, @RequestBody(required = false) ConfigInfoRequestBody requestBody) { executeCheckPassword(request, response); - checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(), PrivPredicate.ADMIN); + checkGlobalAuth(ConnectContext.get().getCurrentUserIdentity(), PrivPredicate.ADMIN_OR_NODE); initHttpExecutor(); diff --git a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java index 4be871f7af8d93..57f7f677d02cb1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java +++ b/fe/fe-core/src/main/java/org/apache/doris/qe/ShowExecutor.java @@ -2262,8 +2262,13 @@ private void handleShowExport() throws AnalysisException { resultSet = new ShowResultSet(showExportStmt.getMetaData(), infos); } - private void handleShowBackends() { + private void handleShowBackends() throws AnalysisException { final ShowBackendsStmt showStmt = (ShowBackendsStmt) stmt; + try { + showStmt.analyze(null); + } catch (Exception e) { + throw (AnalysisException) e; + } List> backendInfos = BackendsProcDir.getBackendInfos(); backendInfos.sort(new Comparator>() { @@ -2276,8 +2281,13 @@ public int compare(List o1, List o2) { resultSet = new ShowResultSet(showStmt.getMetaData(), backendInfos); } - private void handleShowFrontends() { + private void handleShowFrontends() throws AnalysisException { final ShowFrontendsStmt showStmt = (ShowFrontendsStmt) stmt; + try { + showStmt.analyze(null); + } catch (Exception e) { + throw (AnalysisException) e; + } List> infos = Lists.newArrayList(); FrontendsProcNode.getFrontendsInfo(Env.getCurrentEnv(), showStmt.getDetailType(), infos); diff --git a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/BackendsTableValuedFunction.java b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/BackendsTableValuedFunction.java index 04ea7d01eae3dd..a40ec67c0dc72d 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/BackendsTableValuedFunction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/BackendsTableValuedFunction.java @@ -18,9 +18,15 @@ package org.apache.doris.tablefunction; import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.InfoSchemaDb; import org.apache.doris.catalog.PrimitiveType; import org.apache.doris.catalog.ScalarType; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.datasource.InternalCatalog; +import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.qe.ConnectContext; import org.apache.doris.thrift.TBackendsMetadataParams; import org.apache.doris.thrift.TMetaScanRange; import org.apache.doris.thrift.TMetadataType; @@ -83,6 +89,12 @@ public BackendsTableValuedFunction(Map params) throws AnalysisEx if (params.size() != 0) { throw new AnalysisException("backends table-valued-function does not support any params"); } + if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), + InternalCatalog.INTERNAL_CATALOG_NAME, InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT)) { + String message = ErrorCode.ERR_DB_ACCESS_DENIED_ERROR.formatErrorMsg( + PrivPredicate.SELECT.getPrivs().toString(), InfoSchemaDb.DATABASE_NAME); + throw new AnalysisException(message); + } } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/FrontendsDisksTableValuedFunction.java b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/FrontendsDisksTableValuedFunction.java index cc7ff82b8fb0e1..66ac253e88d00c 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/FrontendsDisksTableValuedFunction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/FrontendsDisksTableValuedFunction.java @@ -18,8 +18,14 @@ package org.apache.doris.tablefunction; import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.InfoSchemaDb; import org.apache.doris.catalog.ScalarType; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.datasource.InternalCatalog; +import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.qe.ConnectContext; import org.apache.doris.thrift.TFrontendsMetadataParams; import org.apache.doris.thrift.TMetaScanRange; import org.apache.doris.thrift.TMetadataType; @@ -67,6 +73,12 @@ public FrontendsDisksTableValuedFunction(Map params) throws Anal if (params.size() != 0) { throw new AnalysisException("frontends_disks table-valued-function does not support any params"); } + if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), + InternalCatalog.INTERNAL_CATALOG_NAME, InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT)) { + String message = ErrorCode.ERR_DB_ACCESS_DENIED_ERROR.formatErrorMsg( + PrivPredicate.SELECT.getPrivs().toString(), InfoSchemaDb.DATABASE_NAME); + throw new AnalysisException(message); + } } @Override diff --git a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/FrontendsTableValuedFunction.java b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/FrontendsTableValuedFunction.java index aded1076a83d03..06d323bc66c802 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/tablefunction/FrontendsTableValuedFunction.java +++ b/fe/fe-core/src/main/java/org/apache/doris/tablefunction/FrontendsTableValuedFunction.java @@ -18,8 +18,14 @@ package org.apache.doris.tablefunction; import org.apache.doris.catalog.Column; +import org.apache.doris.catalog.Env; +import org.apache.doris.catalog.InfoSchemaDb; import org.apache.doris.catalog.ScalarType; +import org.apache.doris.common.ErrorCode; +import org.apache.doris.datasource.InternalCatalog; +import org.apache.doris.mysql.privilege.PrivPredicate; import org.apache.doris.nereids.exceptions.AnalysisException; +import org.apache.doris.qe.ConnectContext; import org.apache.doris.thrift.TFrontendsMetadataParams; import org.apache.doris.thrift.TMetaScanRange; import org.apache.doris.thrift.TMetadataType; @@ -76,6 +82,12 @@ public FrontendsTableValuedFunction(Map params) throws AnalysisE if (params.size() != 0) { throw new AnalysisException("frontends table-valued-function does not support any params"); } + if (!Env.getCurrentEnv().getAccessManager().checkDbPriv(ConnectContext.get(), + InternalCatalog.INTERNAL_CATALOG_NAME, InfoSchemaDb.DATABASE_NAME, PrivPredicate.SELECT)) { + String message = ErrorCode.ERR_DB_ACCESS_DENIED_ERROR.formatErrorMsg( + PrivPredicate.SELECT.getPrivs().toString(), InfoSchemaDb.DATABASE_NAME); + throw new AnalysisException(message); + } } @Override diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowBackendsStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowBackendsStmtTest.java index aeb19ef73ef171..c46c6428b96be3 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowBackendsStmtTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowBackendsStmtTest.java @@ -55,7 +55,7 @@ public void testAnalyze() throws Exception { AtomicBoolean privilege = new AtomicBoolean(false); new MockUp() { @Mock - public boolean checkGlobalPriv(ConnectContext ctx, PrivPredicate wanted) { + public boolean checkDbPriv(UserIdentity currentUser, String ctl, String db, PrivPredicate wanted) { return privilege.get(); } }; diff --git a/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowFrontendsStmtTest.java b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowFrontendsStmtTest.java new file mode 100644 index 00000000000000..ebbe555d8cb1fe --- /dev/null +++ b/fe/fe-core/src/test/java/org/apache/doris/analysis/ShowFrontendsStmtTest.java @@ -0,0 +1,68 @@ +// 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.analysis; + +import org.apache.doris.common.AnalysisException; +import org.apache.doris.mysql.privilege.AccessControllerManager; +import org.apache.doris.mysql.privilege.PrivPredicate; +import org.apache.doris.qe.ConnectContext; +import org.apache.doris.qe.SessionVariable; + +import mockit.Mock; +import mockit.MockUp; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; + +import java.util.concurrent.atomic.AtomicBoolean; + +public class ShowFrontendsStmtTest { + private Analyzer analyzer; + private ConnectContext ctx = new ConnectContext(); + + @Before + public void setUp() { + analyzer = AccessTestUtil.fetchAdminAnalyzer(true); + ctx.setSessionVariable(new SessionVariable()); + ctx.setThreadLocalInfo(); + } + + @After + public void tearDown() { + ConnectContext.remove(); + } + + @Test + public void testAnalyze() throws Exception { + AtomicBoolean privilege = new AtomicBoolean(false); + new MockUp() { + @Mock + public boolean checkDbPriv(UserIdentity currentUser, String ctl, String db, PrivPredicate wanted) { + return privilege.get(); + } + }; + + ShowFrontendsStmt stmt = new ShowFrontendsStmt(); + Assertions.assertThrows(AnalysisException.class, () -> stmt.analyze(analyzer)); + + privilege.set(true); + stmt.analyze(analyzer); + } + +} diff --git a/regression-test/suites/auth_call/test_show_backend_auth.groovy b/regression-test/suites/auth_call/test_show_backend_auth.groovy index 13e1fa4fbe480c..627e28c57d1bd7 100644 --- a/regression-test/suites/auth_call/test_show_backend_auth.groovy +++ b/regression-test/suites/auth_call/test_show_backend_auth.groovy @@ -32,14 +32,26 @@ suite("test_show_backend_auth","p0,auth_call") { try_sql("DROP USER ${user}") sql """CREATE USER '${user}' IDENTIFIED BY '${pwd}'""" sql """grant select_priv on regression_test to ${user}""" + sql """grant select_priv on internal.information_schema.* to ${user}""" + def show_grants_result = sql """show grants for ${user}""" + logger.info("show grants result: " + show_grants_result) + sql """revoke select_priv on internal.information_schema.* from ${user}""" connect(user, "${pwd}", context.config.jdbcUrl) { + + try { + def show_result = sql """SHOW BACKENDS""" + logger.info("show_result: " + show_result) + } catch (Exception e) { + logger.info("show_result: " + e) + e.printStackTrace() + } test { sql """SHOW BACKENDS""" exception "denied" } } - sql """grant node_priv on *.*.* to ${user}""" + sql """grant select_priv on internal.information_schema.* to ${user}""" connect(user, "${pwd}", context.config.jdbcUrl) { def res = sql """SHOW BACKENDS""" assertTrue(res.size() > 0) diff --git a/regression-test/suites/auth_call/test_show_frontend_auth.groovy b/regression-test/suites/auth_call/test_show_frontend_auth.groovy index 87f8e9c7c09a68..120698039705b2 100644 --- a/regression-test/suites/auth_call/test_show_frontend_auth.groovy +++ b/regression-test/suites/auth_call/test_show_frontend_auth.groovy @@ -32,8 +32,19 @@ suite("test_show_frontend_auth","p0,auth_call") { try_sql("DROP USER ${user}") sql """CREATE USER '${user}' IDENTIFIED BY '${pwd}'""" sql """grant select_priv on regression_test to ${user}""" + sql """grant select_priv on internal.information_schema.* to ${user}""" + def show_grants_result = sql """show grants for ${user}""" + logger.info("show grants result: " + show_grants_result) + sql """revoke select_priv on internal.information_schema.* from ${user}""" connect(user, "${pwd}", context.config.jdbcUrl) { + try { + def show_result = sql """SHOW frontends""" + logger.info("show_result: " + show_result) + } catch (Exception e) { + logger.info("show_result: " + e) + e.printStackTrace() + } test { sql """SHOW frontends""" exception "denied" @@ -43,7 +54,7 @@ suite("test_show_frontend_auth","p0,auth_call") { exception "denied" } } - sql """grant node_priv on *.*.* to ${user}""" + sql """grant select_priv on internal.information_schema.* to ${user}""" connect(user, "${pwd}", context.config.jdbcUrl) { def res = sql """SHOW frontends""" assertTrue(res.size() > 0)