From e67e742209b0bcade7d5d2ab39ce902d4d61de45 Mon Sep 17 00:00:00 2001
From: EmirVildanov <reddog201030@gmail.com>
Date: Fri, 31 May 2024 10:28:24 +0300
Subject: [PATCH] fix: cover local SQL querying with admin privileges to allow
 VTables interaction

---
 sbroad-core/src/backend/sql/ir.rs             |  1 +
 sbroad-core/src/backend/sql/space.rs          | 58 ++++++++++++-------
 .../engine/helpers/storage/runtime.rs         | 26 +++++++--
 .../src/executor/engine/helpers/vshard.rs     | 36 +++++++-----
 4 files changed, 81 insertions(+), 40 deletions(-)

diff --git a/sbroad-core/src/backend/sql/ir.rs b/sbroad-core/src/backend/sql/ir.rs
index fa28cf3ecf..c74aaf2c2d 100644
--- a/sbroad-core/src/backend/sql/ir.rs
+++ b/sbroad-core/src/backend/sql/ir.rs
@@ -488,6 +488,7 @@ impl ExecutionPlan {
                         // BETWEEN can refer to the same virtual table multiple times.
                         if tmp_spaces.get(&name).is_none() {
                             let space = TmpSpace::initialize(
+                                &name,
                                 self,
                                 name_base,
                                 *motion_id,
diff --git a/sbroad-core/src/backend/sql/space.rs b/sbroad-core/src/backend/sql/space.rs
index 62210140e4..4d6cfb32fd 100644
--- a/sbroad-core/src/backend/sql/space.rs
+++ b/sbroad-core/src/backend/sql/space.rs
@@ -9,6 +9,7 @@ mod prod_imports {
     pub use crate::errors::{Action, Entity};
     pub use crate::ir::value::{EncodedValue, Value};
     pub use tarantool::index::{FieldType, IndexOptions, IndexType, Part};
+    pub use tarantool::session::with_su;
     pub use tarantool::space::{Field, Space, SpaceCreateOptions, SpaceType};
     pub use tarantool::tuple::Tuple;
 }
@@ -23,6 +24,8 @@ pub struct TmpSpace {
     pub name: SmolStr,
 }
 
+pub const ADMIN_ID: u32 = 1;
+
 impl TmpSpace {
     /// Create a temporary space.
     ///
@@ -31,13 +34,13 @@ impl TmpSpace {
     #[allow(unused_variables)]
     #[allow(clippy::too_many_lines)]
     pub fn initialize(
+        table_name: &SmolStr,
         exec_plan: &ExecutionPlan,
         base: &str,
         motion_id: usize,
         buckets: &Buckets,
         engine: &SpaceEngine,
     ) -> Result<TmpSpace, SbroadError> {
-        let name = TmpSpace::generate_space_name(base, motion_id);
         #[cfg(not(feature = "mock"))]
         {
             let vtable = exec_plan.get_motion_vtable(motion_id)?;
@@ -71,30 +74,36 @@ impl TmpSpace {
                 ..Default::default()
             };
 
-            let space = Space::create(&name, &options).map_err(|e| {
-                SbroadError::FailedTo(
-                    Action::Create,
-                    Some(Entity::Space),
-                    format_smolstr!("{name}: {e}"),
-                )
-            })?;
-            let cleanup = |space: Space| match space.drop() {
-                Ok(_) => {}
-                Err(e) => {
-                    error!(
-                        Option::from("Temporary space"),
-                        &format!("Failed to drop {}: {e}", name)
-                    );
+            let space = with_su(ADMIN_ID, || -> Result<Space, SbroadError> {
+                Space::create(table_name, &options).map_err(|e| {
+                    SbroadError::FailedTo(
+                        Action::Create,
+                        Some(Entity::Space),
+                        format_smolstr!("{table_name}: {e}"),
+                    )
+                })
+            })??;
+            let cleanup = |space: Space| {
+                let drop_space_res = with_su(ADMIN_ID, || space.drop());
+                match drop_space_res {
+                    Ok(_) => {}
+                    Err(e) => {
+                        error!(
+                            Option::from("Temporary space"),
+                            &format!("Failed to drop {}: {e}", table_name)
+                        );
+                    }
                 }
             };
-            match space.create_index(&pk_name, &pk) {
+            let create_index_res = with_su(ADMIN_ID, || space.create_index(&pk_name, &pk))?;
+            match create_index_res {
                 Ok(_) => {}
                 Err(e) => {
                     cleanup(space);
                     return Err(SbroadError::FailedTo(
                         Action::Create,
                         Some(Entity::Index),
-                        format_smolstr!("{pk_name} for space {name}: {e}"),
+                        format_smolstr!("{pk_name} for space {table_name}: {e}"),
                     ));
                 }
             }
@@ -116,20 +125,23 @@ impl TmpSpace {
                         ));
                     }
                 };
-                match space.insert(&tuple) {
+                let tuple_insert_res = with_su(ADMIN_ID, || space.insert(&tuple))?;
+                match tuple_insert_res {
                     Ok(_) => {}
                     Err(e) => {
                         cleanup(space);
                         return Err(SbroadError::FailedTo(
                             Action::Insert,
                             Some(Entity::Tuple),
-                            format_smolstr!("tuple {tuple:?} into {name}: {e}"),
+                            format_smolstr!("tuple {tuple:?} into {table_name}: {e}"),
                         ));
                     }
                 }
             }
         }
-        Ok(TmpSpace { name })
+        Ok(TmpSpace {
+            name: table_name.clone(),
+        })
     }
 
     #[must_use]
@@ -147,8 +159,10 @@ impl Drop for TmpSpace {
     fn drop(&mut self) {
         #[cfg(not(feature = "mock"))]
         {
-            if let Some(space) = Space::find(&self.name) {
-                if let Err(e) = space.drop() {
+            let space_find_res = with_su(ADMIN_ID, || Space::find(&self.name));
+            if let Ok(Some(space)) = space_find_res {
+                let space_drop_res = with_su(ADMIN_ID, || space.drop());
+                if let Err(e) = space_drop_res {
                     error!(
                         Option::from("Temporary space"),
                         &format!("Failed to drop {} space: {e}", self.name)
diff --git a/sbroad-core/src/executor/engine/helpers/storage/runtime.rs b/sbroad-core/src/executor/engine/helpers/storage/runtime.rs
index b4bf656e52..2a87af0632 100644
--- a/sbroad-core/src/executor/engine/helpers/storage/runtime.rs
+++ b/sbroad-core/src/executor/engine/helpers/storage/runtime.rs
@@ -2,8 +2,10 @@ use std::any::Any;
 
 use sbroad_proc::otm_child_span;
 use smol_str::{format_smolstr, ToSmolStr};
+use tarantool::session::with_su;
 use tarantool::{tlua::LuaFunction, tuple::Tuple};
 
+use crate::backend::sql::space::ADMIN_ID;
 use crate::ir::ExecuteOptions;
 use crate::{error, errors::SbroadError, ir::value::Value, otm::child_span};
 
@@ -63,7 +65,11 @@ pub fn read_prepared(
         .get("read")
         .ok_or_else(|| SbroadError::LuaError("Lua function `read` not found".into()))?;
 
-    match exec_sql.call_with_args::<Tuple, _>((stmt_id, stmt, params, max_rows, options)) {
+    // `with_su` is used to read from virtual tables previously created by admin.
+    let call_res = with_su(ADMIN_ID, || {
+        exec_sql.call_with_args::<Tuple, _>((stmt_id, stmt, params, max_rows, options))
+    })?;
+    match call_res {
         Ok(v) => Ok(Box::new(v) as Box<dyn Any>),
         Err(e) => {
             error!(Option::from("read_prepared"), &format!("{e:?}"));
@@ -85,7 +91,11 @@ pub fn read_unprepared(
         .get("read")
         .ok_or_else(|| SbroadError::LuaError("Lua function `read` not found".into()))?;
 
-    match exec_sql.call_with_args::<Tuple, _>((0, stmt, params, max_rows, options)) {
+    // `with_su` is used to read from virtual tables previously created by admin.
+    let call_res = with_su(ADMIN_ID, || {
+        exec_sql.call_with_args::<Tuple, _>((0, stmt, params, max_rows, options))
+    })?;
+    match call_res {
         Ok(v) => Ok(Box::new(v) as Box<dyn Any>),
         Err(e) => {
             error!(Option::from("read_unprepared"), &format!("{e:?}"));
@@ -107,7 +117,11 @@ pub fn write_prepared(
         .get("write")
         .ok_or_else(|| SbroadError::LuaError("Lua function `write` not found".into()))?;
 
-    match exec_sql.call_with_args::<Tuple, _>((stmt_id, stmt, params, options)) {
+    // `with_su` is used to read from virtual tables previously created by admin.
+    let call_res = with_su(ADMIN_ID, || {
+        exec_sql.call_with_args::<Tuple, _>((stmt_id, stmt, params, options))
+    })?;
+    match call_res {
         Ok(v) => Ok(Box::new(v) as Box<dyn Any>),
         Err(e) => {
             error!(Option::from("write_prepared"), &format!("{e:?}"));
@@ -128,7 +142,11 @@ pub fn write_unprepared(
         .get("write")
         .ok_or_else(|| SbroadError::LuaError("Lua function `write` not found".into()))?;
 
-    match exec_sql.call_with_args::<Tuple, _>((0, stmt, params, options)) {
+    // `with_su` is used to read from virtual tables previously created by admin.
+    let call_res = with_su(ADMIN_ID, || {
+        exec_sql.call_with_args::<Tuple, _>((0, stmt, params, options))
+    })?;
+    match call_res {
         Ok(v) => Ok(Box::new(v) as Box<dyn Any>),
         Err(e) => {
             error!(Option::from("write_unprepared"), &format!("{e:?}"));
diff --git a/sbroad-core/src/executor/engine/helpers/vshard.rs b/sbroad-core/src/executor/engine/helpers/vshard.rs
index 8446dc79a7..1a5f976640 100644
--- a/sbroad-core/src/executor/engine/helpers/vshard.rs
+++ b/sbroad-core/src/executor/engine/helpers/vshard.rs
@@ -21,8 +21,10 @@ use crate::{
 use rand::{thread_rng, Rng};
 use sbroad_proc::otm_child_span;
 use smol_str::format_smolstr;
+use tarantool::session::with_su;
 use tarantool::{tlua::LuaFunction, tuple::Tuple};
 
+use crate::backend::sql::space::ADMIN_ID;
 use crate::{
     debug, error,
     errors::{Entity, SbroadError},
@@ -57,12 +59,11 @@ fn dql_on_some(
         .ok_or_else(|| SbroadError::LuaError("Lua function `dql_on_some` not found".into()))?;
 
     let waiting_timeout = metadata.waiting_timeout();
-    match exec_sql.call_with_args::<Tuple, _>((
-        rs_ir,
-        is_readonly,
-        waiting_timeout,
-        vtable_max_rows,
-    )) {
+    // `with_su` is used to read from virtual tables previously created by admin.
+    let call_res = with_su(ADMIN_ID, || {
+        exec_sql.call_with_args::<Tuple, _>((rs_ir, is_readonly, waiting_timeout, vtable_max_rows))
+    })?;
+    match call_res {
         Ok(v) => {
             debug!(Option::from("dql_on_some"), &format!("Result: {:?}", &v));
             Ok(Box::new(v))
@@ -92,7 +93,11 @@ fn dml_on_some(
         .ok_or_else(|| SbroadError::LuaError("Lua function `dml_on_some` not found".into()))?;
 
     let waiting_timeout = metadata.waiting_timeout();
-    match exec_sql.call_with_args::<Tuple, _>((rs_ir, is_readonly, waiting_timeout)) {
+    // `with_su` is used to read from virtual tables previously created by admin.
+    let call_res = with_su(ADMIN_ID, || {
+        exec_sql.call_with_args::<Tuple, _>((rs_ir, is_readonly, waiting_timeout))
+    })?;
+    match call_res {
         Ok(v) => Ok(Box::new(v)),
         Err(e) => {
             error!(Option::from("dml_on_some"), &format!("{e:?}"));
@@ -118,12 +123,11 @@ fn dql_on_all(
         .ok_or_else(|| SbroadError::LuaError("Lua function `dql_on_all` not found".into()))?;
 
     let waiting_timeout = metadata.waiting_timeout();
-    match exec_sql.call_with_args::<Tuple, _>((
-        required,
-        optional,
-        waiting_timeout,
-        vtable_max_rows,
-    )) {
+    // `with_su` coverage is used to read from virtual tables previously created by admin.
+    let call_res = with_su(ADMIN_ID, || {
+        exec_sql.call_with_args::<Tuple, _>((required, optional, waiting_timeout, vtable_max_rows))
+    })?;
+    match call_res {
         Ok(v) => {
             debug!(Option::from("dql_on_all"), &format!("Result: {:?}", &v));
             Ok(Box::new(v))
@@ -153,7 +157,11 @@ fn dml_on_all(
         .ok_or_else(|| SbroadError::LuaError("Lua function `dml_on_all` not found".into()))?;
 
     let waiting_timeout = metadata.waiting_timeout();
-    match exec_sql.call_with_args::<Tuple, _>((required, optional, is_readonly, waiting_timeout)) {
+    // `with_su` coverage is used to read from virtual tables previously created by admin.
+    let call_res = with_su(ADMIN_ID, || {
+        exec_sql.call_with_args::<Tuple, _>((required, optional, is_readonly, waiting_timeout))
+    })?;
+    match call_res {
         Ok(v) => Ok(Box::new(v)),
         Err(e) => {
             error!(Option::from("dml_on_all"), &format!("{e:?}"));
-- 
GitLab