identity_jose/jws/
header.rs1use core::ops::Deref;
5use core::ops::DerefMut;
6use std::collections::BTreeMap;
7
8use serde_json::Value;
9
10use crate::jose::JoseHeader;
11use crate::jws::JwsAlgorithm;
12use crate::jwt::JwtHeader;
13
14#[derive(Clone, Debug, PartialEq, Eq, serde::Deserialize, serde::Serialize)]
18pub struct JwsHeader {
19 #[serde(flatten)]
21 common: JwtHeader,
22 #[serde(skip_serializing_if = "Option::is_none")]
28 alg: Option<JwsAlgorithm>,
29 #[serde(skip_serializing_if = "Option::is_none")]
50 b64: Option<bool>,
51
52 #[serde(flatten, skip_serializing_if = "Option::is_none")]
54 custom: Option<BTreeMap<String, Value>>,
55}
56
57impl JwsHeader {
58 pub const fn new() -> Self {
60 Self {
61 common: JwtHeader::new(),
62 alg: None,
63 b64: None,
64 custom: None,
65 }
66 }
67
68 pub fn alg(&self) -> Option<JwsAlgorithm> {
70 self.alg.as_ref().cloned()
71 }
72
73 pub fn set_alg(&mut self, value: impl Into<JwsAlgorithm>) {
75 self.alg = Some(value.into());
76 }
77
78 pub fn b64(&self) -> Option<bool> {
80 self.b64
81 }
82
83 pub fn set_b64(&mut self, value: impl Into<bool>) {
85 self.b64 = Some(value.into());
86 }
87
88 pub fn custom(&self) -> Option<&BTreeMap<String, Value>> {
90 self.custom.as_ref()
91 }
92
93 pub fn set_custom(&mut self, value: BTreeMap<String, Value>) {
95 self.custom = Some(value)
96 }
97
98 pub fn has(&self, claim: &str) -> bool {
100 match claim {
101 "alg" => self.alg().is_some(),
102 "b64" => self.b64().is_some(),
103 _ => {
104 self.common.has(claim)
105 || self
106 .custom
107 .as_ref()
108 .map(|custom| custom.get(claim).is_some())
109 .unwrap_or(false)
110 }
111 }
112 }
113
114 pub fn is_disjoint(&self, other: &JwsHeader) -> bool {
116 let has_duplicate: bool = self.alg().is_some() && other.alg.is_some() || self.b64.is_some() && other.b64.is_some();
117
118 !has_duplicate && self.common.is_disjoint(other.common()) && self.is_custom_disjoint(other)
119 }
120
121 fn is_custom_disjoint(&self, other: &JwsHeader) -> bool {
123 match (&self.custom, &other.custom) {
124 (Some(self_custom), Some(other_custom)) => {
125 for self_key in self_custom.keys() {
126 if other_custom.contains_key(self_key) {
127 return false;
128 }
129 }
130 true
131 }
132 _ => true,
133 }
134 }
135}
136
137impl Deref for JwsHeader {
138 type Target = JwtHeader;
139
140 fn deref(&self) -> &Self::Target {
141 &self.common
142 }
143}
144
145impl DerefMut for JwsHeader {
146 fn deref_mut(&mut self) -> &mut Self::Target {
147 &mut self.common
148 }
149}
150
151impl JoseHeader for JwsHeader {
152 fn common(&self) -> &JwtHeader {
153 self
154 }
155
156 fn has_claim(&self, claim: &str) -> bool {
157 self.has(claim)
158 }
159}
160
161impl Default for JwsHeader {
162 fn default() -> Self {
163 Self::new()
164 }
165}
166
167#[cfg(test)]
168mod tests {
169 use super::*;
170
171 #[test]
172 fn test_custom() {
173 let header1: JwsHeader = serde_json::from_value(serde_json::json!({
174 "alg": "ES256",
175 "b64": false,
176 "test": "tst-value",
177 "test-bool": false
178 }))
179 .unwrap();
180
181 assert_eq!(
182 header1.custom().unwrap().get("test").unwrap().as_str().unwrap(),
183 "tst-value".to_owned()
184 );
185
186 assert!(!header1.custom().unwrap().get("test-bool").unwrap().as_bool().unwrap());
187 assert!(header1.has("test"));
188 assert!(!header1.has("invalid"));
189 }
190
191 #[test]
192 fn test_header_disjoint() {
193 let header1: JwsHeader = serde_json::from_value(serde_json::json!({
194 "alg": "ES256",
195 "b64": false,
196 }))
197 .unwrap();
198 let header2: JwsHeader = serde_json::from_value(serde_json::json!({
199 "alg": "ES256",
200 "crit": ["b64"],
201 }))
202 .unwrap();
203 let header3: JwsHeader = serde_json::from_value(serde_json::json!({
204 "kid": "kid value",
205 "cty": "mediatype",
206 "custom": "test value",
207 }))
208 .unwrap();
209 let header4: JwsHeader = serde_json::from_value(serde_json::json!({
210 "custom": "test value",
211 }))
212 .unwrap();
213
214 assert!(!header1.is_disjoint(&header2));
215 assert!(header1.is_disjoint(&header3));
216 assert!(header2.is_disjoint(&header3));
217 assert!(header1.is_disjoint(&JwsHeader::new()));
218 assert!(!header4.is_disjoint(&header3));
219 assert!(header4.is_disjoint(&header2));
220 }
221}