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::types::PublicKeyTrait;
10
use pgp::{
11
  Deserializable as _,
12
  SignedSecretKey,
13
};
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!("No keys found in location");
54
        return Ok(());
55
      }
56

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

            
68
        let key_id = hex::encode(signed_secret_key.key_id());
69
        let fingerprint = hex::encode(signed_secret_key.fingerprint().as_bytes());
70

            
71
        table.add_row(row![b->&key_name, Fg->&key_id, Fg->&fingerprint]);
72
      }
73
      let msg = style("Available keys:").bold().cyan();
74
      println!();
75
      println!("{msg}");
76
      println!();
77
      table.printstd();
78
    } else {
79
      println!("Location does not exist or is not a directory");
80
    }
81

            
82
    Ok(())
83
  }
84
}