Grcov report - export_secrets.rs

1

    use std::fs::{

2

      self,

3

      File,

4

    };

5

    use std::io::Write as _;

6

    use std::path::Path;

7

8

    use anyhow::Context as _;

9

    use clap::Args;

10

    use pgp::{

11

      Deserializable as _,

12

      Message,

13

      SignedSecretKey,

14

    };

15

16

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

17

    use crate::secrets::vault::{

18

      verify_key,

19

      verify_vault,

20

    };

21

22

    #[derive(Debug, Args)]

23

    pub struct ExportSecrets {

24

      #[arg(help = "The secret identifier or prefix to export")]

25

      path: String,

26

27

      #[arg(short, long, help = "The output file")]

28

      output: Option<String>,

29

30

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

31

      vault_location: Option<String>,

32

33

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

34

      keys_location: Option<String>,

35

36

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

37

      key_name: Option<String>,

38

    }

39

40

    impl ExportSecrets {

41

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

42

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

43

        let vault_location: &str = &self

44

          .vault_location

45

          .clone()

46

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

47

        let keys_location: &str = &self

48

          .keys_location

49

          .clone()

50

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

51

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

52

53

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

54

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

55

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

56

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

57

58

        verify_vault(vault_location)?;

59

        verify_key(keys_location, key_name)?;

60

61

        // Open the secret key file

62

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

63

        let key_path = Path::new(keys_location).join(key_name_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 secret_path = Path::new(vault_location).join(path);

69

        let mut values = Vec::new();

70

71

        if secret_path.exists() && secret_path.is_dir() {

72

          // Check for file and subdirectories

73

          let entries = fs::read_dir(secret_path.clone())?

74

            .filter_map(Result::ok)

75

            .collect::<Vec<_>>();

76

77

          // Check for data files in the subdirectories

78

          for entry in entries {

79

            let data_path = if entry.path().is_dir() {

80

              entry.path().join("data.asc")

81

            } else {

82

              entry.path()

83

            };

84

85

            // Read the data file

86

            if data_path.exists() && data_path.is_file() {

87

              let mut data_file = File::open(data_path)?;

88

              let (message, _) = Message::from_armor_single(&mut data_file)?;

89

              let (decrypted_message, _) = message.decrypt(String::new, &[&signed_secret_key])?;

90

              let value = decrypted_message

91

                .get_literal()

92

                .ok_or_else(|| anyhow::anyhow!("Secret value is not a literal"))?

93

                .to_string()

94

                .context("Failed to read secret value")?;

95

96

              values.push(value);

97

            }

98

          }

99

100

          if values.is_empty() {

101

            println!("No secrets found for path: {}", path);

102

          } else {

103

            // Write the values to the output file if provided

104

            // Otherwise, print the values to stdout which can be redirected

105

            // to a file

106

            if let Some(output) = &self.output {

107

              let mut output_file = File::create(output)?;

108

              for value in values {

109

                writeln!(output_file, "{}", value)?;

110

              }

111

              output_file.flush()?;

112

            } else {

113

              for value in values {

114

                println!("{}", value);

115

              }

116

            }

117

          }

118

        } else {

119

          println!("Path does not exist or is not a directory");

120

        }

121

122

        Ok(())

123

      }

124

    }