Skip to main content

cloud_storage_rs/resources/
hmac_key.rs

1use crate::error::GoogleResponse;
2
3/// The `HmacKey` resource represents an HMAC key within Cloud Storage. The resource consists of a
4/// secret and `HmacMeta`. HMAC keys can be used as credentials for service accounts. For more
5/// information, see HMAC Keys.
6///
7/// Note that the `HmacKey` resource is only returned when you use `HmacKey::create`. Other
8/// methods, such as `HmacKey::read`, return the metadata portion of the HMAC key resource.
9#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
10#[serde(rename_all = "camelCase")]
11pub struct HmacKey {
12    /// The kind of item this is. For HMAC keys, this is always `storage#hmacKey`.
13    pub kind: String,
14    /// HMAC key metadata.
15    pub metadata: HmacMeta,
16    /// HMAC secret key material.
17    pub secret: String,
18}
19
20/// Contains information about an Hmac Key.
21#[derive(Debug, PartialEq, serde::Serialize, serde::Deserialize)]
22#[serde(rename_all = "camelCase")]
23pub struct HmacMeta {
24    /// The kind of item this is. For HMAC key metadata, this is always `storage#hmacKeyMetadata`.
25    pub kind: String,
26    /// The ID of the HMAC key, including the Project ID and the Access ID.
27    pub id: String,
28    /// The link to this resource.
29    pub self_link: String,
30    /// The access ID of the HMAC Key.
31    pub access_id: String,
32    /// The Project ID of the project that owns the service account to which the key authenticates.
33    pub project_id: String,
34    /// The email address of the key's associated service account.
35    pub service_account_email: String,
36    /// The state of the key.
37    pub state: HmacState,
38    /// The creation time of the HMAC key.
39    pub time_created: chrono::DateTime<chrono::Utc>,
40    /// The last modification time of the HMAC key metadata.
41    pub updated: chrono::DateTime<chrono::Utc>,
42    /// HTTP 1.1 Entity tag for the HMAC key.
43    pub etag: String,
44}
45
46/// The state of an Hmac Key.
47#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
48#[serde(rename_all = "UPPERCASE")]
49pub enum HmacState {
50    /// This Hmac key is currently used.
51    Active,
52    /// This Hmac key has been set to inactive.
53    Inactive,
54    /// This Hmac key has been permanently deleted.
55    Deleted,
56}
57
58#[derive(serde::Deserialize)]
59struct ListResponse {
60    items: Vec<HmacMeta>,
61}
62
63#[derive(serde::Serialize)]
64struct UpdateRequest {
65    secret: String,
66    metadata: UpdateMeta,
67}
68
69#[derive(serde::Serialize)]
70struct UpdateMeta {
71    state: HmacState,
72}
73
74impl HmacKey {
75    /// Creates a new HMAC key for the specified service account.
76    /// 
77    /// The authenticated user must have `storage.hmacKeys.create` permission for the project in
78    /// which the key will be created.
79    /// 
80    /// For general information about HMAC keys in Cloud Storage, see
81    /// [HMAC Keys](https://cloud.google.com/storage/docs/authentication/hmackeys).
82    /// ### Example
83    /// ```
84    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
85    /// use cloud_storage::hmac_key::HmacKey;
86    ///
87    /// let hmac_key = HmacKey::create()?;
88    /// # use cloud_storage::hmac_key::HmacState;
89    /// # HmacKey::update(&hmac_key.metadata.access_id, HmacState::Inactive)?;
90    /// # HmacKey::delete(&hmac_key.metadata.access_id)?;
91    /// # Ok(())
92    /// # }
93    /// ```
94    pub fn create() -> Result<Self, crate::Error> {
95        use reqwest::header::CONTENT_LENGTH;
96
97        let url = format!(
98            "{}/projects/{}/hmacKeys",
99            crate::BASE_URL,
100            crate::SERVICE_ACCOUNT.project_id
101        );
102        let query = [("serviceAccountEmail", &crate::SERVICE_ACCOUNT.client_email)];
103        let mut headers = crate::get_headers()?;
104        headers.insert(CONTENT_LENGTH, 0.into());
105        let client = reqwest::blocking::Client::new();
106        let result: GoogleResponse<Self> = client
107            .post(&url)
108            .headers(headers)
109            .query(&query)
110            .send()?
111            .json()?;
112        match result {
113            GoogleResponse::Success(s) => Ok(s),
114            GoogleResponse::Error(e) => Err(e.into()),
115        }
116    }
117
118    /// Retrieves a list of HMAC keys matching the criteria. Since the HmacKey is secret, this does
119    /// not return a `HmacKey`, but a `HmacMeta`. This is a redacted version of a `HmacKey`, but
120    /// with the secret data omitted.
121    /// 
122    /// The authenticated user must have `storage.hmacKeys.list` permission for the project in which
123    /// the key exists.
124    /// 
125    /// For general information about HMAC keys in Cloud Storage, see
126    /// [HMAC Keys](https://cloud.google.com/storage/docs/authentication/hmackeys).
127    /// ### Example
128    /// ```
129    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
130    /// use cloud_storage::hmac_key::HmacKey;
131    /// 
132    /// let all_hmac_keys = HmacKey::list()?;
133    /// # Ok(())
134    /// # }
135    /// ```
136    pub fn list() -> Result<Vec<HmacMeta>, crate::Error> {
137        let url = format!(
138            "{}/projects/{}/hmacKeys",
139            crate::BASE_URL,
140            crate::SERVICE_ACCOUNT.project_id
141        );
142        let client = reqwest::blocking::Client::new();
143        let result: GoogleResponse<ListResponse> = client
144            .get(&url)
145            .headers(crate::get_headers()?)
146            .send()?
147            .json()?;
148        match result {
149            GoogleResponse::Success(s) => Ok(s.items),
150            GoogleResponse::Error(e) => Err(e.into()),
151        }
152    }
153
154    /// Retrieves an HMAC key's metadata. Since the HmacKey is secret, this does not return a
155    /// `HmacKey`, but a `HmacMeta`. This is a redacted version of a `HmacKey`, but with the secret
156    /// data omitted.
157    /// 
158    /// The authenticated user must have `storage.hmacKeys.get` permission for the project in which
159    /// the key exists.
160    /// 
161    /// For general information about HMAC keys in Cloud Storage, see
162    /// [HMAC Keys](https://cloud.google.com/storage/docs/authentication/hmackeys).
163    /// ### Example
164    /// ```no_run
165    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
166    /// use cloud_storage::hmac_key::HmacKey;
167    /// 
168    /// let key = HmacKey::read("some identifier")?;
169    /// # Ok(())
170    /// # }
171    pub fn read(access_id: &str) -> Result<HmacMeta, crate::Error> {
172        let url = format!(
173            "{}/projects/{}/hmacKeys/{}",
174            crate::BASE_URL,
175            crate::SERVICE_ACCOUNT.project_id,
176            access_id
177        );
178        let client = reqwest::blocking::Client::new();
179        let result: GoogleResponse<HmacMeta> = client
180            .get(&url)
181            .headers(crate::get_headers()?)
182            .send()?
183            .json()?;
184        match result {
185            GoogleResponse::Success(s) => Ok(s),
186            GoogleResponse::Error(e) => Err(e.into()),
187        }
188    }
189
190    /// Updates the state of an HMAC key. See the HMAC Key resource descriptor for valid states.
191    /// Since the HmacKey is secret, this does not return a `HmacKey`, but a `HmacMeta`. This is a
192    /// redacted version of a `HmacKey`, but with the secret data omitted.
193    /// 
194    /// The authenticated user must have `storage.hmacKeys.update` permission for the project in
195    /// which the key exists.
196    /// 
197    /// For general information about HMAC keys in Cloud Storage, see
198    /// [HMAC Keys](https://cloud.google.com/storage/docs/authentication/hmackeys).
199    /// ### Example
200    /// ```no_run
201    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
202    /// use cloud_storage::hmac_key::{HmacKey, HmacState};
203    /// 
204    /// let key = HmacKey::update("your key", HmacState::Active)?;
205    /// # Ok(())
206    /// # }
207    pub fn update(access_id: &str, state: HmacState) -> Result<HmacMeta, crate::Error> {
208        let url = format!(
209            "{}/projects/{}/hmacKeys/{}",
210            crate::BASE_URL,
211            crate::SERVICE_ACCOUNT.project_id,
212            access_id
213        );
214        let client = reqwest::blocking::Client::new();
215        serde_json::to_string(&UpdateMeta { state })?;
216        let result: GoogleResponse<HmacMeta> = client
217            .put(&url)
218            .headers(crate::get_headers()?)
219            .json(&UpdateMeta { state })
220            .send()?
221            .json()?;
222        match result {
223            GoogleResponse::Success(s) => Ok(s),
224            GoogleResponse::Error(e) => Err(e.into()),
225        }
226    }
227
228    /// Deletes an HMAC key. Note that a key must be set to `Inactive` first.
229    ///
230    /// The authenticated user must have storage.hmacKeys.delete permission for the project in which
231    /// the key exists.
232    ///
233    /// For general information about HMAC keys in Cloud Storage, see
234    /// [HMAC Keys](https://cloud.google.com/storage/docs/authentication/hmackeys).
235    /// ### Example
236    /// ```no_run
237    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
238    /// use cloud_storage::hmac_key::{HmacKey, HmacState};
239    /// 
240    /// let key = HmacKey::update("your key", HmacState::Inactive)?; // this is required.
241    /// HmacKey::delete(&key.access_id)?;
242    /// # Ok(())
243    /// # }
244    pub fn delete(access_id: &str) -> Result<(), crate::Error> {
245        let url = format!(
246            "{}/projects/{}/hmacKeys/{}",
247            crate::BASE_URL,
248            crate::SERVICE_ACCOUNT.project_id,
249            access_id
250        );
251        let client = reqwest::blocking::Client::new();
252        let response = client.delete(&url).headers(crate::get_headers()?).send()?;
253        if response.status().is_success() {
254            Ok(())
255        } else {
256            Err(crate::Error::Google(response.json()?))
257        }
258    }
259}
260
261#[cfg(test)]
262mod tests {
263    use super::*;
264
265    fn get_test_hmac() -> HmacMeta {
266        match HmacKey::create() {
267            Ok(key) => key.metadata,
268            Err(_) => HmacKey::list().unwrap().pop().unwrap(),
269        }
270    }
271
272    fn remove_test_hmac(access_id: &str) {
273        HmacKey::update(access_id, HmacState::Inactive).unwrap();
274        HmacKey::delete(access_id).unwrap();
275    }
276
277    #[test]
278    fn create() -> Result<(), Box<dyn std::error::Error>> {
279        let key = HmacKey::create()?;
280        remove_test_hmac(&key.metadata.access_id);
281        Ok(())
282    }
283
284    #[test]
285    fn list() -> Result<(), Box<dyn std::error::Error>> {
286        HmacKey::list()?;
287        Ok(())
288    }
289
290    #[test]
291    fn read() -> Result<(), Box<dyn std::error::Error>> {
292        let key = get_test_hmac();
293        HmacKey::read(&key.access_id)?;
294        remove_test_hmac(&key.access_id);
295        Ok(())
296    }
297
298    #[test]
299    fn update() -> Result<(), Box<dyn std::error::Error>> {
300        let key = get_test_hmac();
301        HmacKey::update(&key.access_id, HmacState::Inactive)?;
302        HmacKey::delete(&key.access_id)?;
303        Ok(())
304    }
305
306
307    #[test]
308    fn delete() -> Result<(), Box<dyn std::error::Error>> {
309        let key = get_test_hmac();
310        HmacKey::update(&key.access_id, HmacState::Inactive)?;
311        HmacKey::delete(&key.access_id)?;
312        Ok(())
313    }
314}