use crate::{Attribute, Error};
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fmt::{Debug, Display},
ops::BitOr,
};
#[derive(Copy, Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
pub enum EncryptionHint {
Hybridized,
Classic,
}
impl BitOr for EncryptionHint {
type Output = Self;
#[inline]
fn bitor(self, rhs: Self) -> Self::Output {
if self == Self::Hybridized || rhs == Self::Hybridized {
Self::Hybridized
} else {
Self::Classic
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AxisAttributePorperties {
pub name: String,
pub encryption_hint: EncryptionHint,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PolicyAxis {
pub name: String,
pub attributes_properties: Vec<AxisAttributePorperties>,
pub hierarchical: bool,
}
impl PolicyAxis {
#[must_use]
pub fn new(
name: &str,
attributes_properties: Vec<(&str, EncryptionHint)>,
hierarchical: bool,
) -> Self {
Self {
name: name.to_string(),
attributes_properties: attributes_properties
.into_iter()
.map(|(axis_name, encryption_hint)| AxisAttributePorperties {
name: axis_name.to_string(),
encryption_hint,
})
.collect(),
hierarchical,
}
}
#[must_use]
pub fn len(&self) -> usize {
self.attributes_properties.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.attributes_properties.is_empty()
}
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct PolicyAxesParameters {
pub attribute_names: Vec<String>,
pub is_hierarchical: bool,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct PolicyAttributesParameters {
pub values: Vec<u32>,
pub encryption_hint: EncryptionHint,
}
#[derive(Clone, Serialize, Deserialize, Debug)]
pub struct LegacyPolicy {
pub(crate) last_attribute_value: u32,
pub max_attribute_creations: u32,
pub axes: HashMap<String, PolicyAxesParameters>,
pub attributes: HashMap<Attribute, Vec<u32>>,
}
#[derive(Clone, Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord)]
pub enum PolicyVersion {
V1,
}
#[derive(Clone, PartialEq, Eq, Serialize, Deserialize, Debug)]
pub struct Policy {
pub version: PolicyVersion,
pub(crate) last_attribute_value: u32,
pub max_attribute_creations: u32,
pub axes: HashMap<String, PolicyAxesParameters>,
pub attributes: HashMap<Attribute, PolicyAttributesParameters>,
}
impl Display for Policy {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let json = serde_json::to_string(&self);
match json {
Ok(string) => write!(f, "{string}"),
Err(err) => write!(f, "{err}"),
}
}
}
impl Policy {
pub fn parse_and_convert(bytes: &[u8]) -> Result<Self, Error> {
match serde_json::from_slice(bytes) {
Ok(policy) => Ok(policy),
Err(e) => {
if let Ok(policy) = serde_json::from_slice::<LegacyPolicy>(bytes) {
Ok(Self {
version: PolicyVersion::V1,
max_attribute_creations: policy.max_attribute_creations,
last_attribute_value: policy.last_attribute_value,
axes: policy.axes,
attributes: policy
.attributes
.into_iter()
.map(|(name, values)| {
(
name,
PolicyAttributesParameters {
values,
encryption_hint: EncryptionHint::Classic,
},
)
})
.collect(),
})
} else {
Err(Error::DeserializationError(e))
}
}
}
}
#[inline]
#[must_use]
pub fn new(nb_creations: u32) -> Self {
Self {
version: PolicyVersion::V1,
last_attribute_value: 0,
max_attribute_creations: nb_creations,
axes: HashMap::new(),
attributes: HashMap::new(),
}
}
#[inline]
#[must_use]
pub fn remaining_attribute_creations(&self) -> u32 {
self.max_attribute_creations - self.last_attribute_value
}
pub fn add_axis(&mut self, axis: PolicyAxis) -> Result<(), Error> {
if axis.len() > (self.max_attribute_creations - self.last_attribute_value) as usize {
return Err(Error::CapacityOverflow);
}
if self.axes.get(&axis.name).is_some() {
return Err(Error::ExistingPolicy(axis.name));
}
let mut axis_attributes = Vec::with_capacity(axis.attributes_properties.len());
for properties in axis.attributes_properties {
self.last_attribute_value += 1;
axis_attributes.push(properties.name.clone());
let attribute = (axis.name.clone(), properties.name.clone()).into();
if self.attributes.get(&attribute).is_some() {
return Err(Error::ExistingPolicy(format!("{attribute:?}")));
}
self.attributes.insert(
attribute,
PolicyAttributesParameters {
values: [self.last_attribute_value].into(),
encryption_hint: properties.encryption_hint,
},
);
}
self.axes.insert(
axis.name,
PolicyAxesParameters {
attribute_names: axis_attributes,
is_hierarchical: axis.hierarchical,
},
);
Ok(())
}
pub fn rotate(&mut self, attr: &Attribute) -> Result<(), Error> {
if self.last_attribute_value == self.max_attribute_creations {
Err(Error::CapacityOverflow)
} else if let Some(attribute_parameters) = self.attributes.get_mut(attr) {
self.last_attribute_value += 1;
attribute_parameters.values.push(self.last_attribute_value);
Ok(())
} else {
Err(Error::AttributeNotFound(attr.to_string()))
}
}
#[inline]
#[must_use]
pub fn attributes(&self) -> Vec<Attribute> {
self.attributes.keys().cloned().collect::<Vec<Attribute>>()
}
#[inline]
pub fn attribute_values(&self, attribute: &Attribute) -> Result<Vec<u32>, Error> {
self.attributes
.get(attribute)
.map(|attribute_parameters| attribute_parameters.values.iter().rev().copied().collect())
.ok_or_else(|| Error::AttributeNotFound(attribute.to_string()))
}
#[inline]
pub fn attribute_hybridization_hint(
&self,
attribute: &Attribute,
) -> Result<EncryptionHint, Error> {
self.attributes
.get(attribute)
.map(|attribute_parameters| attribute_parameters.encryption_hint)
.ok_or_else(|| Error::AttributeNotFound(attribute.to_string()))
}
#[inline]
pub fn attribute_current_value(&self, attribute: &Attribute) -> Result<u32, Error> {
self.attributes
.get(attribute)
.map(|attribute_parameters| {
attribute_parameters.values[attribute_parameters.values.len() - 1]
})
.ok_or_else(|| Error::AttributeNotFound(attribute.to_string()))
}
}