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 93addd749a..da18856d26 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 107773826b..414cf4cfef 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: 对指定资源的使用权限和workload group权限
+ 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 9527a061d3..ea3b442fbc 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 88c6c9649b..becbdfd7fe 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 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 61f820f674..6aae0e1c9c 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 680bc1dcae..54130a5d45 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 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 b0afc8055d..5ebfb97e0b 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 0000000000..c847fd1715
--- /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}"""
+}
+