common_access_token/
claims.rs

1use serde::{Deserialize, Serialize};
2use std::collections::HashMap;
3
4/// Standard claim labels as defined in the CAT specification
5#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
6pub enum ClaimLabel {
7    Iss = 1, // Issuer
8    Sub = 2, // Subject
9    Aud = 3, // Audience
10    Exp = 4, // Expiration Time
11    Nbf = 5, // Not Before
12    Iat = 6, // Issued At
13    Cti = 7, // CWT ID
14    Cnf = 8, // Confirmation
15    Geohash = 282,
16    Catreplay = 308,
17    Catpor = 309,
18    Catv = 310,
19    Catnip = 311,
20    Catu = 312,
21    Catm = 313,
22    Catalpn = 314,
23    Cath = 315,
24    Catgeoiso3166 = 316,
25    Catgeocoord = 317,
26    Cattpk = 319,
27    Catifdata = 320,
28    Catadpop = 321,
29    Catif = 322,
30    Catr = 323,
31}
32
33/// Mapping from string claim names to numeric labels
34pub fn claim_to_label(claim: &str) -> Option<ClaimLabel> {
35    match claim {
36        "iss" => Some(ClaimLabel::Iss),
37        "sub" => Some(ClaimLabel::Sub),
38        "aud" => Some(ClaimLabel::Aud),
39        "exp" => Some(ClaimLabel::Exp),
40        "nbf" => Some(ClaimLabel::Nbf),
41        "iat" => Some(ClaimLabel::Iat),
42        "cti" => Some(ClaimLabel::Cti),
43        "cnf" => Some(ClaimLabel::Cnf),
44        "geohash" => Some(ClaimLabel::Geohash),
45        "catreplay" => Some(ClaimLabel::Catreplay),
46        "catpor" => Some(ClaimLabel::Catpor),
47        "catv" => Some(ClaimLabel::Catv),
48        "catnip" => Some(ClaimLabel::Catnip),
49        "catu" => Some(ClaimLabel::Catu),
50        "catm" => Some(ClaimLabel::Catm),
51        "catalpn" => Some(ClaimLabel::Catalpn),
52        "cath" => Some(ClaimLabel::Cath),
53        "catgeoiso3166" => Some(ClaimLabel::Catgeoiso3166),
54        "catgeocoord" => Some(ClaimLabel::Catgeocoord),
55        "cattpk" => Some(ClaimLabel::Cattpk),
56        "catifdata" => Some(ClaimLabel::Catifdata),
57        "catadpop" => Some(ClaimLabel::Catadpop),
58        "catif" => Some(ClaimLabel::Catif),
59        "catr" => Some(ClaimLabel::Catr),
60        _ => None,
61    }
62}
63
64/// Mapping from numeric labels to string claim names
65pub fn label_to_claim(label: ClaimLabel) -> &'static str {
66    match label {
67        ClaimLabel::Iss => "iss",
68        ClaimLabel::Sub => "sub",
69        ClaimLabel::Aud => "aud",
70        ClaimLabel::Exp => "exp",
71        ClaimLabel::Nbf => "nbf",
72        ClaimLabel::Iat => "iat",
73        ClaimLabel::Cti => "cti",
74        ClaimLabel::Cnf => "cnf",
75        ClaimLabel::Geohash => "geohash",
76        ClaimLabel::Catreplay => "catreplay",
77        ClaimLabel::Catpor => "catpor",
78        ClaimLabel::Catv => "catv",
79        ClaimLabel::Catnip => "catnip",
80        ClaimLabel::Catu => "catu",
81        ClaimLabel::Catm => "catm",
82        ClaimLabel::Catalpn => "catalpn",
83        ClaimLabel::Cath => "cath",
84        ClaimLabel::Catgeoiso3166 => "catgeoiso3166",
85        ClaimLabel::Catgeocoord => "catgeocoord",
86        ClaimLabel::Cattpk => "cattpk",
87        ClaimLabel::Catifdata => "catifdata",
88        ClaimLabel::Catadpop => "catadpop",
89        ClaimLabel::Catif => "catif",
90        ClaimLabel::Catr => "catr",
91    }
92}
93
94/// Possible values for a claim
95#[derive(Debug, Clone, Serialize, Deserialize)]
96pub enum ClaimValue {
97    String(String),
98    Integer(i64),
99    Bytes(Vec<u8>),
100    Array(Vec<ClaimValue>),
101    Map(HashMap<String, ClaimValue>),
102}
103
104/// A single claim
105#[derive(Debug, Clone, Serialize, Deserialize)]
106pub struct Claim {
107    pub label: ClaimLabel,
108    pub value: ClaimValue,
109}
110
111/// Collection of claims
112#[derive(Debug, Clone, Default, Serialize, Deserialize)]
113pub struct Claims {
114    claims: HashMap<ClaimLabel, ClaimValue>,
115}
116
117impl Claims {
118    /// Create a new empty claims collection
119    pub fn new() -> Self {
120        Claims {
121            claims: HashMap::new(),
122        }
123    }
124
125    /// Add a claim
126    pub fn add(&mut self, label: ClaimLabel, value: ClaimValue) {
127        self.claims.insert(label, value);
128    }
129
130    /// Get a claim by label
131    pub fn get(&self, label: ClaimLabel) -> Option<&ClaimValue> {
132        self.claims.get(&label)
133    }
134
135    /// Get all claims
136    pub fn all(&self) -> &HashMap<ClaimLabel, ClaimValue> {
137        &self.claims
138    }
139
140    /// Create claims from a JSON-like map
141    pub fn from_map(map: HashMap<String, serde_json::Value>) -> Self {
142        let mut claims = Claims::new();
143
144        for (key, value) in map {
145            if let Some(label) = claim_to_label(&key) {
146                let claim_value = json_to_claim_value(value);
147                claims.add(label, claim_value);
148            }
149        }
150
151        // Ensure catv is set
152        if !claims.claims.contains_key(&ClaimLabel::Catv) {
153            claims.add(ClaimLabel::Catv, ClaimValue::Integer(1));
154        }
155
156        claims
157    }
158
159    /// Convert claims to a JSON-like map
160    pub fn to_map(&self) -> HashMap<String, serde_json::Value> {
161        let mut map = HashMap::new();
162
163        for (label, value) in &self.claims {
164            let key = label_to_claim(*label).to_string();
165            let json_value = claim_value_to_json(value);
166            map.insert(key, json_value);
167        }
168
169        map
170    }
171}
172
173/// Convert a JSON value to a ClaimValue
174fn json_to_claim_value(value: serde_json::Value) -> ClaimValue {
175    match value {
176        serde_json::Value::String(s) => ClaimValue::String(s),
177        serde_json::Value::Number(n) => {
178            if let Some(i) = n.as_i64() {
179                ClaimValue::Integer(i)
180            } else {
181                // Convert float to string as a fallback
182                ClaimValue::String(n.to_string())
183            }
184        }
185        serde_json::Value::Array(arr) => {
186            let values = arr.into_iter().map(json_to_claim_value).collect();
187            ClaimValue::Array(values)
188        }
189        serde_json::Value::Object(obj) => {
190            let mut map = HashMap::new();
191            for (k, v) in obj {
192                map.insert(k, json_to_claim_value(v));
193            }
194            ClaimValue::Map(map)
195        }
196        serde_json::Value::Bool(b) => ClaimValue::String(b.to_string()),
197        serde_json::Value::Null => ClaimValue::String("null".to_string()),
198    }
199}
200
201/// Convert a ClaimValue to a JSON value
202fn claim_value_to_json(value: &ClaimValue) -> serde_json::Value {
203    match value {
204        ClaimValue::String(s) => serde_json::Value::String(s.clone()),
205        ClaimValue::Integer(i) => serde_json::Value::Number((*i).into()),
206        ClaimValue::Bytes(b) => {
207            let hex = b
208                .iter()
209                .map(|byte| format!("{byte:02x}"))
210                .collect::<String>();
211            serde_json::Value::String(hex)
212        }
213        ClaimValue::Array(arr) => {
214            let values = arr.iter().map(claim_value_to_json).collect();
215            serde_json::Value::Array(values)
216        }
217        ClaimValue::Map(map) => {
218            let mut obj = serde_json::Map::new();
219            for (k, v) in map {
220                obj.insert(k.clone(), claim_value_to_json(v));
221            }
222            serde_json::Value::Object(obj)
223        }
224    }
225}