From 9ae328acec6fe6e931b692cd6fd7828771d7b73e Mon Sep 17 00:00:00 2001 From: TCeason Date: Wed, 22 May 2024 15:04:53 +0800 Subject: [PATCH] add test --- src/query/ast/src/ast/statements/statement.rs | 2 + src/query/ast/src/ast/statements/user.rs | 109 +++++++++----- src/query/ast/src/ast/visitors/visitor.rs | 2 + src/query/ast/src/ast/visitors/visitor_mut.rs | 2 + src/query/ast/src/ast/visitors/walk.rs | 1 + src/query/ast/src/ast/visitors/walk_mut.rs | 1 + src/query/ast/src/parser/statement.rs | 47 +++++- src/query/ast/tests/it/parser.rs | 2 + .../ast/tests/it/testdata/statement-error.txt | 10 ++ src/query/ast/tests/it/testdata/statement.txt | 136 ++++++++++-------- .../show_grants/show_grants_table.rs | 38 +---- src/query/sql/src/planner/binder/binder.rs | 1 + .../sql/src/planner/binder/ddl/account.rs | 49 ++++++- .../base/05_ddl/05_0017_ddl_grant_role.test | 68 +++++++++ .../18_rbac/18_0007_privilege_access.result | 6 + .../18_rbac/18_0007_privilege_access.sh | 19 +++ 16 files changed, 359 insertions(+), 134 deletions(-) diff --git a/src/query/ast/src/ast/statements/statement.rs b/src/query/ast/src/ast/statements/statement.rs index 8aaab944d5fb3..bdc79789ddad4 100644 --- a/src/query/ast/src/ast/statements/statement.rs +++ b/src/query/ast/src/ast/statements/statement.rs @@ -204,6 +204,7 @@ pub enum Statement { principal: Option, show_options: Option, }, + ShowObjectPrivileges(ShowObjectPrivilegesStmt), Revoke(RevokeStmt), // UDF @@ -619,6 +620,7 @@ impl Display for Statement { write!(f, " {show_options}")?; } } + Statement::ShowObjectPrivileges(stmt) => write!(f, "{stmt}")?, Statement::Revoke(stmt) => write!(f, "{stmt}")?, Statement::CreateUDF(stmt) => write!(f, "{stmt}")?, Statement::DropUDF { diff --git a/src/query/ast/src/ast/statements/user.rs b/src/query/ast/src/ast/statements/user.rs index 6e844eaa4d46f..a6ab5d4d4e95f 100644 --- a/src/query/ast/src/ast/statements/user.rs +++ b/src/query/ast/src/ast/statements/user.rs @@ -22,6 +22,7 @@ use crate::ast::write_comma_separated_list; use crate::ast::AuthType; use crate::ast::CreateOption; use crate::ast::PrincipalIdentity; +use crate::ast::ShowOptions; use crate::ast::UserIdentity; use crate::ast::UserPrivilegeType; @@ -135,6 +136,50 @@ impl Display for RevokeStmt { } } +#[derive(Debug, Clone, PartialEq, Drive, DriveMut)] +pub struct ShowObjectPrivilegesStmt { + pub object: GrantObjectName, + pub show_option: Option, +} + +#[derive(Debug, Clone, PartialEq, Drive, DriveMut)] +pub enum GrantObjectName { + Database(#[drive(skip)] String), + Table(#[drive(skip)] Option, #[drive(skip)] String), + UDF(#[drive(skip)] String), + Stage(#[drive(skip)] String), +} + +impl Display for GrantObjectName { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + match self { + GrantObjectName::Database(database_name) => { + write!(f, "DATABASE {database_name}") + } + GrantObjectName::Table(database_name, table_name) => { + if let Some(database_name) = database_name { + write!(f, "TABLE {database_name}.{table_name}") + } else { + write!(f, "TABLE {table_name}") + } + } + GrantObjectName::UDF(udf) => write!(f, " UDF {udf}"), + GrantObjectName::Stage(stage) => write!(f, " STAGE {stage}"), + } + } +} + +impl Display for ShowObjectPrivilegesStmt { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + write!(f, "SHOW GRANTS ON {}", self.object)?; + + if let Some(show_option) = &self.show_option { + write!(f, " {show_option}")?; + } + Ok(()) + } +} + #[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)] pub enum AccountMgrSource { Role { @@ -158,48 +203,12 @@ impl Display for AccountMgrSource { write!(f, " ")?; write_comma_separated_list(f, privileges.iter().map(|p| p.to_string()))?; write!(f, " ON")?; - match level { - AccountMgrLevel::Global => write!(f, " *.*")?, - AccountMgrLevel::Database(database_name) => { - if let Some(database_name) = database_name { - write!(f, " {database_name}.*")?; - } else { - write!(f, " *")?; - } - } - AccountMgrLevel::Table(database_name, table_name) => { - if let Some(database_name) = database_name { - write!(f, " {database_name}.{table_name}")?; - } else { - write!(f, " {table_name}")?; - } - } - AccountMgrLevel::UDF(udf) => write!(f, " UDF {udf}")?, - AccountMgrLevel::Stage(stage) => write!(f, " STAGE {stage}")?, - } + write!(f, " {}", level)?; } AccountMgrSource::ALL { level, .. } => { write!(f, " ALL PRIVILEGES")?; write!(f, " ON")?; - match level { - AccountMgrLevel::Global => write!(f, " *.*")?, - AccountMgrLevel::Database(database_name) => { - if let Some(database_name) = database_name { - write!(f, " {database_name}.*")?; - } else { - write!(f, " *")?; - } - } - AccountMgrLevel::Table(database_name, table_name) => { - if let Some(database_name) = database_name { - write!(f, " {database_name}.{table_name}")?; - } else { - write!(f, " {table_name}")?; - } - } - AccountMgrLevel::UDF(udf) => write!(f, " UDF {udf}")?, - AccountMgrLevel::Stage(stage) => write!(f, " STAGE {stage}")?, - } + write!(f, " {}", level)?; } } Ok(()) @@ -215,6 +224,30 @@ pub enum AccountMgrLevel { Stage(#[drive(skip)] String), } +impl Display for AccountMgrLevel { + fn fmt(&self, f: &mut Formatter) -> std::fmt::Result { + match self { + AccountMgrLevel::Global => write!(f, " *.*"), + AccountMgrLevel::Database(database_name) => { + if let Some(database_name) = database_name { + write!(f, " {database_name}.*") + } else { + write!(f, " *") + } + } + AccountMgrLevel::Table(database_name, table_name) => { + if let Some(database_name) = database_name { + write!(f, " {database_name}.{table_name}") + } else { + write!(f, " {table_name}") + } + } + AccountMgrLevel::UDF(udf) => write!(f, " UDF {udf}"), + AccountMgrLevel::Stage(stage) => write!(f, " STAGE {stage}"), + } + } +} + #[derive(Debug, Clone, PartialEq, Eq, Drive, DriveMut)] pub enum SecondaryRolesOption { None, diff --git a/src/query/ast/src/ast/visitors/visitor.rs b/src/query/ast/src/ast/visitors/visitor.rs index 996438751fa78..0040276f1b7f9 100644 --- a/src/query/ast/src/ast/visitors/visitor.rs +++ b/src/query/ast/src/ast/visitors/visitor.rs @@ -619,6 +619,8 @@ pub trait Visitor<'ast>: Sized { ) { } + fn visit_show_object_priv(&mut self, _show: &'ast ShowObjectPrivilegesStmt) {} + fn visit_revoke(&mut self, _revoke: &'ast RevokeStmt) {} fn visit_create_udf(&mut self, _stmt: &'ast CreateUDFStmt) {} diff --git a/src/query/ast/src/ast/visitors/visitor_mut.rs b/src/query/ast/src/ast/visitors/visitor_mut.rs index 9713fe590d262..3952845ce4589 100644 --- a/src/query/ast/src/ast/visitors/visitor_mut.rs +++ b/src/query/ast/src/ast/visitors/visitor_mut.rs @@ -631,6 +631,8 @@ pub trait VisitorMut: Sized { ) { } + fn visit_show_object_priv(&mut self, _show: &mut ShowObjectPrivilegesStmt) {} + fn visit_revoke(&mut self, _revoke: &mut RevokeStmt) {} fn visit_create_udf(&mut self, _stmt: &mut CreateUDFStmt) {} diff --git a/src/query/ast/src/ast/visitors/walk.rs b/src/query/ast/src/ast/visitors/walk.rs index 4e222ef573e20..663cbeebc91de 100644 --- a/src/query/ast/src/ast/visitors/walk.rs +++ b/src/query/ast/src/ast/visitors/walk.rs @@ -507,6 +507,7 @@ pub fn walk_statement<'a, V: Visitor<'a>>(visitor: &mut V, statement: &'a Statem principal, show_options, } => visitor.visit_show_grant(principal, show_options), + Statement::ShowObjectPrivileges(stmt) => visitor.visit_show_object_priv(stmt), Statement::Revoke(stmt) => visitor.visit_revoke(stmt), Statement::CreateUDF(stmt) => visitor.visit_create_udf(stmt), Statement::DropUDF { diff --git a/src/query/ast/src/ast/visitors/walk_mut.rs b/src/query/ast/src/ast/visitors/walk_mut.rs index ed9a75b4ca7fb..baae6a066906f 100644 --- a/src/query/ast/src/ast/visitors/walk_mut.rs +++ b/src/query/ast/src/ast/visitors/walk_mut.rs @@ -502,6 +502,7 @@ pub fn walk_statement_mut(visitor: &mut V, statement: &mut Statem principal, show_options, } => visitor.visit_show_grant(principal, show_options), + Statement::ShowObjectPrivileges(stmt) => visitor.visit_show_object_priv(stmt), Statement::Revoke(stmt) => visitor.visit_revoke(stmt), Statement::CreateUDF(stmt) => visitor.visit_create_udf(stmt), Statement::DropUDF { diff --git a/src/query/ast/src/parser/statement.rs b/src/query/ast/src/parser/statement.rs index 92e9e6a829efd..7e09b1d061c6f 100644 --- a/src/query/ast/src/parser/statement.rs +++ b/src/query/ast/src/parser/statement.rs @@ -43,6 +43,7 @@ use crate::rule; pub enum ShowGrantOption { PrincipalIdentity(PrincipalIdentity), + GrantObjectName(GrantObjectName), ShareGrantObjectName(ShareGrantObjectName), ShareName(String), } @@ -1295,6 +1296,12 @@ pub fn statement_body(i: Input) -> IResult { principal: None, show_options: opt_limit, }, + Some(ShowGrantOption::GrantObjectName(object)) => { + Statement::ShowObjectPrivileges(ShowObjectPrivilegesStmt { + object, + show_option: opt_limit, + }) + } }, ); let revoke = map( @@ -2909,6 +2916,40 @@ pub fn grant_share_object_name(i: Input) -> IResult { )(i) } +pub fn on_object_name(i: Input) -> IResult { + let database = map( + rule! { + DATABASE ~ #ident + }, + |(_, database)| GrantObjectName::Database(database.to_string()), + ); + + // `db01`.'tb1' or `db01`.`tb1` or `db01`.tb1 + let table = map( + rule! { + TABLE ~ #dot_separated_idents_1_to_2 + }, + |(_, (database, table))| { + GrantObjectName::Table(database.map(|db| db.to_string()), table.to_string()) + }, + ); + + let stage = map(rule! { STAGE ~ #ident}, |(_, stage_name)| { + GrantObjectName::Stage(stage_name.to_string()) + }); + + let udf = map(rule! { UDF ~ #ident}, |(_, udf_name)| { + GrantObjectName::UDF(udf_name.to_string()) + }); + + rule!( + #database : "DATABASE " + | #table : "TABLE ." + | #stage : "STAGE " + | #udf : "UDF " + )(i) +} + pub fn grant_level(i: Input) -> IResult { // *.* let global = map(rule! { "*" ~ "." ~ "*" }, |_| AccountMgrLevel::Global); @@ -3027,9 +3068,9 @@ pub fn show_grant_option(i: Input) -> IResult { let share_object_name = map( rule! { - ON ~ #grant_share_object_name + ON ~ #on_object_name }, - |(_, object_name)| ShowGrantOption::ShareGrantObjectName(object_name), + |(_, object_name)| ShowGrantOption::GrantObjectName(object_name), ); let share_name = map( @@ -3041,7 +3082,7 @@ pub fn show_grant_option(i: Input) -> IResult { rule!( #grant_role: "FOR { ROLE | [USER] }" - | #share_object_name: "ON {DATABASE | TABLE .}" + | #share_object_name: "ON {DATABASE | TABLE . | UDF | STAGE }" | #share_name: "OF SHARE " )(i) } diff --git a/src/query/ast/tests/it/parser.rs b/src/query/ast/tests/it/parser.rs index aadde37734a4d..e81aee4177f5c 100644 --- a/src/query/ast/tests/it/parser.rs +++ b/src/query/ast/tests/it/parser.rs @@ -310,6 +310,7 @@ fn test_statement() { r#"SHOW GRANTS FOR USER 'test-grant';"#, r#"SHOW GRANTS FOR ROLE role1;"#, r#"SHOW GRANTS FOR ROLE 'role1';"#, + r#"SHOW GRANTS ON TABLE t;"#, r#"REVOKE SELECT, CREATE ON * FROM 'test-grant';"#, r#"REVOKE SELECT ON tb1 FROM ROLE role1;"#, r#"REVOKE SELECT ON tb1 FROM ROLE 'role1';"#, @@ -911,6 +912,7 @@ fn test_statement_error() { r#"drop table :a"#, r#"drop table IDENTIFIER(a)"#, r#"drop table IDENTIFIER(:a)"#, + r#"SHOW GRANTS ON task t1;"#, ]; for case in cases { diff --git a/src/query/ast/tests/it/testdata/statement-error.txt b/src/query/ast/tests/it/testdata/statement-error.txt index 7f4946109ebbf..3844dd90deca2 100644 --- a/src/query/ast/tests/it/testdata/statement-error.txt +++ b/src/query/ast/tests/it/testdata/statement-error.txt @@ -916,3 +916,13 @@ error: | while parsing `DROP TABLE [IF EXISTS] [.]
` +---------- Input ---------- +SHOW GRANTS ON task t1; +---------- Output --------- +error: + --> SQL:1:16 + | +1 | SHOW GRANTS ON task t1; + | ^^^^ unexpected `task`, expecting `STAGE`, `TABLE`, `DATABASE`, or `UDF` + + diff --git a/src/query/ast/tests/it/testdata/statement.txt b/src/query/ast/tests/it/testdata/statement.txt index 40f59971954b4..c0b665eb9004b 100644 --- a/src/query/ast/tests/it/testdata/statement.txt +++ b/src/query/ast/tests/it/testdata/statement.txt @@ -11681,7 +11681,7 @@ CreateTable( ---------- Input ---------- GRANT CREATE, CREATE USER ON * TO 'test-grant'; ---------- Output --------- -GRANT CREATE, CREATE USER ON * TO USER 'test-grant'@'%' +GRANT CREATE, CREATE USER ON * TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -11707,7 +11707,7 @@ Grant( ---------- Input ---------- GRANT SELECT, CREATE ON * TO 'test-grant'; ---------- Output --------- -GRANT SELECT, CREATE ON * TO USER 'test-grant'@'%' +GRANT SELECT, CREATE ON * TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -11733,7 +11733,7 @@ Grant( ---------- Input ---------- GRANT SELECT, CREATE ON *.* TO 'test-grant'; ---------- Output --------- -GRANT SELECT, CREATE ON *.* TO USER 'test-grant'@'%' +GRANT SELECT, CREATE ON *.* TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -11757,7 +11757,7 @@ Grant( ---------- Input ---------- GRANT SELECT, CREATE ON * TO USER 'test-grant'; ---------- Output --------- -GRANT SELECT, CREATE ON * TO USER 'test-grant'@'%' +GRANT SELECT, CREATE ON * TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -11783,7 +11783,7 @@ Grant( ---------- Input ---------- GRANT SELECT, CREATE ON * TO ROLE role1; ---------- Output --------- -GRANT SELECT, CREATE ON * TO ROLE 'role1' +GRANT SELECT, CREATE ON * TO ROLE 'role1' ---------- AST ------------ Grant( GrantStmt { @@ -11806,7 +11806,7 @@ Grant( ---------- Input ---------- GRANT ALL ON *.* TO 'test-grant'; ---------- Output --------- -GRANT ALL PRIVILEGES ON *.* TO USER 'test-grant'@'%' +GRANT ALL PRIVILEGES ON *.* TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -11826,7 +11826,7 @@ Grant( ---------- Input ---------- GRANT ALL ON *.* TO ROLE role2; ---------- Output --------- -GRANT ALL PRIVILEGES ON *.* TO ROLE 'role2' +GRANT ALL PRIVILEGES ON *.* TO ROLE 'role2' ---------- AST ------------ Grant( GrantStmt { @@ -11843,7 +11843,7 @@ Grant( ---------- Input ---------- GRANT ALL PRIVILEGES ON * TO 'test-grant'; ---------- Output --------- -GRANT ALL PRIVILEGES ON * TO USER 'test-grant'@'%' +GRANT ALL PRIVILEGES ON * TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -11865,7 +11865,7 @@ Grant( ---------- Input ---------- GRANT ALL PRIVILEGES ON * TO ROLE role3; ---------- Output --------- -GRANT ALL PRIVILEGES ON * TO ROLE 'role3' +GRANT ALL PRIVILEGES ON * TO ROLE 'role3' ---------- AST ------------ Grant( GrantStmt { @@ -11941,7 +11941,7 @@ Grant( ---------- Input ---------- GRANT SELECT ON db01.* TO 'test-grant'; ---------- Output --------- -GRANT SELECT ON db01.* TO USER 'test-grant'@'%' +GRANT SELECT ON db01.* TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -11968,7 +11968,7 @@ Grant( ---------- Input ---------- GRANT SELECT ON db01.* TO USER 'test-grant'; ---------- Output --------- -GRANT SELECT ON db01.* TO USER 'test-grant'@'%' +GRANT SELECT ON db01.* TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -11995,7 +11995,7 @@ Grant( ---------- Input ---------- GRANT SELECT ON db01.* TO ROLE role1 ---------- Output --------- -GRANT SELECT ON db01.* TO ROLE 'role1' +GRANT SELECT ON db01.* TO ROLE 'role1' ---------- AST ------------ Grant( GrantStmt { @@ -12019,7 +12019,7 @@ Grant( ---------- Input ---------- GRANT SELECT ON db01.tb1 TO 'test-grant'; ---------- Output --------- -GRANT SELECT ON db01.tb1 TO USER 'test-grant'@'%' +GRANT SELECT ON db01.tb1 TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -12047,7 +12047,7 @@ Grant( ---------- Input ---------- GRANT SELECT ON db01.tb1 TO USER 'test-grant'; ---------- Output --------- -GRANT SELECT ON db01.tb1 TO USER 'test-grant'@'%' +GRANT SELECT ON db01.tb1 TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -12075,7 +12075,7 @@ Grant( ---------- Input ---------- GRANT SELECT ON db01.tb1 TO ROLE role1; ---------- Output --------- -GRANT SELECT ON db01.tb1 TO ROLE 'role1' +GRANT SELECT ON db01.tb1 TO ROLE 'role1' ---------- AST ------------ Grant( GrantStmt { @@ -12100,7 +12100,7 @@ Grant( ---------- Input ---------- GRANT SELECT ON tb1 TO ROLE role1; ---------- Output --------- -GRANT SELECT ON tb1 TO ROLE 'role1' +GRANT SELECT ON tb1 TO ROLE 'role1' ---------- AST ------------ Grant( GrantStmt { @@ -12123,7 +12123,7 @@ Grant( ---------- Input ---------- GRANT ALL ON tb1 TO 'u1'; ---------- Output --------- -GRANT ALL PRIVILEGES ON tb1 TO USER 'u1'@'%' +GRANT ALL PRIVILEGES ON tb1 TO USER 'u1'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -12245,10 +12245,31 @@ ShowGrants { } +---------- Input ---------- +SHOW GRANTS ON TABLE t; +---------- Output --------- +SHOW GRANTS ON TABLE t +---------- AST ------------ +ShowObjectPrivileges( + ShowObjectPrivilegesStmt { + object: Table( + None, + "t", + ), + show_option: Some( + ShowOptions { + show_limit: None, + limit: None, + }, + ), + }, +) + + ---------- Input ---------- REVOKE SELECT, CREATE ON * FROM 'test-grant'; ---------- Output --------- -REVOKE SELECT, CREATE ON * FROM USER 'test-grant'@'%' +REVOKE SELECT, CREATE ON * FROM USER 'test-grant'@'%' ---------- AST ------------ Revoke( RevokeStmt { @@ -12274,7 +12295,7 @@ Revoke( ---------- Input ---------- REVOKE SELECT ON tb1 FROM ROLE role1; ---------- Output --------- -REVOKE SELECT ON tb1 FROM ROLE 'role1' +REVOKE SELECT ON tb1 FROM ROLE 'role1' ---------- AST ------------ Revoke( RevokeStmt { @@ -12297,7 +12318,7 @@ Revoke( ---------- Input ---------- REVOKE SELECT ON tb1 FROM ROLE 'role1'; ---------- Output --------- -REVOKE SELECT ON tb1 FROM ROLE 'role1' +REVOKE SELECT ON tb1 FROM ROLE 'role1' ---------- AST ------------ Revoke( RevokeStmt { @@ -12398,7 +12419,7 @@ SetRole { ---------- Input ---------- REVOKE ALL ON tb1 FROM 'u1'; ---------- Output --------- -REVOKE ALL PRIVILEGES ON tb1 FROM USER 'u1'@'%' +REVOKE ALL PRIVILEGES ON tb1 FROM USER 'u1'@'%' ---------- AST ------------ Revoke( RevokeStmt { @@ -14517,7 +14538,7 @@ GrantShareObject( ---------- Input ---------- GRANT all ON stage s1 TO a; ---------- Output --------- -GRANT ALL PRIVILEGES ON STAGE s1 TO USER 'a'@'%' +GRANT ALL PRIVILEGES ON STAGE s1 TO USER 'a'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -14539,7 +14560,7 @@ Grant( ---------- Input ---------- GRANT read ON stage s1 TO a; ---------- Output --------- -GRANT Read ON STAGE s1 TO USER 'a'@'%' +GRANT Read ON STAGE s1 TO USER 'a'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -14564,7 +14585,7 @@ Grant( ---------- Input ---------- GRANT write ON stage s1 TO a; ---------- Output --------- -GRANT Write ON STAGE s1 TO USER 'a'@'%' +GRANT Write ON STAGE s1 TO USER 'a'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -14589,7 +14610,7 @@ Grant( ---------- Input ---------- REVOKE write ON stage s1 FROM a; ---------- Output --------- -REVOKE Write ON STAGE s1 FROM USER 'a'@'%' +REVOKE Write ON STAGE s1 FROM USER 'a'@'%' ---------- AST ------------ Revoke( RevokeStmt { @@ -14614,7 +14635,7 @@ Revoke( ---------- Input ---------- GRANT all ON UDF a TO 'test-grant'; ---------- Output --------- -GRANT USAGE ON UDF a TO USER 'test-grant'@'%' +GRANT USAGE ON UDF a TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -14639,7 +14660,7 @@ Grant( ---------- Input ---------- GRANT usage ON UDF a TO 'test-grant'; ---------- Output --------- -GRANT USAGE ON UDF a TO USER 'test-grant'@'%' +GRANT USAGE ON UDF a TO USER 'test-grant'@'%' ---------- AST ------------ Grant( GrantStmt { @@ -14664,7 +14685,7 @@ Grant( ---------- Input ---------- REVOKE usage ON UDF a FROM 'test-grant'; ---------- Output --------- -REVOKE USAGE ON UDF a FROM USER 'test-grant'@'%' +REVOKE USAGE ON UDF a FROM USER 'test-grant'@'%' ---------- AST ------------ Revoke( RevokeStmt { @@ -14689,7 +14710,7 @@ Revoke( ---------- Input ---------- REVOKE all ON UDF a FROM 'test-grant'; ---------- Output --------- -REVOKE USAGE ON UDF a FROM USER 'test-grant'@'%' +REVOKE USAGE ON UDF a FROM USER 'test-grant'@'%' ---------- AST ------------ Revoke( RevokeStmt { @@ -14947,26 +14968,20 @@ ShowShares( ---------- Input ---------- SHOW GRANTS ON TABLE db1.tb1; ---------- Output --------- -SHOW GRANTS ON TABLE db1.tb1 +SHOW GRANTS ON TABLE db1.tb1 ---------- AST ------------ -ShowObjectGrantPrivileges( - ShowObjectGrantPrivilegesStmt { +ShowObjectPrivileges( + ShowObjectPrivilegesStmt { object: Table( - Identifier { - span: Some( - 21..24, - ), - name: "db1", - quote: None, - is_hole: false, - }, - Identifier { - span: Some( - 25..28, - ), - name: "tb1", - quote: None, - is_hole: false, + Some( + "db1", + ), + "tb1", + ), + show_option: Some( + ShowOptions { + show_limit: None, + limit: None, }, ), }, @@ -14976,18 +14991,17 @@ ShowObjectGrantPrivileges( ---------- Input ---------- SHOW GRANTS ON DATABASE db; ---------- Output --------- -SHOW GRANTS ON DATABASE db +SHOW GRANTS ON DATABASE db ---------- AST ------------ -ShowObjectGrantPrivileges( - ShowObjectGrantPrivilegesStmt { +ShowObjectPrivileges( + ShowObjectPrivilegesStmt { object: Database( - Identifier { - span: Some( - 24..26, - ), - name: "db", - quote: None, - is_hole: false, + "db", + ), + show_option: Some( + ShowOptions { + show_limit: None, + limit: None, }, ), }, @@ -20163,7 +20177,7 @@ Query( ---------- Input ---------- GRANT OWNERSHIP ON d20_0014.* TO ROLE 'd20_0015_owner'; ---------- Output --------- -GRANT OWNERSHIP ON d20_0014.* TO ROLE 'd20_0015_owner' +GRANT OWNERSHIP ON d20_0014.* TO ROLE 'd20_0015_owner' ---------- AST ------------ Grant( GrantStmt { @@ -20187,7 +20201,7 @@ Grant( ---------- Input ---------- GRANT OWNERSHIP ON d20_0014.t TO ROLE 'd20_0015_owner'; ---------- Output --------- -GRANT OWNERSHIP ON d20_0014.t TO ROLE 'd20_0015_owner' +GRANT OWNERSHIP ON d20_0014.t TO ROLE 'd20_0015_owner' ---------- AST ------------ Grant( GrantStmt { @@ -20212,7 +20226,7 @@ Grant( ---------- Input ---------- GRANT OWNERSHIP ON STAGE s1 TO ROLE 'd20_0015_owner'; ---------- Output --------- -GRANT OWNERSHIP ON STAGE s1 TO ROLE 'd20_0015_owner' +GRANT OWNERSHIP ON STAGE s1 TO ROLE 'd20_0015_owner' ---------- AST ------------ Grant( GrantStmt { @@ -20234,7 +20248,7 @@ Grant( ---------- Input ---------- GRANT OWNERSHIP ON UDF f1 TO ROLE 'd20_0015_owner'; ---------- Output --------- -GRANT OWNERSHIP ON UDF f1 TO ROLE 'd20_0015_owner' +GRANT OWNERSHIP ON UDF f1 TO ROLE 'd20_0015_owner' ---------- AST ------------ Grant( GrantStmt { diff --git a/src/query/service/src/table_functions/show_grants/show_grants_table.rs b/src/query/service/src/table_functions/show_grants/show_grants_table.rs index 3cfa79e6e7a56..73f5a35e68879 100644 --- a/src/query/service/src/table_functions/show_grants/show_grants_table.rs +++ b/src/query/service/src/table_functions/show_grants/show_grants_table.rs @@ -120,16 +120,16 @@ impl ShowGrants { fn schema() -> Arc { TableSchemaRefExt::create(vec![ - TableField::new("Privileges", TableDataType::String), - TableField::new("ObjectName", TableDataType::String), + TableField::new("privileges", TableDataType::String), + TableField::new("object_name", TableDataType::String), TableField::new( - "ObjectId", + "object_id", TableDataType::Nullable(Box::from(TableDataType::Number(NumberDataType::UInt64))), ), - TableField::new("GrantTo", TableDataType::String), - TableField::new("Name", TableDataType::String), + TableField::new("grant_to", TableDataType::String), + TableField::new("name", TableDataType::String), TableField::new( - "Grants", + "grants", TableDataType::Nullable(Box::new(TableDataType::String)), ), ]) @@ -157,30 +157,6 @@ impl Table for ShowGrants { } fn table_args(&self) -> Option { - // match self.grant_type.to_lowercase().as_str() { - // "role" | "user" => { - // Some(TableArgs::new_positioned(vec![ - // Scalar::String(self.grant_type.clone()), - // Scalar::String(self.name.clone()), - // ])) - // } - // "table" => { - // Some(TableArgs::new_positioned(vec![ - // Scalar::String(self.grant_type.clone()), - // Scalar::String(self.name.clone()), - // Scalar::String(self.catalog.clone()), - // Scalar::String(self.db_name.clone()), - // ])) - // } - // "database" => { - // Some(TableArgs::new_positioned(vec![ - // Scalar::String(self.grant_type.clone()), - // Scalar::String(self.name.clone()), - // Scalar::String(self.catalog.clone()), - // ])) - // } - // _ => {None} - // } Some(TableArgs::new_positioned(vec![ Scalar::String(self.grant_type.clone()), Scalar::String(self.name.clone()), @@ -526,7 +502,7 @@ async fn show_object_grant( table_id, ) { return Err(ErrorCode::PermissionDenied(format!( - "Permission denied: No privilege on database {} for user {}.", + "Permission denied: No privilege on table {} for user {}.", name, current_user ))); } diff --git a/src/query/sql/src/planner/binder/binder.rs b/src/query/sql/src/planner/binder/binder.rs index e962e080db50b..c9e6f79ad4e7c 100644 --- a/src/query/sql/src/planner/binder/binder.rs +++ b/src/query/sql/src/planner/binder/binder.rs @@ -446,6 +446,7 @@ impl<'a> Binder { // Permissions Statement::Grant(stmt) => self.bind_grant(stmt).await?, Statement::ShowGrants { principal, show_options } => self.bind_show_account_grants(bind_context, principal, show_options).await?, + Statement::ShowObjectPrivileges(stmt) => self.bind_show_object_privileges(bind_context, stmt).await?, Statement::Revoke(stmt) => self.bind_revoke(stmt).await?, // File Formats diff --git a/src/query/sql/src/planner/binder/ddl/account.rs b/src/query/sql/src/planner/binder/ddl/account.rs index a13cb8e82843b..326f8f8d1d6f7 100644 --- a/src/query/sql/src/planner/binder/ddl/account.rs +++ b/src/query/sql/src/planner/binder/ddl/account.rs @@ -17,9 +17,11 @@ use databend_common_ast::ast::AccountMgrLevel; use databend_common_ast::ast::AccountMgrSource; use databend_common_ast::ast::AlterUserStmt; use databend_common_ast::ast::CreateUserStmt; +use databend_common_ast::ast::GrantObjectName; use databend_common_ast::ast::GrantStmt; use databend_common_ast::ast::PrincipalIdentity as AstPrincipalIdentity; use databend_common_ast::ast::RevokeStmt; +use databend_common_ast::ast::ShowObjectPrivilegesStmt; use databend_common_ast::ast::ShowOptions; use databend_common_exception::ErrorCode; use databend_common_exception::Result; @@ -359,7 +361,52 @@ impl Binder { }; let (show_limit, limit_str) = - get_show_options(show_options, Some("ObjectName".to_string())); + get_show_options(show_options, Some("object_name".to_string())); + let query = format!("{} {} {}", query, show_limit, limit_str,); + + self.bind_rewrite_to_query(bind_context, &query, RewriteKind::ShowGrants) + .await + } + + #[async_backtrace::framed] + pub(in crate::planner::binder) async fn bind_show_object_privileges( + &mut self, + bind_context: &mut BindContext, + stmt: &ShowObjectPrivilegesStmt, + ) -> Result { + let ShowObjectPrivilegesStmt { + object, + show_option, + } = stmt; + + let catalog = self.ctx.get_current_catalog(); + let query = match object { + GrantObjectName::Database(db) => { + format!( + "SELECT * FROM show_grants('database', '{}', '{}')", + db, catalog + ) + } + GrantObjectName::Table(db, tb) => { + let db = if let Some(db) = db { + db.to_string() + } else { + self.ctx.get_current_database() + }; + format!( + "SELECT * FROM show_grants('table', '{}', '{}', '{}')", + tb, catalog, db + ) + } + GrantObjectName::UDF(name) => { + format!("SELECT * FROM show_grants('udf', '{}')", name) + } + GrantObjectName::Stage(name) => { + format!("SELECT * FROM show_grants('stage', '{}')", name) + } + }; + + let (show_limit, limit_str) = get_show_options(show_option, Some("name".to_string())); let query = format!("{} {} {}", query, show_limit, limit_str,); self.bind_rewrite_to_query(bind_context, &query, RewriteKind::ShowGrants) diff --git a/tests/sqllogictests/suites/base/05_ddl/05_0017_ddl_grant_role.test b/tests/sqllogictests/suites/base/05_ddl/05_0017_ddl_grant_role.test index 043ee83087a15..f543469d8d4ab 100644 --- a/tests/sqllogictests/suites/base/05_ddl/05_0017_ddl_grant_role.test +++ b/tests/sqllogictests/suites/base/05_ddl/05_0017_ddl_grant_role.test @@ -92,6 +92,63 @@ DROP ROLE `test-role` statement ok DROP USER 'test-user' +statement ok +create or replace table t(id int); + +statement ok +create or replace database db1; + +statement ok +create or replace stage s1; + +statement ok +create or replace FUNCTION isnotempty AS(p) -> not(is_null(p)); + +statement ok +grant select on default.t to role role3; + +statement ok +grant all on db1.* to role role2; + +statement ok +grant all on db1.* to role role3; + +statement ok +grant read on stage s1 to role role2; + +statement ok +grant usage on udf isnotempty to role role2; + +statement ok +grant usage on udf isnotempty to role role3; + +query TT +show grants on udf isnotempty; +---- +USAGE isnotempty NULL ROLE role2 (empty) +USAGE isnotempty NULL ROLE role3 (empty) + +query TT +show grants on udf isnotempty where name!='role2' limit 1; +---- +USAGE isnotempty NULL ROLE role3 (empty) + +query TT +show grants on stage s1; +---- +Read s1 NULL ROLE role2 (empty) + +query TT +select * EXCLUDE(object_id) from show_grants('database', 'db1', 'default'); +---- +CREATE,SELECT,INSERT,UPDATE,DELETE,DROP,ALTER,GRANT db1 ROLE role2 (empty) +CREATE,SELECT,INSERT,UPDATE,DELETE,DROP,ALTER,GRANT db1 ROLE role3 (empty) + +query TT +select * EXCLUDE(object_id) from show_grants('table', 't', 'default', 'default'); +---- +SELECT t ROLE role3 (empty) + statement ok DROP ROLE role1 @@ -101,3 +158,14 @@ DROP ROLE role2 statement ok DROP ROLE role3 +statement ok +drop stage if exists s1; + +statement ok +drop database if exists db1; + +statement ok +drop table if exists t; + +statement ok +drop function if exists isnotempty; diff --git a/tests/suites/0_stateless/18_rbac/18_0007_privilege_access.result b/tests/suites/0_stateless/18_rbac/18_0007_privilege_access.result index aaed8dcd51454..fd5bc1fc1aa85 100644 --- a/tests/suites/0_stateless/18_rbac/18_0007_privilege_access.result +++ b/tests/suites/0_stateless/18_rbac/18_0007_privilege_access.result @@ -60,6 +60,12 @@ Error: APIError: ResponseError with 1063: Permission denied: User 'a'@'%' does n Error: APIError: ResponseError with 1063: Permission denied: User 'a'@'%' does not have the required privileges for database 'nogrant' 1 0 +=== Test: show grants on privilege check === +Error: APIError: ResponseError with 1063: Permission denied: privilege USAGE is required on udf root_func for user b. +Error: APIError: ResponseError with 1063: Permission denied: privilege READ is required on stage root_stage for user b. Or no need to show the stage privilege +Error: APIError: ResponseError with 1063: Permission denied: No privilege on database root_db for user b. +Error: APIError: ResponseError with 1063: Permission denied: No privilege on table root_table for user b. +Error: APIError: ResponseError with 1063: Permission denied: No privilege on table root_table for user b. 1 64 378 Error: APIError: ResponseError with 1063: Permission denied: privilege [Select] is required on 'default'.'default'.'t1' for user 'b'@'%' with roles [public] Error: APIError: ResponseError with 1063: Permission denied: privilege [Read] is required on STAGE s3 for user 'b'@'%' with roles [public] diff --git a/tests/suites/0_stateless/18_rbac/18_0007_privilege_access.sh b/tests/suites/0_stateless/18_rbac/18_0007_privilege_access.sh index 8fdf73fbf6818..c6b74787ada90 100755 --- a/tests/suites/0_stateless/18_rbac/18_0007_privilege_access.sh +++ b/tests/suites/0_stateless/18_rbac/18_0007_privilege_access.sh @@ -195,6 +195,25 @@ EOF echo "drop user if exists b" | $BENDSQL_CLIENT_CONNECT echo "create user b identified by '$TEST_USER_PASSWORD'" | $BENDSQL_CLIENT_CONNECT +echo "=== Test: show grants on privilege check ===" +echo "drop database if exists root_db" | $BENDSQL_CLIENT_CONNECT +echo "drop table if exists default.root_table" | $BENDSQL_CLIENT_CONNECT +echo "drop stage if exists root_stage" | $BENDSQL_CLIENT_CONNECT +echo "drop function if exists root_func" | $BENDSQL_CLIENT_CONNECT +echo "create database root_db" | $BENDSQL_CLIENT_CONNECT +echo "create table default.root_table(id int)" | $BENDSQL_CLIENT_CONNECT +echo "create stage root_stage" | $BENDSQL_CLIENT_CONNECT +echo "create function root_func as (a) -> (a+1);" | $BENDSQL_CLIENT_CONNECT +echo "show grants on udf root_func" | $USER_B_CONNECT +echo "show grants on stage root_stage" | $USER_B_CONNECT +echo "show grants on database root_db" | $USER_B_CONNECT +echo "show grants on table default.root_table" | $USER_B_CONNECT +echo "show grants on table root_table" | $USER_B_CONNECT +echo "drop database if exists root_db" | $BENDSQL_CLIENT_CONNECT +echo "drop table if exists default.root_table" | $BENDSQL_CLIENT_CONNECT +echo "drop stage if exists root_stage" | $BENDSQL_CLIENT_CONNECT +echo "drop function if exists root_func" | $BENDSQL_CLIENT_CONNECT + echo "drop table if exists t" | $BENDSQL_CLIENT_CONNECT echo "drop table if exists t1" | $BENDSQL_CLIENT_CONNECT echo "drop table if exists t2" | $BENDSQL_CLIENT_CONNECT