From 0718595808be5535b0fbcd4eec476252b1d8e339 Mon Sep 17 00:00:00 2001 From: EmirVildanov <reddog201030@gmail.com> Date: Mon, 18 Sep 2023 17:51:01 +0300 Subject: [PATCH] feat: implement CREATE USER --- doc/sql/query.ebnf | 3 +- .../test_app/test/integration/acl_test.lua | 15 +++- sbroad-core/src/frontend/sql.rs | 89 +++++++++++++++++++ sbroad-core/src/frontend/sql/ast.rs | 28 ++++-- sbroad-core/src/frontend/sql/query.pest | 12 ++- sbroad-core/src/ir/acl.rs | 13 ++- 6 files changed, 150 insertions(+), 10 deletions(-) diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf index a859f97e30..d6723d5b89 100644 --- a/doc/sql/query.ebnf +++ b/doc/sql/query.ebnf @@ -3,8 +3,9 @@ EXPLAIN ::= 'EXPLAIN' ( DML | DQL ) DQL ::= (SELECT | SELECT UNION ALL SELECT | SELECT (EXCEPT 'DISTINCT'? ) SELECT ) Options? DML ::= (DELETE | INSERT) Options? DDL ::= CreateTable | DropTable -ACL ::= DropUser +ACL ::= CreateUser | DropUser 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)')')? 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 d9256d69f8..d77db6fc99 100644 --- a/sbroad-cartridge/test_app/test/integration/acl_test.lua +++ b/sbroad-cartridge/test_app/test/integration/acl_test.lua @@ -28,4 +28,17 @@ g.test_drop_user = function() string.format("%s", err), [[Sbroad Error: ACL queries are not supported]] ) -end \ No newline at end of file +end + +g.test_create_user = function() + local api = cluster:server("api-1").net_box + + local _, err = api:call( + "sbroad.execute", + { [[ CREATE USER "user" WITH PASSWORD '123' USING MD5 ]], {} } + ) + t.assert_equals( + string.format("%s", err), + [[Sbroad Error: ACL queries are not supported]] + ) +end diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs index f8157bb50a..af3d400ddf 100644 --- a/sbroad-core/src/frontend/sql.rs +++ b/sbroad-core/src/frontend/sql.rs @@ -1915,6 +1915,90 @@ impl Ast for AbstractSyntaxTree { let plan_id = plan.nodes.push(Node::Ddl(drop_table)); map.add(id, plan_id); } + Type::CreateUser => { + 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 under CreateUser 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 create user AST".into(), + ) + })?, + ); + // Password + let pwd_id = iter.next().ok_or_else(|| { + SbroadError::Invalid( + Entity::ParseNode, + Some(String::from("Password expected under CreateUser node")), + ) + })?; + let pwd_node = self.nodes.get_node(*pwd_id)?; + let password: String = pwd_node + .value + .as_ref() + .ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + "password in the create user AST".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 = self.nodes.get_node(*child_id)?; + match child_node.rule { + Type::Timeout => { + timeout = get_timeout(self, *child_id)?; + } + Type::AuthMethod => { + let method_id = child_node.children.first().ok_or_else(|| { + SbroadError::Invalid( + Entity::ParseNode, + Some(String::from("Method expected under AuthMethod node")), + ) + })?; + let method_node = self.nodes.get_node(*method_id)?; + auth_method = method_node + .value + .as_ref() + .ok_or_else(|| { + SbroadError::NotFound( + Entity::Node, + "auth method in the create user AST".into(), + ) + })? + .to_string(); + } + _ => { + return Err(SbroadError::Invalid( + Entity::Node, + Some(format!( + "AST create user node {:?} contains unexpected children", + child_node, + )), + )); + } + } + } + let create_user = Acl::CreateUser { + name: user_name, + password, + auth_method, + timeout, + }; + let plan_id = plan.nodes.push(Node::Acl(create_user)); + map.add(id, plan_id); + } Type::DropUser => { let user_name_id = node.children.first().ok_or_else(|| { SbroadError::Invalid( @@ -1945,6 +2029,8 @@ impl Ast for AbstractSyntaxTree { } Type::Add | Type::AliasName + | Type::AuthMethod + | Type::ChapSha1 | Type::Columns | Type::ColumnDef | Type::ColumnDefName @@ -1968,15 +2054,18 @@ impl Ast for AbstractSyntaxTree { | Type::GtEq | Type::In | Type::InnerJoinKind + | Type::Ldap | Type::LeftJoinKind | Type::Length | Type::Lt | Type::LtEq + | Type::Md5 | Type::Memtx | Type::Multiply | Type::NewTable | Type::NotEq | Type::NotFlag + | Type::Password | Type::PrimaryKey | Type::PrimaryKeyColumn | Type::ScanName diff --git a/sbroad-core/src/frontend/sql/ast.rs b/sbroad-core/src/frontend/sql/ast.rs index 9d87f2110a..f6cefa94fc 100644 --- a/sbroad-core/src/frontend/sql/ast.rs +++ b/sbroad-core/src/frontend/sql/ast.rs @@ -33,10 +33,13 @@ pub enum Type { ArithmeticExpr, ArithParentheses, Asterisk, + AuthMethod, Between, Cast, + ChapSha1, Cmp, CreateTable, + CreateUser, Column, Columns, ColumnDef, @@ -82,9 +85,11 @@ pub enum Type { DoReplace, DoNothing, DoFail, + Ldap, + LeftJoinKind, Lt, LtEq, - LeftJoinKind, + Md5, Memtx, Multiplication, Multiply, @@ -95,6 +100,7 @@ pub enum Type { NotFlag, Null, Or, + Password, Query, SqlVdbeMaxSteps, VTableMaxRows, @@ -156,10 +162,13 @@ impl Type { Rule::ArithmeticExpr => Ok(Type::ArithmeticExpr), Rule::ArithParentheses => Ok(Type::ArithParentheses), Rule::Asterisk => Ok(Type::Asterisk), + Rule::AuthMethod => Ok(Type::AuthMethod), Rule::Between => Ok(Type::Between), Rule::Cast => Ok(Type::Cast), + Rule::ChapSha1 => Ok(Type::ChapSha1), Rule::Cmp => Ok(Type::Cmp), Rule::CreateTable => Ok(Type::CreateTable), + Rule::CreateUser => Ok(Type::CreateUser), Rule::Column => Ok(Type::Column), Rule::ColumnDef => Ok(Type::ColumnDef), Rule::ColumnDefName => Ok(Type::ColumnDefName), @@ -204,10 +213,12 @@ impl Type { Rule::Insert => Ok(Type::Insert), Rule::Integer => Ok(Type::Integer), Rule::IsNull => Ok(Type::IsNull), + Rule::Ldap => Ok(Type::Ldap), Rule::Length => Ok(Type::Length), Rule::LeftJoinKind => Ok(Type::LeftJoinKind), Rule::Lt => Ok(Type::Lt), Rule::LtEq => Ok(Type::LtEq), + Rule::Md5 => Ok(Type::Md5), Rule::Memtx => Ok(Type::Memtx), Rule::Multiplication => Ok(Type::Multiplication), Rule::Multiply => Ok(Type::Multiply), @@ -218,6 +229,7 @@ impl Type { Rule::NotFlag => Ok(Type::NotFlag), Rule::Null => Ok(Type::Null), Rule::Or => Ok(Type::Or), + Rule::Password => Ok(Type::Password), Rule::Query => Ok(Type::Query), Rule::SqlVdbeMaxSteps => Ok(Type::SqlVdbeMaxSteps), Rule::VTableMaxRows => Ok(Type::VTableMaxRows), @@ -284,10 +296,13 @@ impl fmt::Display for Type { 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::CreateTable => "CreateTable".to_string(), + Type::CreateUser => "CreateUser".to_string(), Type::Column => "Column".to_string(), Type::ColumnDef => "ColumnDef".to_string(), Type::ColumnDefIsNull => "ColumnDefIsNull".to_string(), @@ -330,10 +345,12 @@ impl fmt::Display for Type { 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(), @@ -343,16 +360,15 @@ impl fmt::Display for Type { Type::NotEq => "NotEq".to_string(), Type::NotFlag => "NotFlag".to_string(), Type::Null => "Null".to_string(), - Type::Query => "Query".to_string(), - Type::VTableMaxRows => "vtable_max_rows".to_string(), - Type::SqlVdbeMaxSteps => "sql_vdbe_max_steps".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::Row => "Row".to_string(), Type::Scan => "Scan".to_string(), @@ -363,6 +379,7 @@ impl fmt::Display for Type { 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(), @@ -388,8 +405,9 @@ impl fmt::Display for Type { Type::Unsigned => "Unsigned".to_string(), Type::Value => "Value".to_string(), Type::Values => "Values".to_string(), - Type::Vinyl => "Vinyl".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(), }; diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest index 549119b6c3..09ca8c2943 100644 --- a/sbroad-core/src/frontend/sql/query.pest +++ b/sbroad-core/src/frontend/sql/query.pest @@ -1,6 +1,16 @@ Command = _{ SOI ~ (ExplainQuery | Query | DDL | ACL) ~ EOF } -ACL = _{ DropUser } +ACL = _{ CreateUser | DropUser } + CreateUser = { + ^"create" ~ ^"user" ~ UserName ~ (^"with")? ~ ^"password" ~ PasswordString ~ + AuthMethod? ~ Option? + } + PasswordString = _{ "'" ~ Password ~ "'" } + Password = @{ String } + AuthMethod = { ^"using" ~ (ChapSha1 | Md5 | Ldap) } + ChapSha1 = { ^"chap-sha1" } + Md5 = { ^"md5" } + Ldap = { ^"ldap" } DropUser = { ^"drop" ~ ^"user" ~ UserName ~ Option? } UserName = @{ Name } diff --git a/sbroad-core/src/ir/acl.rs b/sbroad-core/src/ir/acl.rs index 53a702b62b..5aaec8f997 100644 --- a/sbroad-core/src/ir/acl.rs +++ b/sbroad-core/src/ir/acl.rs @@ -4,7 +4,16 @@ use tarantool::decimal::Decimal; #[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)] pub enum Acl { - DropUser { name: String, timeout: Decimal }, + CreateUser { + name: String, + password: String, + auth_method: String, + timeout: Decimal, + }, + DropUser { + name: String, + timeout: Decimal, + }, } impl Acl { @@ -14,7 +23,7 @@ impl Acl { /// - timeout parsing error pub fn timeout(&self) -> Result<f64, SbroadError> { match self { - Acl::DropUser { ref timeout, .. } => timeout, + Acl::CreateUser { ref timeout, .. } | Acl::DropUser { ref timeout, .. } => timeout, } .to_string() .parse() -- GitLab