use chrono::{DateTime, Utc};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::BTreeSet;
use std::fmt;
use std::str::FromStr;
use super::error::ValidationError;
mod base64_bytes {
use base64::{engine::general_purpose::STANDARD, Engine};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
pub fn serialize<S: Serializer>(bytes: &[u8], s: S) -> Result<S::Ok, S::Error> {
STANDARD.encode(bytes).serialize(s)
}
pub fn deserialize<'de, D: Deserializer<'de>>(d: D) -> Result<Vec<u8>, D::Error> {
let s = String::deserialize(d)?;
STANDARD.decode(&s).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Operation(String);
impl Operation {
pub fn new(s: impl Into<String>) -> Result<Self, ValidationError> {
let s = s.into();
if s.is_empty() {
return Err(ValidationError::InvalidOperation(s));
}
if !s
.chars()
.all(|c| c.is_alphanumeric() || matches!(c, ':' | '-' | '_' | '*' | '.'))
{
return Err(ValidationError::InvalidOperation(s));
}
if s.contains("::") {
return Err(ValidationError::InvalidOperation(s));
}
if s.starts_with(':') || s.ends_with(':') {
return Err(ValidationError::InvalidOperation(s));
}
if s.contains('*') {
if s == "*" {
} else if s.ends_with(":*") {
let prefix = &s[..s.len() - 2];
if prefix.contains('*') {
return Err(ValidationError::InvalidOperation(s));
}
} else {
return Err(ValidationError::InvalidOperation(s));
}
}
Ok(Self(s))
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for Operation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl FromStr for Operation {
type Err = ValidationError;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Self::new(s)
}
}
impl Serialize for Operation {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
self.0.serialize(s)
}
}
impl<'de> Deserialize<'de> for Operation {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
let s = String::deserialize(d)?;
Operation::new(s).map_err(serde::de::Error::custom)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct Pca {
pub p_0: String,
pub ops: BTreeSet<Operation>,
pub kid: String,
pub exp: Option<DateTime<Utc>>,
pub nbf: Option<DateTime<Utc>>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SignedPca {
#[serde(with = "base64_bytes")]
pub raw: Vec<u8>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct AuthorityChain {
hops: Vec<SignedPca>,
origin_principal: String,
final_ops: BTreeSet<Operation>,
}
impl AuthorityChain {
pub(crate) fn new(
hops: Vec<SignedPca>,
origin_principal: String,
final_ops: BTreeSet<Operation>,
) -> Self {
Self {
hops,
origin_principal,
final_ops,
}
}
pub fn hops(&self) -> &[SignedPca] {
&self.hops
}
pub fn origin_principal(&self) -> &str {
&self.origin_principal
}
pub fn final_ops(&self) -> &BTreeSet<Operation> {
&self.final_ops
}
}