keeper_secrets_manager_core/dto/
payload.rs

1// -*- coding: utf-8 -*-
2//  _  __
3// | |/ /___ ___ _ __  ___ _ _ (R)
4// | ' </ -_) -_) '_ \/ -_) '_|
5// |_|\_\___\___| .__/\___|_|
6//              |_|
7//
8// Keeper Secrets Manager
9// Copyright 2024 Keeper Security Inc.
10// Contact: sm@keepersecurity.com
11//
12
13use crate::custom_error::KSMRError;
14use serde::{Deserialize, Serialize};
15use std::any::Any;
16
17fn custom_pretty_json<T: Serialize>(
18    value: &T,
19    indent_size: usize,
20) -> Result<String, serde_json::Error> {
21    let raw_json = serde_json::to_value(value)?;
22    let mut result = String::new();
23    format_json(&raw_json, &mut result, 0, indent_size);
24    Ok(result)
25}
26
27fn format_json(value: &serde_json::Value, result: &mut String, level: usize, indent_size: usize) {
28    let indent = " ".repeat(level * indent_size);
29    let next_indent = " ".repeat((level + 1) * indent_size);
30
31    match value {
32        serde_json::Value::Object(map) => {
33            result.push_str("{\n");
34            for (i, (key, val)) in map.iter().enumerate() {
35                result.push_str(&next_indent);
36                result.push('"');
37                result.push_str(key);
38                result.push_str("\": ");
39                format_json(val, result, level + 1, indent_size);
40                if i < map.len() - 1 {
41                    result.push(',');
42                }
43                result.push('\n');
44            }
45            result.push_str(&indent);
46            result.push('}');
47        }
48        serde_json::Value::Array(array) => {
49            result.push_str("[\n");
50            for (i, val) in array.iter().enumerate() {
51                result.push_str(&next_indent);
52                format_json(val, result, level + 1, indent_size);
53                if i < array.len() - 1 {
54                    result.push(',');
55                }
56                result.push('\n');
57            }
58            result.push_str(&indent);
59            result.push(']');
60        }
61        serde_json::Value::String(s) => {
62            result.push('"');
63            result.push_str(s);
64            result.push('"');
65        }
66        serde_json::Value::Number(num) => {
67            result.push_str(&num.to_string());
68        }
69        serde_json::Value::Bool(b) => {
70            result.push_str(&b.to_string());
71        }
72        serde_json::Value::Null => {
73            result.push_str("null");
74        }
75    }
76}
77
78#[derive(Serialize, Deserialize, Debug)]
79pub struct Context {
80    transmission_key: TransmissionKey,
81    client_id: String,
82    client_key: String,
83}
84
85impl Context {
86    pub fn new(transmission_key: TransmissionKey, client_id: String, client_key: String) -> Self {
87        Context {
88            transmission_key,
89            client_id,
90            client_key,
91        }
92    }
93}
94
95#[derive(Serialize, Deserialize, Debug)]
96pub struct TransmissionKey {
97    pub public_key_id: String,
98    pub key: Vec<u8>,
99    pub encrypted_key: Vec<u8>,
100}
101
102impl TransmissionKey {
103    pub fn new(public_key_id: String, key: Vec<u8>, encrypted_key: Vec<u8>) -> Self {
104        TransmissionKey {
105            public_key_id,
106            key,
107            encrypted_key,
108        }
109    }
110}
111
112impl Clone for TransmissionKey {
113    fn clone(&self) -> Self {
114        TransmissionKey {
115            // Clone each field of the struct
116            public_key_id: self.public_key_id.clone(),
117            key: self.key.clone(),
118            encrypted_key: self.encrypted_key.clone(),
119        }
120    }
121}
122
123#[derive(Serialize, Deserialize)]
124#[serde(rename_all = "camelCase")]
125pub struct GetPayload {
126    client_version: String,
127    client_id: String,
128    public_key: Option<String>,
129    requested_records: Option<Vec<String>>,
130    requested_folders: Option<Vec<String>>,
131}
132
133impl GetPayload {
134    pub fn new(
135        client_version: String,
136        client_id: String,
137        public_key: Option<String>,
138        requested_records: Option<Vec<String>>,
139        requested_folders: Option<Vec<String>>,
140    ) -> GetPayload {
141        GetPayload {
142            client_version,
143            client_id,
144            public_key,
145            requested_records,
146            requested_folders,
147        }
148    }
149
150    pub fn set_optional_field<T>(&mut self, field: &str, value: T)
151    where
152        T: Into<Option<Vec<String>>>,
153    {
154        match field {
155            "records_filter" => self.requested_records = value.into(),
156            "folders_filter" => self.requested_folders = value.into(),
157            _ => (),
158        }
159    }
160
161    pub fn to_json(&self) -> Result<String, KSMRError> {
162        Ok(custom_pretty_json(&self, 4).unwrap())
163    }
164
165    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
166        serde_json::from_str(json_data).map_err(|e| {
167            log::error!("Error deserializing GetPayload from JSON: {}", e);
168            KSMRError::DeserializationError(format!(
169                "Error deserializing GetPayload from JSON: {}",
170                e
171            ))
172        })
173    }
174}
175
176#[derive(Debug, PartialEq, Eq, Clone, Serialize, Deserialize, Default)]
177pub enum UpdateTransactionType {
178    #[default]
179    None,
180    General,
181    Rotation,
182}
183
184// impl Default for UpdateTransactionType {
185//     fn default() -> Self {
186//         UpdateTransactionType::None
187//     }
188// }
189
190impl UpdateTransactionType {
191    pub fn as_str(&self) -> &'static str {
192        match self {
193            UpdateTransactionType::None => "",
194            UpdateTransactionType::General => "general",
195            UpdateTransactionType::Rotation => "rotation",
196        }
197    }
198}
199
200impl std::str::FromStr for UpdateTransactionType {
201    type Err = ();
202
203    fn from_str(s: &str) -> Result<Self, Self::Err> {
204        match s {
205            "" => Ok(UpdateTransactionType::None),
206            "general" => Ok(UpdateTransactionType::General),
207            "rotation" => Ok(UpdateTransactionType::Rotation),
208            _ => Err(()),
209        }
210    }
211}
212
213#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
214#[serde(rename_all = "camelCase")]
215pub struct UpdatePayload {
216    pub client_version: String,
217    pub client_id: String,
218    pub record_uid: String,
219    pub revision: i64,
220    pub data: String,
221    #[serde(skip_serializing_if = "Option::is_none")]
222    pub transaction_type: Option<UpdateTransactionType>,
223}
224
225impl UpdatePayload {
226    pub fn new(
227        client_version: String,
228        client_id: String,
229        record_uid: String,
230        revision: i64,
231        data: String,
232    ) -> Self {
233        UpdatePayload {
234            client_version,
235            client_id,
236            record_uid,
237            revision,
238            data,
239            transaction_type: None,
240        }
241    }
242
243    pub fn set_transaction_type(&mut self, transaction_type: UpdateTransactionType) {
244        if transaction_type != UpdateTransactionType::None {
245            self.transaction_type = Some(transaction_type);
246        } else {
247            self.transaction_type = None;
248        }
249    }
250
251    /// Converts `UpdatePayload` to a JSON string.
252    pub fn to_json(&self) -> Result<String, KSMRError> {
253        serde_json::to_string(self).map_err(|err| {
254            KSMRError::SerializationError(format!(
255                "Error serializing UpdatePayload to JSON: {}",
256                err
257            ))
258        })
259    }
260
261    /// Populates `UpdatePayload` fields from a JSON string.
262    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
263        serde_json::from_str(json_data).map_err(|err| {
264            KSMRError::DeserializationError(format!(
265                "Error deserializing UpdatePayload from JSON: {}",
266                err
267            ))
268        })
269    }
270}
271
272#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
273pub struct CompleteTransactionPayload {
274    pub client_version: String,
275    pub client_id: String,
276    pub record_uid: String,
277}
278
279impl CompleteTransactionPayload {
280    pub fn new(client_version: String, client_id: String, record_uid: String) -> Self {
281        CompleteTransactionPayload {
282            client_version,
283            client_id,
284            record_uid,
285        }
286    }
287
288    /// Converts `CompleteTransactionPayload` to a JSON string.
289    pub fn to_json(&self) -> Result<String, KSMRError> {
290        serde_json::to_string(self).map_err(|err| {
291            KSMRError::SerializationError(format!(
292                "Error serializing CompleteTransactionPayload to JSON: {}",
293                err
294            ))
295        })
296    }
297
298    /// Populates `CompleteTransactionPayload` fields from a JSON string.
299    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
300        serde_json::from_str(json_data).map_err(|err| {
301            KSMRError::DeserializationError(format!(
302                "Error deserializing CompleteTransactionPayload from JSON: {}",
303                err
304            ))
305        })
306    }
307}
308
309#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
310#[serde(rename_all = "camelCase")]
311pub struct CreatePayload {
312    pub client_version: String,
313    pub client_id: String,
314    pub record_uid: String,
315    pub record_key: String,
316    pub folder_uid: String,
317    pub folder_key: String,
318    pub data: String,
319    #[serde(skip_serializing_if = "Option::is_none")]
320    pub sub_folder_uid: Option<String>,
321}
322
323impl CreatePayload {
324    /// Constructor for `CreatePayload`
325    #[allow(clippy::too_many_arguments)]
326    pub fn new(
327        client_version: String,
328        client_id: String,
329        record_uid: String,
330        record_key: String,
331        folder_uid: String,
332        folder_key: String,
333        data: String,
334        sub_folder_uid: Option<String>,
335    ) -> Self {
336        Self {
337            client_version,
338            client_id,
339            record_uid,
340            record_key,
341            folder_uid,
342            folder_key,
343            data,
344            sub_folder_uid,
345        }
346    }
347
348    /// Converts `CreatePayload` to a JSON string.
349    pub fn to_json(&self) -> Result<String, KSMRError> {
350        serde_json::to_string(self).map_err(|err| {
351            KSMRError::SerializationError(format!(
352                "Error serializing CreatePayload to JSON: {}",
353                err
354            ))
355        })
356    }
357
358    /// Populates `CreatePayload` fields from a JSON string.
359    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
360        serde_json::from_str(json_data).map_err(|err| {
361            KSMRError::DeserializationError(format!(
362                "Error deserializing CreatePayload from JSON: {}",
363                err
364            ))
365        })
366    }
367}
368
369#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
370#[serde(rename_all = "camelCase")]
371pub struct DeletePayload {
372    pub client_version: String,
373    pub client_id: String,
374    pub record_uids: Vec<String>,
375}
376
377impl DeletePayload {
378    /// Constructor for `DeletePayload`
379    pub fn new(client_version: String, client_id: String, record_uids: Vec<String>) -> Self {
380        Self {
381            client_version,
382            client_id,
383            record_uids,
384        }
385    }
386
387    /// Converts `DeletePayload` to a JSON string.
388    pub fn to_json(&self) -> Result<String, KSMRError> {
389        serde_json::to_string(self).map_err(|err| {
390            KSMRError::SerializationError(format!(
391                "Error serializing DeletePayload to JSON: {}",
392                err
393            ))
394        })
395    }
396
397    /// Populates `DeletePayload` fields from a JSON string.
398    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
399        serde_json::from_str(json_data).map_err(|err| {
400            KSMRError::DeserializationError(format!(
401                "Error deserializing DeletePayload from JSON: {}",
402                err
403            ))
404        })
405    }
406}
407
408#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
409#[serde(rename_all = "camelCase")]
410pub struct CreateFolderPayload {
411    pub client_version: String,
412    pub client_id: String,
413    pub folder_uid: String,
414    pub shared_folder_uid: String,
415    pub shared_folder_key: String,
416    pub data: String,
417    pub parent_uid: String,
418}
419
420impl CreateFolderPayload {
421    pub fn new(
422        client_version: String,
423        client_id: String,
424        folder_uid: String,
425        shared_folder_uid: String,
426        shared_folder_key: String,
427        data: String,
428        parent_uid: Option<String>,
429    ) -> Self {
430        match parent_uid {
431            Some(uid) => Self {
432                client_version,
433                client_id,
434                folder_uid,
435                shared_folder_uid,
436                shared_folder_key,
437                data,
438                parent_uid: uid,
439            },
440            None => Self {
441                client_version,
442                client_id,
443                folder_uid,
444                shared_folder_uid,
445                shared_folder_key,
446                data,
447                parent_uid: "".to_string(),
448            },
449        }
450    }
451
452    /// Converts `CreateFolderPayload` to a JSON string.
453    pub fn to_json(&self) -> Result<String, KSMRError> {
454        Ok(custom_pretty_json(&self, 4).unwrap())
455    }
456
457    /// Populates `CreateFolderPayload` fields from a JSON string.
458    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
459        serde_json::from_str(json_data).map_err(|err| {
460            KSMRError::DeserializationError(format!(
461                "Error deserializing CreateFolderPayload from JSON: {}",
462                err
463            ))
464        })
465    }
466}
467
468#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
469#[serde(rename_all = "camelCase")]
470pub struct UpdateFolderPayload {
471    pub client_version: String,
472    pub client_id: String,
473    pub folder_uid: String,
474    pub data: String,
475}
476
477impl UpdateFolderPayload {
478    /// Constructor for `UpdateFolderPayload`
479    pub fn new(
480        client_version: String,
481        client_id: String,
482        folder_uid: String,
483        data: String,
484    ) -> Self {
485        Self {
486            client_version,
487            client_id,
488            folder_uid,
489            data,
490        }
491    }
492
493    /// Converts `UpdateFolderPayload` to a JSON string.
494    pub fn to_json(&self) -> Result<String, KSMRError> {
495        serde_json::to_string(self).map_err(|err| {
496            KSMRError::SerializationError(format!(
497                "Error serializing UpdateFolderPayload to JSON: {}",
498                err
499            ))
500        })
501    }
502
503    /// Populates `UpdateFolderPayload` fields from a JSON string.
504    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
505        serde_json::from_str(json_data).map_err(|err| {
506            KSMRError::DeserializationError(format!(
507                "Error deserializing UpdateFolderPayload from JSON: {}",
508                err
509            ))
510        })
511    }
512}
513
514#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
515#[serde(rename_all = "camelCase")]
516pub struct DeleteFolderPayload {
517    pub client_version: String,
518    pub client_id: String,
519    pub folder_uids: Vec<String>,
520    pub force_deletion: bool,
521}
522
523impl DeleteFolderPayload {
524    /// Constructor for `DeleteFolderPayload`
525    pub fn new(
526        client_version: String,
527        client_id: String,
528        folder_uids: Vec<String>,
529        force_deletion: bool,
530    ) -> Self {
531        Self {
532            client_version,
533            client_id,
534            folder_uids,
535            force_deletion,
536        }
537    }
538
539    /// Converts `DeleteFolderPayload` to a JSON string.
540    pub fn to_json(&self) -> Result<String, KSMRError> {
541        serde_json::to_string(self).map_err(|err| {
542            KSMRError::SerializationError(format!(
543                "Error serializing DeleteFolderPayload to JSON: {}",
544                err
545            ))
546        })
547    }
548
549    /// Populates `DeleteFolderPayload` fields from a JSON string.
550    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
551        serde_json::from_str(json_data).map_err(|err| {
552            KSMRError::DeserializationError(format!(
553                "Error deserializing DeleteFolderPayload from JSON: {}",
554                err
555            ))
556        })
557    }
558}
559
560#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
561#[serde(rename_all = "camelCase")]
562pub struct FileUploadPayload {
563    pub client_version: String,
564    pub client_id: String,
565    pub file_record_uid: String,
566    pub file_record_key: String,
567    pub file_record_data: String,
568    pub owner_record_uid: String,
569    pub owner_record_data: String,
570    pub link_key: String,
571    pub file_size: i32,
572}
573
574impl FileUploadPayload {
575    /// Constructor for `FileUploadPayload`
576    #[allow(clippy::too_many_arguments)]
577    pub fn new(
578        client_version: String,
579        client_id: String,
580        file_record_uid: String,
581        file_record_key: String,
582        file_record_data: String,
583        owner_record_uid: String,
584        owner_record_data: String,
585        link_key: String,
586        file_size: i32,
587    ) -> Self {
588        Self {
589            client_version,
590            client_id,
591            file_record_uid,
592            file_record_key,
593            file_record_data,
594            owner_record_uid,
595            owner_record_data,
596            link_key,
597            file_size,
598        }
599    }
600
601    /// Converts `FileUploadPayload` to a JSON string.
602    pub fn to_json(&self) -> Result<String, KSMRError> {
603        serde_json::to_string(self).map_err(|err| {
604            KSMRError::SerializationError(format!(
605                "Error serializing FileUploadPayload to JSON: {}",
606                err
607            ))
608        })
609    }
610
611    /// Populates `FileUploadPayload` fields from a JSON string.
612    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
613        serde_json::from_str(json_data).map_err(|err| {
614            KSMRError::DeserializationError(format!(
615                "Error deserializing FileUploadPayload from JSON: {}",
616                err
617            ))
618        })
619    }
620}
621
622#[derive(Debug, Clone)]
623pub struct EncryptedPayload {
624    pub encrypted_payload: Vec<u8>,
625    pub signature: ecdsa::der::Signature<p256::NistP256>,
626}
627
628impl EncryptedPayload {
629    /// Constructor for `EncryptedPayload`
630    pub fn new(
631        encrypted_payload: Vec<u8>,
632        signature: ecdsa::der::Signature<p256::NistP256>,
633    ) -> Self {
634        EncryptedPayload {
635            encrypted_payload,
636            signature,
637        }
638    }
639
640    pub fn to_json(&self) -> Result<String, KSMRError> {
641        Err(KSMRError::NotImplemented(
642            "serialization has not been implemented for private keys".to_string(),
643        ))
644    }
645
646    pub fn from_json(json_data: &str) -> Result<Self, KSMRError> {
647        let _ = json_data;
648        Err(KSMRError::NotImplemented(
649            "de-serialization has not been implemented for private keys".to_string(),
650        ))
651    }
652}
653
654#[derive(Debug, Clone)]
655pub struct KsmHttpResponse {
656    pub status_code: u16,
657    pub data: Vec<u8>,
658    pub http_response: Option<String>,
659}
660
661impl KsmHttpResponse {
662    /// Constructor for `KsmHttpResponse`
663    pub fn new(status_code: u16, data: Vec<u8>, http_response: String) -> Self {
664        KsmHttpResponse {
665            status_code,
666            data,
667            http_response: Some(http_response),
668        }
669    }
670}
671
672#[derive(Debug, Clone)]
673pub struct QueryOptions {
674    pub records_filter: Vec<String>,
675    pub folders_filter: Vec<String>,
676}
677
678impl QueryOptions {
679    pub fn new(records_filter: Vec<String>, folders_filter: Vec<String>) -> Self {
680        QueryOptions {
681            records_filter,
682            folders_filter,
683        }
684    }
685
686    pub fn get_records_filter(&self) -> Option<Vec<String>> {
687        match self.records_filter.len() {
688            0 => None,
689            _ => Some(self.records_filter.clone()),
690        }
691    }
692
693    pub fn get_folders_filter(&self) -> Option<Vec<String>> {
694        match self.folders_filter.len() {
695            0 => None,
696            _ => Some(self.folders_filter.clone()),
697        }
698    }
699}
700
701#[derive(Debug, Clone)]
702pub struct CreateOptions {
703    pub folder_uid: String,
704    pub sub_folder_uid: Option<String>,
705}
706
707impl CreateOptions {
708    pub fn new(folder_uid: String, sub_folder_uid: Option<String>) -> Self {
709        CreateOptions {
710            folder_uid,
711            sub_folder_uid,
712        }
713    }
714}
715
716pub trait Payload: Any {
717    fn as_any(&self) -> &dyn Any;
718    fn to_json(&self) -> Result<String, KSMRError>;
719}
720
721macro_rules! impl_payload {
722    ($($type:ty),*) => {
723        $(
724            impl Payload for $type {
725                fn as_any(&self) -> &dyn Any {
726                    self
727                }
728
729                fn to_json(&self) -> Result<String, KSMRError> {
730                    self.to_json()
731                }
732            }
733        )*
734    };
735}
736
737impl_payload!(
738    GetPayload,
739    UpdatePayload,
740    CreatePayload,
741    FileUploadPayload,
742    CompleteTransactionPayload,
743    DeletePayload,
744    CreateFolderPayload,
745    UpdateFolderPayload,
746    DeleteFolderPayload
747);
748
749// Helper function to check if a payload is of an expected type
750fn is_instance_of<T: Any>(payload: &dyn Payload) -> bool {
751    payload.as_any().is::<T>()
752}
753
754// Validate the payload type
755pub fn validate_payload(payload: &dyn Payload) -> Result<(), KSMRError> {
756    if is_instance_of::<GetPayload>(payload)
757        || is_instance_of::<UpdatePayload>(payload)
758        || is_instance_of::<CreatePayload>(payload)
759        || is_instance_of::<FileUploadPayload>(payload)
760        || is_instance_of::<CompleteTransactionPayload>(payload)
761        || is_instance_of::<DeletePayload>(payload)
762        || is_instance_of::<CreateFolderPayload>(payload)
763        || is_instance_of::<UpdateFolderPayload>(payload)
764        || is_instance_of::<DeleteFolderPayload>(payload)
765    {
766        Ok(())
767    } else {
768        Err(KSMRError::InvalidPayloadError(format!(
769            "Unknown payload type: {:?}",
770            payload.as_any().type_id()
771        )))
772    }
773}
774
775#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
776#[serde(rename_all = "camelCase")]
777pub struct FileUploadFunctionResult {
778    payload: FileUploadPayload,
779    encrypted_file_data: Vec<u8>,
780}
781
782impl FileUploadFunctionResult {
783    pub fn new(payload: FileUploadPayload, encrypted_file_data: Vec<u8>) -> Self {
784        FileUploadFunctionResult {
785            payload,
786            encrypted_file_data,
787        }
788    }
789
790    pub fn get_encrypted_data(&self) -> Vec<u8> {
791        self.encrypted_file_data.clone()
792    }
793
794    pub fn get_payload(&self) -> FileUploadPayload {
795        self.payload.clone()
796    }
797}