use bytes::Bytes;
use fastly_shared::FastlyStatus;
#[cfg(not(target_env = "p1"))]
use fastly_sys::service0_1_0::fastly::compute as wit;
#[cfg(target_env = "p1")]
use {crate::abi, bytes::BytesMut};
#[derive(Debug)]
#[cfg_attr(target_env = "p1", derive(Eq, Hash, PartialEq))]
#[repr(transparent)]
pub struct SecretStoreHandle {
#[cfg(target_env = "p1")]
handle: u32,
#[cfg(not(target_env = "p1"))]
handle: wit::secret_store::Store,
}
impl SecretStoreHandle {
#[cfg(target_env = "p1")]
pub const INVALID: Self = SecretStoreHandle {
handle: fastly_shared::INVALID_SECRET_STORE_HANDLE,
};
pub fn open(secret_store_name: &str) -> Result<Self, OpenError> {
use OpenError::*;
#[cfg(target_env = "p1")]
{
let mut handle = Self::INVALID;
unsafe {
abi::fastly_secret_store::open(
secret_store_name.as_ptr(),
secret_store_name.len(),
handle.as_u32_mut(),
)
}
.result()
.map(|_| handle)
.map_err(|s| match s {
FastlyStatus::NONE => SecretStoreDoesNotExist(secret_store_name.to_string()),
FastlyStatus::INVAL => InvalidSecretStoreName(secret_store_name.to_string()),
_ => Unexpected(s),
})
}
#[cfg(not(target_env = "p1"))]
{
let handle =
wit::secret_store::Store::open(secret_store_name).map_err(|err| match err {
wit::types::OpenError::NameTooLong | wit::types::OpenError::InvalidSyntax => {
InvalidSecretStoreName(secret_store_name.to_string())
}
wit::types::OpenError::NotFound => {
SecretStoreDoesNotExist(secret_store_name.to_string())
}
wit::types::OpenError::Unsupported => Unexpected(FastlyStatus::UNSUPPORTED),
wit::types::OpenError::LimitExceeded => Unexpected(FastlyStatus::LIMITEXCEEDED),
_ => Unexpected(FastlyStatus::ERROR),
})?;
Ok(Self { handle })
}
}
pub fn get(&self, secret_name: &str) -> Result<Option<SecretHandle>, LookupError> {
use LookupError::*;
#[cfg(target_env = "p1")]
{
let mut handle = fastly_shared::INVALID_SECRET_HANDLE;
let status = unsafe {
abi::fastly_secret_store::get(
self.as_u32(),
secret_name.as_ptr(),
secret_name.len(),
&mut handle,
)
};
match status {
FastlyStatus::OK => Ok(Some(SecretHandle { handle })),
FastlyStatus::NONE => Ok(None),
FastlyStatus::BADF => Err(InvalidSecretStoreHandle),
FastlyStatus::INVAL => Err(InvalidSecretName(secret_name.to_string())),
_ => Err(Unexpected(status)),
}
}
#[cfg(not(target_env = "p1"))]
{
let maybe = self.handle.get(secret_name).map_err(|err| match err {
wit::types::Error::InvalidArgument => InvalidSecretName(secret_name.to_string()),
_ => Unexpected(FastlyStatus::ERROR),
})?;
Ok(maybe.map(|handle| SecretHandle { handle }))
}
}
pub fn contains(&self, name: &str) -> Result<bool, LookupError> {
match self.get(name) {
Ok(Some(_)) => Ok(true),
Ok(None) => Ok(false),
Err(e) => Err(e),
}
}
#[cfg(target_env = "p1")]
pub(crate) fn as_u32(&self) -> u32 {
self.handle
}
#[cfg(target_env = "p1")]
pub(crate) fn as_u32_mut(&mut self) -> &mut u32 {
&mut self.handle
}
#[cfg(not(target_env = "p1"))]
pub fn from_handle(handle: wit::secret_store::Store) -> Self {
Self { handle }
}
#[cfg(target_env = "p1")]
pub unsafe fn from_u32(handle: u32) -> Self {
Self { handle }
}
}
#[derive(Debug)]
#[cfg_attr(target_env = "p1", derive(Eq, Hash, PartialEq))]
#[repr(transparent)]
pub struct SecretHandle {
#[cfg(target_env = "p1")]
handle: u32,
#[cfg(not(target_env = "p1"))]
handle: wit::secret_store::Secret,
}
impl SecretHandle {
#[cfg(target_env = "p1")]
pub const INVALID: Self = SecretHandle {
handle: fastly_shared::INVALID_SECRET_HANDLE,
};
pub fn plaintext(&self) -> Result<Bytes, DecryptError> {
#[cfg(target_env = "p1")]
{
const INITIAL_SECRET_PLAINTEXT_BUF_SIZE: usize = 1024;
if self.is_invalid() {
panic!("cannot lookup plaintext with invalid secret handle");
}
let mut plaintext_buf = BytesMut::zeroed(INITIAL_SECRET_PLAINTEXT_BUF_SIZE);
let mut nwritten = 0usize;
let status = unsafe {
abi::fastly_secret_store::plaintext(
self.as_u32(),
plaintext_buf.as_mut_ptr(),
plaintext_buf.len(),
&mut nwritten,
)
};
let status = match status {
FastlyStatus::BUFLEN if nwritten != 0 => {
plaintext_buf.resize(nwritten, 0);
nwritten = 0;
unsafe {
abi::fastly_secret_store::plaintext(
self.as_u32(),
plaintext_buf.as_mut_ptr(),
plaintext_buf.len(),
&mut nwritten,
)
}
}
s => s,
};
match status.result() {
Ok(()) => {
unsafe {
plaintext_buf.set_len(nwritten);
}
Ok(plaintext_buf.freeze())
}
Err(status) => Err(DecryptError::Unexpected(status)),
}
}
#[cfg(not(target_env = "p1"))]
{
let buf = self
.handle
.plaintext(u64::MAX)
.map_err(|err| DecryptError::Unexpected(err.into()))?;
Ok(buf.into())
}
}
pub fn new(secret: &[u8]) -> Result<SecretHandle, FastlyStatus> {
let len = secret.len();
if len > (64 * 1024) {
return Err(FastlyStatus::INVAL);
}
#[cfg(target_env = "p1")]
{
let ptr = secret.as_ptr();
let mut handle = fastly_shared::INVALID_SECRET_HANDLE;
let res = unsafe { fastly_sys::fastly_secret_store::from_bytes(ptr, len, &mut handle) };
if res != FastlyStatus::OK {
return Err(res);
}
if handle == fastly_shared::INVALID_SECRET_HANDLE {
return Err(FastlyStatus::ERROR);
}
Ok(SecretHandle { handle })
}
#[cfg(not(target_env = "p1"))]
{
let handle = wit::secret_store::Secret::from_bytes(secret)?;
Ok(SecretHandle { handle })
}
}
#[cfg(target_env = "p1")]
pub fn is_invalid(&self) -> bool {
self.handle == Self::INVALID.handle
}
#[cfg(target_env = "p1")]
pub(crate) fn as_u32(&self) -> u32 {
self.handle
}
#[cfg(not(target_env = "p1"))]
pub(crate) fn as_handle(&self) -> &wit::secret_store::Secret {
&self.handle
}
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum OpenError {
#[error("secret store could not be found: {0}")]
SecretStoreDoesNotExist(String),
#[error("invalid secret store name: {0}")]
InvalidSecretStoreName(String),
#[error("unexpected error: {0:?}")]
Unexpected(FastlyStatus),
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum LookupError {
#[error("invalid secret store handle")]
InvalidSecretStoreHandle,
#[error("invalid secret name: {0}")]
InvalidSecretName(String),
#[error("invalid secret handle")]
InvalidSecretHandle,
#[error("unexpected error: {0:?}")]
Unexpected(FastlyStatus),
}
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum DecryptError {
#[error("unexpected error: {0:?}")]
Unexpected(FastlyStatus),
}