From e2b6207d15aa8ec6eacbdeb25b5c53dde000a1e1 Mon Sep 17 00:00:00 2001 From: Emir Vildanov <e.vildanov@picodata.io> Date: Mon, 27 Nov 2023 08:24:27 +0000 Subject: [PATCH] feat: support GRANT/REVOKE PRIVILEGE, add LOGIN/NOLOGIN options for ALTER USER --- doc/sql/query.ebnf | 13 +- .../test_app/test/integration/acl_test.lua | 26 + sbroad-core/src/errors.rs | 3 + sbroad-core/src/frontend/sql.rs | 953 +++++++++--------- sbroad-core/src/frontend/sql/ast.rs | 182 +--- sbroad-core/src/frontend/sql/query.pest | 40 +- sbroad-core/src/ir/acl.rs | 163 ++- 7 files changed, 764 insertions(+), 616 deletions(-) diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf index 1ea1616184..00e8fff7c7 100644 --- a/doc/sql/query.ebnf +++ b/doc/sql/query.ebnf @@ -3,11 +3,20 @@ EXPLAIN ::= 'EXPLAIN' ( DML | DQL ) DQL ::= (SELECT | SELECT UNION ALL SELECT | SELECT (EXCEPT 'DISTINCT'? ) SELECT ) Options? DML ::= (DELETE | INSERT | UPDATE) Options? DDL ::= CreateTable | DropTable -ACL ::= DropRole | DropUser | CreateRole | CreateUser | AlterUser +ACL ::= DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivilege | RevokePrivilege +RevokePrivilege ::= 'revoke' Privilege 'from' (role | user) ('option' '(' ('timeout' '=' DOUBLE)')')? +GrantPrivilege ::= 'grant' Privilege 'to' (role | user) ('option' '(' ('timeout' '=' DOUBLE)')')? +Privilege ::= (('create' | 'alter' | 'drop') 'user') + | (('alter' | 'drop') 'on' 'user' (role | user)) + | (('create' | 'drop') 'role') + | ('drop' 'on' 'role' (role | user)) + | (('read' | 'write' | 'create' | 'alter' | 'drop') 'table') + | (('alter' | 'drop' | 'read' | 'write') 'on' 'table'? table) + | role CreateRole ::= 'create role' role ('option' '(' ('timeout' '=' DOUBLE)')')? CreateTable ::= 'create table' table '(' Column (',' Column)* ',' PrimaryKey ')' ('using' ('memtx' | 'vinyl'))? Distribution ('option' '(' ('timeout' '=' DOUBLE)')')? CreateUser ::= 'create user' user 'with'? 'password' "'" password "'" ('using' ('chap-sha1' | 'ldap' | 'md5'))? ('option' '(' ('timeout' '=' DOUBLE)')')? -AlterUser ::= 'alter user' user 'with'? 'password' "'" password "'" ('using' ('chap-sha1' | 'ldap' | 'md5'))? ('option' '(' ('timeout' '=' DOUBLE)')')? +AlterUser ::= 'alter user' user 'with'? ('login' | 'nologin' | 'password' "'" password "'" ('using' ('chap-sha1' | 'ldap' | 'md5'))?) ('option' '(' ('timeout' '=' DOUBLE)')')? Column ::= name ('Bool' | 'Decimal' | 'Double' | 'Int' | 'Number' | 'Scalar' | 'String' | 'Text' | 'Unsigned' | 'Varchar') (('not'?) 'null')? PrimaryKey ::= 'primary key' '(' name (',' name)* ')' Distribution ::= 'global' | ('distributed by' '(' name (',' name)* ')') diff --git a/sbroad-cartridge/test_app/test/integration/acl_test.lua b/sbroad-cartridge/test_app/test/integration/acl_test.lua index 99f987eb23..d88e37bf84 100644 --- a/sbroad-cartridge/test_app/test/integration/acl_test.lua +++ b/sbroad-cartridge/test_app/test/integration/acl_test.lua @@ -81,3 +81,29 @@ g.test_alter_user = function() [[Sbroad Error: ACL queries are not supported]] ) end + +g.test_grant_privilege = function() + local api = cluster:server("api-1").net_box + + local _, err = api:call( + "sbroad.execute", + { [[ GRANT READ ON TABLE "t" TO "role" ]], {} } + ) + t.assert_equals( + string.format("%s", err), + [[Sbroad Error: ACL queries are not supported]] + ) +end + +g.test_revoke_privilege = function() + local api = cluster:server("api-1").net_box + + local _, err = api:call( + "sbroad.execute", + { [[ REVOKE READ ON TABLE "t" FROM "role" ]], {} } + ) + t.assert_equals( + string.format("%s", err), + [[Sbroad Error: ACL queries are not supported]] + ) +end diff --git a/sbroad-core/src/errors.rs b/sbroad-core/src/errors.rs index 2fc13ab51e..345ef62aee 100644 --- a/sbroad-core/src/errors.rs +++ b/sbroad-core/src/errors.rs @@ -70,6 +70,8 @@ pub enum Entity { Plan, /// primary key of tarantool space PrimaryKey, + /// privilege participating in GRANT/REVOKE query + Privilege, /// corresponds to struct ProducerResult ProducerResult, /// SQL query @@ -173,6 +175,7 @@ impl fmt::Display for Entity { Entity::PatternWithParams => "pattern with parameters".to_string(), Entity::Plan => "plan".to_string(), Entity::PrimaryKey => "primary key".to_string(), + Entity::Privilege => "privilege".to_string(), Entity::ProducerResult => "producer result".to_string(), Entity::ParseNode => "parse node".to_string(), Entity::Query => "query".to_string(), diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index 21daff6fa3..1b2a06c8a6 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -30,7 +30,8 @@ use crate::ir::{Node, OptionKind, OptionSpec, Plan}; use crate::otm::child_span; use crate::errors::Entity::AST; -use crate::ir::acl::Acl; +use crate::ir::acl::AlterOption; +use crate::ir::acl::{Acl, GrantRevokeType, Privilege}; use crate::ir::aggregates::AggregateKind; use crate::ir::helpers::RepeatableState; use crate::ir::transformation::redistribution::ColumnPosition; @@ -39,7 +40,16 @@ use tarantool::decimal::Decimal; use tarantool::space::SpaceEngineType; // DDL timeout in seconds (1 day). -const DEFAULT_TIMEOUT: f64 = 24.0 * 60.0 * 60.0; +const DEFAULT_TIMEOUT_F64: f64 = 24.0 * 60.0 * 60.0; +const DEFAULT_AUTH_METHOD: &str = "chap-sha1"; + +fn get_default_timeout() -> Decimal { + Decimal::from_str(&format!("{DEFAULT_TIMEOUT_F64}")).unwrap() +} + +fn get_default_auth_method() -> String { + String::from(DEFAULT_AUTH_METHOD) +} /// Helper structure to fix the double linking /// problem in the BETWEEN operator. @@ -98,6 +108,16 @@ fn get_timeout(ast: &AbstractSyntaxTree, node_id: usize) -> Result<Decimal, Sbro )) } +/// Parse node from which we want to get String value. +fn parse_string_value_node(ast: &AbstractSyntaxTree, node_id: usize) -> Result<&str, SbroadError> { + let string_value_node = ast.nodes.get_node(node_id)?; + let string_value = string_value_node + .value + .as_ref() + .expect("Rule node must contain string value."); + Ok(string_value.as_str()) +} + #[allow(clippy::too_many_lines)] #[allow(clippy::uninlined_format_args)] fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, SbroadError> { @@ -112,20 +132,12 @@ fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, let mut pk_keys: Vec<String> = Vec::new(); let mut shard_key: Vec<String> = Vec::new(); let mut engine_type: SpaceEngineType = SpaceEngineType::default(); - let mut timeout: Decimal = Decimal::from_str(&format!("{DEFAULT_TIMEOUT}")).map_err(|_| { - SbroadError::Invalid(Entity::Type, Some("timeout value in create table".into())) - })?; + let mut timeout = get_default_timeout(); for child_id in &node.children { let child_node = ast.nodes.get_node(*child_id)?; match child_node.rule { Type::NewTable => { - table_name = - normalize_name_for_space_api(child_node.value.as_ref().ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "table name in the create table AST".into(), - ) - })?); + table_name = normalize_name_for_space_api(parse_string_value_node(ast, *child_id)?); } Type::Columns => { let columns_node = ast.nodes.get_node(*child_id)?; @@ -137,15 +149,7 @@ fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, match def_child_node.rule { Type::ColumnDefName => { column_def.name = normalize_name_for_space_api( - def_child_node.value.as_ref().ok_or_else(|| { - SbroadError::Invalid( - Entity::Column, - Some( - "column name is empty in the create table AST" - .into(), - ), - ) - })?, + parse_string_value_node(ast, *def_child_id)?, ); } Type::ColumnDefType => { @@ -253,17 +257,8 @@ fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, )), )); } - let pk_col_name = normalize_name_for_space_api( - pk_col_node.value.as_ref().ok_or_else(|| { - SbroadError::Invalid( - Entity::Column, - Some( - "primary key column name is empty in the create table AST" - .into(), - ), - ) - })?, - ); + let pk_col_name = + normalize_name_for_space_api(parse_string_value_node(ast, *pk_col_id)?); let mut column_found = false; for column in &columns { if column.name == pk_col_name { @@ -344,12 +339,7 @@ fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, )); } let shard_col_name = normalize_name_for_space_api( - shard_col_node.value.as_ref().ok_or_else(|| { - SbroadError::Invalid( - Entity::Column, - Some("AST shard key column name is empty".into()), - ) - })?, + parse_string_value_node(ast, *shard_col_id)?, ); let column_found = columns.iter().any(|c| c.name == shard_col_name); @@ -427,74 +417,122 @@ fn parse_create_table(ast: &AbstractSyntaxTree, node: &ParseNode) -> Result<Ddl, Ok(create_sharded_table) } -/// Code block common for CREATE/ALTER USER moved to separate function. -fn parse_create_alter_user( - ast: &AbstractSyntaxTree, +/// Get String value under `RoleName` node. +fn parse_role_name(ast: &AbstractSyntaxTree, node_id: usize) -> Result<String, SbroadError> { + Ok(normalize_name_for_space_api(parse_string_value_node( + ast, node_id, + )?)) +} + +/// Common logic for parsing GRANT/REVOKE queries. +#[allow(clippy::too_many_lines)] +fn parse_grant_revoke( node: &ParseNode, - default_timeout: Decimal, -) -> Result<(String, String, String, Decimal), SbroadError> { - let mut iter = node.children.iter(); - // User name - let user_name_id = iter.next().ok_or_else(|| { - SbroadError::Invalid( - Entity::ParseNode, - Some(String::from("UserName expected as a first child")), - ) - })?; - let user_name_node = ast.nodes.get_node(*user_name_id)?; - let user_name = - normalize_name_for_space_api(user_name_node.value.as_ref().ok_or_else(|| { - SbroadError::NotFound(Entity::Node, "user name in UserName node".into()) - })?); - // Password - let pwd_id = iter.next().ok_or_else(|| { - SbroadError::Invalid( - Entity::ParseNode, - Some(String::from("Password expected as a second child")), - ) - })?; - let pwd_node = ast.nodes.get_node(*pwd_id)?; - let password: String = pwd_node - .value - .as_ref() - .ok_or_else(|| SbroadError::NotFound(Entity::Node, "password in Password node".into()))? - .to_string(); - // Optional parameters: auth method and timeout. - let mut timeout = default_timeout; - let mut auth_method = String::from("chap-sha1"); - for child_id in iter { - let child_node = ast.nodes.get_node(*child_id)?; - match child_node.rule { - Type::Timeout => { - timeout = get_timeout(ast, *child_id)?; - } - Type::AuthMethod => { - let method_id = child_node.children.first().ok_or_else(|| { - SbroadError::Invalid( + ast: &AbstractSyntaxTree, +) -> Result<(GrantRevokeType, String, Decimal), SbroadError> { + let privilege_block_node_id = node + .children + .first() + .expect("Specific privilege block expected under GRANT/REVOKE"); + let privilege_block_node = ast.nodes.get_node(*privilege_block_node_id)?; + let grant_revoke_type = match privilege_block_node.rule { + Type::PrivBlockPrivilege => { + let privilege_node_id = privilege_block_node + .children + .first() + .expect("Expected to see Privilege under PrivBlockPrivilege"); + let privilege_node = ast.nodes.get_node(*privilege_node_id)?; + let privilege = match privilege_node.rule { + Type::PrivilegeAlter => Privilege::Alter, + Type::PrivilegeCreate => Privilege::Create, + Type::PrivilegeDrop => Privilege::Drop, + Type::PrivilegeExecute => Privilege::Execute, + Type::PrivilegeRead => Privilege::Read, + Type::PrivilegeSession => Privilege::Session, + Type::PrivilegeUsage => Privilege::Usage, + Type::PrivilegeWrite => Privilege::Write, + _ => { + return Err(SbroadError::Invalid( + Entity::Privilege, + Some(format!( + "Expected to see Privilege node. Got: {privilege_node:?}" + )), + )) + } + }; + let inner_privilege_block_node_id = privilege_block_node + .children + .get(1) + .expect("Expected to see inner priv block under PrivBlockPrivilege"); + let inner_privilege_block_node = ast.nodes.get_node(*inner_privilege_block_node_id)?; + match inner_privilege_block_node.rule { + Type::PrivBlockUser => GrantRevokeType::user(privilege)?, + Type::PrivBlockSpecificUser => { + let user_name_node_id = inner_privilege_block_node + .children + .first() + .expect("Expected to see RoleName under PrivBlockSpecificUser"); + let user_name = parse_role_name(ast, *user_name_node_id)?; + GrantRevokeType::specific_user(privilege, user_name)? + } + Type::PrivBlockRole => GrantRevokeType::role(privilege)?, + Type::PrivBlockSpecificRole => { + let role_name_node_id = inner_privilege_block_node + .children + .first() + .expect("Expected to see RoleName under PrivBlockSpecificUser"); + let role_name = parse_role_name(ast, *role_name_node_id)?; + GrantRevokeType::specific_role(privilege, role_name)? + } + Type::PrivBlockTable => GrantRevokeType::table(privilege)?, + Type::PrivBlockSpecificTable => { + let table_node_id = inner_privilege_block_node.children.first().expect( + "Expected to see TableName as a first child of PrivBlockSpecificTable", + ); + let table_name = + normalize_name_for_space_api(parse_string_value_node(ast, *table_node_id)?); + GrantRevokeType::specific_table(privilege, table_name)? + } + _ => { + return Err(SbroadError::Invalid( Entity::ParseNode, - Some(String::from("Method expected under AuthMethod node")), - ) - })?; - let method_node = ast.nodes.get_node(*method_id)?; - auth_method = method_node - .value - .as_ref() - .ok_or_else(|| { - SbroadError::NotFound(Entity::Node, "auth method in AuthMethod node".into()) - })? - .to_string(); - } - _ => { - return Err(SbroadError::Invalid( - Entity::Node, - Some(format!( - "ACL node contains unexpected child: {child_node:?}", - )), - )); + Some(format!( + "Expected specific priv block, got: {inner_privilege_block_node:?}" + )), + )) + } } } + Type::PrivBlockRolePass => { + let role_name_id = privilege_block_node + .children + .first() + .expect("RoleName must be a first child of PrivBlockRolePass"); + let role_name = parse_role_name(ast, *role_name_id)?; + GrantRevokeType::role_pass(role_name) + } + _ => { + return Err(SbroadError::Invalid( + Entity::ParseNode, + Some(format!( + "Expected specific priv block, got: {privilege_block_node:?}" + )), + )) + } + }; + + let grantee_name_node_id = node + .children + .get(1) + .expect("RoleName must be a second child of GRANT/REVOKE"); + let grantee_name = parse_role_name(ast, *grantee_name_node_id)?; + + let mut timeout = get_default_timeout(); + if let Some(timeout_child_id) = node.children.get(2) { + timeout = get_timeout(ast, *timeout_child_id)?; } - Ok((user_name, password, auth_method, timeout)) + + Ok((grant_revoke_type, grantee_name, timeout)) } impl Ast for AbstractSyntaxTree { @@ -608,11 +646,10 @@ impl Ast for AbstractSyntaxTree { // also we will mark this expr to add in the future `()` let arithmetic_parse_node = self.nodes.get_node(ast_id)?; if arithmetic_parse_node.rule == Type::ArithParentheses { - let arithmetic_id = arithmetic_parse_node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "ArithParentheses has no children.".into(), - ) - })?; + let arithmetic_id = arithmetic_parse_node + .children + .first() + .expect("ArithParentheses has no children."); plan_id = plan.as_row(map.get(*arithmetic_id)?, rows)?; arith_expr_with_parentheses_ids.push(plan_id); } else { @@ -628,11 +665,10 @@ impl Ast for AbstractSyntaxTree { map: &Translation, arith_expr_with_parentheses_ids: &mut Vec<usize>, rows: &mut HashSet<usize>| { - let ast_left_id = current_node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "Multiplication or Addition has no children.".into(), - ) - })?; + let ast_left_id = current_node + .children + .first() + .expect("Multiplication or Addition has no children."); let plan_left_id = get_arithmetic_plan_id( plan, map, @@ -641,13 +677,9 @@ impl Ast for AbstractSyntaxTree { *ast_left_id, )?; - let ast_right_id = current_node.children.get(2).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "that is right node with index 2 among Multiplication or Addition children" - .into(), - ) - })?; + let ast_right_id = current_node.children.get(2).expect( + "that is right node with index 2 among Multiplication or Addition children", + ); let plan_right_id = get_arithmetic_plan_id( plan, map, @@ -656,13 +688,7 @@ impl Ast for AbstractSyntaxTree { *ast_right_id, )?; - let ast_op_id = current_node.children.get(1).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "that is center node (operator) with index 1 among Multiplication or Addition children" - .into(), - ) - })?; + let ast_op_id = current_node.children.get(1).expect("that is center node (operator) with index 1 among Multiplication or Addition children"); let op_node = self.nodes.get_node(*ast_op_id)?; let op = Arithmetic::from_node_type(&op_node.rule)?; @@ -672,18 +698,14 @@ impl Ast for AbstractSyntaxTree { Ok::<usize, SbroadError>(op_id) }; - let default_timeout: Decimal = Decimal::from_str(&format!("{DEFAULT_TIMEOUT}")) - .map_err(|_| SbroadError::Invalid(Entity::Type, Some("timeout option".into())))?; - for (_, id) in dft_post.iter(top) { let node = self.nodes.get_node(id)?; match &node.rule { Type::Scan => { - let ast_scan_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "could not find first child id in scan node".to_string(), - ) - })?; + let ast_scan_id = node + .children + .first() + .expect("could not find first child id in scan node"); let plan_child_id = map.get(*ast_scan_id)?; map.add(id, plan_child_id); if let Some(ast_alias_id) = node.children.get(1) { @@ -703,11 +725,10 @@ impl Ast for AbstractSyntaxTree { } } Type::ScanTable => { - let ast_table_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "could not find first child id in scan table node".to_string(), - ) - })?; + let ast_table_id = node + .children + .first() + .expect("could not find first child id in scan table node"); let ast_table = self.nodes.get_node(*ast_table_id)?; match ast_table.value { Some(ref table) if ast_table.rule == Type::Table => { @@ -735,11 +756,10 @@ impl Ast for AbstractSyntaxTree { } } Type::SubQuery => { - let ast_child_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "child node id is not found among sub-query children.".into(), - ) - })?; + let ast_child_id = node + .children + .first() + .expect("child node id is not found among sub-query children."); let plan_child_id = map.get(*ast_child_id)?; let plan_sq_id = plan.add_sub_query(plan_child_id, None)?; map.add(id, plan_sq_id); @@ -756,14 +776,9 @@ impl Ast for AbstractSyntaxTree { let get_column_name = |ast_id: usize| -> Result<String, SbroadError> { let ast_col_name = self.nodes.get_node(ast_id)?; if let Type::ColumnName = ast_col_name.rule { - let name: Option<String> = - ast_col_name.value.as_deref().map(normalize_name_from_sql); - Ok(name.ok_or_else(|| { - SbroadError::Invalid( - Entity::Name, - Some("empty AST column name".into()), - ) - })?) + let name = + normalize_name_from_sql(parse_string_value_node(self, ast_id)?); + Ok(name) } else { Err(SbroadError::Invalid( Entity::Type, @@ -920,11 +935,7 @@ impl Ast for AbstractSyntaxTree { let ast_scan = self.nodes.get_node(*ast_scan_id)?; if let Type::ScanName = ast_scan.rule { let ast_scan_name = Some(normalize_name_from_sql( - ast_scan.value.as_ref().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "empty scan name for AST node.".into(), - ) - })?, + parse_string_value_node(self, *ast_scan_id)?, )); let plan_scan_name = get_scan_name(&col_name, *plan_rel_id)?; if plan_scan_name != ast_scan_name { @@ -985,13 +996,11 @@ impl Ast for AbstractSyntaxTree { map.add(id, plan.add_const(val)); } Type::VTableMaxRows => { - let ast_child_id = *node.children.first().ok_or_else(|| { - SbroadError::Invalid( - Entity::AST, - Some("no children for sql_vdbe_max_steps option".into()), - ) - })?; - let ast_child_node = self.nodes.get_node(ast_child_id)?; + let ast_child_id = node + .children + .first() + .expect("no children for sql_vdbe_max_steps option"); + let ast_child_node = self.nodes.get_node(*ast_child_id)?; let val: Option<Value> = match ast_child_node.rule { Type::Parameter => None, Type::Unsigned => { @@ -1025,13 +1034,11 @@ impl Ast for AbstractSyntaxTree { }); } Type::SqlVdbeMaxSteps => { - let ast_child_id = *node.children.first().ok_or_else(|| { - SbroadError::Invalid( - Entity::AST, - Some("no children for sql_vdbe_max_steps option".into()), - ) - })?; - let ast_child_node = self.nodes.get_node(ast_child_id)?; + let ast_child_id = node + .children + .first() + .expect("no children for sql_vdbe_max_steps option"); + let ast_child_node = self.nodes.get_node(*ast_child_id)?; let val: Option<Value> = match ast_child_node.rule { Type::Parameter => None, Type::Unsigned => { @@ -1090,31 +1097,20 @@ impl Ast for AbstractSyntaxTree { map.add(id, plan_asterisk_id); } Type::Alias => { - let ast_ref_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "list of alias children is empty, Reference node id is not found." - .into(), - ) - })?; + let ast_ref_id = node + .children + .first() + .expect("list of alias children is empty, Reference node id is not found."); let plan_ref_id = map.get(*ast_ref_id)?; - let ast_name_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound(Entity::Node, "(Alias name) with index 1".into()) - })?; - let name = self - .nodes - .get_node(*ast_name_id)? - .value - .as_ref() - .ok_or_else(|| SbroadError::NotFound(Entity::Name, "of Alias".into()))?; + let ast_name_id = node.children.get(1).expect("(Alias name) with index 1"); + let name = parse_string_value_node(self, *ast_name_id)?; let plan_alias_id = plan .nodes .add_alias(&normalize_name_from_sql(name), plan_ref_id)?; map.add(id, plan_alias_id); } Type::Column => { - let ast_child_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Column has no children.".into()) - })?; + let ast_child_id = node.children.first().expect("Column has no children."); let plan_child_id = map.get(*ast_child_id)?; map.add(id, plan_child_id); } @@ -1138,39 +1134,29 @@ impl Ast for AbstractSyntaxTree { map.add(id, plan_row_id); } Type::And | Type::Or => { - let ast_left_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Comparison has no children.".into()) - })?; + let ast_left_id = node.children.first().expect("Comparison has no children."); let plan_left_id = plan.as_row(map.get(*ast_left_id)?, &mut rows)?; - let ast_right_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "that is right node with index 1 among comparison children".into(), - ) - })?; + let ast_right_id = node + .children + .get(1) + .expect("that is right node with index 1 among comparison children"); let plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; let op = Bool::from_node_type(&node.rule)?; let cond_id = plan.add_cond(plan_left_id, op, plan_right_id)?; map.add(id, cond_id); } Type::Cmp => { - let ast_left_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Comparison has no children.".into()) - })?; + let ast_left_id = node.children.first().expect("Comparison has no children."); let plan_left_id = plan.as_row(map.get(*ast_left_id)?, &mut rows)?; - let ast_right_id = node.children.get(2).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "that is right node with index 2 among comparison children".into(), - ) - })?; + let ast_right_id = node + .children + .get(2) + .expect("that is right node with index 2 among comparison children"); let plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; - let ast_op_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "that is operator node with index 1 among comparison children".into(), - ) - })?; + let ast_op_id = node + .children + .get(1) + .expect("that is operator node with index 1 among comparison children"); let op_node = self.nodes.get_node(*ast_op_id)?; let op = Bool::from_node_type(&op_node.rule)?; let is_not = match op { @@ -1194,12 +1180,7 @@ impl Ast for AbstractSyntaxTree { &node.rule ))); } - let ast_child_id = node.children.get(1).ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues(format!( - "{:?} has no children.", - &node.rule - )) - })?; + let ast_child_id = node.children.get(1).expect("Not has no children."); let plan_child_id = plan.as_row(map.get(*ast_child_id)?, &mut rows)?; let op = Unary::from_node_type(&node.rule)?; let unary_id = plan.add_unary(op, plan_child_id)?; @@ -1219,12 +1200,10 @@ impl Ast for AbstractSyntaxTree { )) } }; - let ast_child_id = node.children.get(child_index).ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues(format!( - "{:?} has no children.", - &node.rule - )) - })?; + let ast_child_id = node + .children + .get(child_index) + .expect("{:?} has no children."); let plan_child_id = plan.as_row(map.get(*ast_child_id)?, &mut rows)?; let op = Unary::from_node_type(&node.rule)?; let op_id = plan.add_unary(op, plan_child_id)?; @@ -1249,12 +1228,7 @@ impl Ast for AbstractSyntaxTree { )) } }; - let ast_child_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues(format!( - "{:?} has no children.", - &node.rule - )) - })?; + let ast_child_id = node.children.first().expect("IsNull has no children."); let plan_child_id = plan.as_row(map.get(*ast_child_id)?, &mut rows)?; let op = Unary::from_node_type(&node.rule)?; let op_id = plan.add_unary(op, plan_child_id)?; @@ -1281,20 +1255,20 @@ impl Ast for AbstractSyntaxTree { )) } }; - let ast_left_id = node.children.get(left_index).ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Between has no children.".into()) - })?; + let ast_left_id = node + .children + .get(left_index) + .expect("Between has no children."); let plan_left_id = plan.as_row(map.get(*ast_left_id)?, &mut rows)?; - let ast_center_id = node.children.get(center_index).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "(center) among between children".into(), - ) - })?; + let ast_center_id = node + .children + .get(center_index) + .expect("Center not found among between children"); let plan_center_id = plan.as_row(map.get(*ast_center_id)?, &mut rows)?; - let ast_right_id = node.children.get(right_index).ok_or_else(|| { - SbroadError::NotFound(Entity::Node, "(right) among between children".into()) - })?; + let ast_right_id = node + .children + .get(right_index) + .expect("Right not found among between children"); let plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; let greater_eq_id = plan.add_cond(plan_left_id, Bool::GtEq, plan_center_id)?; @@ -1309,25 +1283,19 @@ impl Ast for AbstractSyntaxTree { betweens.push(Between::new(plan_left_id, less_eq_id)); } Type::Cast => { - let ast_child_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Condition has no children.".into()) - })?; + let ast_child_id = node.children.first().expect("Condition has no children."); let plan_child_id = map.get(*ast_child_id)?; - let ast_type_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "(Cast type) among cast children".into(), - ) - })?; + let ast_type_id = node + .children + .get(1) + .expect("Cast type not found among cast children"); let ast_type = self.nodes.get_node(*ast_type_id)?; let cast_type = if ast_type.rule == Type::TypeVarchar { // Get the length of the varchar. - let ast_len_id = ast_type.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "Cast has no children. Cast type length node id is not found." - .into(), - ) - })?; + let ast_len_id = ast_type + .children + .first() + .expect("Cast has no children. Cast type length node id is not found."); let ast_len = self.nodes.get_node(*ast_len_id)?; let len = ast_len .value @@ -1352,31 +1320,25 @@ impl Ast for AbstractSyntaxTree { map.add(id, cast_id); } Type::Concat => { - let ast_left_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Concat has no children.".into()) - })?; + let ast_left_id = node.children.first().expect("Concat has no children."); let plan_left_id = plan.as_row(map.get(*ast_left_id)?, &mut rows)?; - let ast_right_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound(Entity::Node, "(right) among concat children".into()) - })?; + let ast_right_id = node + .children + .get(1) + .expect("Right not found among concat children"); let plan_right_id = plan.as_row(map.get(*ast_right_id)?, &mut rows)?; let concat_id = plan.add_concat(plan_left_id, plan_right_id)?; map.add(id, concat_id); } Type::Condition => { - let ast_child_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Condition has no children.".into()) - })?; + let ast_child_id = node.children.first().expect("Condition has no children."); let plan_child_id = map.get(*ast_child_id)?; map.add(id, plan_child_id); } Type::Function => { if let Some((first, mut other)) = node.children.split_first() { let mut is_distinct = false; - let function_name = - self.nodes.get_node(*first)?.value.as_ref().ok_or_else(|| { - SbroadError::NotFound(Entity::Name, "of sql function".into()) - })?; + let function_name = parse_string_value_node(self, *first)?; if let Some(first_id) = other.first() { let rule = &self.nodes.get_node(*first_id)?.rule; match rule { @@ -1417,7 +1379,7 @@ impl Ast for AbstractSyntaxTree { if let Some(kind) = AggregateKind::new(function_name) { let plan_id = plan.add_aggregate_function( - &function_name.to_string(), + function_name, kind, plan_arg_list.clone(), is_distinct, @@ -1473,23 +1435,21 @@ impl Ast for AbstractSyntaxTree { // - kind // - right scan // - condition - let ast_left_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Join has no children.".into()) - })?; + let ast_left_id = node.children.first().expect("Join has no children."); let plan_left_id = map.get(*ast_left_id)?; - let ast_right_id = node.children.get(2).ok_or_else(|| { - SbroadError::NotFound(Entity::Node, "(right) among Join children.".into()) - })?; + let ast_right_id = node + .children + .get(2) + .expect("Right not found among Join children."); let plan_right_id = map.get(*ast_right_id)?; - let ast_cond_id = node.children.get(3).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "(Condition) among Join children".into(), - ) - })?; - let ast_kind_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound(Entity::Node, "(kind) among Join children.".into()) - })?; + let ast_cond_id = node + .children + .get(3) + .expect("Condition not found among Join children"); + let ast_kind_id = node + .children + .get(1) + .expect("Kind not found among Join children."); let ast_kind_node = self.nodes.get_node(*ast_kind_id)?; let kind = match ast_kind_node.rule { Type::LeftJoinKind => JoinKind::LeftOuter, @@ -1510,16 +1470,15 @@ impl Ast for AbstractSyntaxTree { map.add(id, plan_join_id); } Type::Selection | Type::Having => { - let ast_child_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues(format!("{node:?} has no children.")) - })?; + let ast_child_id = node + .children + .first() + .expect("Selection or Having has no children."); let plan_child_id = map.get(*ast_child_id)?; - let ast_filter_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "(Filter) among Selection children".into(), - ) - })?; + let ast_filter_id = node + .children + .get(1) + .expect("Filter not found among Selection children"); let plan_filter_id = map.get(*ast_filter_id)?; let plan_node_id = match &node.rule { Type::Selection => plan.add_select(&[plan_child_id], plan_filter_id)?, @@ -1559,13 +1518,11 @@ impl Ast for AbstractSyntaxTree { let ast_column = self.nodes.get_node(*ast_column_id)?; match ast_column.rule { Type::Column => { - let ast_alias_id = - *ast_column.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "Column has no children.".into(), - ) - })?; - let plan_alias_id = map.get(ast_alias_id)?; + let ast_alias_id = ast_column + .children + .first() + .expect("Column has no children."); + let plan_alias_id = map.get(*ast_alias_id)?; proj_columns.push(plan_alias_id); } Type::Asterisk => { @@ -1612,48 +1569,39 @@ impl Ast for AbstractSyntaxTree { map.add(id, cond_id); } Type::ArithParentheses => { - let ast_child_id = *node.children.first().ok_or_else(|| { - SbroadError::Invalid( - Entity::AST, - Some(format!("ArithParentheses ({id}) have no child!")), - ) - })?; - let plan_child_id = map.get(ast_child_id)?; + let ast_child_id = node + .children + .first() + .expect("ArithParentheses have no child!"); + let plan_child_id = map.get(*ast_child_id)?; arith_expr_with_parentheses_ids.push(plan_child_id); map.add(id, plan_child_id); } Type::Except => { - let ast_left_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Except has no children.".into()) - })?; + let ast_left_id = node.children.first().expect("Except has no children."); let plan_left_id = map.get(*ast_left_id)?; - let ast_right_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound(Entity::Node, "(right) among Except children".into()) - })?; + let ast_right_id = node + .children + .get(1) + .expect("Right not found among Except children"); let plan_right_id = map.get(*ast_right_id)?; let plan_except_id = plan.add_except(plan_left_id, plan_right_id)?; map.add(id, plan_except_id); } Type::UnionAll => { - let ast_left_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Union All has no children.".into()) - })?; + let ast_left_id = node.children.first().expect("Union All has no children."); let plan_left_id = map.get(*ast_left_id)?; - let ast_right_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "(right) among Union All children".into(), - ) - })?; + let ast_right_id = node + .children + .get(1) + .expect("Right not found among Union All children"); let plan_right_id = map.get(*ast_right_id)?; let plan_union_all_id = plan.add_union_all(plan_left_id, plan_right_id)?; map.add(id, plan_union_all_id); } Type::ValuesRow => { // TODO(ars): check that all row elements are constants - let ast_child_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Values Row has no children.".into()) - })?; + let ast_child_id = node.children.first().expect("Values Row has no children."); let plan_child_id = map.get(*ast_child_id)?; let values_row_id = plan.add_values_row(plan_child_id, &mut col_idx)?; map.add(id, values_row_id); @@ -1669,14 +1617,10 @@ impl Ast for AbstractSyntaxTree { map.add(id, plan_values_id); } Type::Update => { - let rel_child_ast_id = *node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Update has no children.".into()) - })?; - let rel_child_id = map.get(rel_child_ast_id)?; - let ast_table_id = *node.children.get(1).ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Update has no children.".into()) - })?; - let ast_table = self.nodes.get_node(ast_table_id)?; + let rel_child_ast_id = node.children.first().expect("Update has no children."); + let rel_child_id = map.get(*rel_child_ast_id)?; + let ast_table_id = node.children.get(1).expect("Update has no children."); + let ast_table = self.nodes.get_node(*ast_table_id)?; if let Type::ScanTable = ast_table.rule { } else { return Err(SbroadError::Invalid( @@ -1686,7 +1630,7 @@ impl Ast for AbstractSyntaxTree { )), )); } - let plan_scan_id = map.get(ast_table_id)?; + let plan_scan_id = map.get(*ast_table_id)?; let plan_scan_node = plan.get_relation_node(plan_scan_id)?; let relation = if let Relational::ScanRelation { relation, .. } = plan_scan_node { @@ -1699,7 +1643,10 @@ impl Ast for AbstractSyntaxTree { )), )); }; - let update_list_id = node.children.get(2).unwrap(); + let update_list_id = node + .children + .get(2) + .expect("Update lise expected as a second child of Update"); let update_list = self.nodes.get_node(*update_list_id)?; // Maps position of column in table to corresponding update expression let mut update_defs: HashMap<ColumnPosition, ExpressionId, RepeatableState> = @@ -1725,9 +1672,15 @@ impl Ast for AbstractSyntaxTree { }); for update_item_id in &update_list.children { let update_item = self.nodes.get_node(*update_item_id)?; - let ast_column_id = update_item.children.first().unwrap(); - let expr_ast_id = *update_item.children.get(1).unwrap(); - let expr_id = map.get(expr_ast_id)?; + let ast_column_id = update_item + .children + .first() + .expect("Column expected as first child of UpdateItem"); + let expr_ast_id = update_item + .children + .get(1) + .expect("Expression expected as second child of UpdateItem"); + let expr_id = map.get(*expr_ast_id)?; if plan.contains_aggregates(expr_id, true)? { return Err(SbroadError::Invalid( Entity::Query, @@ -1738,28 +1691,23 @@ impl Ast for AbstractSyntaxTree { )); } let col = self.nodes.get_node(*ast_column_id)?; - let name = col.value.as_ref().ok_or_else(|| { - SbroadError::NotFound( - Entity::Name, - "of Column among the AST target columns (update)".into(), - ) - })?; + let col_name = parse_string_value_node(self, *ast_column_id)?; if let Type::ColumnName = col.rule { - match names.get(name.as_str()) { + match names.get(col_name) { Some((&ColumnRole::User, pos)) => { if pk_positions.contains(pos) { return Err(SbroadError::Invalid( Entity::Query, Some(format!( "it is illegal to update primary key column: {}", - name + col_name )), )); } if update_defs.contains_key(pos) { return Err(SbroadError::Invalid( Entity::Query, - Some(format!("The same column is specified twice in update list: {}", name)) + Some(format!("The same column is specified twice in update list: {}", col_name)) )); } update_defs.insert(*pos, expr_id); @@ -1768,13 +1716,13 @@ impl Ast for AbstractSyntaxTree { return Err(SbroadError::FailedTo( Action::Update, Some(Entity::Column), - format!("system column {name} cannot be updated"), + format!("system column {col_name} cannot be updated"), )) } None => { return Err(SbroadError::NotFound( Entity::Column, - (*name).to_string(), + (*col_name).to_string(), )) } } @@ -1813,14 +1761,11 @@ impl Ast for AbstractSyntaxTree { (plan_scan_id, table) } Type::DeleteFilter => { - let ast_table_id = - *child_node.children.first().ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "(Table) among DeleteFilter children".into(), - ) - })?; - let plan_scan_id = map.get(ast_table_id)?; + let ast_table_id = child_node + .children + .first() + .expect("Table not found among DeleteFilter children"); + let plan_scan_id = map.get(*ast_table_id)?; let plan_scan_node = plan.get_relation_node(plan_scan_id)?; let table = if let Relational::ScanRelation { relation, .. } = plan_scan_node @@ -1835,13 +1780,10 @@ impl Ast for AbstractSyntaxTree { )), )); }; - let ast_filter_id = - child_node.children.get(1).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "(Expr) among DeleteFilter children".into(), - ) - })?; + let ast_filter_id = child_node + .children + .get(1) + .expect("Expr not found among DeleteFilter children"); let plan_filter_id = map.get(*ast_filter_id)?; let plan_select_id = plan.add_select(&[plan_scan_id], plan_filter_id)?; @@ -1898,9 +1840,7 @@ impl Ast for AbstractSyntaxTree { map.add(id, plan_delete_id); } Type::Insert => { - let ast_table_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Insert has no children.".into()) - })?; + let ast_table_id = node.children.first().expect("Insert has no children."); let ast_table = self.nodes.get_node(*ast_table_id)?; if let Type::Table = ast_table.rule { } else { @@ -1910,16 +1850,12 @@ impl Ast for AbstractSyntaxTree { )); } let relation = - normalize_name_from_sql(ast_table.value.as_ref().ok_or_else(|| { - SbroadError::NotFound(Entity::Name, "of table in the AST".into()) - })?); + normalize_name_from_sql(parse_string_value_node(self, *ast_table_id)?); - let ast_child_id = node.children.get(1).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "(second child) among insert children".into(), - ) - })?; + let ast_child_id = node + .children + .get(1) + .expect("Second child not found among Insert children"); let get_conflict_strategy = |child_idx: usize| -> Result<ConflictStrategy, SbroadError> { let Some(child_id) = node.children.get(child_idx).copied() else { @@ -1950,12 +1886,7 @@ impl Ast for AbstractSyntaxTree { for col_id in &ast_child.children { let col = self.nodes.get_node(*col_id)?; if let Type::ColumnName = col.rule { - selected_col_names.push(col.value.as_ref().ok_or_else(|| { - SbroadError::NotFound( - Entity::Name, - "of Column among the AST target columns (insert)".into(), - ) - })?); + selected_col_names.push(parse_string_value_node(self, *col_id)?); } else { return Err(SbroadError::Invalid( Entity::Type, @@ -1987,12 +1918,10 @@ impl Ast for AbstractSyntaxTree { } } - let ast_rel_child_id = node.children.get(2).ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "(third child) among Insert children".into(), - ) - })?; + let ast_rel_child_id = node + .children + .get(2) + .expect("Third child not found among Insert children"); let plan_rel_child_id = map.get(*ast_rel_child_id)?; let conflict_strategy = get_conflict_strategy(3)?; plan.add_insert( @@ -2012,17 +1941,14 @@ impl Ast for AbstractSyntaxTree { Type::Explain => { plan.mark_as_explain(); - let ast_child_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues("Explain has no children.".into()) - })?; + let ast_child_id = node.children.first().expect("Explain has no children."); map.add(0, map.get(*ast_child_id)?); } Type::SingleQuotedString => { - let ast_child_id = node.children.first().ok_or_else(|| { - SbroadError::UnexpectedNumberOfValues( - "SingleQuotedString has no children.".into(), - ) - })?; + let ast_child_id = node + .children + .first() + .expect("SingleQuotedString has no children."); map.add(id, map.get(*ast_child_id)?); } Type::CountAsterisk => { @@ -2033,9 +1959,8 @@ impl Ast for AbstractSyntaxTree { // Query may have two children: // 1. select | insert | except | .. // 2. Option child - for which no plan node is created - let child_id = map.get(*node.children.first().ok_or_else(|| { - SbroadError::Invalid(Entity::AST, Some("no children for Query rule".into())) - })?)?; + let child_id = + map.get(*node.children.first().expect("no children for Query rule"))?; map.add(id, child_id); } Type::CreateTable => { @@ -2043,24 +1968,35 @@ impl Ast for AbstractSyntaxTree { let plan_id = plan.nodes.push(Node::Ddl(create_sharded_table)); map.add(id, plan_id); } + Type::GrantPrivilege => { + let (grant_type, grantee_name, timeout) = parse_grant_revoke(node, self)?; + let grant_privilege = Acl::GrantPrivilege { + grant_type, + grantee_name, + timeout, + }; + let plan_id = plan.nodes.push(Node::Acl(grant_privilege)); + map.add(id, plan_id); + } + Type::RevokePrivilege => { + let (revoke_type, grantee_name, timeout) = parse_grant_revoke(node, self)?; + let revoke_privilege = Acl::RevokePrivilege { + revoke_type, + grantee_name, + timeout, + }; + let plan_id = plan.nodes.push(Node::Acl(revoke_privilege)); + map.add(id, plan_id); + } Type::DropRole => { - let role_name_id = node.children.first().ok_or_else(|| { - SbroadError::Invalid( - Entity::ParseNode, - Some(String::from("RoleName expected under DropRole node")), - ) - })?; - let role_name_node = self.nodes.get_node(*role_name_id)?; - let role_name = normalize_name_for_space_api( - role_name_node.value.as_ref().ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "role name in the drop role AST".into(), - ) - })?, - ); + let role_name_id = node + .children + .first() + .expect("RoleName expected under DropRole node"); + let role_name = + normalize_name_for_space_api(parse_string_value_node(self, *role_name_id)?); - let mut timeout = default_timeout; + let mut timeout = get_default_timeout(); if let Some(timeout_child_id) = node.children.get(1) { timeout = get_timeout(self, *timeout_child_id)?; } @@ -2073,19 +2009,14 @@ impl Ast for AbstractSyntaxTree { } Type::DropTable => { let mut table_name: String = String::new(); - let mut timeout = default_timeout; + let mut timeout = get_default_timeout(); for child_id in &node.children { let child_node = self.nodes.get_node(*child_id)?; match child_node.rule { Type::DeletedTable => { - table_name = normalize_name_for_space_api( - child_node.value.as_ref().ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "table name in the drop table AST".into(), - ) - })?, - ); + table_name = normalize_name_for_space_api(parse_string_value_node( + self, *child_id, + )?); } Type::Timeout => { timeout = get_timeout(self, *child_id)?; @@ -2109,20 +2040,114 @@ impl Ast for AbstractSyntaxTree { map.add(id, plan_id); } Type::AlterUser => { - let (user_name, password, auth_method, timeout) = - parse_create_alter_user(self, node, default_timeout)?; + let user_name_node_id = node + .children + .first() + .expect("RoleName expected as a first child"); + let user_name = parse_role_name(self, *user_name_node_id)?; + + let alter_option_node_id = node + .children + .get(1) + .expect("Some AlterOption expected as a second child"); + let alter_option_node = self.nodes.get_node(*alter_option_node_id)?; + let alter_option = match alter_option_node.rule { + Type::AlterLogin => AlterOption::Login, + Type::AlterNoLogin => AlterOption::NoLogin, + Type::AlterPassword => { + let pwd_node_id = alter_option_node + .children + .first() + .expect("Password expected as a first child"); + let password = + String::from(parse_string_value_node(self, *pwd_node_id)?); + + let mut auth_method = get_default_auth_method(); + if let Some(auth_method_node_id) = alter_option_node.children.get(1) { + let auth_method_node = self.nodes.get_node(*auth_method_node_id)?; + let auth_method_string_node_id = auth_method_node + .children + .first() + .expect("Method expected under AuthMethod node"); + auth_method = String::from(parse_string_value_node( + self, + *auth_method_string_node_id, + )?); + } + + AlterOption::Password { + password, + auth_method, + } + } + _ => { + return Err(SbroadError::Invalid( + Entity::ParseNode, + Some(String::from("Expected to see concrete alter option")), + )) + } + }; + + let mut timeout = get_default_timeout(); + if let Some(timeout_node_id) = node.children.get(2) { + timeout = get_timeout(self, *timeout_node_id)?; + } + let alter_user = Acl::AlterUser { name: user_name, - password, - auth_method, + alter_option, timeout, }; let plan_id = plan.nodes.push(Node::Acl(alter_user)); map.add(id, plan_id); } Type::CreateUser => { - let (user_name, password, auth_method, timeout) = - parse_create_alter_user(self, node, default_timeout)?; + let mut iter = node.children.iter(); + let user_name_node_id = iter.next().ok_or_else(|| { + SbroadError::Invalid( + Entity::ParseNode, + Some(String::from("RoleName expected as a first child")), + ) + })?; + let user_name = parse_role_name(self, *user_name_node_id)?; + + let pwd_node_id = iter.next().ok_or_else(|| { + SbroadError::Invalid( + Entity::ParseNode, + Some(String::from("Password expected as a second child")), + ) + })?; + let password = String::from(parse_string_value_node(self, *pwd_node_id)?); + + let mut timeout = get_default_timeout(); + let mut auth_method = get_default_auth_method(); + for child_id in iter { + let child_node = self.nodes.get_node(*child_id)?; + match child_node.rule { + Type::Timeout => { + timeout = get_timeout(self, *child_id)?; + } + Type::AuthMethod => { + let auth_method_node_id = child_node + .children + .first() + .expect("Method expected under AuthMethod node"); + auth_method = String::from(parse_string_value_node( + self, + *auth_method_node_id, + )?); + } + _ => { + return Err(SbroadError::Invalid( + Entity::Node, + Some(format!( + "ACL node contains unexpected child: {child_node:?}", + )), + )); + } + } + } + let create_user = Acl::CreateUser { name: user_name, password, @@ -2133,23 +2158,13 @@ impl Ast for AbstractSyntaxTree { map.add(id, plan_id); } Type::DropUser => { - let user_name_id = node.children.first().ok_or_else(|| { - SbroadError::Invalid( - Entity::ParseNode, - Some(String::from("UserName expected under DropUser node")), - ) - })?; - let user_name_node = self.nodes.get_node(*user_name_id)?; - let user_name = normalize_name_for_space_api( - user_name_node.value.as_ref().ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "user name in the drop user AST".into(), - ) - })?, - ); + let user_name_id = node + .children + .first() + .expect("RoleName expected under DropUser node"); + let user_name = parse_role_name(self, *user_name_id)?; - let mut timeout = default_timeout; + let mut timeout = get_default_timeout(); if let Some(timeout_child_id) = node.children.get(1) { timeout = get_timeout(self, *timeout_child_id)?; } @@ -2161,23 +2176,13 @@ impl Ast for AbstractSyntaxTree { map.add(id, plan_id); } Type::CreateRole => { - let role_name_id = node.children.first().ok_or_else(|| { - SbroadError::Invalid( - Entity::ParseNode, - Some(String::from("RoleName expected under CreateRole node")), - ) - })?; - let role_name_node = self.nodes.get_node(*role_name_id)?; - let role_name = normalize_name_for_space_api( - role_name_node.value.as_ref().ok_or_else(|| { - SbroadError::NotFound( - Entity::Node, - "role name in the create role AST".into(), - ) - })?, - ); + let role_name_id = node + .children + .first() + .expect("RoleName expected under CreateRole node"); + let role_name = parse_role_name(self, *role_name_id)?; - let mut timeout = default_timeout; + let mut timeout = get_default_timeout(); if let Some(timeout_child_id) = node.children.get(1) { timeout = get_timeout(self, *timeout_child_id)?; } @@ -2190,6 +2195,9 @@ impl Ast for AbstractSyntaxTree { } Type::Add | Type::AliasName + | Type::AlterLogin + | Type::AlterNoLogin + | Type::AlterPassword | Type::AuthMethod | Type::ChapSha1 | Type::Columns @@ -2230,6 +2238,22 @@ impl Ast for AbstractSyntaxTree { | Type::PrimaryKey | Type::PrimaryKeyColumn | Type::RoleName + | Type::PrivBlockPrivilege + | Type::PrivBlockUser + | Type::PrivBlockSpecificUser + | Type::PrivBlockRole + | Type::PrivBlockSpecificRole + | Type::PrivBlockTable + | Type::PrivBlockSpecificTable + | Type::PrivBlockRolePass + | Type::PrivilegeAlter + | Type::PrivilegeCreate + | Type::PrivilegeDrop + | Type::PrivilegeExecute + | Type::PrivilegeRead + | Type::PrivilegeSession + | Type::PrivilegeUsage + | Type::PrivilegeWrite | Type::ScanName | Type::Select | Type::Sharding @@ -2250,7 +2274,6 @@ impl Ast for AbstractSyntaxTree { | Type::TypeVarchar | Type::UpdateList | Type::UpdateItem - | Type::UserName | Type::Vinyl => {} rule => { return Err(SbroadError::NotImplemented( diff --git a/sbroad-core/src/frontend/sql/ast.rs b/sbroad-core/src/frontend/sql/ast.rs index be74c1e117..137a93db5d 100644 --- a/sbroad-core/src/frontend/sql/ast.rs +++ b/sbroad-core/src/frontend/sql/ast.rs @@ -6,7 +6,6 @@ extern crate pest; use std::collections::{hash_map::Entry, HashMap, HashSet}; -use std::fmt; use std::mem::swap; use pest::iterators::Pair; @@ -30,6 +29,10 @@ pub enum Type { Alias, AliasName, AlterUser, + AlterOption, + AlterLogin, + AlterNoLogin, + AlterPassword, And, ArithmeticExpr, ArithParentheses, @@ -73,6 +76,7 @@ pub enum Type { Function, FunctionName, Global, + GrantPrivilege, Gt, GtEq, GroupBy, @@ -112,8 +116,25 @@ pub enum Type { Primary, PrimaryKey, PrimaryKeyColumn, + PrivBlockPrivilege, + PrivBlockUser, + PrivBlockSpecificUser, + PrivBlockRole, + PrivBlockSpecificRole, + PrivBlockTable, + PrivBlockSpecificTable, + PrivBlockRolePass, + PrivilegeAlter, + PrivilegeCreate, + PrivilegeDrop, + PrivilegeExecute, + PrivilegeRead, + PrivilegeSession, + PrivilegeUsage, + PrivilegeWrite, Projection, Reference, + RevokePrivilege, RoleName, Row, Scan, @@ -147,7 +168,6 @@ pub enum Type { UpdateList, UpdateItem, Unsigned, - UserName, Value, Values, ValuesRow, @@ -164,6 +184,9 @@ impl Type { Rule::Alias => Ok(Type::Alias), Rule::AliasName => Ok(Type::AliasName), Rule::AlterUser => Ok(Type::AlterUser), + Rule::AlterLogin => Ok(Type::AlterLogin), + Rule::AlterNoLogin => Ok(Type::AlterNoLogin), + Rule::AlterPassword => Ok(Type::AlterPassword), Rule::And => Ok(Type::And), Rule::ArithmeticExpr => Ok(Type::ArithmeticExpr), Rule::ArithParentheses => Ok(Type::ArithParentheses), @@ -210,6 +233,7 @@ impl Type { Rule::Engine => Ok(Type::Engine), Rule::FunctionName => Ok(Type::FunctionName), Rule::Global => Ok(Type::Global), + Rule::GrantPrivilege => Ok(Type::GrantPrivilege), Rule::GroupBy => Ok(Type::GroupBy), Rule::GroupingElement => Ok(Type::GroupingElement), Rule::Gt => Ok(Type::Gt), @@ -246,8 +270,25 @@ impl Type { Rule::Primary => Ok(Type::Primary), Rule::PrimaryKey => Ok(Type::PrimaryKey), Rule::PrimaryKeyColumn => Ok(Type::PrimaryKeyColumn), + Rule::PrivBlockPrivilege => Ok(Type::PrivBlockPrivilege), + Rule::PrivBlockUser => Ok(Type::PrivBlockUser), + Rule::PrivBlockSpecificUser => Ok(Type::PrivBlockSpecificUser), + Rule::PrivBlockRole => Ok(Type::PrivBlockRole), + Rule::PrivBlockSpecificRole => Ok(Type::PrivBlockSpecificRole), + Rule::PrivBlockTable => Ok(Type::PrivBlockTable), + Rule::PrivBlockSpecificTable => Ok(Type::PrivBlockSpecificTable), + Rule::PrivBlockRolePass => Ok(Type::PrivBlockRolePass), + Rule::PrivilegeAlter => Ok(Type::PrivilegeAlter), + Rule::PrivilegeCreate => Ok(Type::PrivilegeCreate), + Rule::PrivilegeDrop => Ok(Type::PrivilegeDrop), + Rule::PrivilegeExecute => Ok(Type::PrivilegeExecute), + Rule::PrivilegeRead => Ok(Type::PrivilegeRead), + Rule::PrivilegeSession => Ok(Type::PrivilegeSession), + Rule::PrivilegeUsage => Ok(Type::PrivilegeUsage), + Rule::PrivilegeWrite => Ok(Type::PrivilegeWrite), Rule::Projection => Ok(Type::Projection), Rule::Reference => Ok(Type::Reference), + Rule::RevokePrivilege => Ok(Type::RevokePrivilege), Rule::RoleName => Ok(Type::RoleName), Rule::Row => Ok(Type::Row), Rule::Scan => Ok(Type::Scan), @@ -281,7 +322,6 @@ impl Type { Rule::UpdateList => Ok(Type::UpdateList), Rule::UpdateItem => Ok(Type::UpdateItem), Rule::Unsigned => Ok(Type::Unsigned), - Rule::UserName => Ok(Type::UserName), Rule::Value => Ok(Type::Value), Rule::Values => Ok(Type::Values), Rule::ValuesRow => Ok(Type::ValuesRow), @@ -294,142 +334,6 @@ impl Type { } } -impl fmt::Display for Type { - #[allow(clippy::too_many_lines)] - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let p = match self { - Type::Add => "Add".to_string(), - Type::Addition => "Addition".to_string(), - Type::Alias => "Alias".to_string(), - Type::AliasName => "AliasName".to_string(), - Type::AlterUser => "AlterUser".to_string(), - Type::And => "And".to_string(), - Type::ArithmeticExpr => "ArithmeticExpr".to_string(), - Type::ArithParentheses => "ArithParentheses".to_string(), - Type::Asterisk => "Asterisk".to_string(), - Type::AuthMethod => "AuthMethod".to_string(), - Type::Between => "Between".to_string(), - Type::Cast => "Cast".to_string(), - Type::ChapSha1 => "ChapSha1".to_string(), - Type::Cmp => "Cmp".to_string(), - Type::CreateRole => "CreateRole".to_string(), - Type::CreateTable => "CreateTable".to_string(), - Type::CreateUser => "CreateUser".to_string(), - Type::Column => "Column".to_string(), - Type::ColumnDef => "ColumnDef".to_string(), - Type::ColumnDefIsNull => "ColumnDefIsNull".to_string(), - Type::ColumnDefName => "ColumnDefName".to_string(), - Type::ColumnDefType => "ColumnDefType".to_string(), - Type::Columns => "Columns".to_string(), - Type::ColumnName => "ColumnName".to_string(), - Type::Concat => "Concat".to_string(), - Type::CountAsterisk => "CountAsterisk".to_string(), - Type::Condition => "Condition".to_string(), - Type::Decimal => "Decimal".to_string(), - Type::Delete => "Delete".to_string(), - Type::DeleteFilter => "DeleteFilter".to_string(), - Type::DeletedTable => "DeletedTable".to_string(), - Type::Distinct => "Distinct".to_string(), - Type::Distribution => "Distribution".to_string(), - Type::Divide => "Divide".to_string(), - Type::Double => "Double".to_string(), - Type::DropRole => "DropRole".to_string(), - Type::DropTable => "DropTable".to_string(), - Type::DropUser => "DropUser".to_string(), - Type::Duration => "Duration".to_string(), - Type::DoReplace => "DoUpdateStrategy".to_string(), - Type::DoNothing => "DoNothingStrategy".to_string(), - Type::DoFail => "DoFailStrategy".to_string(), - Type::Eq => "Eq".to_string(), - Type::Engine => "Engine".to_string(), - Type::Except => "Except".to_string(), - Type::Exists => "Exists".to_string(), - Type::Explain => "Explain".to_string(), - Type::False => "False".to_string(), - Type::Function => "Function".to_string(), - Type::FunctionName => "FunctionName".to_string(), - Type::Global => "Global".to_string(), - Type::Gt => "Gt".to_string(), - Type::GtEq => "GtEq".to_string(), - Type::Having => "Having".to_string(), - Type::InnerJoinKind => "inner".to_string(), - Type::In => "In".to_string(), - Type::Join => "Join".to_string(), - Type::Insert => "Insert".to_string(), - Type::Integer => "Integer".to_string(), - Type::IsNull => "IsNull".to_string(), - Type::Ldap => "Ldap".to_string(), - Type::Length => "Length".to_string(), - Type::LeftJoinKind => "left".to_string(), - Type::Lt => "Lt".to_string(), - Type::LtEq => "LtEq".to_string(), - Type::Md5 => "Md5".to_string(), - Type::Memtx => "Memtx".to_string(), - Type::Multiplication => "Multiplication".to_string(), - Type::Multiply => "Multiply".to_string(), - Type::Name => "Name".to_string(), - Type::NewTable => "NewTable".to_string(), - Type::Not => "Not".to_string(), - Type::NotEq => "NotEq".to_string(), - Type::NotFlag => "NotFlag".to_string(), - Type::Null => "Null".to_string(), - Type::Or => "Or".to_string(), - Type::Parameter => "Parameter".to_string(), - Type::Parentheses => "Parentheses".to_string(), - Type::Password => "Password".to_string(), - Type::Primary => "Primary".to_string(), - Type::PrimaryKey => "PrimaryKey".to_string(), - Type::PrimaryKeyColumn => "PrimaryKeyColumn".to_string(), - Type::Projection => "Projection".to_string(), - Type::Query => "Query".to_string(), - Type::Reference => "Reference".to_string(), - Type::RoleName => "RoleName".to_string(), - Type::Row => "Row".to_string(), - Type::Scan => "Scan".to_string(), - Type::ScanName => "ScanName".to_string(), - Type::ScanTable => "ScanTable".to_string(), - Type::Select => "Select".to_string(), - Type::Selection => "Selection".to_string(), - Type::Sharding => "Sharding".to_string(), - Type::ShardingColumn => "ShardingColumn".to_string(), - Type::String => "String".to_string(), - Type::SingleQuotedString => "SingleQuotedString".to_string(), - Type::SqlVdbeMaxSteps => "sql_vdbe_max_steps".to_string(), - Type::SubQuery => "SubQuery".to_string(), - Type::Subtract => "Subtract".to_string(), - Type::Table => "Table".to_string(), - Type::TargetColumns => "TargetColumns".to_string(), - Type::Timeout => "Timeout".to_string(), - Type::True => "True".to_string(), - Type::TypeAny => "TypeAny".to_string(), - Type::TypeBool => "TypeBool".to_string(), - Type::TypeDecimal => "TypeDecimal".to_string(), - Type::TypeDouble => "TypeDouble".to_string(), - Type::TypeInt => "TypeInt".to_string(), - Type::TypeNumber => "TypeNumber".to_string(), - Type::TypeScalar => "TypeScalar".to_string(), - Type::TypeString => "TypeString".to_string(), - Type::TypeText => "TypeText".to_string(), - Type::TypeUnsigned => "TypeUnsigned".to_string(), - Type::TypeVarchar => "TypeVarchar".to_string(), - Type::UnionAll => "UnionAll".to_string(), - Type::Update => "Update".to_string(), - Type::UpdateItem => "UpdateItem".to_string(), - Type::UpdateList => "UpdateList".to_string(), - Type::UserName => "UserName".to_string(), - Type::Unsigned => "Unsigned".to_string(), - Type::Value => "Value".to_string(), - Type::Values => "Values".to_string(), - Type::ValuesRow => "ValuesRow".to_string(), - Type::Vinyl => "Vinyl".to_string(), - Type::VTableMaxRows => "vtable_max_rows".to_string(), - Type::GroupBy => "GroupBy".to_string(), - Type::GroupingElement => "GroupingElement".to_string(), - }; - write!(f, "{p}") - } -} - /// Parse node is a wrapper over the pest pair. #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub struct ParseNode { diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest index 380270c35d..72d11a80ed 100644 --- a/sbroad-core/src/frontend/sql/query.pest +++ b/sbroad-core/src/frontend/sql/query.pest @@ -1,25 +1,51 @@ Command = _{ SOI ~ (ExplainQuery | Query | DDL | ACL) ~ EOF } -ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser } +ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivilege | RevokePrivilege } + /// Represents both RoleName and UserName. + RoleName = @{ Name } CreateUser = { - ^"create" ~ ^"user" ~ UserName ~ (^"with")? ~ ^"password" ~ PasswordString ~ + ^"create" ~ ^"user" ~ RoleName ~ (^"with")? ~ ^"password" ~ PasswordString ~ AuthMethod? ~ Option? } PasswordString = _{ "'" ~ Password ~ "'" } Password = @{ String } AlterUser = { - ^"alter" ~ ^"user" ~ UserName ~ (^"with")? ~ ^"password" ~ PasswordString ~ - AuthMethod? ~ Option? + ^"alter" ~ ^"user" ~ RoleName ~ (^"with")? ~ AlterOption ~ Option? } + AlterOption = _{ AlterLogin | AlterNoLogin | AlterPassword } + AlterLogin = { ^"login" } + AlterNoLogin = { ^"nologin" } + AlterPassword = { ^"password" ~ PasswordString ~ AuthMethod? } AuthMethod = { ^"using" ~ (ChapSha1 | Md5 | Ldap) } ChapSha1 = { ^"chap-sha1" } Md5 = { ^"md5" } Ldap = { ^"ldap" } - DropUser = { ^"drop" ~ ^"user" ~ UserName ~ Option? } - UserName = @{ Name } + DropUser = { ^"drop" ~ ^"user" ~ RoleName ~ Option? } CreateRole = { ^"create" ~ ^"role" ~ RoleName ~ Option? } - RoleName = @{ Name } DropRole = { ^"drop" ~ ^"role" ~ RoleName ~ Option? } + GrantPrivilege = { ^"grant" ~ PrivBlock ~ ^"to" ~ RoleName ~ Option? } + RevokePrivilege = { ^"revoke" ~ PrivBlock ~ ^"from" ~ RoleName ~ Option? } + PrivBlock = _{ PrivBlockPrivilege | PrivBlockRolePass } + PrivBlockPrivilege = {Privilege ~ (PrivBlockUser | PrivBlockSpecificUser | PrivBlockRole + | PrivBlockSpecificRole | PrivBlockTable | PrivBlockSpecificTable)} + PrivBlockUser = { ^"user" } + PrivBlockSpecificUser = { ^"on" ~ ^"user" ~ RoleName } + PrivBlockRole = { ^"role" } + PrivBlockSpecificRole = { ^"on" ~ ^"role" ~ RoleName } + PrivBlockTable = { ^"table" } + PrivBlockSpecificTable = { ^"on" ~ ^"table" ~ Table } + PrivBlockRolePass = { RoleName } + Privilege = _{ PrivilegeRead | PrivilegeWrite | PrivilegeExecute | + PrivilegeCreate | PrivilegeAlter | PrivilegeDrop | + PrivilegeSession | PrivilegeUsage } + PrivilegeAlter = { ^"alter" } + PrivilegeCreate = { ^"create" } + PrivilegeDrop = { ^"drop" } + PrivilegeExecute = { ^"execute" } + PrivilegeRead = { ^"read" } + PrivilegeSession = { ^"session" } + PrivilegeUsage = { ^"usage" } + PrivilegeWrite = { ^"write" } DDL = _{ CreateTable | DropTable } CreateTable = { diff --git a/sbroad-core/src/ir/acl.rs b/sbroad-core/src/ir/acl.rs index 029d512862..339c9e1d46 100644 --- a/sbroad-core/src/ir/acl.rs +++ b/sbroad-core/src/ir/acl.rs @@ -2,6 +2,152 @@ use crate::ir::{Entity, Node, Plan, SbroadError}; use serde::{Deserialize, Serialize}; use tarantool::decimal::Decimal; +::tarantool::define_str_enum! { + /// Revoked or granted privilege. + pub enum Privilege { + Read = "read", + Write = "write", + Execute = "execute", + Create = "create", + Alter = "alter", + Drop = "drop", + Session = "session", + Usage = "usage", + } +} + +/// Helper enum representing one of possible revoking/granting options: +/// * Table: for specific table. +/// * Universe: for any object supporting passed privilege. +/// * Role: for making user/role a part of another role. +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub enum GrantRevokeType { + User { + privilege: Privilege, + }, + SpecificUser { + privilege: Privilege, + user_name: String, + }, + Role { + privilege: Privilege, + }, + SpecificRole { + privilege: Privilege, + role_name: String, + }, + Table { + privilege: Privilege, + }, + SpecificTable { + privilege: Privilege, + table_name: String, + }, + RolePass { + role_name: String, + }, +} + +/// Check passed privilege is in the list of accepted privileges. +fn check_privilege(privilege: Privilege, accepted: &[Privilege]) -> Result<(), SbroadError> { + if !accepted.contains(&privilege) { + return Err(SbroadError::Invalid( + Entity::Privilege, + Some(format!("Supported privileges are: {accepted:?}")), + )); + } + Ok(()) +} + +impl GrantRevokeType { + /// # Errors + /// - Unacceptable privilege for user was passed. + pub fn user(privilege: Privilege) -> Result<Self, SbroadError> { + check_privilege( + privilege, + &[Privilege::Create, Privilege::Alter, Privilege::Drop], + )?; + Ok(Self::User { privilege }) + } + + /// # Errors + /// - Unacceptable privilege for specific user was passed. + pub fn specific_user(privilege: Privilege, user_name: String) -> Result<Self, SbroadError> { + check_privilege(privilege, &[Privilege::Alter, Privilege::Drop])?; + Ok(Self::SpecificUser { + privilege, + user_name, + }) + } + + /// # Errors + /// - Unacceptable privilege for role was passed. + pub fn role(privilege: Privilege) -> Result<Self, SbroadError> { + check_privilege(privilege, &[Privilege::Create, Privilege::Drop])?; + Ok(Self::Role { privilege }) + } + + /// # Errors + /// - Unacceptable privilege for specific role was passed. + pub fn specific_role(privilege: Privilege, role_name: String) -> Result<Self, SbroadError> { + check_privilege(privilege, &[Privilege::Drop])?; + Ok(Self::SpecificRole { + privilege, + role_name, + }) + } + + /// # Errors + /// - Unacceptable privilege for table was passed. + pub fn table(privilege: Privilege) -> Result<Self, SbroadError> { + check_privilege( + privilege, + &[ + Privilege::Read, + Privilege::Write, + Privilege::Create, + Privilege::Alter, + Privilege::Drop, + ], + )?; + Ok(Self::Table { privilege }) + } + + /// # Errors + /// - Unacceptable privilege for specific table was passed. + pub fn specific_table(privilege: Privilege, table_name: String) -> Result<Self, SbroadError> { + check_privilege( + privilege, + &[ + Privilege::Read, + Privilege::Write, + Privilege::Alter, + Privilege::Drop, + ], + )?; + Ok(Self::SpecificTable { + privilege, + table_name, + }) + } + + #[must_use] + pub fn role_pass(role_name: String) -> Self { + Self::RolePass { role_name } + } +} + +/// Type of ALTER USER command execution. +#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] +pub enum AlterOption { + Login, + NoLogin, + Password { + password: String, + auth_method: String, + }, +} + #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub enum Acl { DropRole { @@ -24,8 +170,17 @@ pub enum Acl { }, AlterUser { name: String, - password: String, - auth_method: String, + alter_option: AlterOption, + timeout: Decimal, + }, + GrantPrivilege { + grant_type: GrantRevokeType, + grantee_name: String, + timeout: Decimal, + }, + RevokePrivilege { + revoke_type: GrantRevokeType, + grantee_name: String, timeout: Decimal, }, } @@ -41,7 +196,9 @@ impl Acl { | Acl::DropUser { ref timeout, .. } | Acl::CreateRole { ref timeout, .. } | Acl::AlterUser { ref timeout, .. } - | Acl::CreateUser { ref timeout, .. } => timeout, + | Acl::CreateUser { ref timeout, .. } + | Acl::RevokePrivilege { ref timeout, .. } + | Acl::GrantPrivilege { ref timeout, .. } => timeout, } .to_string() .parse() -- GitLab