use std::{any::Any, str::FromStr, sync::Arc};
use arc_swap::ArcSwap;
use async_trait::async_trait;
use base64::{Engine as _, engine::general_purpose::STANDARD};
use better_default::Default;
use serde_json::{Map, Value};
use super::Module;
use crate::{
core::Core,
errors::RvError,
handler::AuthHandler,
logical::{Backend, Request, Response},
rv_error_response_status,
};
#[allow(clippy::module_inception)]
pub mod policy;
pub use policy::{Permissions, Policy, PolicyPathRules, PolicyType};
pub mod policy_store;
pub use policy_store::PolicyStore;
pub mod acl;
#[derive(Default)]
pub struct PolicyModule {
#[default("policy".into())]
pub name: String,
pub core: Arc<Core>,
pub policy_store: ArcSwap<PolicyStore>,
}
impl PolicyModule {
pub fn new(core: Arc<Core>) -> Self {
Self {
name: "policy".into(),
core,
policy_store: ArcSwap::new(Arc::new(PolicyStore::default())),
}
}
pub async fn setup_policy(&self) -> Result<(), RvError> {
self.policy_store.load().load_default_acl_policy().await
}
pub async fn handle_policy_list(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let mut policies = self
.policy_store
.load()
.list_policy(PolicyType::Acl)
.await?;
policies.push("root".into());
let mut resp = Response::list_response(&policies);
if req.path.starts_with("policy") {
let data = resp.data.as_mut().unwrap();
data.insert("policies".into(), data["keys"].clone());
}
Ok(Some(resp))
}
pub async fn handle_policy_read(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let name = req.get_data_as_str("name")?;
if let Some(policy) = self
.policy_store
.load()
.get_policy(&name, PolicyType::Acl)
.await?
{
let mut resp_data = Map::new();
resp_data.insert("name".into(), Value::String(name));
if req.path.starts_with("policy") {
resp_data.insert("rules".into(), Value::String(policy.raw.clone()));
} else {
resp_data.insert("policy".into(), Value::String(policy.raw.clone()));
}
let resp = Response::data_response(Some(resp_data));
if policy.policy_type == PolicyType::Egp || policy.policy_type == PolicyType::Rgp {
policy.add_sentinel_policy_data(&resp)?;
}
return Ok(Some(resp));
}
Err(rv_error_response_status!(
404,
&format!("No policy named: {name}")
))
}
pub async fn handle_policy_write(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let name = req.get_data_as_str("name")?;
let policy_str = req
.get_data("policy")?
.as_str()
.ok_or(RvError::ErrRequestFieldInvalid)?
.to_string();
let policy_raw = if let Ok(policy_bytes) = STANDARD.decode(&policy_str) {
String::from_utf8_lossy(&policy_bytes).to_string()
} else {
policy_str
};
let mut policy = Policy::from_str(&policy_raw)?;
policy.name = name;
if policy.policy_type == PolicyType::Egp || policy.policy_type == PolicyType::Rgp {
policy.input_sentinel_policy_data(req)?;
}
self.policy_store.load().set_policy(policy).await?;
Ok(None)
}
pub async fn handle_policy_delete(
&self,
_backend: &dyn Backend,
req: &mut Request,
) -> Result<Option<Response>, RvError> {
let name = req.get_data_as_str("name")?;
self.policy_store
.load()
.delete_policy(&name, PolicyType::Acl)
.await?;
Ok(None)
}
}
#[async_trait]
impl Module for PolicyModule {
fn name(&self) -> String {
self.name.clone()
}
fn as_any_arc(self: Arc<Self>) -> Arc<dyn Any + Send + Sync> {
self
}
async fn init(&self, core: &Core) -> Result<(), RvError> {
let policy_store = PolicyStore::new(core).await?;
self.policy_store.store(policy_store.clone());
self.setup_policy().await?;
core.add_auth_handler(policy_store as Arc<dyn AuthHandler>)?;
Ok(())
}
fn setup(&self, _core: &Core) -> Result<(), RvError> {
Ok(())
}
fn cleanup(&self, core: &Core) -> Result<(), RvError> {
core.delete_auth_handler(self.policy_store.load().clone() as Arc<dyn AuthHandler>)?;
let policy_store = Arc::new(PolicyStore::default());
self.policy_store.swap(policy_store);
Ok(())
}
}