#[derive(Debug, serde::Serialize)]
pub(super) struct KeyStatusPayload<'a, T: AsRef<str>> {
#[serde(with = "serde_fingerprints")]
pub(super) fingerprints: &'a [T],
}
mod serde_fingerprints {
use super::normalize_fingerprint;
pub fn serialize<S, T>(obj: &[T], serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
T: AsRef<str>,
{
use serde::ser::SerializeSeq;
let mut seq = serializer.serialize_seq(Some(obj.len()))?;
for fingerprint in obj {
seq.serialize_element(
&normalize_fingerprint(fingerprint.as_ref()).map_err(serde::ser::Error::custom)?,
)?;
}
seq.end()
}
}
fn normalize_fingerprint(raw_fingerprint: &str) -> Result<String, String> {
let fingerprint = raw_fingerprint
.parse()
.map_err(|e| format!("Invalid fingerprint '{raw_fingerprint}': {e}"))?;
let sequoia_openpgp::Fingerprint::V4(_) = &fingerprint else {
return Err(format!("Invalid fingerprint '{raw_fingerprint}'"));
};
Ok(fingerprint.to_hex())
}
fn metadata_as_json_string<S>(
metadata: &crate::package::Metadata,
serializer: S,
) -> Result<S::Ok, S::Error>
where
S: serde::ser::Serializer,
{
serializer.serialize_str(&serde_json::to_string(metadata).map_err(serde::ser::Error::custom)?)
}
#[derive(Debug, serde::Serialize)]
pub(super) struct CheckPackage<'a> {
#[serde(serialize_with = "metadata_as_json_string")]
pub(super) metadata: &'a crate::package::Metadata,
pub(super) file_name: &'a str,
}
#[cfg(feature = "auth")]
#[derive(Debug, serde::Serialize)]
#[serde(untagged)]
pub(super) enum Sts<'a> {
Read { resource: StsResource<'a> },
Write { dtr: u32 },
}
#[cfg(feature = "auth")]
#[derive(Debug, serde::Serialize)]
#[serde(untagged)]
pub(super) enum StsResource<'a> {
Dtr(u32),
ObjectName(&'a str),
}
#[cfg(feature = "auth")]
impl<'a> Sts<'a> {
pub(super) fn from_permission(permission: &'a crate::remote::s3::AccessPermission) -> Self {
use crate::remote::s3::AccessPermission;
match permission {
AccessPermission::GetObject(name) => Self::Read {
resource: StsResource::ObjectName(name),
},
AccessPermission::ListObjects(dtr) => Self::Read {
resource: StsResource::Dtr(*dtr),
},
AccessPermission::PutObject(dtr) => Self::Write { dtr: *dtr },
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::portal::{ApprovalStatus, KeyStatus};
#[test]
fn test_err_fingerprints() {
for fingerprint in ["1234af", &"M".repeat(4), &"1234af".repeat(40)] {
assert!(normalize_fingerprint(fingerprint).is_err())
}
}
#[test]
fn test_ok_fingerprints() {
for fingerprint in [
"D99AD936FC83C9BABDE7C33E1CF8C1A2076818C3", "d99ad936fc83c9babde7c33e1cf8c1a2076818c3", "0xd99ad936fc83c9babde7c33e1cf8c1a2076818c3", "01AB 4567 89AB CDEF 0123 4567 89AB CDEF 0123 4567", ] {
assert!(
normalize_fingerprint(fingerprint).is_ok(),
"'{fingerprint}' is NOT OK"
)
}
}
#[test]
fn test_serialize_keystatus() {
let fingerprints = KeyStatusPayload {
fingerprints: &[
"D99AD936FC83C9BABDE7C33E1CF8C1A2076818C3",
"95D6F0110B446D6EABFEA10C6B52BA2D01E77CA5",
],
};
let serialized = serde_json::to_string(&fingerprints).unwrap();
assert_eq!(
&serialized,
r#"{"fingerprints":["D99AD936FC83C9BABDE7C33E1CF8C1A2076818C3","95D6F0110B446D6EABFEA10C6B52BA2D01E77CA5"]}"#
);
}
#[test]
fn test_deserialize_fingerprints() {
let payload = r#"[{"fingerprint":"D99AD936FC83C9BABDE7C33E1CF8C1A2076818C3","status":"APPROVED"},
{"fingerprint":"95D6F0110B446D6EABFEA10C6B52BA2D01E77CA5","status":"APPROVAL_REVOKED"}]"#;
let key_statuses = serde_json::from_str::<Vec<KeyStatus>>(payload).unwrap();
let f1 = KeyStatus {
fingerprint: String::from("D99AD936FC83C9BABDE7C33E1CF8C1A2076818C3"),
status: ApprovalStatus::Approved,
};
let f2 = KeyStatus {
fingerprint: String::from("95D6F0110B446D6EABFEA10C6B52BA2D01E77CA5"),
status: ApprovalStatus::ApprovalRevoked,
};
assert!(
&key_statuses
.iter()
.zip([f1, f2].iter())
.all(|(a, b)| a == b)
);
}
#[cfg(feature = "auth")]
#[test]
fn serialize_sts_payload() {
assert_eq!(
r#"{"dtr":42}"#,
&serde_json::to_string(&Sts::Write { dtr: 42 }).unwrap()
);
assert_eq!(
r#"{"resource":42}"#,
&serde_json::to_string(&Sts::Read {
resource: StsResource::Dtr(42)
})
.unwrap()
);
assert_eq!(
r#"{"resource":"foo.zip"}"#,
&serde_json::to_string(&Sts::Read {
resource: StsResource::ObjectName("foo.zip")
})
.unwrap()
);
}
}