tailf_acm_demo/
tailf_acm_demo.rs

1use nacm_validator::{AccessRequest, NacmConfig, Operation, RuleEffect, RequestContext};
2use std::path::Path;
3
4fn main() -> Result<(), Box<dyn std::error::Error>> {
5    // Load the Tail-f ACM example configuration
6    let xml_path = Path::new(env!("CARGO_MANIFEST_DIR"))
7        .join("examples")
8        .join("data")
9        .join("tailf_acm_example.xml");
10    
11    let xml_content = std::fs::read_to_string(&xml_path)
12        .map_err(|e| format!("Failed to read XML file at {:?}: {}", xml_path, e))?;
13    
14    let config = NacmConfig::from_xml(&xml_content)?;
15    
16    println!("šŸ”§ Tail-f ACM Configuration loaded:");
17    println!("- NACM enabled: {}", config.enable_nacm);
18    println!("- Default policies:");
19    println!("  * Data: read={:?}, write={:?}, exec={:?}", 
20             config.read_default, config.write_default, config.exec_default);
21    println!("  * Commands: cmd_read={:?}, cmd_exec={:?}",
22             config.cmd_read_default, config.cmd_exec_default);
23    println!("- Logging: default_permit={}, default_deny={}",
24             config.log_if_default_permit, config.log_if_default_deny);
25    println!("- Groups: {:?}", config.groups.keys().collect::<Vec<_>>());
26    
27    // Show group details with GIDs
28    for (name, group) in &config.groups {
29        let gid_str = if let Some(gid) = group.gid {
30            format!(" (GID: {})", gid)
31        } else {
32            String::new()
33        };
34        println!("  * {}{}: {:?}", name, gid_str, group.users);
35    }
36    
37    println!("- Rule lists: {}", config.rule_lists.len());
38    for rule_list in &config.rule_lists {
39        println!("  * {}: {} rules, {} command rules", 
40                 rule_list.name, rule_list.rules.len(), rule_list.command_rules.len());
41    }
42    
43    // Test command access scenarios
44    println!("\nšŸ” Command access validation results:");
45    
46    let cli_context = RequestContext::CLI;
47    let webui_context = RequestContext::WebUI;
48    let netconf_context = RequestContext::NETCONF;
49    
50    let command_test_cases = vec![
51        ("Alice (operator) - CLI show status", AccessRequest {
52            user: "alice",
53            module_name: None,
54            rpc_name: None,
55            operation: Operation::Read,
56            path: None,
57            context: Some(&cli_context),
58            command: Some("show status"),
59        }),
60        ("Alice (operator) - CLI show interfaces", AccessRequest {
61            user: "alice",
62            module_name: None,
63            rpc_name: None,
64            operation: Operation::Read,
65            path: None,
66            context: Some(&cli_context),
67            command: Some("show interfaces"),
68        }),
69        ("Alice (operator) - WebUI help", AccessRequest {
70            user: "alice",
71            module_name: None,
72            rpc_name: None,
73            operation: Operation::Read,
74            path: None,
75            context: Some(&webui_context),
76            command: Some("help"),
77        }),
78        ("Alice (operator) - CLI reboot (should deny)", AccessRequest {
79            user: "alice",
80            module_name: None,
81            rpc_name: None,
82            operation: Operation::Exec,
83            path: None,
84            context: Some(&cli_context),
85            command: Some("reboot"),
86        }),
87        ("Admin - CLI reboot (should permit)", AccessRequest {
88            user: "admin",
89            module_name: None,
90            rpc_name: None,
91            operation: Operation::Exec,
92            path: None,
93            context: Some(&cli_context),
94            command: Some("reboot"),
95        }),
96        ("Bob (operator) - Unknown command (should use default)", AccessRequest {
97            user: "bob",
98            module_name: None,
99            rpc_name: None,
100            operation: Operation::Exec,
101            path: None,
102            context: Some(&cli_context),
103            command: Some("unknown-command"),
104        }),
105    ];
106    
107    for (description, request) in command_test_cases {
108        let result = config.validate(&request);
109        let result_icon = match result.effect {
110            RuleEffect::Permit => "āœ…",
111            RuleEffect::Deny => "āŒ",
112        };
113        let log_str = if result.should_log { " šŸ“[LOG]" } else { "" };
114        println!("  {} {}: {}{}", result_icon, description, 
115                 format!("{:?}", result.effect).to_uppercase(), log_str);
116    }
117    
118    // Test mixed data and command access
119    println!("\nšŸ“Š Mixed data access validation:");
120    
121    let data_test_cases = vec![
122        ("Alice - NETCONF read interfaces", AccessRequest {
123            user: "alice",
124            module_name: Some("ietf-interfaces"),
125            rpc_name: None,
126            operation: Operation::Read,
127            path: Some("/interfaces"),
128            context: Some(&netconf_context),
129            command: None,
130        }),
131        ("Alice - NETCONF write interfaces (should deny)", AccessRequest {
132            user: "alice",
133            module_name: None,
134            rpc_name: Some("edit-config"),
135            operation: Operation::Exec,
136            path: None,
137            context: Some(&netconf_context),
138            command: None,
139        }),
140        ("Admin - NETCONF write (should permit)", AccessRequest {
141            user: "admin",
142            module_name: None,
143            rpc_name: Some("edit-config"),
144            operation: Operation::Exec,
145            path: None,
146            context: Some(&netconf_context),
147            command: None,
148        }),
149    ];
150    
151    for (description, request) in data_test_cases {
152        let result = config.validate(&request);
153        let result_icon = match result.effect {
154            RuleEffect::Permit => "āœ…",
155            RuleEffect::Deny => "āŒ",
156        };
157        let log_str = if result.should_log { " šŸ“[LOG]" } else { "" };
158        println!("  {} {}: {}{}", result_icon, description, 
159                 format!("{:?}", result.effect).to_uppercase(), log_str);
160    }
161    
162    println!("\nšŸŽÆ Summary:");
163    println!("The Tail-f ACM extensions provide:");
164    println!("- Command-based access control for CLI/WebUI operations");
165    println!("- Enhanced logging controls for audit trails");
166    println!("- Group ID mapping for OS integration");
167    println!("- Context-aware rules (CLI vs NETCONF vs WebUI)");
168    println!("- Granular control over what gets logged");
169    
170    Ok(())
171}