1mod base64_array;
2mod base64_vec;
3mod error;
4mod identified_by;
5
6use cts_common::claims::{
7 ClientPermission, DataKeyPermission, KeysetPermission, Permission, Scope,
8};
9pub use identified_by::*;
10
11mod unverified_context;
12
13use serde::{Deserialize, Serialize};
14use std::{
15 borrow::Cow,
16 fmt::{self, Debug, Display, Formatter},
17 ops::Deref,
18};
19use utoipa::ToSchema;
20use uuid::Uuid;
21use zeroize::{Zeroize, ZeroizeOnDrop};
22
23pub use cipherstash_config;
24pub use error::*;
26
27pub use crate::unverified_context::{UnverifiedContext, UnverifiedContextValue};
28pub use crate::{IdentifiedBy, Name};
29pub mod testing;
30
31pub trait ViturResponse: Serialize + for<'de> Deserialize<'de> + Send {}
32
33pub trait ViturRequest: Serialize + for<'de> Deserialize<'de> + Sized + Send {
34 type Response: ViturResponse;
35
36 const SCOPE: Scope;
37 const ENDPOINT: &'static str;
38}
39
40#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
42#[serde(rename_all = "snake_case")]
43pub enum ClientType {
44 Device,
45}
46
47#[derive(Debug, Serialize, Deserialize, ToSchema)]
49pub struct CreateClientSpec<'a> {
50 pub client_type: ClientType,
51 #[schema(value_type = String)]
53 pub name: Cow<'a, str>,
54}
55
56#[derive(Debug, Serialize, Deserialize, ToSchema)]
58pub struct CreatedClient {
59 pub id: Uuid,
60 #[schema(value_type = String, format = Byte)]
62 pub client_key: ViturKeyMaterial,
63}
64
65#[derive(Debug, Serialize, Deserialize, ToSchema)]
69pub struct CreateKeysetResponse {
70 #[serde(flatten)]
71 pub keyset: Keyset,
72 #[serde(default, skip_serializing_if = "Option::is_none")]
73 pub client: Option<CreatedClient>,
74}
75
76impl ViturResponse for CreateKeysetResponse {}
77
78#[derive(Debug, Serialize, Deserialize, ToSchema)]
82pub struct CreateKeysetRequest<'a> {
83 #[schema(value_type = String)]
85 pub name: Cow<'a, str>,
86 #[schema(value_type = String)]
88 pub description: Cow<'a, str>,
89 #[serde(default, skip_serializing_if = "Option::is_none")]
90 pub client: Option<CreateClientSpec<'a>>,
91}
92
93impl ViturRequest for CreateKeysetRequest<'_> {
94 type Response = CreateKeysetResponse;
95
96 const ENDPOINT: &'static str = "create-keyset";
97 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Create));
98}
99
100#[derive(Default, Debug, Serialize, Deserialize, ToSchema)]
105pub struct ListKeysetRequest {
106 #[serde(default)]
107 pub show_disabled: bool,
108}
109
110impl ViturRequest for ListKeysetRequest {
111 type Response = Vec<Keyset>;
112
113 const ENDPOINT: &'static str = "list-keysets";
114 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::List));
115}
116
117#[derive(Debug, Serialize, Deserialize, PartialEq, Eq, ToSchema)]
120pub struct Keyset {
121 pub id: Uuid,
122 pub name: String,
123 pub description: String,
124 pub is_disabled: bool,
125 #[serde(default)]
126 pub is_default: bool,
127}
128
129impl ViturResponse for Vec<Keyset> {}
130
131#[derive(Default, Debug, Serialize, Deserialize, ToSchema)]
133pub struct EmptyResponse {}
134
135impl ViturResponse for EmptyResponse {}
136
137#[derive(Debug, Serialize, Deserialize, ToSchema)]
144pub struct CreateClientRequest<'a> {
145 #[serde(alias = "dataset_id", default, skip_serializing_if = "Option::is_none")]
148 #[schema(value_type = Option<String>, example = "550e8400-e29b-41d4-a716-446655440000")]
149 pub keyset_id: Option<IdentifiedBy>,
150 #[schema(value_type = String)]
152 pub name: Cow<'a, str>,
153 #[schema(value_type = String)]
155 pub description: Cow<'a, str>,
156}
157
158impl ViturRequest for CreateClientRequest<'_> {
159 type Response = CreateClientResponse;
160
161 const ENDPOINT: &'static str = "create-client";
162 const SCOPE: Scope = Scope::with_permission(Permission::Client(ClientPermission::Create));
163}
164
165#[derive(Debug, Serialize, Deserialize, ToSchema)]
170pub struct CreateClientResponse {
171 pub id: Uuid,
173 #[serde(rename = "dataset_id")]
175 pub keyset_id: Uuid,
176 pub name: String,
178 pub description: String,
180 #[schema(value_type = String, format = Byte)]
182 pub client_key: ViturKeyMaterial,
183}
184
185impl ViturResponse for CreateClientResponse {}
186
187#[derive(Debug, Serialize, Deserialize)]
192pub struct ListClientRequest;
193
194impl ViturRequest for ListClientRequest {
195 type Response = Vec<KeysetClient>;
196
197 const ENDPOINT: &'static str = "list-clients";
198 const SCOPE: Scope = Scope::with_permission(Permission::Client(ClientPermission::List));
199}
200
201#[derive(Debug, Deserialize, Serialize, PartialEq, Eq, ToSchema)]
204#[serde(untagged)]
205pub enum ClientKeysetId {
206 Single(Uuid),
207 Multiple(Vec<Uuid>),
208}
209
210impl PartialEq<Uuid> for ClientKeysetId {
212 fn eq(&self, other: &Uuid) -> bool {
213 if let ClientKeysetId::Single(id) = self {
214 id == other
215 } else {
216 false
217 }
218 }
219}
220
221#[derive(Debug, Serialize, Deserialize, ToSchema)]
223pub struct KeysetClient {
224 pub id: Uuid,
225 #[serde(alias = "dataset_id")]
226 pub keyset_id: ClientKeysetId,
227 pub name: String,
228 pub description: String,
229}
230
231impl ViturResponse for Vec<KeysetClient> {}
232
233#[derive(Debug, Serialize, Deserialize, ToSchema)]
238pub struct DeleteClientRequest {
239 pub client_id: Uuid,
240}
241
242impl ViturRequest for DeleteClientRequest {
243 type Response = DeleteClientResponse;
244
245 const ENDPOINT: &'static str = "delete-client";
246 const SCOPE: Scope = Scope::with_permission(Permission::Client(ClientPermission::Delete));
247}
248
249#[derive(Default, Debug, Serialize, Deserialize, ToSchema)]
250pub struct DeleteClientResponse {}
251
252impl ViturResponse for DeleteClientResponse {}
253
254#[derive(Serialize, Deserialize, Zeroize, ZeroizeOnDrop)]
256pub struct ViturKeyMaterial(#[serde(with = "base64_vec")] Vec<u8>);
257opaque_debug::implement!(ViturKeyMaterial);
258
259impl From<Vec<u8>> for ViturKeyMaterial {
260 fn from(inner: Vec<u8>) -> Self {
261 Self(inner)
262 }
263}
264
265impl Deref for ViturKeyMaterial {
266 type Target = [u8];
267
268 fn deref(&self) -> &Self::Target {
269 &self.0
270 }
271}
272
273#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize, Zeroize)]
274#[serde(transparent)]
275pub struct KeyId(#[serde(with = "base64_array")] [u8; 16]);
276
277impl KeyId {
278 pub fn into_inner(self) -> [u8; 16] {
279 self.0
280 }
281}
282
283impl Display for KeyId {
284 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
285 write!(f, "{}", const_hex::encode(self.0))
286 }
287}
288
289impl From<[u8; 16]> for KeyId {
290 fn from(inner: [u8; 16]) -> Self {
291 Self(inner)
292 }
293}
294
295impl AsRef<[u8; 16]> for KeyId {
296 fn as_ref(&self) -> &[u8; 16] {
297 &self.0
298 }
299}
300
301#[derive(Debug, Serialize, Deserialize, ToSchema)]
305pub struct GeneratedKey {
306 #[schema(value_type = String, format = Byte)]
307 pub key_material: ViturKeyMaterial,
308 #[serde(with = "base64_vec")]
310 #[schema(value_type = String, format = Byte)]
311 pub tag: Vec<u8>,
312}
313
314#[derive(Debug, Serialize, Deserialize, ToSchema)]
316pub struct GenerateKeyResponse {
317 pub keys: Vec<GeneratedKey>,
318}
319
320impl ViturResponse for GenerateKeyResponse {}
321
322#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
324pub struct GenerateKeySpec<'a> {
325 #[serde(alias = "id")]
327 #[schema(value_type = String, format = Byte)]
328 pub iv: KeyId,
329 #[schema(value_type = String)]
331 pub descriptor: Cow<'a, str>,
332
333 #[serde(default)]
334 #[schema(value_type = Vec<Context>)]
335 pub context: Cow<'a, [Context]>,
336}
337
338impl<'a> GenerateKeySpec<'a> {
339 pub fn new(iv: [u8; 16], descriptor: &'a str) -> Self {
340 Self {
341 iv: KeyId(iv),
342 descriptor: Cow::from(descriptor),
343 context: Default::default(),
344 }
345 }
346
347 pub fn new_with_context(
348 iv: [u8; 16],
349 descriptor: &'a str,
350 context: Cow<'a, [Context]>,
351 ) -> Self {
352 Self {
353 iv: KeyId(iv),
354 descriptor: Cow::from(descriptor),
355 context,
356 }
357 }
358}
359#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
363pub enum Context {
365 Tag(String),
367
368 Value(String, String),
371
372 #[serde(alias = "identityClaim")]
377 IdentityClaim(String),
378}
379
380impl Context {
381 pub fn new_tag(tag: impl Into<String>) -> Self {
382 Self::Tag(tag.into())
383 }
384
385 pub fn new_value(key: impl Into<String>, value: impl Into<String>) -> Self {
386 Self::Value(key.into(), value.into())
387 }
388
389 pub fn new_identity_claim(claim: &str) -> Self {
390 Self::IdentityClaim(claim.to_string())
391 }
392}
393
394#[derive(Debug, Serialize, Deserialize, ToSchema)]
402pub struct GenerateKeyRequest<'a> {
403 pub client_id: Uuid,
404 #[serde(alias = "dataset_id")]
405 #[schema(value_type = Option<String>, example = "550e8400-e29b-41d4-a716-446655440000")]
406 pub keyset_id: Option<IdentifiedBy>,
407 #[schema(value_type = Vec<GenerateKeySpec>)]
408 pub keys: Cow<'a, [GenerateKeySpec<'a>]>,
409 #[serde(default)]
410 #[schema(value_type = Object)]
411 pub unverified_context: Cow<'a, UnverifiedContext>,
412}
413
414impl ViturRequest for GenerateKeyRequest<'_> {
415 type Response = GenerateKeyResponse;
416
417 const ENDPOINT: &'static str = "generate-data-key";
418 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Generate));
419}
420
421#[derive(Debug, Serialize, Deserialize, ToSchema)]
423pub struct RetrievedKey {
424 #[schema(value_type = String, format = Byte)]
426 pub key_material: ViturKeyMaterial,
427}
428
429#[derive(Debug, Serialize, Deserialize, ToSchema)]
432pub struct RetrieveKeyResponse {
433 pub keys: Vec<RetrievedKey>,
434}
435
436impl ViturResponse for RetrieveKeyResponse {}
437
438#[derive(Debug, Serialize, Deserialize, Clone, ToSchema)]
440pub struct RetrieveKeySpec<'a> {
441 #[serde(alias = "id")]
442 #[schema(value_type = String, format = Byte)]
443 pub iv: KeyId,
444 #[schema(value_type = String)]
446 pub descriptor: Cow<'a, str>,
447 #[schema(value_type = String, format = Byte)]
448 pub tag: Cow<'a, [u8]>,
449
450 #[serde(default)]
451 #[schema(value_type = Vec<Context>)]
452 pub context: Cow<'a, [Context]>,
453
454 #[serde(default)]
457 pub tag_version: usize,
458}
459
460impl<'a> RetrieveKeySpec<'a> {
461 const DEFAULT_TAG_VERSION: usize = 0;
462
463 pub fn new(id: KeyId, tag: &'a [u8], descriptor: &'a str) -> Self {
464 Self {
465 iv: id,
466 descriptor: Cow::from(descriptor),
467 tag: Cow::from(tag),
468 context: Cow::Owned(Vec::new()),
469 tag_version: Self::DEFAULT_TAG_VERSION,
470 }
471 }
472
473 pub fn with_context(mut self, context: Cow<'a, [Context]>) -> Self {
474 self.context = context;
475 self
476 }
477}
478
479#[derive(Debug, Serialize, Deserialize, ToSchema)]
485pub struct RetrieveKeyRequest<'a> {
486 pub client_id: Uuid,
487 #[serde(alias = "dataset_id")]
488 #[schema(value_type = Option<String>, example = "550e8400-e29b-41d4-a716-446655440000")]
489 pub keyset_id: Option<IdentifiedBy>,
490 #[schema(value_type = Vec<RetrieveKeySpec>)]
491 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
492 #[serde(default)]
493 #[schema(value_type = Object)]
494 pub unverified_context: UnverifiedContext,
495}
496
497impl ViturRequest for RetrieveKeyRequest<'_> {
498 type Response = RetrieveKeyResponse;
499
500 const ENDPOINT: &'static str = "retrieve-data-key";
501 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Retrieve));
502}
503
504#[derive(Debug, Serialize, Deserialize)]
510pub struct RetrieveKeyRequestFallible<'a> {
511 pub client_id: Uuid,
512 #[serde(alias = "dataset_id")]
513 pub keyset_id: Option<IdentifiedBy>,
514 pub keys: Cow<'a, [RetrieveKeySpec<'a>]>,
515 #[serde(default)]
516 pub unverified_context: Cow<'a, UnverifiedContext>,
517}
518
519impl ViturRequest for RetrieveKeyRequestFallible<'_> {
520 type Response = RetrieveKeyResponseFallible;
521
522 const ENDPOINT: &'static str = "retrieve-data-key-fallible";
523 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Retrieve));
524}
525
526#[derive(Debug, Serialize, Deserialize, ToSchema)]
528pub struct RetrieveKeyResponseFallible {
529 #[schema(value_type = Vec<serde_json::Value>)]
530 pub keys: Vec<Result<RetrievedKey, String>>, }
532
533impl ViturResponse for RetrieveKeyResponseFallible {}
534
535#[derive(Debug, Serialize, Deserialize, ToSchema)]
539pub struct DisableKeysetRequest {
540 #[serde(alias = "dataset_id")]
542 #[schema(value_type = String, example = "550e8400-e29b-41d4-a716-446655440000")]
543 pub keyset_id: IdentifiedBy,
544}
545
546impl ViturRequest for DisableKeysetRequest {
547 type Response = EmptyResponse;
548
549 const ENDPOINT: &'static str = "disable-keyset";
550 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Disable));
551}
552
553#[derive(Debug, Serialize, Deserialize, ToSchema)]
557pub struct EnableKeysetRequest {
558 #[serde(alias = "dataset_id")]
560 #[schema(value_type = String, example = "550e8400-e29b-41d4-a716-446655440000")]
561 pub keyset_id: IdentifiedBy,
562}
563
564impl ViturRequest for EnableKeysetRequest {
565 type Response = EmptyResponse;
566
567 const ENDPOINT: &'static str = "enable-keyset";
568 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Enable));
569}
570
571#[derive(Debug, Serialize, Deserialize, ToSchema)]
577pub struct ModifyKeysetRequest<'a> {
578 #[serde(alias = "dataset_id")]
580 #[schema(value_type = String, example = "550e8400-e29b-41d4-a716-446655440000")]
581 pub keyset_id: IdentifiedBy,
582 #[schema(value_type = Option<String>)]
584 pub name: Option<Cow<'a, str>>,
585 #[schema(value_type = Option<String>)]
587 pub description: Option<Cow<'a, str>>,
588}
589
590impl ViturRequest for ModifyKeysetRequest<'_> {
591 type Response = EmptyResponse;
592
593 const ENDPOINT: &'static str = "modify-keyset";
594 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Modify));
595}
596
597#[derive(Debug, Serialize, Deserialize, ToSchema)]
602pub struct GrantKeysetRequest {
603 pub client_id: Uuid,
604 #[serde(alias = "dataset_id")]
606 #[schema(value_type = String, example = "550e8400-e29b-41d4-a716-446655440000")]
607 pub keyset_id: IdentifiedBy,
608}
609
610impl ViturRequest for GrantKeysetRequest {
611 type Response = EmptyResponse;
612
613 const ENDPOINT: &'static str = "grant-keyset";
614 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Grant));
615}
616
617#[derive(Debug, Serialize, Deserialize, ToSchema)]
621pub struct RevokeKeysetRequest {
622 pub client_id: Uuid,
623 #[serde(alias = "dataset_id")]
625 #[schema(value_type = String, example = "550e8400-e29b-41d4-a716-446655440000")]
626 pub keyset_id: IdentifiedBy,
627}
628
629impl ViturRequest for RevokeKeysetRequest {
630 type Response = EmptyResponse;
631
632 const ENDPOINT: &'static str = "revoke-keyset";
633 const SCOPE: Scope = Scope::with_permission(Permission::Keyset(KeysetPermission::Revoke));
634}
635
636#[derive(Debug, Serialize, Deserialize, PartialEq, PartialOrd, ToSchema)]
645pub struct LoadKeysetRequest {
646 pub client_id: Uuid,
647 #[serde(alias = "dataset_id")]
649 #[schema(value_type = Option<String>, example = "550e8400-e29b-41d4-a716-446655440000")]
650 pub keyset_id: Option<IdentifiedBy>,
651}
652
653impl ViturRequest for LoadKeysetRequest {
654 type Response = LoadKeysetResponse;
655
656 const ENDPOINT: &'static str = "load-keyset";
657
658 const SCOPE: Scope = Scope::with_permission(Permission::DataKey(DataKeyPermission::Retrieve));
662}
663
664#[derive(Debug, Serialize, Deserialize, ToSchema)]
668pub struct LoadKeysetResponse {
669 pub partial_index_key: RetrievedKey,
670 #[serde(rename = "dataset")]
671 pub keyset: Keyset,
672}
673
674impl ViturResponse for LoadKeysetResponse {}
675
676#[cfg(test)]
677mod test {
678 use serde_json::json;
679 use uuid::Uuid;
680
681 use crate::{CreateKeysetResponse, CreatedClient, IdentifiedBy, LoadKeysetRequest, Name};
682
683 mod create_keyset_response_serialization {
684 use super::*;
685 use crate::{Keyset, ViturKeyMaterial};
686
687 #[test]
688 fn without_client_is_flat_keyset() {
689 let id = Uuid::new_v4();
690 let response = CreateKeysetResponse {
691 keyset: Keyset {
692 id,
693 name: "test-keyset".into(),
694 description: "A test keyset".into(),
695 is_disabled: false,
696 is_default: false,
697 },
698 client: None,
699 };
700
701 let serialized = serde_json::to_value(&response).unwrap();
702
703 assert_eq!(
705 serialized,
706 json!({
707 "id": id,
708 "name": "test-keyset",
709 "description": "A test keyset",
710 "is_disabled": false,
711 "is_default": false,
712 })
713 );
714
715 let deserialized: CreateKeysetResponse = serde_json::from_value(serialized).unwrap();
717 assert_eq!(deserialized.keyset.id, id);
718 assert!(deserialized.client.is_none());
719 }
720
721 #[test]
722 fn with_client_includes_client_field() {
723 let keyset_id = Uuid::new_v4();
724 let client_id = Uuid::new_v4();
725
726 let response = CreateKeysetResponse {
727 keyset: Keyset {
728 id: keyset_id,
729 name: "device-keyset".into(),
730 description: "Keyset with device client".into(),
731 is_disabled: false,
732 is_default: false,
733 },
734 client: Some(CreatedClient {
735 id: client_id,
736 client_key: ViturKeyMaterial::from(vec![1, 2, 3, 4]),
737 }),
738 };
739
740 let serialized = serde_json::to_value(&response).unwrap();
741
742 assert_eq!(
744 serialized,
745 json!({
746 "id": keyset_id,
747 "name": "device-keyset",
748 "description": "Keyset with device client",
749 "is_disabled": false,
750 "is_default": false,
751 "client": {
752 "id": client_id,
753 "client_key": "AQIDBA==",
754 },
755 })
756 );
757
758 let deserialized: CreateKeysetResponse = serde_json::from_value(serialized).unwrap();
760 assert_eq!(deserialized.keyset.id, keyset_id);
761 let created_client = deserialized.client.unwrap();
762 assert_eq!(created_client.id, client_id);
763 assert_eq!(&*created_client.client_key, &[1, 2, 3, 4]);
764 }
765 }
766
767 mod create_client_request_serialization {
768 use super::*;
769 use crate::CreateClientRequest;
770
771 #[test]
772 fn with_keyset_id_round_trips() {
773 let keyset_id = Uuid::new_v4();
774 let req = CreateClientRequest {
775 keyset_id: Some(IdentifiedBy::Uuid(keyset_id)),
776 name: "my-client".into(),
777 description: "desc".into(),
778 };
779
780 let serialized = serde_json::to_value(&req).unwrap();
781 assert!(serialized.get("keyset_id").is_some());
782
783 let deserialized: CreateClientRequest = serde_json::from_value(serialized).unwrap();
784 assert_eq!(deserialized.keyset_id, Some(IdentifiedBy::Uuid(keyset_id)));
785 }
786
787 #[test]
788 fn without_keyset_id_round_trips() {
789 let req = CreateClientRequest {
790 keyset_id: None,
791 name: "my-client".into(),
792 description: "desc".into(),
793 };
794
795 let serialized = serde_json::to_value(&req).unwrap();
796 assert!(serialized.get("keyset_id").is_none());
797
798 let deserialized: CreateClientRequest = serde_json::from_value(serialized).unwrap();
799 assert_eq!(deserialized.keyset_id, None);
800 }
801
802 #[test]
803 fn backwards_compatible_with_dataset_id() {
804 let dataset_id = Uuid::new_v4();
805 let json = json!({
806 "dataset_id": dataset_id,
807 "name": "old-client",
808 "description": "old desc",
809 });
810
811 let req: CreateClientRequest = serde_json::from_value(json).unwrap();
812 assert_eq!(req.keyset_id, Some(IdentifiedBy::Uuid(dataset_id)));
813 }
814
815 #[test]
816 fn omitted_keyset_id_defaults_to_none() {
817 let json = json!({
818 "name": "no-keyset",
819 "description": "no keyset",
820 });
821
822 let req: CreateClientRequest = serde_json::from_value(json).unwrap();
823 assert_eq!(req.keyset_id, None);
824 }
825 }
826
827 mod backwards_compatible_deserialisation {
828 use super::*;
829
830 #[test]
831 fn when_dataset_id_is_uuid() {
832 let client_id = Uuid::new_v4();
833 let dataset_id = Uuid::new_v4();
834
835 let json = json!({
836 "client_id": client_id,
837 "dataset_id": dataset_id,
838 });
839
840 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
841
842 assert_eq!(
843 req,
844 LoadKeysetRequest {
845 client_id,
846 keyset_id: Some(IdentifiedBy::Uuid(dataset_id))
847 }
848 );
849 }
850
851 #[test]
852 fn when_keyset_id_is_uuid() {
853 let client_id = Uuid::new_v4();
854 let keyset_id = Uuid::new_v4();
855
856 let json = json!({
857 "client_id": client_id,
858 "keyset_id": keyset_id,
859 });
860
861 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
862
863 assert_eq!(
864 req,
865 LoadKeysetRequest {
866 client_id,
867 keyset_id: Some(IdentifiedBy::Uuid(keyset_id))
868 }
869 );
870 }
871
872 #[test]
873 fn when_dataset_id_is_id_name() {
874 let client_id = Uuid::new_v4();
875 let dataset_id = IdentifiedBy::Name(Name::new_untrusted("some-dataset-name"));
876
877 let json = json!({
878 "client_id": client_id,
879 "dataset_id": dataset_id,
880 });
881
882 let req: LoadKeysetRequest = serde_json::from_value(json).unwrap();
883
884 assert_eq!(
885 req,
886 LoadKeysetRequest {
887 client_id,
888 keyset_id: Some(dataset_id)
889 }
890 );
891 }
892 }
893}