decide-core 0.1.0

A fast, lightweight permission engine for Rust and TypeScript with condition support.
Documentation
use crate::types::{DecideError, Resource, RoleMap, User};
use crate::utils::{evaluate_condition, log_debug};
use std::fs::{self};

// Core class
pub struct Decide {
    roles: RoleMap,
}

impl Decide {
    // General constructor
    pub fn new (roles: RoleMap) -> Self {
        Self {
            roles,
        }
    }

    // Create from a custom JSON config file
    pub fn from_file (path: &str) -> Result<Self, Box<dyn std::error::Error>> {
        let content = fs::read_to_string(path)?;
        let roles: RoleMap = serde_json::from_str(&content)?;
        Ok(Self::new(roles))
    }

    // Create from default JSON config file
    pub fn default () -> Result<Self, Box<dyn std::error::Error>> {
        Self::from_file("decide.config.json")
    }

    pub fn can (&self, user: &User, action: &str, resource: &Resource) -> Result<bool, DecideError> {
        for role_name in &user.roles {
            if let Some(role) = self.roles.get(role_name) {
                for pmsn in &role.permissions {
                    if pmsn.action != action {
                        continue;
                    }

                    match &pmsn.condition {
                        Some(expr) => {
                            let passed = evaluate_condition(expr, user, resource);
                            log_debug(&format!(
                                "Action '{}', condition '{}' -> {}",
                                action, expr, passed
                            ));
                            if passed {
                                return Ok(true);
                            }
                        }
                        None => {
                            log_debug(&format!("Action '{}': No condition, allowing.", action));
                            return Ok(true);
                        }
                    }
                }
            } else {
                return Err(DecideError::MissingRole(role_name.clone()));
            }
        }

        Ok(false)
    }
}