mirror of
https://github.com/haexhub/haex-hub.git
synced 2025-12-16 22:20:51 +01:00
Applied cargo clippy fixes to clean up codebase: - Removed unused imports (serde_json::json, std::collections::HashSet) - Removed unused function encode_hex_for_log - Modernized format strings to use inline variables - Fixed clippy warnings for better code quality All changes applied automatically by cargo clippy --fix
210 lines
7.2 KiB
Rust
210 lines
7.2 KiB
Rust
// src-tauri/src/extension/permissions/validator.rs
|
|
|
|
use crate::database::core::{extract_table_names_from_sql, parse_single_statement};
|
|
use crate::database::error::DatabaseError;
|
|
use crate::extension::error::ExtensionError;
|
|
use crate::extension::permissions::manager::PermissionManager;
|
|
use crate::extension::permissions::types::Action;
|
|
use crate::AppState;
|
|
use sqlparser::ast::{Statement, TableFactor, TableObject};
|
|
use tauri::State;
|
|
|
|
pub struct SqlPermissionValidator;
|
|
|
|
impl SqlPermissionValidator {
|
|
/// Prüft ob eine Tabelle zur Extension gehört (basierend auf keyHash Präfix)
|
|
/// Format: {keyHash}_{extensionName}_{tableName}
|
|
fn is_own_table(extension_id: &str, table_name: &str) -> bool {
|
|
// Tabellennamen sind im Format: {keyHash}_{extensionName}_{tableName}
|
|
// extension_id ist der keyHash der Extension
|
|
table_name.starts_with(&format!("{extension_id}_"))
|
|
}
|
|
|
|
/// Validiert ein SQL-Statement gegen die Permissions einer Extension
|
|
pub async fn validate_sql(
|
|
app_state: &State<'_, AppState>,
|
|
extension_id: &str,
|
|
sql: &str,
|
|
) -> Result<(), ExtensionError> {
|
|
let statement = parse_single_statement(sql).map_err(|e| DatabaseError::ParseError {
|
|
reason: e.to_string(),
|
|
sql: sql.to_string(),
|
|
})?;
|
|
|
|
match &statement {
|
|
Statement::Query(_) => {
|
|
Self::validate_read_statement(app_state, extension_id, sql).await
|
|
}
|
|
Statement::Insert(_) | Statement::Update { .. } | Statement::Delete(_) => {
|
|
Self::validate_write_statement(app_state, extension_id, &statement).await
|
|
}
|
|
Statement::CreateTable(_) => {
|
|
Self::validate_create_statement(app_state, extension_id, &statement).await
|
|
}
|
|
Statement::AlterTable { .. } | Statement::Drop { .. } => {
|
|
Self::validate_schema_statement(app_state, extension_id, &statement).await
|
|
}
|
|
_ => Err(ExtensionError::ValidationError {
|
|
reason: format!("Statement type not allowed: {sql}"),
|
|
}),
|
|
}
|
|
}
|
|
|
|
/// Validiert READ-Operationen (SELECT)
|
|
async fn validate_read_statement(
|
|
app_state: &State<'_, AppState>,
|
|
extension_id: &str,
|
|
sql: &str,
|
|
) -> Result<(), ExtensionError> {
|
|
let tables = extract_table_names_from_sql(sql)?;
|
|
|
|
for table_name in tables {
|
|
PermissionManager::check_database_permission(
|
|
app_state,
|
|
extension_id,
|
|
Action::Database(super::types::DbAction::Read),
|
|
&table_name,
|
|
)
|
|
.await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validiert WRITE-Operationen (INSERT, UPDATE, DELETE)
|
|
async fn validate_write_statement(
|
|
app_state: &State<'_, AppState>,
|
|
extension_id: &str,
|
|
statement: &Statement,
|
|
) -> Result<(), ExtensionError> {
|
|
let table_names = Self::extract_table_names_from_statement(statement)?;
|
|
|
|
for table_name in table_names {
|
|
PermissionManager::check_database_permission(
|
|
app_state,
|
|
extension_id,
|
|
Action::Database(super::types::DbAction::ReadWrite),
|
|
&table_name,
|
|
)
|
|
.await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validiert CREATE TABLE
|
|
async fn validate_create_statement(
|
|
app_state: &State<'_, AppState>,
|
|
extension_id: &str,
|
|
statement: &Statement,
|
|
) -> Result<(), ExtensionError> {
|
|
if let Statement::CreateTable(create_table) = statement {
|
|
let table_name = create_table.name.to_string();
|
|
|
|
// Prüfe ob Extension überhaupt CREATE-Rechte hat (z.B. auf "*")
|
|
PermissionManager::check_database_permission(
|
|
app_state,
|
|
extension_id,
|
|
Action::Database(super::types::DbAction::Create),
|
|
&table_name,
|
|
)
|
|
.await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Validiert Schema-Änderungen (ALTER, DROP)
|
|
async fn validate_schema_statement(
|
|
app_state: &State<'_, AppState>,
|
|
extension_id: &str,
|
|
statement: &Statement,
|
|
) -> Result<(), ExtensionError> {
|
|
let table_names = Self::extract_table_names_from_statement(statement)?;
|
|
|
|
for table_name in table_names {
|
|
// ALTER/DROP benötigen WRITE-Rechte
|
|
PermissionManager::check_database_permission(
|
|
app_state,
|
|
extension_id,
|
|
Action::Database(super::types::DbAction::AlterDrop),
|
|
&table_name,
|
|
)
|
|
.await?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
/// Extrahiert alle Tabellennamen aus einem Statement
|
|
fn extract_table_names_from_statement(
|
|
statement: &Statement,
|
|
) -> Result<Vec<String>, ExtensionError> {
|
|
match statement {
|
|
Statement::Insert(insert) => Ok(vec![Self::extract_table_name_from_insert(insert)?]),
|
|
Statement::Update { table, .. } => {
|
|
Ok(vec![Self::extract_table_name_from_table_factor(
|
|
&table.relation,
|
|
)?])
|
|
}
|
|
Statement::Delete(delete) => Ok(vec![Self::extract_table_name_from_delete(delete)?]),
|
|
Statement::CreateTable(create_table) => Ok(vec![create_table.name.to_string()]),
|
|
Statement::AlterTable { name, .. } => Ok(vec![name.to_string()]),
|
|
Statement::Drop { names, .. } => {
|
|
Ok(names.iter().map(|name| name.to_string()).collect())
|
|
}
|
|
_ => Ok(vec![]),
|
|
}
|
|
}
|
|
|
|
/// Extrahiert Tabellenname aus INSERT
|
|
fn extract_table_name_from_insert(
|
|
insert: &sqlparser::ast::Insert,
|
|
) -> Result<String, ExtensionError> {
|
|
match &insert.table {
|
|
TableObject::TableName(name) => Ok(name.to_string()),
|
|
_ => Err(DatabaseError::NoTableError {
|
|
sql: insert.to_string(),
|
|
}
|
|
.into()),
|
|
}
|
|
}
|
|
|
|
/// Extrahiert Tabellenname aus TableFactor
|
|
fn extract_table_name_from_table_factor(
|
|
table_factor: &TableFactor,
|
|
) -> Result<String, ExtensionError> {
|
|
match table_factor {
|
|
TableFactor::Table { name, .. } => Ok(name.to_string()),
|
|
_ => Err(DatabaseError::StatementError {
|
|
reason: "Complex table references not supported".to_string(),
|
|
}
|
|
.into()),
|
|
}
|
|
}
|
|
|
|
/// Extrahiert Tabellenname aus DELETE
|
|
fn extract_table_name_from_delete(
|
|
delete: &sqlparser::ast::Delete,
|
|
) -> Result<String, ExtensionError> {
|
|
use sqlparser::ast::FromTable;
|
|
|
|
let table_name = match &delete.from {
|
|
FromTable::WithFromKeyword(tables) | FromTable::WithoutKeyword(tables) => {
|
|
if !tables.is_empty() {
|
|
Self::extract_table_name_from_table_factor(&tables[0].relation)?
|
|
} else if !delete.tables.is_empty() {
|
|
delete.tables[0].to_string()
|
|
} else {
|
|
return Err(DatabaseError::NoTableError {
|
|
sql: delete.to_string(),
|
|
}
|
|
.into());
|
|
}
|
|
}
|
|
};
|
|
|
|
Ok(table_name)
|
|
}
|
|
}
|