Skip to content

Commit

Permalink
[improvement](auth) support show view priv (apache#25370)
Browse files Browse the repository at this point in the history
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
  • Loading branch information
zddr authored Oct 14, 2023
1 parent ed3e8f9 commit 471cf2c
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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。
Expand Down Expand Up @@ -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
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
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;
Expand Down Expand Up @@ -103,8 +105,19 @@ public void analyze(Analyzer analyzer) throws AnalysisException {
}
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(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -49,7 +50,7 @@ private AccessPrivilege(int flag, String desc) {
}

public List<Privilege> toDorisPrivilege() {
Preconditions.checkState(flag > 0 && flag < 13);
Preconditions.checkState(flag > 0 && flag < 14);
switch (flag) {
case 1:
case 6:
Expand All @@ -75,6 +76,8 @@ public List<Privilege> toDorisPrivilege() {
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;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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 =
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2218,4 +2218,89 @@ public void testWorkloadGroupPriv() {
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);
}
}
62 changes: 62 additions & 0 deletions regression-test/suites/account_p0/test_auth_show.groovy
Original file line number Diff line number Diff line change
@@ -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}"""
}

0 comments on commit 471cf2c

Please sign in to comment.