use serde::{Deserialize, Serialize};
use crate::Result;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
#[non_exhaustive]
pub enum ActType {
Use,
Export,
Write,
Rotate,
Enroll,
Revoke,
Custom(String),
}
impl ActType {
pub fn is_rotation_class(&self) -> bool {
matches!(
self,
ActType::Write | ActType::Rotate | ActType::Enroll | ActType::Revoke
)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Act {
#[serde(rename = "type")]
pub kind: ActType,
pub target: String,
#[serde(default)]
pub scope: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Bind {
pub redeemer: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub recipient: Option<RecipientPk>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RecipientPk {
pub alg: String,
pub bytes: String,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Default)]
#[serde(rename_all = "lowercase")]
pub enum Multiplicity {
#[default]
One,
Unbounded,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
pub struct Valid {
pub iat: u64,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub exp: Option<u64>,
#[serde(default)]
pub multiplicity: Multiplicity,
}
impl Valid {
pub fn single_use(iat: u64, exp: Option<u64>) -> Self {
Self {
iat,
exp,
multiplicity: Multiplicity::One,
}
}
pub fn check(&self, now_unix: u64, iat_skew_secs: u64) -> Result<()> {
if self.iat > now_unix + iat_skew_secs {
return Err(crate::Error::OperationIatSkew);
}
if let Some(exp) = self.exp {
if exp < now_unix {
return Err(crate::Error::OperationExpired);
}
}
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Operation {
pub act: Act,
pub bind: Bind,
pub valid: Valid,
}
impl Operation {
pub fn check_validity(&self, now_unix: u64, iat_skew_secs: u64) -> Result<()> {
self.valid.check(now_unix, iat_skew_secs)
}
pub fn canonical_bytes(&self) -> Result<Vec<u8>> {
let v =
serde_json::to_value(self).map_err(|_| crate::Error::Encoding("Operation→Value"))?;
crate::canonical::canonicalize_strict(&v)
}
}