1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
//! Interface to the Compute@Edge Secret Store.
pub use self::handle::{LookupError, OpenError};
use self::handle::{SecretHandle, SecretStoreHandle};
use bytes::Bytes;
pub(crate) mod handle;
/// A Compute@Edge Secret Store.
///
/// A secret store name has a maximum length of 255 bytes and must
/// contain only letters, numbers, dashes (-), underscores (_), and
/// periods (.).
pub struct SecretStore {
handle: SecretStoreHandle,
}
impl SecretStore {
/// Open the Secret Store with the given name.
pub fn open(name: &str) -> Result<Self, OpenError> {
SecretStoreHandle::open(name).map(|handle| Self { handle })
}
/// Lookup a [`Secret`] by name in this secret store.
///
/// Returns `Some(secret)` if the secret is found, and `None` if the secret was not found.
///
/// See [`try_get()`][`SecretStore::try_get()`] for a fallible equivalent of this method.
pub fn get(&self, name: &str) -> Option<Secret> {
self.try_get(name)
.unwrap_or_else(|e| panic!("lookup for secret `{}` failed: {}", name, e))
}
/// Try to lookup a [`Secret`] by name in this secret store.
///
/// If successful, this method returns `Ok(Some(secret))` if the secret is found, or `Ok(None)`
/// if the secret was not found.
pub fn try_get(&self, name: &str) -> Result<Option<Secret>, LookupError> {
let handle = match self.handle.get(name)? {
Some(h) => h,
None => return Ok(None),
};
let secret = Secret {
name: name.to_owned(),
handle,
plaintext: std::cell::RefCell::new(None),
};
Ok(Some(secret))
}
/// Return true if the secret store contains a secret with the given
/// name.
pub fn contains(&self, name: &str) -> Result<bool, LookupError> {
self.handle.contains(name)
}
}
/// A secret from a secret store.
///
/// A secret name has a maximum length of 255 bytes and must contain
/// only letters, numbers, dashes (-), underscores (_), and periods (.).
///
/// A secret value has a maximum length of 64 KiB.
pub struct Secret {
name: String,
handle: SecretHandle,
plaintext: std::cell::RefCell<Option<Bytes>>,
}
impl Secret {
/// Read the plaintext contents of a secret into memory as a byte buffer.
///
/// Once a secret is read into memory, a secret's contents can be repeatedly accessed cheaply.
///
/// # Examples
///
/// ```no_run
/// # use fastly::secret_store::SecretStore;
/// # let secret_store = SecretStore::open("secret store").unwrap();
/// let secret = secret_store.get("example").unwrap();
/// assert_eq!(secret.plaintext(), "hello world!")
/// ```
///
/// Check if a [`HeaderValue`][`http::HeaderValue`] matches the contents of a secret.
///
/// ```no_run
/// # use fastly::Request;
/// # use fastly::secret_store::SecretStore;
/// # let secret_store = SecretStore::open("secret store").unwrap();
/// # let request = Request::from_client();
/// let secret = secret_store.get("example").unwrap();
/// let header = request.get_header("example").unwrap();
/// if secret.plaintext() == header.as_bytes() {
/// println!("you have guessed correctly!");
/// }
/// ```
pub fn plaintext(&self) -> Bytes {
use std::ops::Deref;
// If we have already paged the plaintext contents of this secret into memory, we can
// cheaply clone a new reference to the existing byte buffer. We are done!
if let Some(plaintext) = self.plaintext.borrow().deref() {
return plaintext.clone();
}
// Use our secret handle to read the contents of the secret into a byte buffer.
let bytes = self
.handle
.plaintext()
.unwrap_or_else(|e| panic!("lookup for secret `{}` failed: {}", self.name, e));
// Before we return store a reference to the bytes, so that future reads are amortized.
self.plaintext.borrow_mut().replace(bytes.clone());
bytes
}
}