1use std::path::Path;
2
3use crate::base::Vault;
4use crate::spec::WriteMode;
5use crate::util::export_key_with_progress;
6use crate::util::extract_at_least_one_secret_key;
7use crate::util::new_context;
8use crate::TrustModel;
9use failure::{Error, ResultExt};
10use std::fs::create_dir_all;
11use std::io::Write;
12
13#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)]
14pub enum DirectoryInfo {
15 Existed,
16 Created,
17}
18
19pub fn assure_empty_directory_exists(dir: &Path) -> Result<DirectoryInfo, Error> {
20 Ok(if dir.is_dir() {
21 let num_entries = dir
22 .read_dir()
23 .with_context(|_| format!("Failed to open directory '{}' to see if it is empty", dir.display()))?
24 .count();
25 if num_entries > 0 {
26 bail!("Refusing to write into non-empty directory at '{}'", dir.display())
27 }
28 DirectoryInfo::Existed
29 } else {
30 create_dir_all(&dir).with_context(|_| format!("Failed to create directory at '{}'", dir.display()))?;
31 DirectoryInfo::Created
32 })
33}
34
35impl Vault {
36 #[cfg_attr(feature = "cargo-clippy", allow(clippy::too_many_arguments))]
37 pub fn init(
38 secrets: &Path,
39 gpg_key_ids: &[String],
40 gpg_keys_dir: &Path,
41 recipients_file: &Path,
42 vault_path: &Path,
43 name: Option<String>,
44 trust_model: Option<TrustModel>,
45 auto_import: Option<bool>,
46 output: &mut dyn Write,
47 ) -> Result<Self, Error> {
48 let vault = Vault {
49 gpg_keys: Some(gpg_keys_dir.to_owned()),
50 recipients: recipients_file.to_owned(),
51 name,
52 secrets: secrets.to_owned(),
53 auto_import,
54 trust_model,
55 ..Default::default()
56 }
57 .set_resolved_at(vault_path)?;
58
59 let mut gpg_ctx = new_context()?;
60 let keys = extract_at_least_one_secret_key(&mut gpg_ctx, gpg_key_ids)?;
61 vault.to_file(vault_path, WriteMode::RefuseOverwrite)?;
62
63 let gpg_keys_dir = vault.absolute_path(gpg_keys_dir);
64 let recipients_file = vault.absolute_path(recipients_file);
65 assure_empty_directory_exists(&gpg_keys_dir).with_context(|_| "Cannot create gpg keys directory")?;
66
67 let mut bytes_buf = Vec::new();
68 if recipients_file.is_file() {
69 return Err(format_err!(
70 "Cannot write recipients into existing file at '{}'",
71 recipients_file.display()
72 ));
73 }
74
75 let mut recipients_fprs = Vec::new();
76 for key in keys {
77 let (fingerprint, _) = export_key_with_progress(&mut gpg_ctx, &gpg_keys_dir, &key, &mut bytes_buf, output)?;
78 recipients_fprs.push(fingerprint);
79 }
80
81 vault.write_recipients_list(&mut recipients_fprs)?;
82 writeln!(output, "vault initialized at '{}'", vault_path.display()).ok();
83 Ok(vault)
84 }
85}