use super::types_gen::{AuthZenError, AuthZenErrorCode};
use serde::de::{MapAccess, Visitor};
use serde::ser::{Error as _, SerializeMap};
use serde::{Deserialize, Deserializer, Serialize, Serializer};
use std::collections::BTreeMap;
use std::fmt;
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum Attribute<T> {
Known(T),
Absent,
Unknown(String),
}
impl<T> Attribute<T> {
pub fn known(value: impl Into<T>) -> Self {
Attribute::Known(value.into())
}
pub fn absent() -> Self {
Attribute::Absent
}
pub fn unknown(why: impl Into<String>) -> Self {
Attribute::Unknown(why.into())
}
pub fn fold<R>(
&self,
on_known: impl FnOnce(&T) -> R,
on_absent: impl FnOnce() -> R,
on_unknown: impl FnOnce(&str) -> R,
) -> R {
match self {
Attribute::Known(v) => on_known(v),
Attribute::Absent => on_absent(),
Attribute::Unknown(why) => on_unknown(why),
}
}
pub fn as_known(&self) -> Option<&T> {
match self {
Attribute::Known(v) => Some(v),
_ => None,
}
}
pub fn is_known(&self) -> bool {
matches!(self, Attribute::Known(_))
}
pub fn is_absent(&self) -> bool {
matches!(self, Attribute::Absent)
}
pub fn is_unknown(&self) -> bool {
matches!(self, Attribute::Unknown(_))
}
pub fn map<U>(self, f: impl FnOnce(T) -> U) -> Attribute<U> {
match self {
Attribute::Known(v) => Attribute::Known(f(v)),
Attribute::Absent => Attribute::Absent,
Attribute::Unknown(why) => Attribute::Unknown(why),
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct AttributeValue(AttributeValueInner);
#[derive(Clone, Debug, PartialEq)]
enum AttributeValueInner {
Json(serde_json::Value),
Nested(AttributeMap),
}
impl AttributeValue {
pub fn fold<R>(
&self,
on_json: impl FnOnce(&serde_json::Value) -> R,
on_nested: impl FnOnce(&AttributeMap) -> R,
) -> R {
match &self.0 {
AttributeValueInner::Json(v) => on_json(v),
AttributeValueInner::Nested(m) => on_nested(m),
}
}
pub fn as_nested(&self) -> Option<&AttributeMap> {
match &self.0 {
AttributeValueInner::Nested(m) => Some(m),
AttributeValueInner::Json(_) => None,
}
}
pub fn as_json(&self) -> Option<&serde_json::Value> {
match &self.0 {
AttributeValueInner::Json(v) => Some(v),
AttributeValueInner::Nested(_) => None,
}
}
pub(crate) fn as_nested_mut(&mut self) -> Option<&mut AttributeMap> {
match &mut self.0 {
AttributeValueInner::Nested(m) => Some(m),
AttributeValueInner::Json(_) => None,
}
}
}
impl From<serde_json::Value> for AttributeValue {
fn from(v: serde_json::Value) -> Self {
match v {
serde_json::Value::Object(map) => {
AttributeValue(AttributeValueInner::Nested(AttributeMap(
map.into_iter()
.map(|(k, v)| (k, Attribute::Known(AttributeValue::from(v))))
.collect(),
)))
}
other => AttributeValue(AttributeValueInner::Json(other)),
}
}
}
impl From<&str> for AttributeValue {
fn from(v: &str) -> Self {
AttributeValue(AttributeValueInner::Json(serde_json::Value::String(
v.to_string(),
)))
}
}
impl From<String> for AttributeValue {
fn from(v: String) -> Self {
AttributeValue(AttributeValueInner::Json(serde_json::Value::String(v)))
}
}
impl From<bool> for AttributeValue {
fn from(v: bool) -> Self {
AttributeValue(AttributeValueInner::Json(serde_json::Value::Bool(v)))
}
}
impl From<i64> for AttributeValue {
fn from(v: i64) -> Self {
AttributeValue(AttributeValueInner::Json(serde_json::Value::Number(
v.into(),
)))
}
}
impl From<AttributeMap> for AttributeValue {
fn from(v: AttributeMap) -> Self {
AttributeValue(AttributeValueInner::Nested(v))
}
}
impl Serialize for AttributeValue {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
match &self.0 {
AttributeValueInner::Json(v) => v.serialize(s),
AttributeValueInner::Nested(m) => m.serialize(s),
}
}
}
impl<'de> Deserialize<'de> for AttributeValue {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
Ok(AttributeValue::from(serde_json::Value::deserialize(d)?))
}
}
#[derive(Clone, Debug, Default, PartialEq)]
pub struct AttributeMap(BTreeMap<String, Attribute<AttributeValue>>);
impl AttributeMap {
pub fn new() -> Self {
AttributeMap(BTreeMap::new())
}
pub fn is_empty(&self) -> bool {
self.0.is_empty()
}
pub fn len(&self) -> usize {
self.0.len()
}
pub fn insert(&mut self, key: impl Into<String>, value: Attribute<AttributeValue>) {
self.0.insert(key.into(), value);
}
pub fn record(&mut self, key: impl Into<String>, value: Attribute<AttributeValue>) -> bool {
let key = key.into();
if self.holds_unresolved(&key) {
return false;
}
self.0.insert(key, value);
true
}
fn holds_unresolved(&self, key: &str) -> bool {
matches!(self.0.get(key), Some(Attribute::Unknown(_)))
}
pub fn insert_known(&mut self, key: impl Into<String>, value: impl Into<AttributeValue>) {
self.insert(key, Attribute::Known(value.into()));
}
pub fn insert_absent(&mut self, key: impl Into<String>) {
self.insert(key, Attribute::Absent);
}
pub fn insert_unknown(&mut self, key: impl Into<String>, why: impl Into<String>) {
self.insert(key, Attribute::Unknown(why.into()));
}
pub fn get(&self, key: &str) -> Option<&Attribute<AttributeValue>> {
self.0.get(key)
}
pub fn iter(&self) -> impl Iterator<Item = (&String, &Attribute<AttributeValue>)> {
self.0.iter()
}
pub(crate) fn nested_for_write(&mut self, key: &str) -> Option<&mut AttributeMap> {
if self.holds_unresolved(key) {
return None;
}
match self.0.get(key) {
Some(Attribute::Known(value)) if value.as_nested().is_some() => {}
_ => {
self.insert_known(key, AttributeMap::new());
}
}
match self.0.get_mut(key) {
Some(Attribute::Known(value)) => value.as_nested_mut(),
_ => None,
}
}
pub fn validate(&self, at: &str) -> Result<(), AuthZenError> {
for (key, value) in &self.0 {
let pointer = format!("{at}/{key}");
match value {
Attribute::Unknown(why) => {
return Err(AuthZenError::new(
AuthZenErrorCode::EvaluationUnavailable,
format!(
"the attribute {key:?} could not be resolved ({why}); sending the \
request without it would obtain a decision that weighed every \
attribute except the one nobody could read, and report it as complete"
),
)
.at(&pointer));
}
Attribute::Known(value) => {
if let Some(nested) = value.as_nested() {
nested.validate(&pointer)?;
}
}
Attribute::Absent => {}
}
}
Ok(())
}
}
impl FromIterator<(String, Attribute<AttributeValue>)> for AttributeMap {
fn from_iter<I: IntoIterator<Item = (String, Attribute<AttributeValue>)>>(iter: I) -> Self {
AttributeMap(iter.into_iter().collect())
}
}
impl Serialize for AttributeMap {
fn serialize<S: Serializer>(&self, s: S) -> Result<S::Ok, S::Error> {
let mut map = s.serialize_map(None)?;
for (key, value) in &self.0 {
match value {
Attribute::Known(v) => map.serialize_entry(key, v)?,
Attribute::Absent => {}
Attribute::Unknown(why) => {
return Err(S::Error::custom(format!(
"the attribute {key:?} could not be resolved ({why}) and has no wire \
representation; validate the envelope before encoding it"
)))
}
}
}
map.end()
}
}
impl<'de> Deserialize<'de> for AttributeMap {
fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Self, D::Error> {
struct BagVisitor;
impl<'de> Visitor<'de> for BagVisitor {
type Value = AttributeMap;
fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("a JSON object of attributes")
}
fn visit_map<M: MapAccess<'de>>(self, mut access: M) -> Result<AttributeMap, M::Error> {
let mut out = BTreeMap::new();
while let Some((k, v)) = access.next_entry::<String, AttributeValue>()? {
out.insert(k, Attribute::Known(v));
}
Ok(AttributeMap(out))
}
}
d.deserialize_map(BagVisitor)
}
}