This is an automated email from the ASF dual-hosted git repository. kxiao pushed a commit to branch branch-2.0 in repository https://gitbox.apache.org/repos/asf/doris.git
commit ebdd79b8f5bcb85dd5fb77d0d635855c47f6b256 Author: zhangdong <493738...@qq.com> AuthorDate: Sat Oct 14 22:37:51 2023 +0800 [improvement](auth) support show view priv (#25370) Issue Number: close #xxx current ,if user has select_priv or load_priv,he can show create table view_name, but this is not safe,so add show_view_priv for show create table view_name mysql SHOW VIEW description: https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_show-view --- .../Account-Management-Statements/GRANT.md | 7 ++ .../Account-Management-Statements/GRANT.md | 7 ++ .../apache/doris/analysis/ShowCreateTableStmt.java | 15 +++- .../org/apache/doris/catalog/AccessPrivilege.java | 7 +- .../doris/mysql/privilege/PrivPredicate.java | 8 ++ .../apache/doris/mysql/privilege/Privilege.java | 13 +++- .../org/apache/doris/mysql/privilege/AuthTest.java | 85 ++++++++++++++++++++++ .../suites/account_p0/test_auth_show.groovy | 62 ++++++++++++++++ 8 files changed, 197 insertions(+), 7 deletions(-) diff --git a/docs/en/docs/sql-manual/sql-reference/Account-Management-Statements/GRANT.md b/docs/en/docs/sql-manual/sql-reference/Account-Management-Statements/GRANT.md index 4e29c85d2c7..cc85bacd9cb 100644 --- a/docs/en/docs/sql-manual/sql-reference/Account-Management-Statements/GRANT.md +++ b/docs/en/docs/sql-manual/sql-reference/Account-Management-Statements/GRANT.md @@ -62,6 +62,7 @@ privilege_list is a list of privileges to be granted, separated by commas. Curre CREATE_PRIV: Create permission on the specified database or table DROP_PRIV: drop privilege on the specified database or table USAGE_PRIV: access to the specified resource + SHOW_VIEW_PRIV: View permission to `view` creation statements (starting from version 2.0.3, 'SELECT_PRIV' and 'LOAD_PRIV' permissions cannot be 'SHOW CREATE TABLE view_name', has one of `CREATE_PRIV`,`ALTER_PRIV`,`DROP_PRIV`,`SHOW_VIEW_PRIV` can `SHOW CREATE TABLE view_name`) ALL and READ_WRITE in legacy permissions will be converted to: SELECT_PRIV,LOAD_PRIV,ALTER_PRIV,CREATE_PRIV,DROP_PRIV; READ_ONLY is converted to SELECT_PRIV. @@ -164,6 +165,12 @@ role_list is the list of roles to be assigned, separated by commas,the specified GRANT USAGE_PRIV ON WORKLOAD GROUP 'g1' TO ROLE 'my_role'. ```` +11. Allow jack to view the creation statement of view1 under db1 + + ```sql + GRANT SHOW_VIEW_PRIV ON db1.view1 TO 'jack'@'%'; + ```` + ### Keywords GRANT diff --git a/docs/zh-CN/docs/sql-manual/sql-reference/Account-Management-Statements/GRANT.md b/docs/zh-CN/docs/sql-manual/sql-reference/Account-Management-Statements/GRANT.md index 30839d0531d..2e2ea7aa485 100644 --- a/docs/zh-CN/docs/sql-manual/sql-reference/Account-Management-Statements/GRANT.md +++ b/docs/zh-CN/docs/sql-manual/sql-reference/Account-Management-Statements/GRANT.md @@ -62,6 +62,7 @@ privilege_list 是需要赋予的权限列表,以逗号分隔。当前 Doris CREATE_PRIV:对指定的库或表的创建权限 DROP_PRIV:对指定的库或表的删除权限 USAGE_PRIV: 对指定资源的使用权限<version since="dev" type="inline" >和workload group权限</version> + SHOW_VIEW_PRIV: 查看`view`创建语句的权限(从2.0.3版本开始,`SELECT_PRIV`和`LOAD_PRIV`权限不能`SHOW CREATE TABLE view_name`,拥有`CREATE_PRIV`,`ALTER_PRIV`,`DROP_PRIV`,`SHOW_VIEW_PRIV`权限项中的任何一个,有权`SHOW CREATE TABLE view_name`) 旧版权限中的 ALL 和 READ_WRITE 会被转换成:SELECT_PRIV,LOAD_PRIV,ALTER_PRIV,CREATE_PRIV,DROP_PRIV; READ_ONLY 会被转换为 SELECT_PRIV。 @@ -164,6 +165,12 @@ role_list 是需要赋予的角色列表,以逗号分隔,指定的角色必 GRANT USAGE_PRIV ON WORKLOAD GROUP 'g1' TO ROLE 'my_role'; ```` +11. 允许jack查看db1下view1的创建语句 + + ```sql + GRANT SHOW_VIEW_PRIV ON db1.view1 TO 'jack'@'%'; + ```` + ### Keywords ``` diff --git a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateTableStmt.java b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateTableStmt.java index 9527a061d31..ea3b442fbc7 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateTableStmt.java +++ b/fe/fe-core/src/main/java/org/apache/doris/analysis/ShowCreateTableStmt.java @@ -20,6 +20,8 @@ package org.apache.doris.analysis; import org.apache.doris.catalog.Column; import org.apache.doris.catalog.Env; import org.apache.doris.catalog.ScalarType; +import org.apache.doris.catalog.TableIf; +import org.apache.doris.catalog.View; import org.apache.doris.common.AnalysisException; import org.apache.doris.common.ErrorCode; import org.apache.doris.common.ErrorReport; @@ -103,8 +105,19 @@ public class ShowCreateTableStmt extends ShowStmt { } tbl.analyze(analyzer); + TableIf tableIf = Env.getCurrentEnv().getCatalogMgr() + .getCatalogOrAnalysisException(tbl.getCtl()) + .getDbOrAnalysisException(tbl.getDb()).getTableOrAnalysisException(tbl.getTbl()); + + PrivPredicate wanted; + if (tableIf instanceof View) { + wanted = PrivPredicate.SHOW_VIEW; + } else { + wanted = PrivPredicate.SHOW; + } + if (!Env.getCurrentEnv().getAccessManager().checkTblPriv(ConnectContext.get(), tbl.getCtl(), tbl.getDb(), - tbl.getTbl(), PrivPredicate.SHOW)) { + tbl.getTbl(), wanted)) { ErrorReport.reportAnalysisException(ErrorCode.ERR_TABLEACCESS_DENIED_ERROR, "SHOW CREATE TABLE", ConnectContext.get().getQualifiedUser(), ConnectContext.get().getRemoteIP(), diff --git a/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilege.java b/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilege.java index 88c6c9649ba..becbdfd7fe1 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilege.java +++ b/fe/fe-core/src/main/java/org/apache/doris/catalog/AccessPrivilege.java @@ -38,7 +38,8 @@ public enum AccessPrivilege { CREATE_PRIV(9, "Privilege for creating database or table"), DROP_PRIV(10, "Privilege for dropping database or table"), ADMIN_PRIV(11, "All privileges except NODE_PRIV"), - USAGE_PRIV(12, "Privilege for use resource"); + USAGE_PRIV(12, "Privilege for use resource"), + SHOW_VIEW_PRIV(13, "Privilege for show view"); private int flag; private String desc; @@ -49,7 +50,7 @@ public enum AccessPrivilege { } public List<Privilege> toDorisPrivilege() { - Preconditions.checkState(flag > 0 && flag < 13); + Preconditions.checkState(flag > 0 && flag < 14); switch (flag) { case 1: case 6: @@ -75,6 +76,8 @@ public enum AccessPrivilege { return Lists.newArrayList(Privilege.ADMIN_PRIV); case 12: return Lists.newArrayList(Privilege.USAGE_PRIV); + case 13: + return Lists.newArrayList(Privilege.SHOW_VIEW_PRIV); default: return null; } diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java index 61f820f6745..6aae0e1c9c9 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/PrivPredicate.java @@ -29,8 +29,16 @@ public class PrivPredicate { Privilege.LOAD_PRIV, Privilege.ALTER_PRIV, Privilege.CREATE_PRIV, + Privilege.SHOW_VIEW_PRIV, Privilege.DROP_PRIV), Operator.OR); + // show create table 'view' + public static final PrivPredicate SHOW_VIEW = PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, + Privilege.CREATE_PRIV, + Privilege.ALTER_PRIV, + Privilege.DROP_PRIV, + Privilege.SHOW_VIEW_PRIV), + Operator.OR); // show resources public static final PrivPredicate SHOW_RESOURCES = PrivPredicate.of(PrivBitSet.of(Privilege.ADMIN_PRIV, Privilege.USAGE_PRIV), diff --git a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Privilege.java b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Privilege.java index 680bc1dcaef..54130a5d450 100644 --- a/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Privilege.java +++ b/fe/fe-core/src/main/java/org/apache/doris/mysql/privilege/Privilege.java @@ -30,7 +30,8 @@ public enum Privilege { ALTER_PRIV("Alter_priv", 5, "Privilege for alter database or table"), CREATE_PRIV("Create_priv", 6, "Privilege for creating database or table"), DROP_PRIV("Drop_priv", 7, "Privilege for dropping database or table"), - USAGE_PRIV("Usage_priv", 8, "Privilege for using resource or workloadGroup"); + USAGE_PRIV("Usage_priv", 8, "Privilege for using resource or workloadGroup"), + SHOW_VIEW_PRIV("Show_view_priv", 9, "Privilege for show create view"); public static Privilege[] privileges = { NODE_PRIV, @@ -41,7 +42,8 @@ public enum Privilege { ALTER_PRIV, CREATE_PRIV, DROP_PRIV, - USAGE_PRIV + USAGE_PRIV, + SHOW_VIEW_PRIV }; // only GRANT_PRIV and USAGE_PRIV can grant on resource @@ -52,7 +54,8 @@ public enum Privilege { LOAD_PRIV, ALTER_PRIV, CREATE_PRIV, - DROP_PRIV + DROP_PRIV, + SHOW_VIEW_PRIV }; // only GRANT_PRIV and USAGE_PRIV can grant on workloadGroup @@ -63,7 +66,8 @@ public enum Privilege { LOAD_PRIV, ALTER_PRIV, CREATE_PRIV, - DROP_PRIV + DROP_PRIV, + SHOW_VIEW_PRIV }; public static Map<Privilege, String> privInDorisToMysql = @@ -74,6 +78,7 @@ public enum Privilege { .put(CREATE_PRIV, "CREATE") .put(DROP_PRIV, "DROP") .put(USAGE_PRIV, "USAGE") + .put(SHOW_VIEW_PRIV, "SHOW VIEW") .build(); private String name; diff --git a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java index b0afc8055d3..5ebfb97e0b1 100644 --- a/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java +++ b/fe/fe-core/src/test/java/org/apache/doris/mysql/privilege/AuthTest.java @@ -2218,4 +2218,89 @@ public class AuthTest { ExceptionChecker.expectThrowsWithMsg(AnalysisException.class, "Can not grant/revoke USAGE_PRIV to/from database or table", () -> grantStmt3.analyze(analyzer)); } + + private void createUser(UserIdentity userIdentity) throws UserException { + UserDesc userDesc = new UserDesc(userIdentity, "12345", true); + CreateUserStmt createUserStmt = new CreateUserStmt(false, userDesc, null); + createUserStmt.analyze(analyzer); + auth.createUser(createUserStmt); + } + + private void grant(GrantStmt grantStmt) throws UserException { + grantStmt.analyze(analyzer); + auth.grant(grantStmt); + } + + private void revoke(RevokeStmt revokeStmt) throws UserException { + revokeStmt.analyze(analyzer); + auth.revoke(revokeStmt); + } + + @Test + public void testShowViewPriv() throws UserException { + UserIdentity userIdentity = new UserIdentity("viewUser", "%"); + createUser(userIdentity); + // `load_priv` and `select_priv` can not `show create view` + GrantStmt grantStmt = new GrantStmt(userIdentity, null, new TablePattern("viewdb", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.SELECT_PRIV), + new AccessPrivilegeWithCols(AccessPrivilege.LOAD_PRIV))); + grant(grantStmt); + Assert.assertFalse(accessManager + .checkDbPriv(userIdentity, SystemInfoService.DEFAULT_CLUSTER + ":viewdb", PrivPredicate.SHOW_VIEW)); + + // `SHOW_VIEW_PRIV` can `show create view` + grantStmt = new GrantStmt(userIdentity, null, new TablePattern("viewdb", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.SHOW_VIEW_PRIV))); + grant(grantStmt); + Assert.assertTrue(accessManager + .checkDbPriv(userIdentity, SystemInfoService.DEFAULT_CLUSTER + ":viewdb", PrivPredicate.SHOW_VIEW)); + + RevokeStmt revokeStmt = new RevokeStmt(userIdentity, null, new TablePattern("viewdb", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.SHOW_VIEW_PRIV))); + revoke(revokeStmt); + + // 'admin_priv' can `show create view` + grantStmt = new GrantStmt(userIdentity, null, new TablePattern("*", "*", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.ADMIN_PRIV))); + grant(grantStmt); + Assert.assertTrue(accessManager + .checkDbPriv(userIdentity, SystemInfoService.DEFAULT_CLUSTER + ":viewdb", PrivPredicate.SHOW_VIEW)); + + revokeStmt = new RevokeStmt(userIdentity, null, new TablePattern("*", "*", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.ADMIN_PRIV))); + revoke(revokeStmt); + + // 'create_priv' can `show create view` + grantStmt = new GrantStmt(userIdentity, null, new TablePattern("viewdb", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.CREATE_PRIV))); + grant(grantStmt); + Assert.assertTrue(accessManager + .checkDbPriv(userIdentity, SystemInfoService.DEFAULT_CLUSTER + ":viewdb", PrivPredicate.SHOW_VIEW)); + + revokeStmt = new RevokeStmt(userIdentity, null, new TablePattern("viewdb", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.CREATE_PRIV))); + revoke(revokeStmt); + + // 'alter_priv' can `show create view` + grantStmt = new GrantStmt(userIdentity, null, new TablePattern("viewdb", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.ALTER_PRIV))); + grant(grantStmt); + Assert.assertTrue(accessManager + .checkDbPriv(userIdentity, SystemInfoService.DEFAULT_CLUSTER + ":viewdb", PrivPredicate.SHOW_VIEW)); + + revokeStmt = new RevokeStmt(userIdentity, null, new TablePattern("viewdb", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.ALTER_PRIV))); + revoke(revokeStmt); + + // 'drop_priv' can `show create view` + grantStmt = new GrantStmt(userIdentity, null, new TablePattern("viewdb", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV))); + grant(grantStmt); + Assert.assertTrue(accessManager + .checkDbPriv(userIdentity, SystemInfoService.DEFAULT_CLUSTER + ":viewdb", PrivPredicate.SHOW_VIEW)); + + revokeStmt = new RevokeStmt(userIdentity, null, new TablePattern("viewdb", "*"), + Lists.newArrayList(new AccessPrivilegeWithCols(AccessPrivilege.DROP_PRIV))); + revoke(revokeStmt); + } } diff --git a/regression-test/suites/account_p0/test_auth_show.groovy b/regression-test/suites/account_p0/test_auth_show.groovy new file mode 100644 index 00000000000..c847fd1715a --- /dev/null +++ b/regression-test/suites/account_p0/test_auth_show.groovy @@ -0,0 +1,62 @@ +// 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_auth_show", "account") { + + def create_table = { tableName -> + sql "DROP TABLE IF EXISTS ${tableName}" + sql """ + CREATE TABLE ${tableName} ( + `key` INT, + value INT + ) DUPLICATE KEY (`key`) DISTRIBUTED BY HASH (`key`) BUCKETS 1 + PROPERTIES ('replication_num' = '1') + """ + } + + def user = 'acount_auth_show_user' + def pwd = 'C123_567p' + def dbName = 'account_auth_show_db' + def tableName = 'account_auth_show_table' + + try_sql("DROP USER ${user}") + sql """DROP DATABASE IF EXISTS ${dbName}""" + sql """CREATE DATABASE ${dbName}""" + sql """USE ${dbName}""" + create_table.call(tableName); + sql """CREATE USER '${user}' IDENTIFIED BY '${pwd}'""" + + def tokens = context.config.jdbcUrl.split('/') + def url=tokens[0] + "//" + tokens[2] + "/" + dbName + "?" + + // With select priv for table, should be able to see db + sql """GRANT SELECT_PRIV ON ${dbName}.${tableName} TO ${user}""" + def result1 = connect(user=user, password="${pwd}", url=url) { + sql """show databases like '${dbName}'""" + } + assertEquals(result1.size(), 1) + sql """REVOKE SELECT_PRIV ON ${dbName}.${tableName} FROM ${user}""" + + // With show_view priv for table, should be able to see db + sql """GRANT SHOW_VIEW_PRIV ON ${dbName}.${tableName} TO ${user}""" + def result2 = connect(user=user, password="${pwd}", url=url) { + sql """show databases like '${dbName}'""" + } + assertEquals(result2.size(), 1) + sql """REVOKE SHOW_VIEW_PRIV ON ${dbName}.${tableName} FROM ${user}""" +} + --------------------------------------------------------------------- To unsubscribe, e-mail: commits-unsubscr...@doris.apache.org For additional commands, e-mail: commits-h...@doris.apache.org