docbox_core/secrets/
aws.rs1use std::{collections::HashMap, fmt::Debug};
2
3use aws_sdk_secretsmanager::{
4 error::SdkError,
5 operation::{
6 create_secret::CreateSecretError, get_secret_value::GetSecretValueError,
7 update_secret::UpdateSecretError,
8 },
9};
10use serde::{Deserialize, Serialize};
11use thiserror::Error;
12
13use crate::aws::SecretsManagerClient;
14
15use super::{Secret, SecretManager};
16
17#[derive(Clone, Deserialize, Serialize)]
18pub struct AwsSecretManagerConfig {
19 #[serde(default)]
21 pub secrets: HashMap<String, String>,
22 #[serde(default)]
24 pub default: Option<String>,
25}
26
27impl AwsSecretManagerConfig {
28 pub fn from_env() -> anyhow::Result<Self> {
29 let default = std::env::var("DOCBOX_SECRET_MANAGER_DEFAULT").ok();
30
31 Ok(Self {
32 default,
33 secrets: Default::default(),
34 })
35 }
36}
37
38impl Debug for AwsSecretManagerConfig {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.debug_struct("AwsSecretManagerConfig").finish()
41 }
42}
43
44pub struct AwsSecretManager {
45 client: SecretsManagerClient,
46}
47
48impl AwsSecretManager {
49 pub fn new(client: SecretsManagerClient) -> Self {
50 Self { client }
51 }
52}
53
54#[derive(Debug, Error)]
55pub enum AwsSecretError {
56 #[error("failed to get secret value: {0}")]
57 GetSecretValue(SdkError<GetSecretValueError>),
58 #[error("failed to create secret: {0}")]
59 CreateSecret(SdkError<CreateSecretError>),
60 #[error("failed to update secret: {0}")]
61 UpdateSecret(SdkError<UpdateSecretError>),
62}
63
64impl SecretManager for AwsSecretManager {
65 async fn get_secret(&self, name: &str) -> anyhow::Result<Option<super::Secret>> {
66 let result = self
67 .client
68 .get_secret_value()
69 .secret_id(name)
70 .send()
71 .await
72 .map_err(AwsSecretError::GetSecretValue)?;
73
74 if let Some(value) = result.secret_string {
75 return Ok(Some(Secret::String(value)));
76 }
77
78 if let Some(value) = result.secret_binary {
79 return Ok(Some(Secret::Binary(value.into_inner())));
80 }
81
82 Ok(None)
83 }
84
85 async fn create_secret(&self, name: &str, value: &str) -> anyhow::Result<()> {
86 let err = match self
87 .client
88 .create_secret()
89 .secret_string(value)
90 .name(name)
91 .send()
92 .await
93 {
94 Ok(_) => return Ok(()),
95 Err(err) => err,
96 };
97
98 if err
100 .as_service_error()
101 .is_some_and(|value| value.is_resource_exists_exception())
102 {
103 self.client
104 .update_secret()
105 .secret_string(value)
106 .secret_id(name)
107 .send()
108 .await
109 .map_err(AwsSecretError::UpdateSecret)?;
110
111 return Ok(());
112 }
113
114 Err(AwsSecretError::CreateSecret(err).into())
115 }
116}