2023-05-03 09:13:05 -07:00
|
|
|
//! This query router plugin will check if the user can access a particular
|
|
|
|
|
//! table as part of their query. If they can't, the query will not be routed.
|
|
|
|
|
|
|
|
|
|
use async_trait::async_trait;
|
|
|
|
|
use sqlparser::ast::{visit_relations, Statement};
|
|
|
|
|
|
|
|
|
|
use crate::{
|
2023-05-03 16:13:45 -07:00
|
|
|
config::TableAccess as TableAccessConfig,
|
2023-05-03 09:13:05 -07:00
|
|
|
errors::Error,
|
|
|
|
|
plugins::{Plugin, PluginOutput},
|
|
|
|
|
query_router::QueryRouter,
|
|
|
|
|
};
|
|
|
|
|
|
2023-05-03 16:13:45 -07:00
|
|
|
use log::{debug, info};
|
|
|
|
|
|
|
|
|
|
use arc_swap::ArcSwap;
|
2023-05-03 09:13:05 -07:00
|
|
|
use core::ops::ControlFlow;
|
2023-05-03 16:13:45 -07:00
|
|
|
use once_cell::sync::Lazy;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
|
|
static CONFIG: Lazy<ArcSwap<Vec<String>>> = Lazy::new(|| ArcSwap::from_pointee(vec![]));
|
|
|
|
|
|
|
|
|
|
pub fn setup(config: &TableAccessConfig) {
|
|
|
|
|
CONFIG.store(Arc::new(config.tables.clone()));
|
|
|
|
|
|
|
|
|
|
info!("Blocking access to {} tables", config.tables.len());
|
|
|
|
|
}
|
2023-05-03 09:13:05 -07:00
|
|
|
|
2023-05-03 16:13:45 -07:00
|
|
|
pub fn enabled() -> bool {
|
|
|
|
|
!CONFIG.load().is_empty()
|
2023-05-03 09:13:05 -07:00
|
|
|
}
|
|
|
|
|
|
2023-05-03 16:13:45 -07:00
|
|
|
pub fn disable() {
|
|
|
|
|
CONFIG.store(Arc::new(vec![]));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct TableAccess;
|
|
|
|
|
|
2023-05-03 09:13:05 -07:00
|
|
|
#[async_trait]
|
|
|
|
|
impl Plugin for TableAccess {
|
|
|
|
|
async fn run(
|
|
|
|
|
&mut self,
|
|
|
|
|
_query_router: &QueryRouter,
|
|
|
|
|
ast: &Vec<Statement>,
|
|
|
|
|
) -> Result<PluginOutput, Error> {
|
|
|
|
|
let mut found = None;
|
2023-05-03 16:13:45 -07:00
|
|
|
let forbidden_tables = CONFIG.load();
|
2023-05-03 09:13:05 -07:00
|
|
|
|
|
|
|
|
visit_relations(ast, |relation| {
|
|
|
|
|
let relation = relation.to_string();
|
|
|
|
|
let parts = relation.split(".").collect::<Vec<&str>>();
|
|
|
|
|
let table_name = parts.last().unwrap();
|
|
|
|
|
|
2023-05-03 16:13:45 -07:00
|
|
|
if forbidden_tables.contains(&table_name.to_string()) {
|
2023-05-03 09:13:05 -07:00
|
|
|
found = Some(table_name.to_string());
|
|
|
|
|
ControlFlow::<()>::Break(())
|
|
|
|
|
} else {
|
|
|
|
|
ControlFlow::<()>::Continue(())
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
if let Some(found) = found {
|
2023-05-03 16:13:45 -07:00
|
|
|
debug!("Blocking access to table \"{}\"", found);
|
|
|
|
|
|
2023-05-03 09:13:05 -07:00
|
|
|
Ok(PluginOutput::Deny(format!(
|
|
|
|
|
"permission for table \"{}\" denied",
|
|
|
|
|
found
|
|
|
|
|
)))
|
|
|
|
|
} else {
|
|
|
|
|
Ok(PluginOutput::Allow)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|