feat(gui): pure-logic helpers for Policy tab (mode mapping, violation grouping)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,7 @@ mod enroll_dialog;
|
||||
mod error;
|
||||
mod firstrun;
|
||||
mod keys_page;
|
||||
mod policy_form;
|
||||
|
||||
const APP_ID: &str = "io.dangerousthings.AuthForge";
|
||||
|
||||
|
||||
92
gui/src/policy_form.rs
Normal file
92
gui/src/policy_form.rs
Normal file
@@ -0,0 +1,92 @@
|
||||
//! Pure helpers backing the Policy tab. Mode <-> combo index, violation
|
||||
//! grouping. Widget code calls these; nothing here touches GTK.
|
||||
//!
|
||||
//! `dead_code` allowances mirror `bus.rs` — these helpers are wired through
|
||||
//! `policy_page.rs` in Task 4; without the allow, this commit's `-D warnings`
|
||||
//! gate would reject the staged-but-unused symbols.
|
||||
|
||||
#![allow(dead_code)]
|
||||
|
||||
use authforge_common::types::{Mode, Violation};
|
||||
use std::collections::BTreeMap;
|
||||
|
||||
pub(crate) const MODE_LABELS: &[(&str, Mode)] = &[
|
||||
("Disabled", Mode::Disabled),
|
||||
("Optional", Mode::Optional),
|
||||
("Required", Mode::Required),
|
||||
];
|
||||
|
||||
pub(crate) fn mode_to_combo_index(m: Mode) -> u32 {
|
||||
match m {
|
||||
Mode::Disabled => 0,
|
||||
Mode::Optional => 1,
|
||||
Mode::Required => 2,
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn combo_index_to_mode(idx: u32) -> Mode {
|
||||
match idx {
|
||||
0 => Mode::Disabled,
|
||||
1 => Mode::Optional,
|
||||
_ => Mode::Required,
|
||||
}
|
||||
}
|
||||
|
||||
/// Group violations by stack, joining reasons with "; ". Used to render the
|
||||
/// inline AdwBanner content when `SetPolicy(force=false)` returns violations.
|
||||
pub(crate) fn group_violations_by_stack(vs: &[Violation]) -> BTreeMap<String, String> {
|
||||
let mut by_stack: BTreeMap<String, Vec<String>> = BTreeMap::new();
|
||||
for v in vs {
|
||||
by_stack
|
||||
.entry(v.stack.clone())
|
||||
.or_default()
|
||||
.push(format!("{}: {}", v.user, v.reason));
|
||||
}
|
||||
by_stack
|
||||
.into_iter()
|
||||
.map(|(k, vs)| (k, vs.join("; ")))
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn mode_combo_roundtrips() {
|
||||
for m in [Mode::Disabled, Mode::Optional, Mode::Required] {
|
||||
assert_eq!(combo_index_to_mode(mode_to_combo_index(m)), m);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn out_of_range_index_clamps_to_required() {
|
||||
assert_eq!(combo_index_to_mode(99), Mode::Required);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn violations_group_by_stack() {
|
||||
let vs = vec![
|
||||
Violation {
|
||||
user: "alice".into(),
|
||||
stack: "sudo".into(),
|
||||
reason: "no fido2".into(),
|
||||
},
|
||||
Violation {
|
||||
user: "bob".into(),
|
||||
stack: "sudo".into(),
|
||||
reason: "no fido2".into(),
|
||||
},
|
||||
Violation {
|
||||
user: "alice".into(),
|
||||
stack: "sshd".into(),
|
||||
reason: "no fido2".into(),
|
||||
},
|
||||
];
|
||||
let g = group_violations_by_stack(&vs);
|
||||
assert_eq!(g.len(), 2);
|
||||
assert!(g.get("sudo").unwrap().contains("alice"));
|
||||
assert!(g.get("sudo").unwrap().contains("bob"));
|
||||
assert_eq!(g.get("sshd").unwrap(), "alice: no fido2");
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user