Skip to main content

mz_deploy/secret_resolver/
json_field.rs

1// Copyright Materialize, Inc. and contributors. All rights reserved.
2//
3// Use of this software is governed by the Business Source License
4// included in the LICENSE file.
5//
6// As of the Change Date specified in that file, in accordance with
7// the Business Source License, use of this software will be governed
8// by the Apache License, Version 2.0.
9
10//! Shared helper for extracting a top-level string field from a JSON secret.
11//!
12//! Used by providers that support the `name(secret, field)` shape — `aws_secret`
13//! for RDS-style credential blobs.
14
15/// Extract a top-level string field from a JSON secret.
16///
17/// Returns the field's string content on success. On failure, returns a
18/// reason string suitable for `SecretResolveError::ResolutionFailed`.
19pub(super) fn extract_json_field(
20    secret_string: &str,
21    json_key: &str,
22    secret_name: &str,
23) -> Result<String, String> {
24    let value: serde_json::Value = serde_json::from_str(secret_string).map_err(|e| {
25        format!(
26            "secret '{}' is not valid JSON; cannot extract field '{}': {}",
27            secret_name, json_key, e
28        )
29    })?;
30
31    let field = value
32        .get(json_key)
33        .ok_or_else(|| format!("secret '{}' has no field '{}'", secret_name, json_key))?;
34
35    match field {
36        serde_json::Value::String(s) => Ok(s.clone()),
37        _ => Err(format!(
38            "field '{}' in secret '{}' is not a string",
39            json_key, secret_name
40        )),
41    }
42}
43
44#[cfg(test)]
45mod tests {
46    use super::*;
47
48    #[mz_ore::test]
49    fn extract_json_field_returns_string_value() {
50        let secret = r#"{"username":"alice","password":"s3cr3t"}"#;
51        assert_eq!(
52            extract_json_field(secret, "password", "rds-creds").unwrap(),
53            "s3cr3t"
54        );
55        assert_eq!(
56            extract_json_field(secret, "username", "rds-creds").unwrap(),
57            "alice"
58        );
59    }
60
61    #[mz_ore::test]
62    fn extract_json_field_errors_on_missing_key() {
63        let secret = r#"{"username":"alice"}"#;
64        let err = extract_json_field(secret, "password", "rds-creds").unwrap_err();
65        assert!(err.contains("rds-creds"));
66        assert!(err.contains("password"));
67    }
68
69    #[mz_ore::test]
70    fn extract_json_field_errors_on_non_string_value() {
71        let secret = r#"{"port":5432}"#;
72        let err = extract_json_field(secret, "port", "rds-creds").unwrap_err();
73        assert!(err.contains("rds-creds"));
74        assert!(err.contains("port"));
75        assert!(err.contains("not a string"));
76    }
77
78    #[mz_ore::test]
79    fn extract_json_field_errors_on_invalid_json() {
80        let secret = "not json at all";
81        let err = extract_json_field(secret, "password", "rds-creds").unwrap_err();
82        assert!(err.contains("rds-creds"));
83        assert!(err.contains("not valid JSON"));
84    }
85}