Skip to main content

vta_cli_common/commands/
step_up.rs

1//! `… step-up policy` operator commands (online, via the VTA REST surface).
2//!
3//! Thin wrappers over [`VtaClient::get_step_up_policy`] /
4//! [`VtaClient::set_step_up_policy`]. The policy JSON is the `0.2` shape
5//! (`{ enabled, floors: [{ operation, mode, allowAal1IfNonEscalating }] }`).
6
7use serde_json::Value;
8use vta_sdk::prelude::*;
9
10/// `step-up policy show` — print the maintainer's current effective policy.
11pub async fn cmd_policy_show(client: &VtaClient) -> Result<(), Box<dyn std::error::Error>> {
12    let policy = client.get_step_up_policy().await?;
13    print_policy(&policy);
14    Ok(())
15}
16
17/// `step-up policy set` — apply `policy` (the `0.2` payload) and print the
18/// effective (canonicalized) result the maintainer now holds.
19pub async fn cmd_policy_set(
20    client: &VtaClient,
21    policy: Value,
22) -> Result<(), Box<dyn std::error::Error>> {
23    let effective = client.set_step_up_policy(policy).await?;
24    println!("Step-up policy updated:");
25    print_policy(&effective);
26    Ok(())
27}
28
29/// `step-up policy disable` — revert to AAL1 everywhere (the shipping default).
30pub async fn cmd_policy_disable(client: &VtaClient) -> Result<(), Box<dyn std::error::Error>> {
31    let effective = client
32        .set_step_up_policy(serde_json::json!({ "enabled": false, "floors": [] }))
33        .await?;
34    println!("Step-up policy disabled (AAL1 everywhere):");
35    print_policy(&effective);
36    Ok(())
37}
38
39/// Pretty-print a `0.2` policy value.
40pub fn print_policy(p: &Value) {
41    let enabled = p.get("enabled").and_then(Value::as_bool).unwrap_or(false);
42    println!(
43        "  Enforcement: {}",
44        if enabled {
45            "ENABLED"
46        } else {
47            "disabled (AAL1 everywhere)"
48        }
49    );
50    let floors = p.get("floors").and_then(Value::as_array);
51    match floors {
52        Some(floors) if !floors.is_empty() => {
53            println!("  Floors:");
54            for f in floors {
55                let op = f.get("operation").and_then(Value::as_str).unwrap_or("?");
56                let mode = f.get("mode").and_then(Value::as_str).unwrap_or("?");
57                let carve = f
58                    .get("allowAal1IfNonEscalating")
59                    .and_then(Value::as_bool)
60                    .unwrap_or(false);
61                let suffix = if carve {
62                    "  (AAL1 carve-out for non-escalating self-service)"
63                } else {
64                    ""
65                };
66                println!("    {op:<18} → {mode}{suffix}");
67            }
68        }
69        _ => println!("  Floors: (none)"),
70    }
71}