diff --git a/cli/tests/integration/secret_store.rs b/cli/tests/integration/secret_store.rs
index 25ef674a..dbc911ed 100644
--- a/cli/tests/integration/secret_store.rs
+++ b/cli/tests/integration/secret_store.rs
@@ -14,6 +14,7 @@ viceroy_test!(secret_store_works, |is_component| {
language = "rust"
[local_server]
secret_stores.store_one = [{key = "first", data = "This is some data"},{key = "second", file = "../test-fixtures/data/kv-store.txt"}]
+ secret_stores.store_two = {file = "../test-fixtures/data/json-secret_store.json", format = "json"}
"#;
let resp = Test::using_fixture("secret-store.wasm")
diff --git a/lib/src/config/secret_store.rs b/lib/src/config/secret_store.rs
index 28c00b29..e2395ac5 100644
--- a/lib/src/config/secret_store.rs
+++ b/lib/src/config/secret_store.rs
@@ -3,7 +3,7 @@ use {
error::{FastlyConfigError, SecretStoreConfigError},
secret_store::{SecretStore, SecretStores},
},
- std::{convert::TryFrom, fs},
+ std::{collections::HashMap, convert::TryFrom, fs},
toml::value::Table,
};
@@ -23,12 +23,66 @@ impl TryFrom
for SecretStoreConfig {
});
}
- let items = items.as_array().ok_or_else(|| {
- FastlyConfigError::InvalidSecretStoreDefinition {
- name: store_name.to_string(),
- err: SecretStoreConfigError::NotAnArray,
+ // Either the items here is from a top-level file with
+ // "file" and "format" keys or it's an inline array.
+ // We try to parse either one of them to the same Vec
+ // to allow them to run through the same validation path futher down
+ let file_path = items
+ .as_table()
+ .and_then(|table| table.get("file"))
+ .and_then(|file| file.as_str());
+ let file_format = items
+ .as_table()
+ .and_then(|table| table.get("format"))
+ .and_then(|format| format.as_str());
+
+ let items: Vec = match (file_path, file_format) {
+ (Some(file_path), Some(file_type)) => {
+ if file_type != "json" {
+ return Err(FastlyConfigError::InvalidSecretStoreDefinition {
+ name: store_name.to_string(),
+ err: SecretStoreConfigError::InvalidFileFormat(file_type.to_string()),
+ });
+ }
+
+ let json = read_json_contents(&file_path).map_err(|e| {
+ FastlyConfigError::InvalidSecretStoreDefinition {
+ name: store_name.to_string(),
+ err: e,
+ }
+ })?;
+
+ let toml: Vec = json
+ .into_iter()
+ .map(|(key, value)| {
+ toml::toml! {
+ key = key
+ data = value
+ }
+ })
+ .collect();
+
+ toml
+ }
+ (None, None) => {
+ // No file or format specified, parse the TOML as an array
+ items
+ .as_array()
+ .ok_or_else(|| FastlyConfigError::InvalidSecretStoreDefinition {
+ name: store_name.to_string(),
+ err: SecretStoreConfigError::NotAnArray,
+ })?
+ .clone()
}
- })?;
+ // This means that *either* `format` or `file` is set, which isn't allowed
+ // we need both or neither.
+ (_, _) => {
+ return Err(FastlyConfigError::InvalidSecretStoreDefinition {
+ name: store_name.to_string(),
+ err: SecretStoreConfigError::OnlyOneFormatOrFileSet,
+ });
+ }
+ };
let mut secret_store = SecretStore::new();
for item in items.iter() {
@@ -112,3 +166,10 @@ fn is_valid_name(name: &str) -> bool {
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.')
}
+
+fn read_json_contents(filename: &str) -> Result, SecretStoreConfigError> {
+ let data = fs::read_to_string(filename).map_err(SecretStoreConfigError::IoError)?;
+ let map: HashMap =
+ serde_json::from_str(&data).map_err(|_| SecretStoreConfigError::FileWrongFormat)?;
+ Ok(map)
+}
diff --git a/lib/src/error.rs b/lib/src/error.rs
index b6ff8538..765399a8 100644
--- a/lib/src/error.rs
+++ b/lib/src/error.rs
@@ -666,6 +666,14 @@ pub enum SecretStoreConfigError {
#[error(transparent)]
IoError(std::io::Error),
+ #[error("'{0}' is not a valid format for the secret store. Supported format(s) are: 'json'.")]
+ InvalidFileFormat(String),
+ #[error("When using a top-level 'file' to load data, both 'file' and 'format' must be set.")]
+ OnlyOneFormatOrFileSet,
+ #[error(
+ "The file is of the wrong format. The file is expected to contain a single JSON object."
+ )]
+ FileWrongFormat,
#[error("The `file` and `data` keys for the object `{0}` are set. Only one can be used.")]
FileAndData(String),
#[error("The `file` or `data` key for the object `{0}` is not set. One must be used.")]
diff --git a/test-fixtures/data/json-secret_store.json b/test-fixtures/data/json-secret_store.json
new file mode 100644
index 00000000..c62326c1
--- /dev/null
+++ b/test-fixtures/data/json-secret_store.json
@@ -0,0 +1,4 @@
+{
+ "first": "first secret",
+ "second": "another secret"
+}
diff --git a/test-fixtures/src/bin/secret-store.rs b/test-fixtures/src/bin/secret-store.rs
index 33644fd8..f8771fe0 100644
--- a/test-fixtures/src/bin/secret-store.rs
+++ b/test-fixtures/src/bin/secret-store.rs
@@ -22,6 +22,13 @@ fn main() {
_ => panic!(),
}
+ let store_two = SecretStore::open("store_two").unwrap();
+ assert_eq!(store_two.get("first").unwrap().plaintext(), "first secret");
+ assert_eq!(
+ store_two.get("second").unwrap().plaintext(),
+ "another secret",
+ );
+
let hello_bytes = "hello, wasm_world!".as_bytes().to_vec();
let secret = Secret::from_bytes(hello_bytes).unwrap();
assert_eq!("hello, wasm_world!", secret.plaintext());