Grcov report - store_secret.rs

1

    use std::fs::{

2

      self,

3

      File,

4

    };

5

    use std::io::{

6

      self,

7

      IsTerminal,

8

      Read as _,

9

      Write as _,

10

    };

11

    use std::path::Path;

12

13

    use clap::Args;

14

    use mk_lib::file::ToUtf8 as _;

15

    use pgp::crypto::sym::SymmetricKeyAlgorithm;

16

    use pgp::types::SecretKeyTrait;

17

    use pgp::{

18

      ArmorOptions,

19

      Deserializable,

20

      Message,

21

      SignedSecretKey,

22

    };

23

    use rand::thread_rng;

24

25

    use crate::secrets::context::Context;

26

    use crate::secrets::vault::{

27

      verify_key,

28

      verify_vault,

29

    };

30

31

    #[derive(Debug, Args)]

32

    pub struct StoreSecret {

33

      #[arg(help = "The secret identifier")]

34

      path: String,

35

36

      #[arg(help = "The secret value")]

37

      value: Option<String>,

38

39

      #[arg(short, long, help = "The path to the secret vault")]

40

      vault_location: Option<String>,

41

42

      #[arg(long, help = "The key location")]

43

      keys_location: Option<String>,

44

45

      #[arg(short, long, help = "The key name")]

46

      key_name: Option<String>,

47

48

      /// If the secret already exists, it will be overwritten

49

      #[arg(short, long, help = "Force overwrite the secret")]

50

      force: bool,

51

    }

52

53

    impl StoreSecret {

54

      pub fn execute(&self, context: &Context) -> anyhow::Result<()> {

55

        let path: &str = &self.path.clone();

56

        let value: &str = &match &self.value {

57

          Some(value) => value.clone(),

58

          None => {

59

            let stdin = io::stdin();

60

            if stdin.is_terminal() {

61

              return Err(anyhow::anyhow!("No value provided"));

62

            }

63

64

            let mut buffer = String::new();

65

            let mut handle = stdin.lock();

66

            match handle.read_to_string(&mut buffer) {

67

              Ok(0) => return Err(anyhow::anyhow!("No value provided")),

68

              Ok(_) => buffer.trim().to_string(),

69

              Err(e) => return Err(anyhow::anyhow!("Failed to read from stdin: {}", e)),

70

            }

71

          },

72

        };

73

74

        let vault_location: &str = &self

75

          .vault_location

76

          .clone()

77

          .unwrap_or_else(|| context.vault_location());

78

        let keys_location: &str = &self

79

          .keys_location

80

          .clone()

81

          .unwrap_or_else(|| context.keys_location());

82

        let key_name: &str = &self.key_name.clone().unwrap_or_else(|| context.key_name());

83

84

        assert!(!path.is_empty(), "Path must be provided");

85

        assert!(!value.is_empty(), "Value must be provided");

86

        assert!(!vault_location.is_empty(), "Store location must be provided");

87

        assert!(!keys_location.is_empty(), "Keys location must be provided");

88

        assert!(!key_name.is_empty(), "Key name must be provided");

89

90

        verify_vault(vault_location)?;

91

        verify_key(keys_location, key_name)?;

92

93

        let secret_path = Path::new(vault_location).join(path);

94

        let data_path = secret_path.clone().join("data.asc");

95

        if secret_path.exists()

96

          && secret_path.is_dir()

97

          && data_path.exists()

98

          && data_path.is_file()

99

          && !self.force

100

        {

101

          println!(

102

            "Secret already exists at path {path} in {}",

103

            secret_path.to_utf8()?

104

          );

105

        } else {

106

          fs::create_dir_all(secret_path.clone())?;

107

108

          // Open the secret key file

109

          let key_name = format!("{}.key", key_name);

110

          let key_path = Path::new(keys_location).join(key_name);

111

          let mut secret_key_string = File::open(key_path)?;

112

          let (signed_secret_key, _) = SignedSecretKey::from_armor_single(&mut secret_key_string)?;

113

          signed_secret_key.verify()?;

114

115

          // Get the public key

116

          let pubkey = signed_secret_key.public_key();

117

118

          // Encrypt the value

119

          let message = Message::new_literal("none", value);

120

          let encrypted_message =

121

            message.encrypt_to_keys_seipdv1(&mut thread_rng(), SymmetricKeyAlgorithm::AES128, &[&pubkey])?;

122

123

          // Save the encrypted message to a file

124

          let mut writer = File::create(data_path)?;

125

          encrypted_message.to_armored_writer(&mut writer, ArmorOptions::default())?;

126

          writer.flush()?;

127

128

          println!("Secret stored at {}", secret_path.to_utf8()?);

129

        }

130

        Ok(())

131

      }

132

    }