rusttls_jwt_authorizer/
claims.rs1use serde::{Deserialize, Serialize};
2
3#[derive(Deserialize, Serialize, Clone, PartialEq, Eq, Debug)]
6pub struct NumericDate(pub i64);
7
8impl From<NumericDate> for i64 {
10 fn from(t: NumericDate) -> Self {
11 t.0
12 }
13}
14
15#[cfg(feature = "chrono")]
16use chrono::{DateTime, TimeZone, Utc};
17
18#[cfg(feature = "chrono")]
19impl From<NumericDate> for DateTime<Utc> {
20 fn from(t: NumericDate) -> Self {
21 Utc.timestamp_opt(t.0, 0).unwrap()
22 }
23}
24
25#[cfg(feature = "time")]
26use time::OffsetDateTime;
27
28#[cfg(feature = "time")]
29impl From<NumericDate> for OffsetDateTime {
30 fn from(t: NumericDate) -> Self {
31 OffsetDateTime::from_unix_timestamp(t.0).unwrap()
32 }
33}
34
35#[derive(PartialEq, Debug, Clone, Deserialize, Serialize)]
36#[serde(untagged)]
37pub enum OneOrArray<T> {
38 One(T),
39 Array(Vec<T>),
40}
41
42impl<T> OneOrArray<T> {
43 pub fn iter<'a>(&'a self) -> Box<dyn Iterator<Item = &'a T> + 'a> {
44 match self {
45 OneOrArray::One(v) => Box::new(std::iter::once(v)),
46 OneOrArray::Array(vector) => Box::new(vector.iter()),
47 }
48 }
49}
50
51#[derive(PartialEq, Debug, Clone)]
52pub struct StringList(Vec<String>);
53
54#[derive(Deserialize, Serialize, Clone, Debug)]
58pub struct RegisteredClaims {
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub iss: Option<String>,
61 #[serde(skip_serializing_if = "Option::is_none")]
62 pub sub: Option<String>,
63 #[serde(skip_serializing_if = "Option::is_none")]
64 pub aud: Option<OneOrArray<String>>,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 pub exp: Option<NumericDate>,
67 #[serde(skip_serializing_if = "Option::is_none")]
68 pub nbf: Option<NumericDate>,
69 #[serde(skip_serializing_if = "Option::is_none")]
70 pub iat: Option<NumericDate>,
71 #[serde(skip_serializing_if = "Option::is_none")]
72 pub jti: Option<String>,
73}
74
75#[cfg(test)]
76mod tests {
77
78 use chrono::{DateTime, TimeZone, Utc};
79 use serde::Deserialize;
80 use serde_json::json;
81
82 use crate::claims::{NumericDate, OneOrArray, RegisteredClaims};
83
84 #[derive(Deserialize)]
85 struct TestStruct {
86 v: OneOrArray<String>,
87 }
88
89 #[test]
90 fn one_or_array_iter() {
91 let o = OneOrArray::One("aaa".to_owned());
92 let mut i = o.iter();
93 assert_eq!(Some(&"aaa".to_owned()), i.next());
94
95 let a = OneOrArray::Array(vec!["aaa".to_owned()]);
96 let mut i = a.iter();
97 assert_eq!(Some(&"aaa".to_owned()), i.next());
98
99 let a = OneOrArray::Array(vec!["aaa".to_owned(), "bbb".to_owned()]);
100 let mut i = a.iter();
101 assert_eq!(Some(&"aaa".to_owned()), i.next());
102 assert_eq!(Some(&"bbb".to_owned()), i.next());
103 assert_eq!(None, i.next());
104 }
105
106 #[test]
107 fn rfc_claims_aud() {
108 let a: TestStruct = serde_json::from_str(r#"{"v":"a"}"#).unwrap();
109 assert_eq!(a.v, OneOrArray::One("a".to_owned()));
110
111 let a: TestStruct = serde_json::from_str(r#"{"v":["a", "b"]}"#).unwrap();
112 assert_eq!(a.v, OneOrArray::Array(vec!["a".to_owned(), "b".to_owned()]));
113 }
114
115 #[test]
116 fn from_numeric_date() {
117 let exp: i64 = NumericDate(1516239022).into();
118 assert_eq!(exp, 1516239022);
119 }
120
121 #[test]
122 fn chrono_from_numeric_date() {
123 let exp: DateTime<Utc> = NumericDate(1516239022).into();
124 assert_eq!(exp, Utc.timestamp_opt(1516239022, 0).unwrap());
125 assert_eq!(exp, DateTime::parse_from_rfc3339("2018-01-18T01:30:22.000Z").unwrap());
126 }
127
128 #[cfg(feature = "time")]
129 #[test]
130 fn time_from_numeric_date() {
131 use time::macros::datetime;
132 use time::OffsetDateTime;
133
134 let exp: OffsetDateTime = NumericDate(1516239022).into();
135 assert_eq!(exp, datetime!(2018-01-18 01:30:22 UTC));
136 }
137
138 #[test]
139 fn rfc_claims() {
140 let jwt_json = json!({
141 "iss": "http://localhost:3001",
142 "aud": ["aud1", "aud2"],
143 "sub": "bob",
144 "exp": 1516240122,
145 "iat": 1516239022,
146 }
147 );
148
149 let claims: RegisteredClaims = serde_json::from_value(jwt_json).expect("Failed RfcClaims deserialisation");
150 assert_eq!(claims.iss.unwrap(), "http://localhost:3001");
151 assert_eq!(
152 claims.aud.unwrap(),
153 OneOrArray::Array(vec!["aud1".to_owned(), "aud2".to_owned()])
154 );
155 assert_eq!(claims.exp.unwrap(), NumericDate(1516240122));
156 assert_eq!(claims.nbf, None);
157
158 let dt: DateTime<Utc> = claims.iat.unwrap().into();
159 assert_eq!(dt, Utc.timestamp_opt(1516239022, 0).unwrap());
160 }
161
162 #[test]
163 fn rfc_claims_serde() {
164 let claims_str = r#"{
165 "iss": "http://localhost:3001",
166 "sub": "bob",
167 "aud": ["aud1", "aud2"],
168 "exp": 1516240122,
169 "iat": 1516239022
170 }"#;
171
172 let claims: RegisteredClaims = serde_json::from_str(claims_str).expect("Failed RfcClaims deserialisation");
173
174 let jwt_serd = serde_json::to_string(&claims).unwrap();
175 let mut trimed_claims = claims_str.to_owned();
176 trimed_claims.retain(|c| !c.is_whitespace());
177 assert_eq!(trimed_claims, jwt_serd);
178 }
179}