1use std::{
2 borrow::Cow,
3 collections::{HashMap, HashSet},
4 time::Duration,
5};
6
7use serde::{Deserialize, Serialize};
8
9#[derive(Clone, Serialize, Deserialize, Default)]
10pub struct SigningContext {
11 pub expiry: Option<Duration>,
12 pub issuer: Option<String>,
13 pub audience: Option<String>,
14 pub not_before: Option<Duration>,
15}
16
17#[derive(Clone, Serialize, Deserialize, Default, Debug, PartialEq, Eq)]
18pub enum ValidationType {
19 Reject,
21 Ignore,
23 #[default]
25 Allow,
26 Require,
28}
29
30#[derive(Clone, Serialize, Deserialize, Default)]
31pub struct ValidationContext {
32 pub allow_list: HashMap<String, HashSet<String>>,
33 pub deny_list: HashMap<String, HashSet<String>>,
34 pub claims: HashMap<String, ValidationType>,
35 pub expiry: ValidationType,
36 pub not_before: ValidationType,
37}
38
39impl ValidationContext {
40 pub fn require_claim_with_allow_list(&mut self, claim: &str, values: &[&str]) {
41 self.claims
42 .insert(claim.to_string(), ValidationType::Require);
43 self.allow_list.insert(
44 claim.to_string(),
45 values.iter().map(|s| s.to_string()).collect(),
46 );
47 }
48
49 pub fn require_claim_with_deny_list(&mut self, claim: &str, values: &[&str]) {
50 self.claims
51 .insert(claim.to_string(), ValidationType::Require);
52 self.deny_list.insert(
53 claim.to_string(),
54 values.iter().map(|s| s.to_string()).collect(),
55 );
56 }
57
58 pub fn require_claim(&mut self, claim: &str) {
59 self.claims
60 .insert(claim.to_string(), ValidationType::Require);
61 }
62
63 pub fn reject_claim(&mut self, claim: &str) {
64 self.claims
65 .insert(claim.to_string(), ValidationType::Reject);
66 }
67
68 pub fn ignore_claim(&mut self, claim: &str) {
69 self.claims
70 .insert(claim.to_string(), ValidationType::Ignore);
71 }
72
73 pub fn allow_claim(&mut self, claim: &str) {
74 self.claims.insert(claim.to_string(), ValidationType::Allow);
75 }
76}
77
78#[derive(Clone, serde::Serialize, serde::Deserialize, Debug, PartialEq)]
81#[serde(untagged)]
82pub enum Any {
83 None,
84 String(Cow<'static, str>),
85 Bool(bool),
86 Number(isize),
87 Array(Vec<Any>),
88 Object(HashMap<Cow<'static, str>, Any>),
89}
90
91impl Any {
92 pub fn as_str(&self) -> Option<&str> {
93 match self {
94 Any::String(s) => Some(s.as_ref()),
95 _ => None,
96 }
97 }
98
99 pub fn as_array(&self) -> Option<&[Any]> {
100 match self {
101 Any::Array(a) => Some(a),
102 _ => None,
103 }
104 }
105
106 pub fn as_object(&self) -> Option<&HashMap<Cow<'static, str>, Any>> {
107 match self {
108 Any::Object(o) => Some(o),
109 _ => None,
110 }
111 }
112}
113
114impl From<bool> for Any {
115 fn from(value: bool) -> Self {
116 Any::Bool(value)
117 }
118}
119
120impl From<&'static str> for Any {
121 fn from(value: &'static str) -> Self {
122 Any::String(Cow::Borrowed(value))
123 }
124}
125
126impl From<String> for Any {
127 fn from(value: String) -> Self {
128 Any::String(Cow::Owned(value))
129 }
130}
131
132impl<T> From<Option<T>> for Any
133where
134 T: Into<Any>,
135{
136 fn from(value: Option<T>) -> Self {
137 value.map(T::into).unwrap_or(Any::None)
138 }
139}
140
141impl<T> From<Vec<T>> for Any
142where
143 T: Into<Any>,
144{
145 fn from(value: Vec<T>) -> Self {
146 Any::Array(value.into_iter().map(T::into).collect())
147 }
148}
149
150#[cfg(feature = "python_extension")]
151impl<'py> pyo3::FromPyObject<'py> for Any {
152 fn extract_bound(ob: &pyo3::Bound<'py, pyo3::PyAny>) -> pyo3::PyResult<Self> {
153 use pyo3::types::PyAnyMethods;
154 if ob.is_none() {
155 return Ok(Any::None);
156 }
157 if let Ok(value) = ob.extract::<bool>() {
158 return Ok(Any::Bool(value));
159 }
160 if let Ok(value) = ob.extract::<isize>() {
161 return Ok(Any::Number(value));
162 }
163 if let Ok(value) = ob.extract::<String>() {
164 return Ok(Any::String(Cow::Owned(value)));
165 }
166 let res: Result<pyo3::Bound<pyo3::types::PyList>, pyo3::PyErr> = ob.extract();
167 if let Ok(list) = res {
168 let mut items = Vec::new();
169 for item in list {
170 items.push(Any::extract_bound(&item)?);
171 }
172 return Ok(Any::Array(items));
173 }
174 let res: Result<pyo3::Bound<pyo3::types::PyDict>, pyo3::PyErr> = ob.extract();
175 if let Ok(dict) = res {
176 let mut items = HashMap::new();
177 for (k, v) in dict {
178 items.insert(Cow::Owned(k.extract::<String>()?), Any::extract_bound(&v)?);
179 }
180 return Ok(Any::Object(items));
181 }
182 Err(pyo3::PyErr::new::<pyo3::exceptions::PyTypeError, _>(
183 "Invalid Any value",
184 ))
185 }
186}
187
188#[cfg(feature = "python_extension")]
189impl<'py> pyo3::IntoPyObject<'py> for Any {
190 type Target = pyo3::PyAny;
191 type Output = pyo3::Bound<'py, pyo3::PyAny>;
192 type Error = pyo3::PyErr;
193 fn into_pyobject(self, py: pyo3::Python<'py>) -> Result<Self::Output, Self::Error> {
194 use pyo3::IntoPyObjectExt;
195
196 Ok(match self {
197 Any::None => py.None(),
198 Any::String(s) => s.as_ref().into_py_any(py)?,
199 Any::Bool(b) => b.into_py_any(py)?,
200 Any::Number(n) => n.into_py_any(py)?,
201 Any::Array(a) => a.into_py_any(py)?,
202 Any::Object(o) => o.into_py_any(py)?,
203 }
204 .into_bound(py))
205 }
206}