1
use std::fs::{
2
  self,
3
  File,
4
};
5
use std::path::Path;
6

            
7
use clap::Args;
8
use console::style;
9
use pgp::composed::{
10
  Deserializable as _,
11
  SignedSecretKey,
12
};
13
use pgp::types::KeyDetails as _;
14
use prettytable::format::consts;
15
use prettytable::{
16
  row,
17
  Table,
18
};
19

            
20
use crate::secrets::context::Context;
21

            
22
use super::KEY_LOCATION_HELP;
23

            
24
#[derive(Debug, Args)]
25
pub struct ListKeys {
26
  /// The location to store the private key
27
  #[arg(short, long, help = KEY_LOCATION_HELP)]
28
  location: Option<String>,
29
}
30

            
31
impl ListKeys {
32
  pub fn execute(&self, context: &Context) -> anyhow::Result<()> {
33
    let location: &str = &self.location.clone().unwrap_or_else(|| context.keys_location());
34

            
35
    assert!(!location.is_empty(), "Location must be provided");
36

            
37
    let path = Path::new(location);
38
    if path.exists() && path.is_dir() {
39
      let entries = fs::read_dir(path)?
40
        .filter_map(Result::ok)
41
        .filter(|entry| entry.path().extension().and_then(|ext| ext.to_str()) == Some("key"))
42
        .map(|entry| {
43
          entry
44
            .path()
45
            .file_stem()
46
            .and_then(|stem| stem.to_str())
47
            .unwrap_or("")
48
            .to_string()
49
        })
50
        .collect::<Vec<_>>();
51

            
52
      if entries.is_empty() {
53
        println!(
54
          "No keys found at '{}'. Generate one with: mk secrets key gen",
55
          location
56
        );
57
        return Ok(());
58
      }
59

            
60
      let mut table = Table::new();
61
      table.set_format(*consts::FORMAT_CLEAN);
62
      table.set_titles(row![Fbb->"Name", Fbb->"Key ID", Fbb->"Fingerprint"]);
63
      for entry in entries {
64
        let key_name: &str = &entry.clone();
65
        let filename_with_ext = format!("{key_name}.key");
66
        let key_path = Path::new(location).join(filename_with_ext);
67
        let mut secret_key_string = File::open(key_path)?;
68
        let (signed_secret_key, _) = SignedSecretKey::from_armor_single(&mut secret_key_string)?;
69
        signed_secret_key.verify_bindings()?;
70

            
71
        let key_id = hex::encode(signed_secret_key.legacy_key_id());
72
        let fingerprint = hex::encode(signed_secret_key.fingerprint().as_bytes());
73

            
74
        table.add_row(row![b->&key_name, Fg->&key_id, Fg->&fingerprint]);
75
      }
76
      let msg = style("Available keys:").bold().cyan();
77
      println!();
78
      println!("{msg}");
79
      println!();
80
      table.printstd();
81
    } else {
82
      println!(
83
        "Keys directory '{}' does not exist. Generate a key first with: mk secrets key gen",
84
        location
85
      );
86
    }
87

            
88
    Ok(())
89
  }
90
}