bitwarden_auth/send_access/
access_token_request.rs1#[cfg(feature = "wasm")]
2use tsify::Tsify;
3
4#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
8#[serde(rename_all = "camelCase", deny_unknown_fields)]
9#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
10pub struct SendPasswordCredentials {
11 pub password_hash_b64: String,
13}
14
15#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
18#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
19pub struct SendEmailCredentials {
20 pub email: String,
22}
23
24#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
26#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
27pub struct SendEmailOtpCredentials {
28 pub email: String,
30 pub otp: String,
32}
33
34#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
36#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
37#[serde(untagged)]
41pub enum SendAccessCredentials {
42 #[allow(missing_docs)]
43 Password(SendPasswordCredentials),
44 #[allow(missing_docs)]
51 EmailOtp(SendEmailOtpCredentials),
52 #[allow(missing_docs)]
53 Email(SendEmailCredentials),
54}
55
56#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
58#[serde(rename_all = "camelCase", deny_unknown_fields)]
59#[cfg_attr(feature = "wasm", derive(Tsify), tsify(into_wasm_abi, from_wasm_abi))]
60pub struct SendAccessTokenRequest {
61 pub send_id: String,
63
64 #[serde(default, skip_serializing_if = "Option::is_none")]
65 #[cfg_attr(feature = "wasm", tsify(optional))]
66 pub send_access_credentials: Option<SendAccessCredentials>,
68}
69
70#[cfg(test)]
71mod tests {
72 use super::*;
73
74 mod send_access_token_request_tests {
75 use serde_json::{from_str, to_string};
76
77 use super::*;
78
79 #[test]
80 fn deserialize_camelcase_request() {
81 let json = r#"
82 {
83 "sendId": "abc123",
84 "sendAccessCredentials": { "passwordHashB64": "ha$h" }
85 }"#;
86
87 let req: SendAccessTokenRequest = from_str(json).unwrap();
88 assert_eq!(req.send_id, "abc123");
89
90 let creds = req.send_access_credentials.expect("expected Some");
91 match creds {
92 SendAccessCredentials::Password(p) => assert_eq!(p.password_hash_b64, "ha$h"),
93 _ => panic!("expected Password variant"),
94 }
95 }
96
97 #[test]
98 fn serialize_camelcase_request_with_credentials() {
99 let req = SendAccessTokenRequest {
100 send_id: "abc123".into(),
101 send_access_credentials: Some(SendAccessCredentials::Password(
102 SendPasswordCredentials {
103 password_hash_b64: "ha$h".into(),
104 },
105 )),
106 };
107 let json = to_string(&req).unwrap();
108 assert_eq!(
109 json,
110 r#"{"sendId":"abc123","sendAccessCredentials":{"passwordHashB64":"ha$h"}}"#
111 );
112 }
113
114 #[test]
115 fn serialize_omits_optional_credentials_when_none() {
116 let req = SendAccessTokenRequest {
117 send_id: "abc123".into(),
118 send_access_credentials: None,
119 };
120 let json = to_string(&req).unwrap();
121 assert_eq!(json, r#"{"sendId":"abc123"}"#);
122 }
123
124 #[test]
125 fn roundtrip_camel_in_to_camel_out() {
126 let in_json = r#"
127 {
128 "sendId": "abc123",
129 "sendAccessCredentials": { "passwordHashB64": "ha$h" }
130 }"#;
131
132 let req: SendAccessTokenRequest = from_str(in_json).unwrap();
133 let out_json = to_string(&req).unwrap();
134 assert_eq!(
135 out_json,
136 r#"{"sendId":"abc123","sendAccessCredentials":{"passwordHashB64":"ha$h"}}"#
137 );
138 }
139
140 #[test]
141 fn snakecase_top_level_keys_are_rejected() {
142 let json = r#"
143 {
144 "send_id": "abc123",
145 "sendAccessCredentials": { "passwordHashB64": "ha$h" }
146 }"#;
147 let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
148 let msg = err.to_string();
149 assert!(
150 msg.contains("unknown field") && msg.contains("send_id"),
151 "unexpected: {msg}"
152 );
153 }
154
155 #[test]
156 fn extra_top_level_key_is_rejected() {
157 let json = r#"
158 {
159 "sendId": "abc123",
160 "sendAccessCredentials": { "passwordHashB64": "ha$h" },
161 "extra": "nope"
162 }"#;
163 let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
164 let msg = err.to_string();
165 assert!(
166 msg.contains("unknown field") && msg.contains("extra"),
167 "unexpected: {msg}"
168 );
169 }
170
171 #[test]
172 fn snakecase_nested_keys_are_rejected() {
173 let json = r#"
174 {
175 "sendId": "abc123",
176 "sendAccessCredentials": { "password_hash_b64": "ha$h" }
177 }"#;
178
179 let err = serde_json::from_str::<SendAccessTokenRequest>(json).unwrap_err();
180 let msg = err.to_string();
181 assert!(
182 msg.contains("did not match any variant"),
183 "unexpected: {msg}"
184 );
185 }
186
187 #[test]
188 fn extra_nested_key_is_rejected() {
189 let json = r#"
190 {
191 "sendId": "abc123",
192 "sendAccessCredentials": {
193 "passwordHashB64": "ha$h",
194 "extra": "nope"
195 }
196 }"#;
197 let err = from_str::<SendAccessTokenRequest>(json).unwrap_err();
198 let msg = err.to_string();
199 assert!(
200 msg.contains("did not match any variant"),
201 "unexpected: {msg}"
202 );
203 }
204 }
205
206 mod send_access_credentials_tests {
207 use super::*;
208
209 mod send_access_password_credentials_tests {
210 use serde_json::{from_str, to_string};
211
212 use super::*;
213
214 #[test]
215 fn deserialize_struct_camelcase_from_ts() {
216 let json = r#"{ "passwordHashB64": "ha$h" }"#;
217 let s: SendPasswordCredentials = from_str(json).unwrap();
218 assert_eq!(s.password_hash_b64, "ha$h");
219 }
220
221 #[test]
222 fn serialize_struct_camelcase_to_wire() {
223 let s = SendPasswordCredentials {
224 password_hash_b64: "ha$h".into(),
225 };
226 let json = to_string(&s).unwrap();
227 assert_eq!(json, r#"{"passwordHashB64":"ha$h"}"#);
228 }
229
230 #[test]
231 fn roundtrip_struct_camel_in_to_camel_out() {
232 let in_json = r#"{ "passwordHashB64": "ha$h" }"#;
233 let parsed: SendPasswordCredentials = from_str(in_json).unwrap();
234 let out_json = to_string(&parsed).unwrap();
235 assert_eq!(out_json, r#"{"passwordHashB64":"ha$h"}"#);
236 }
237
238 #[test]
239 fn deserialize_enum_variant_from_json() {
240 let json = r#"{"passwordHashB64":"ha$h"}"#;
241 let creds: SendAccessCredentials = from_str(json).unwrap();
242 match creds {
243 SendAccessCredentials::Password(password_creds) => {
244 assert_eq!(password_creds.password_hash_b64, "ha$h");
245 }
246 _ => panic!("Expected Password variant"),
247 }
248 }
249
250 #[test]
251 fn serialize_enum_variant_to_json() {
252 let creds = SendAccessCredentials::Password(SendPasswordCredentials {
253 password_hash_b64: "ha$h".into(),
254 });
255 let json = to_string(&creds).unwrap();
256 assert_eq!(json, r#"{"passwordHashB64":"ha$h"}"#);
257 }
258
259 #[test]
260 fn roundtrip_enum_variant_through_json() {
261 let in_json = r#"{"passwordHashB64":"ha$h"}"#;
262 let creds: SendAccessCredentials = from_str(in_json).unwrap();
263 let out_json = to_string(&creds).unwrap();
264 assert_eq!(out_json, r#"{"passwordHashB64":"ha$h"}"#);
265 }
266 }
267
268 mod send_access_email_credentials_tests {
269 use serde_json::{from_str, to_string};
270
271 use super::*;
272
273 #[test]
274 fn deserialize_struct_camelcase_from_ts() {
275 let json = r#"{ "email": "[email protected]" }"#;
276 let s: SendEmailCredentials = from_str(json).unwrap();
277 assert_eq!(s.email, "[email protected]");
278 }
279
280 #[test]
281 fn serialize_struct_camelcase_to_wire() {
282 let s = SendEmailCredentials {
283 email: "[email protected]".into(),
284 };
285 let json = to_string(&s).unwrap();
286 assert_eq!(json, r#"{"email":"[email protected]"}"#);
287 }
288
289 #[test]
290 fn roundtrip_struct_camel_in_to_camel_out() {
291 let in_json = r#"{ "email": "[email protected]" }"#;
292 let parsed: SendEmailCredentials = from_str(in_json).unwrap();
293 let out_json = to_string(&parsed).unwrap();
294 assert_eq!(out_json, r#"{"email":"[email protected]"}"#);
295 }
296
297 #[test]
298 fn deserialize_enum_variant_from_json() {
299 let json = r#"{"email":"[email protected]"}"#;
300 let creds: SendAccessCredentials = from_str(json).unwrap();
301 match creds {
302 SendAccessCredentials::Email(email_creds) => {
303 assert_eq!(email_creds.email, "[email protected]");
304 }
305 _ => panic!("Expected Email variant"),
306 }
307 }
308
309 #[test]
310 fn serialize_enum_variant_to_json() {
311 let creds = SendAccessCredentials::Email(SendEmailCredentials {
312 email: "[email protected]".into(),
313 });
314 let json = to_string(&creds).unwrap();
315 assert_eq!(json, r#"{"email":"[email protected]"}"#);
316 }
317
318 #[test]
319 fn roundtrip_enum_variant_through_json() {
320 let in_json = r#"{"email":"[email protected]"}"#;
321 let creds: SendAccessCredentials = from_str(in_json).unwrap();
322 let out_json = to_string(&creds).unwrap();
323 assert_eq!(out_json, r#"{"email":"[email protected]"}"#);
324 }
325 }
326
327 mod send_access_email_otp_credentials_tests {
328 use serde_json::{from_str, to_string};
329
330 use super::*;
331
332 #[test]
333 fn deserialize_struct_camelcase_from_ts() {
334 let json = r#"{ "email": "[email protected]", "otp": "123456" }"#;
335 let s: SendEmailOtpCredentials = from_str(json).unwrap();
336 assert_eq!(s.email, "[email protected]");
337 assert_eq!(s.otp, "123456");
338 }
339
340 #[test]
341 fn serialize_struct_camelcase_to_wire() {
342 let s = SendEmailOtpCredentials {
343 email: "[email protected]".into(),
344 otp: "123456".into(),
345 };
346 let json = to_string(&s).unwrap();
347 assert_eq!(json, r#"{"email":"[email protected]","otp":"123456"}"#);
348 }
349
350 #[test]
351 fn roundtrip_struct_camel_in_to_camel_out() {
352 let in_json = r#"{ "email": "[email protected]", "otp": "123456" }"#;
353 let parsed: SendEmailOtpCredentials = from_str(in_json).unwrap();
354 let out_json = to_string(&parsed).unwrap();
355 assert_eq!(out_json, r#"{"email":"[email protected]","otp":"123456"}"#);
356 }
357
358 #[test]
359 fn deserialize_enum_variant_from_json() {
360 let json = r#"{"email":"[email protected]","otp":"123456"}"#;
361 let creds: SendAccessCredentials = from_str(json).unwrap();
362 match creds {
363 SendAccessCredentials::EmailOtp(otp_creds) => {
364 assert_eq!(otp_creds.email, "[email protected]");
365 assert_eq!(otp_creds.otp, "123456");
366 }
367 _ => panic!("Expected EmailOtp variant"),
368 }
369 }
370
371 #[test]
372 fn serialize_enum_variant_to_json() {
373 let creds = SendAccessCredentials::EmailOtp(SendEmailOtpCredentials {
374 email: "[email protected]".into(),
375 otp: "123456".into(),
376 });
377 let json = to_string(&creds).unwrap();
378 assert_eq!(json, r#"{"email":"[email protected]","otp":"123456"}"#);
379 }
380
381 #[test]
382 fn roundtrip_enum_variant_through_json() {
383 let in_json = r#"{"email":"[email protected]","otp":"123456"}"#;
384 let creds: SendAccessCredentials = from_str(in_json).unwrap();
385 let out_json = to_string(&creds).unwrap();
386 assert_eq!(out_json, r#"{"email":"[email protected]","otp":"123456"}"#);
387 }
388 }
389 }
390}