From c80357889018a04443ecdf01f6762481c23053a5 Mon Sep 17 00:00:00 2001
From: Kaitmazian Maksim <m.kaitmazian@picodata.io>
Date: Sat, 10 Feb 2024 22:47:25 +0300
Subject: [PATCH] feat: implement SQL support for grant procedure

---
 doc/sql/query.ebnf                      |  2 ++
 sbroad-core/src/frontend/sql.rs         |  9 +++++++
 sbroad-core/src/frontend/sql/query.pest |  5 +++-
 sbroad-core/src/ir/acl.rs               | 35 +++++++++++++++++++++++++
 4 files changed, 50 insertions(+), 1 deletion(-)

diff --git a/doc/sql/query.ebnf b/doc/sql/query.ebnf
index 388dfdd1d0..6632d84c37 100644
--- a/doc/sql/query.ebnf
+++ b/doc/sql/query.ebnf
@@ -13,6 +13,8 @@ privilege ::= (('CREATE' | 'ALTER' | 'DROP') 'USER')
               | ('DROP' 'ON' 'ROLE' (role | user))
               | (('READ' | 'WRITE' | 'CREATE' | 'ALTER' | 'DROP') 'TABLE')
               | (('ALTER' | 'DROP' | 'READ' | 'WRITE') 'ON' 'TABLE'? table)
+              | (('CREATE' | 'EXECUTE') 'PROCEDURE')
+              | ('EXECUTE' 'PROCEDURE' procedure ('(' type (',' type)* ')')?)
               | role
 create_procedure ::= 'CREATE PROCEDURE' procedure '(' type (',' type)* ')'
                      ('language' 'SQL')? (('as' '$$' body '$$') | ('begin' 'atomic' body 'end'))
diff --git a/sbroad-core/src/frontend/sql.rs b/sbroad-core/src/frontend/sql.rs
index bb5cd6f798..453e945ec5 100644
--- a/sbroad-core/src/frontend/sql.rs
+++ b/sbroad-core/src/frontend/sql.rs
@@ -655,6 +655,15 @@ fn parse_grant_revoke(
                     let table_name = parse_identifier(ast, *table_node_id)?;
                     GrantRevokeType::specific_table(privilege, table_name)?
                 }
+                Rule::PrivBlockProcedure => GrantRevokeType::procedure(privilege)?,
+                Rule::PrivBlockSpecificProcedure => {
+                    let proc_node_id = inner_privilege_block_node.children.first().expect(
+                        "Expected to see Name as a first child of PrivBlockSpecificProcedure",
+                    );
+                    let proc_node = ast.nodes.get_node(*proc_node_id)?;
+                    let (proc_name, proc_params) = parse_proc_with_optional_params(ast, proc_node)?;
+                    GrantRevokeType::specific_procedure(privilege, proc_name, proc_params)?
+                }
                 _ => {
                     return Err(SbroadError::Invalid(
                         Entity::ParseNode,
diff --git a/sbroad-core/src/frontend/sql/query.pest b/sbroad-core/src/frontend/sql/query.pest
index ba399ce5e3..ae0fd58c0e 100644
--- a/sbroad-core/src/frontend/sql/query.pest
+++ b/sbroad-core/src/frontend/sql/query.pest
@@ -30,7 +30,8 @@ ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivil
     RevokePrivilege = { ^"revoke" ~ PrivBlock ~ ^"from" ~ Identifier ~ TimeoutOption? }
         PrivBlock = _{ PrivBlockPrivilege | PrivBlockRolePass }
             PrivBlockPrivilege = {Privilege ~ (PrivBlockUser | PrivBlockSpecificUser | PrivBlockRole
-                                                | PrivBlockSpecificRole | PrivBlockTable | PrivBlockSpecificTable)}
+                                                | PrivBlockSpecificRole | PrivBlockTable | PrivBlockSpecificTable
+                                                | PrivBlockProcedure | PrivBlockSpecificProcedure)}
             PrivBlockUser = { ^"user" }
             PrivBlockSpecificUser = { ^"on" ~ ^"user" ~ Identifier }
             PrivBlockRole = { ^"role" }
@@ -38,6 +39,8 @@ ACL = _{ DropRole | DropUser | CreateRole | CreateUser | AlterUser | GrantPrivil
             PrivBlockTable = { ^"table" }
             PrivBlockSpecificTable = { ^"on" ~ ^"table" ~ Table }
             PrivBlockRolePass = { Identifier }
+            PrivBlockProcedure = { ^"procedure" }
+            PrivBlockSpecificProcedure = { ^"on" ~ ^"procedure" ~ ProcWithOptionalParams }
         Privilege = _{ PrivilegeRead | PrivilegeWrite | PrivilegeExecute |
                       PrivilegeCreate | PrivilegeAlter | PrivilegeDrop |
                       PrivilegeSession | PrivilegeUsage }
diff --git a/sbroad-core/src/ir/acl.rs b/sbroad-core/src/ir/acl.rs
index 339c9e1d46..1bfb77377e 100644
--- a/sbroad-core/src/ir/acl.rs
+++ b/sbroad-core/src/ir/acl.rs
@@ -2,6 +2,8 @@ use crate::ir::{Entity, Node, Plan, SbroadError};
 use serde::{Deserialize, Serialize};
 use tarantool::decimal::Decimal;
 
+use super::ddl::ParamDef;
+
 ::tarantool::define_str_enum! {
     /// Revoked or granted privilege.
     pub enum Privilege {
@@ -43,6 +45,14 @@ pub enum GrantRevokeType {
         privilege: Privilege,
         table_name: String,
     },
+    Procedure {
+        privilege: Privilege,
+    },
+    SpecificProcedure {
+        privilege: Privilege,
+        proc_name: String,
+        proc_params: Option<Vec<ParamDef>>,
+    },
     RolePass {
         role_name: String,
     },
@@ -131,6 +141,31 @@ impl GrantRevokeType {
         })
     }
 
+    /// # Errors
+    /// - Unacceptable privilege for procedure was passed.
+    pub fn procedure(privilege: Privilege) -> Result<Self, SbroadError> {
+        check_privilege(
+            privilege,
+            &[Privilege::Create, Privilege::Drop, Privilege::Execute],
+        )?;
+        Ok(Self::Procedure { privilege })
+    }
+
+    /// # Errors
+    /// - Unacceptable privilege for specific procedure was passed.
+    pub fn specific_procedure(
+        privilege: Privilege,
+        proc_name: String,
+        proc_params: Option<Vec<ParamDef>>,
+    ) -> Result<Self, SbroadError> {
+        check_privilege(privilege, &[Privilege::Drop, Privilege::Execute])?;
+        Ok(Self::SpecificProcedure {
+            privilege,
+            proc_name,
+            proc_params,
+        })
+    }
+
     #[must_use]
     pub fn role_pass(role_name: String) -> Self {
         Self::RolePass { role_name }
-- 
GitLab