redact_crypto/storage/
gcs.rs

1use crate::{CryptoError, Entry, NonIndexedTypeStorer, StorableType, Storer, TypeStorer};
2use async_trait::async_trait;
3use cloud_storage::Client;
4use cloud_storage::Error::Other;
5use serde::{Deserialize, Serialize};
6use std::{
7    error::Error,
8    fmt::{self, Display, Formatter},
9};
10
11#[derive(Debug)]
12pub enum GoogleCloudStorerError {
13    /// Represents an error which occurred in some internal system
14    InternalError {
15        source: Box<dyn Error + Send + Sync>,
16    },
17
18    /// Requested document was not found
19    NotFound,
20}
21
22impl Error for GoogleCloudStorerError {
23    fn source(&self) -> Option<&(dyn Error + 'static)> {
24        match *self {
25            GoogleCloudStorerError::InternalError { ref source } => Some(source.as_ref()),
26            GoogleCloudStorerError::NotFound => None,
27        }
28    }
29}
30
31impl Display for GoogleCloudStorerError {
32    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
33        match *self {
34            GoogleCloudStorerError::InternalError { .. } => {
35                write!(f, "Internal error occurred")
36            }
37            GoogleCloudStorerError::NotFound => {
38                write!(f, "Requested document not found")
39            }
40        }
41    }
42}
43
44impl From<GoogleCloudStorerError> for CryptoError {
45    fn from(gcse: GoogleCloudStorerError) -> Self {
46        match gcse {
47            GoogleCloudStorerError::InternalError { .. } => CryptoError::InternalError {
48                source: Box::new(gcse),
49            },
50            GoogleCloudStorerError::NotFound => CryptoError::NotFound {
51                source: Box::new(gcse),
52            },
53        }
54    }
55}
56
57/// Stores an instance of a mongodb-backed key storer
58#[derive(Serialize, Deserialize, Debug, Clone)]
59pub struct GoogleCloudStorer {
60    bucket_name: String,
61}
62
63impl From<GoogleCloudStorer> for NonIndexedTypeStorer {
64    fn from(gcs: GoogleCloudStorer) -> Self {
65        NonIndexedTypeStorer::GoogleCloud(gcs)
66    }
67}
68
69impl From<GoogleCloudStorer> for TypeStorer {
70    fn from(gcs: GoogleCloudStorer) -> Self {
71        TypeStorer::NonIndexed(NonIndexedTypeStorer::GoogleCloud(gcs))
72    }
73}
74
75impl GoogleCloudStorer {
76    pub fn new(bucket_name: String) -> Self {
77        GoogleCloudStorer { bucket_name }
78    }
79}
80
81#[async_trait]
82impl Storer for GoogleCloudStorer {
83    async fn get<T: StorableType>(&self, path: &str) -> Result<Entry<T>, CryptoError> {
84        let client = Client::new();
85        let bytes = client
86            .object()
87            .download(&self.bucket_name, path)
88            .await
89            .map_err(|e| match e {
90                Other(_) => GoogleCloudStorerError::NotFound {},
91                _ => GoogleCloudStorerError::InternalError {
92                    source: Box::new(e),
93                },
94            })?;
95
96        let s = String::from_utf8(bytes).map_err(|e| GoogleCloudStorerError::InternalError {
97            source: Box::new(e),
98        })?;
99
100        Ok(
101            serde_json::from_str(&s).map_err(|e| GoogleCloudStorerError::InternalError {
102                source: Box::new(e),
103            })?,
104        )
105    }
106
107    async fn create<T: StorableType>(&self, entry: Entry<T>) -> Result<Entry<T>, CryptoError> {
108        let entry_string =
109            serde_json::to_string(&entry).map_err(|e| GoogleCloudStorerError::InternalError {
110                source: Box::new(e),
111            })?;
112        let client = Client::new();
113
114        match client
115            .object()
116            .create(
117                &self.bucket_name,
118                entry_string.as_bytes().to_vec(),
119                &entry.path.clone(),
120                "application/json",
121            )
122            .await
123        {
124            Ok(_) => Ok(entry),
125            Err(e) => Err(GoogleCloudStorerError::InternalError {
126                source: Box::new(e),
127            }
128            .into()),
129        }
130    }
131}