58 lines
1.8 KiB
Rust
58 lines
1.8 KiB
Rust
|
|
//! RBAC policy loader for build-time auth pattern extraction.
|
||
|
|
//!
|
||
|
|
//! Reads `site/rbac.ncl` (via `nickel export`) and returns the URL patterns
|
||
|
|
//! that are denied for anonymous users. These patterns are the **single source
|
||
|
|
//! of truth** for route protection — no duplication in `config.ncl`.
|
||
|
|
|
||
|
|
use anyhow::{Context, Result};
|
||
|
|
use serde::Deserialize;
|
||
|
|
use std::path::Path;
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
struct RbacConfig {
|
||
|
|
defaults: RbacDefaults,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
struct RbacDefaults {
|
||
|
|
unauthenticated_allow: Vec<RbacEntry>,
|
||
|
|
}
|
||
|
|
|
||
|
|
#[derive(Debug, Deserialize)]
|
||
|
|
struct RbacEntry {
|
||
|
|
resource: String,
|
||
|
|
pattern: String,
|
||
|
|
outcome: String,
|
||
|
|
}
|
||
|
|
|
||
|
|
/// Return URL patterns denied for anonymous users (from `defaults.unauthenticated_allow`).
|
||
|
|
///
|
||
|
|
/// Filters to `resource = "page"` + `outcome = "deny"` entries in the order they
|
||
|
|
/// appear in the policy — the order matters for display but not for the generated
|
||
|
|
/// pattern matcher, which checks all entries.
|
||
|
|
///
|
||
|
|
/// Returns an empty vec if `rbac.ncl` is absent (non-fatal; no protected routes).
|
||
|
|
pub fn load_rbac_anonymous_deny_patterns(manifest_dir: &Path) -> Result<Vec<String>> {
|
||
|
|
let rbac_ncl = manifest_dir.join("site").join("rbac.ncl");
|
||
|
|
|
||
|
|
if !rbac_ncl.exists() {
|
||
|
|
return Ok(Vec::new());
|
||
|
|
}
|
||
|
|
|
||
|
|
let json = rustelo_config::nickel::export_to_json(&rbac_ncl)
|
||
|
|
.with_context(|| format!("Nickel export failed for {}", rbac_ncl.display()))?;
|
||
|
|
|
||
|
|
let config: RbacConfig = serde_json::from_str(&json)
|
||
|
|
.with_context(|| format!("JSON deserialisation failed for {}", rbac_ncl.display()))?;
|
||
|
|
|
||
|
|
let patterns = config
|
||
|
|
.defaults
|
||
|
|
.unauthenticated_allow
|
||
|
|
.into_iter()
|
||
|
|
.filter(|e| e.resource == "page" && e.outcome == "deny")
|
||
|
|
.map(|e| e.pattern)
|
||
|
|
.collect();
|
||
|
|
|
||
|
|
Ok(patterns)
|
||
|
|
}
|