jsonprooftoken/jpt/
claims.rs

1// Copyright 2023 Fondazione LINKS
2
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6
7//     http://www.apache.org/licenses/LICENSE-2.0
8
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::iter::zip;
16
17use indexmap::IndexMap;
18use json_unflattening::{flattening::flatten, unflattening::unflatten};
19use serde::{Deserialize, Serialize};
20use serde_json::{json, Map, Value};
21
22use super::payloads::Payloads;
23
24#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
25pub struct Claims(pub Vec<String>);
26
27impl Claims {
28    pub fn get_claim_index(&self, name: String) -> Option<usize> {
29        self.0.iter().position(|x| *x == name)
30    }
31}
32
33#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
34pub struct CustomValue {
35    value: Value,
36    #[serde(skip_serializing)]
37    flattening: bool,
38}
39
40/** These claims are taken from the JWT RFC (https://tools.ietf.org/html/rfc7519)
41 * making the hypothesis that in the future will be used also for the JPTs **/
42
43#[derive(Clone, Debug, Default, PartialEq, Eq, Deserialize, Serialize)]
44pub struct JptClaims {
45    /** Apparently the "aud" that in JWT was a claim, now should be an presentation protected header parameter
46     * (https://datatracker.ietf.org/doc/html/draft-ietf-jose-json-web-proof#name-presentation-protected-head) **/
47    /// Who issued the JWP
48    #[serde(skip_serializing_if = "Option::is_none")]
49    pub iss: Option<String>,
50    /// Subject of the JPT.
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub sub: Option<String>,
53    /// Expiration time
54    #[serde(skip_serializing_if = "Option::is_none")]
55    pub exp: Option<i64>,
56    /// Time before which the JPT MUST NOT be accepted
57    #[serde(skip_serializing_if = "Option::is_none")]
58    pub nbf: Option<i64>,
59    /// Issuance time
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub iat: Option<i64>,
62    /// Unique ID for the JPT.
63    #[serde(skip_serializing_if = "Option::is_none")]
64    pub jti: Option<String>,
65    /// Other custom claims (age, name, surname, Verifiable Credential, ...)
66    #[serde(flatten)]
67    pub custom: IndexMap<String, Value>,
68}
69
70impl JptClaims {
71    pub fn new() -> Self {
72        Self {
73            iss: None,
74            sub: None,
75            exp: None,
76            nbf: None,
77            iat: None,
78            jti: None,
79            custom: IndexMap::new(),
80        }
81    }
82
83    pub fn set_iss(&mut self, value: String) {
84        self.iss = Some(value);
85    }
86
87    pub fn set_sub(&mut self, value: String) {
88        self.sub = Some(value);
89    }
90
91    pub fn set_exp(&mut self, value: i64) {
92        self.exp = Some(value);
93    }
94
95    pub fn set_nbf(&mut self, value: i64) {
96        self.nbf = Some(value);
97    }
98
99    pub fn set_iat(&mut self, value: i64) {
100        self.iat = Some(value);
101    }
102
103    pub fn set_jti(&mut self, value: String) {
104        self.jti = Some(value);
105    }
106
107    pub fn set_claim<T: Serialize>(&mut self, claim: Option<&str>, value: T, flattened: bool) {
108        let serde_value = serde_json::to_value(value).unwrap();
109        if !serde_value.is_object() {
110            self.custom
111                .insert(claim.unwrap_or("").to_string(), serde_value);
112        } else {
113            if flattened {
114                let v = match claim {
115                    Some(c) => json!({c: serde_value}),
116                    None => serde_value,
117                };
118                self.custom.extend(flatten(&v).unwrap());
119            } else {
120                self.custom
121                    .insert(claim.unwrap_or("").to_string(), serde_value);
122            }
123        };
124    }
125
126    pub fn get_claim(&self, claim: &str) -> Option<&Value> {
127        self.custom.get(claim)
128    }
129
130    pub fn update_claim_and_return_older(&mut self, claim: &str, value: Value) -> Option<Value> {
131        self.custom.insert(claim.to_owned(), value)
132    }
133
134    /// Extracts claims and payloads into separate vectors.
135    pub fn get_claims_and_payloads(&self) -> (Claims, Payloads) {
136        let jptclaims_json_value = serde_json::to_value(self).unwrap();
137
138        let claims_payloads_pairs = jptclaims_json_value.as_object().unwrap().to_owned();
139
140        let (keys, values): (Vec<String>, Vec<Value>) =
141            claims_payloads_pairs.to_owned().into_iter().unzip();
142
143        (Claims(keys), Payloads::new_from_values(values))
144    }
145
146    /// Reconstruct JptClaims from Claims and Payloads
147    pub fn from_claims_and_payloads(claims: &Claims, payloads: &Payloads) -> Self {
148        let zip: Map<String, Value> = zip(claims.0.clone(), payloads.get_values()).collect();
149        let unflat = unflatten(&zip).unwrap();
150        let jpt_claims: Self = serde_json::from_value(unflat).unwrap();
151
152        jpt_claims
153    }
154}