gel_jwt/
sig.rs

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    /// Require the claim to be absent and fail if it is present.
20    Reject,
21    /// Ignore the claim.
22    Ignore,
23    /// If the claim is present, it must be valid.
24    #[default]
25    Allow,
26    /// Require the claim to be present and be valid.
27    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/// A type similar to `serde_json::Value` that can be serialized and deserialized
79/// from a JWT token.
80#[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}