use std::path::Path;
use epics_base_rs::server::access_security::{AccessLevel, AccessSecurityConfig, parse_acf};
use crate::error::{BridgeError, BridgeResult};
pub struct AccessConfig {
config: Option<AccessSecurityConfig>,
allow_all: bool,
}
impl AccessConfig {
pub fn allow_all() -> Self {
Self {
config: None,
allow_all: true,
}
}
pub fn from_file(path: &Path) -> BridgeResult<Self> {
let content = std::fs::read_to_string(path)?;
Self::from_string(&content)
}
pub fn from_string(content: &str) -> BridgeResult<Self> {
let config = parse_acf(content)
.map_err(|e| BridgeError::GroupConfigError(format!("ACF parse: {e}")))?;
Ok(Self {
config: Some(config),
allow_all: false,
})
}
pub fn can_read(&self, asg: &str, asl: i32, user: &str, host: &str) -> bool {
if self.allow_all {
return true;
}
let asl = asl.max(0).min(u8::MAX as i32) as u8;
match &self.config {
Some(cfg) => matches!(
cfg.check_access_asl(asg, host, user, asl),
AccessLevel::Read | AccessLevel::ReadWrite
),
None => true,
}
}
pub fn can_write(&self, asg: &str, asl: i32, user: &str, host: &str) -> bool {
if self.allow_all {
return true;
}
let asl = asl.max(0).min(u8::MAX as i32) as u8;
match &self.config {
Some(cfg) => matches!(
cfg.check_access_asl(asg, host, user, asl),
AccessLevel::ReadWrite
),
None => true,
}
}
pub fn has_rules(&self) -> bool {
self.config.is_some()
}
}
impl Default for AccessConfig {
fn default() -> Self {
Self::allow_all()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn allow_all_default() {
let acc = AccessConfig::allow_all();
assert!(!acc.has_rules());
assert!(acc.can_read("BeamGroup", 1, "anyone", "anywhere"));
assert!(acc.can_write("BeamGroup", 1, "anyone", "anywhere"));
}
#[test]
fn allow_all_default_via_default_trait() {
let acc = AccessConfig::default();
assert!(acc.can_read("X", 0, "u", "h"));
}
#[test]
fn from_string_with_minimal_acf() {
let content = r#"
ASG(DEFAULT) {
RULE(1, READ)
RULE(1, WRITE)
}
"#;
let acc = AccessConfig::from_string(content);
let _ = acc;
}
}