lean_ctx/core/policy/org/
mod.rs1pub mod model;
9pub mod store;
10pub mod trust;
11
12use std::path::PathBuf;
13
14pub use model::{OrgPolicyV1, OrgVerifyResult};
15pub use trust::{TrustStore, TrustedKey};
16
17use crate::core::policy::ResolvedPolicy;
18
19#[must_use]
20pub fn org_key_id(org: &str) -> String {
21 let safe: String = org
22 .chars()
23 .map(|c| {
24 if c.is_ascii_alphanumeric() || c == '-' {
25 c.to_ascii_lowercase()
26 } else {
27 '-'
28 }
29 })
30 .collect();
31 format!("org-{safe}")
32}
33
34#[must_use]
41pub fn active_resolved() -> Option<ResolvedPolicy> {
42 let artifact = store::load_active()?;
43
44 let result = artifact.verify();
45 if !result.signature_valid {
46 tracing::warn!(
47 "org policy: signature invalid ({}), ignoring",
48 result.error.as_deref().unwrap_or("unknown")
49 );
50 return None;
51 }
52
53 let signer = artifact.signer_public_key.as_deref()?;
54 if !trust::is_trusted(signer) {
55 tracing::debug!("org policy: signer not trust-pinned, ignoring (fail-open)");
56 return None;
57 }
58
59 if !artifact.enforced {
60 tracing::debug!("org policy: admin did not set enforced=true, skipping");
61 return None;
62 }
63
64 match artifact.resolved() {
65 Ok(resolved) => Some(resolved),
66 Err(e) => {
67 tracing::warn!("org policy: failed to resolve pack: {e}");
68 None
69 }
70 }
71}
72
73#[derive(Debug, Clone, Default)]
74pub struct OrgStatus {
75 pub present: bool,
76 pub source: Option<PathBuf>,
77 pub org: Option<String>,
78 pub policy_version: Option<String>,
79 pub enforced: bool,
80 pub issued_at: Option<String>,
81 pub signer_public_key: Option<String>,
82 pub signature_valid: bool,
83 pub trusted: bool,
84 pub applied: bool,
85 pub resolve_error: Option<String>,
86 pub pinned_anchors: usize,
87}
88
89#[must_use]
90pub fn status() -> OrgStatus {
91 let Some(artifact) = store::load_active() else {
92 return OrgStatus {
93 pinned_anchors: trust::trusted_keys().len(),
94 ..Default::default()
95 };
96 };
97 let verify = artifact.verify();
98 let signer_key = artifact.signer_public_key.clone();
99 let trusted = signer_key.as_deref().is_some_and(trust::is_trusted);
100 let resolve_result = artifact.resolved();
101
102 OrgStatus {
103 present: true,
104 source: store::source_path(),
105 org: Some(artifact.org.clone()),
106 policy_version: Some(artifact.policy_version.clone()),
107 enforced: artifact.enforced,
108 issued_at: Some(artifact.issued_at.clone()),
109 signer_public_key: signer_key,
110 signature_valid: verify.signature_valid,
111 trusted,
112 applied: trusted && verify.signature_valid && artifact.enforced && resolve_result.is_ok(),
113 resolve_error: resolve_result.err().map(|e| e.to_string()),
114 pinned_anchors: trust::trusted_keys().len(),
115 }
116}