feldera_types/
secret_ref.rs

1use std::fmt;
2use std::fmt::Formatter;
3
4use serde::Deserialize;
5use utoipa::ToSchema;
6
7/// Enumeration which holds a string or a secret reference which is
8/// not yet resolved.
9///
10/// Instantiation is done either directly, using deserialization or
11/// by matching a basic string to a particular pattern. Note that
12/// the former two allow simple strings to be instantiated that
13/// the latter with the pattern would normally map into secret
14/// reference.
15///
16/// This class is located in the pipeline-types crate because in future
17/// versions it will likely become an object within the API.
18/// Deserialization is implemented already for that reason.
19#[derive(Debug, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, ToSchema)]
20pub enum MaybeSecretRef {
21    #[serde(rename = "string")]
22    String(String),
23    #[serde(rename = "secret_ref")]
24    SecretRef(String),
25}
26
27impl fmt::Display for MaybeSecretRef {
28    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
29        write!(f, "{:?}", self)
30    }
31}
32
33impl MaybeSecretRef {
34    /// Determines whether a given string value refers to a secret or is simply
35    /// a string based on whether it matches the pattern.
36    ///
37    /// A secret must follow the pattern:
38    /// "${secret:secret-identifier}"
39    ///
40    /// For example for a secret identified by "database-1-user":
41    /// "${secret:database-1-user}"
42    ///
43    /// Any string value which does not follow the above pattern is determined
44    /// to be a simple string.
45    ///
46    /// Note that here is not checked whether the secret identifier can be
47    /// resolved, nor is specified which requirements the resolver has for
48    /// the identifier to be valid.
49    pub fn new_using_pattern_match(value: String) -> MaybeSecretRef {
50        if value.starts_with("${secret:") && value.ends_with('}') {
51            // Because the pattern only has ASCII characters, they are encoded as single
52            // bytes. The secret reference is extracted by slicing away the
53            // first 9 bytes and the last byte.
54            let from_idx_incl = 9;
55            let till_idx_excl = value.len() - 1;
56            MaybeSecretRef::SecretRef(value[from_idx_incl..till_idx_excl].to_string())
57        } else {
58            MaybeSecretRef::String(value)
59        }
60    }
61}
62
63#[cfg(test)]
64mod tests {
65    use super::MaybeSecretRef;
66
67    #[test]
68    fn test_format() {
69        assert_eq!(
70            format!("{}", MaybeSecretRef::String("example123".to_string())),
71            "String(\"example123\")"
72        );
73        assert_eq!(
74            format!("{:?}", MaybeSecretRef::String("example123".to_string())),
75            "String(\"example123\")"
76        );
77        assert_eq!(
78            format!("{}", MaybeSecretRef::SecretRef("example123".to_string())),
79            "SecretRef(\"example123\")"
80        );
81        assert_eq!(
82            format!("{:?}", MaybeSecretRef::SecretRef("example123".to_string())),
83            "SecretRef(\"example123\")"
84        );
85    }
86
87    #[test]
88    fn test_deserialize_string() {
89        let data = "{ \"string\": \"example123\" }";
90        assert_eq!(
91            serde_json::from_str::<MaybeSecretRef>(data).unwrap(),
92            MaybeSecretRef::String("example123".to_string())
93        );
94        let data = "!string \"example123\"";
95        assert_eq!(
96            serde_yaml::from_str::<MaybeSecretRef>(data).unwrap(),
97            MaybeSecretRef::String("example123".to_string())
98        );
99    }
100
101    #[test]
102    fn test_deserialize_secret_ref() {
103        let data = "{ \"secret_ref\": \"example456\" }";
104        assert_eq!(
105            serde_json::from_str::<MaybeSecretRef>(data).unwrap(),
106            MaybeSecretRef::SecretRef("example456".to_string())
107        );
108        let data = "!secret_ref \"example456\"";
109        assert_eq!(
110            serde_yaml::from_str::<MaybeSecretRef>(data).unwrap(),
111            MaybeSecretRef::SecretRef("example456".to_string())
112        );
113    }
114
115    #[test]
116    fn test_string() {
117        let test_values_and_expectations = vec![
118            ("", MaybeSecretRef::String("".to_string())),
119            ("a", MaybeSecretRef::String("a".to_string())),
120            ("abc", MaybeSecretRef::String("abc".to_string())),
121            (
122                "/some/path/to/file.txt",
123                MaybeSecretRef::String("/some/path/to/file.txt".to_string()),
124            ),
125            ("$abc", MaybeSecretRef::String("$abc".to_string())),
126            ("${secret", MaybeSecretRef::String("${secret".to_string())),
127            ("}", MaybeSecretRef::String("}".to_string())),
128            ("${secre:}", MaybeSecretRef::String("${secre:}".to_string())),
129            // Unicode "Slightly Smiling Face": U+1F642
130            ("\u{1F642}", MaybeSecretRef::String("\u{1F642}".to_string())),
131        ];
132        for (value, expectation) in test_values_and_expectations {
133            assert_eq!(
134                MaybeSecretRef::new_using_pattern_match(value.to_string()),
135                expectation
136            );
137        }
138    }
139
140    #[test]
141    fn test_secret_ref() {
142        let test_values_and_expectations = vec![
143            ("${secret:}", MaybeSecretRef::SecretRef("".to_string())),
144            ("${secret:a}", MaybeSecretRef::SecretRef("a".to_string())),
145            (
146                "${secret:abc}",
147                MaybeSecretRef::SecretRef("abc".to_string()),
148            ),
149            (
150                "${secret:/some/path/to/file.txt}",
151                MaybeSecretRef::SecretRef("/some/path/to/file.txt".to_string()),
152            ),
153            (
154                "${secret:$abc}",
155                MaybeSecretRef::SecretRef("$abc".to_string()),
156            ),
157            (
158                "${secret:${secret:}}",
159                MaybeSecretRef::SecretRef("${secret:}".to_string()),
160            ),
161            (
162                "${secret:${secret:abc}}",
163                MaybeSecretRef::SecretRef("${secret:abc}".to_string()),
164            ),
165            // Unicode "Slightly Smiling Face": U+1F642
166            (
167                "${secret:\u{1F642}}",
168                MaybeSecretRef::SecretRef("\u{1F642}".to_string()),
169            ),
170        ];
171        for (value, expectation) in test_values_and_expectations {
172            assert_eq!(
173                MaybeSecretRef::new_using_pattern_match(value.to_string()),
174                expectation
175            );
176        }
177    }
178}