bitwarden_sm/secrets/
update.rs1use bitwarden_api_api::models::SecretUpdateRequestModel;
2use bitwarden_core::{key_management::SymmetricKeyId, Client};
3use bitwarden_crypto::Encryptable;
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6use uuid::Uuid;
7use validator::Validate;
8
9use crate::{
10 error::{validate_only_whitespaces, SecretsManagerError},
11 secrets::SecretResponse,
12};
13
14#[derive(Serialize, Deserialize, Debug, JsonSchema, Validate)]
15#[serde(rename_all = "camelCase", deny_unknown_fields)]
16pub struct SecretPutRequest {
17 pub id: Uuid,
19 pub organization_id: Uuid,
21 #[validate(length(min = 1, max = 500), custom(function = validate_only_whitespaces))]
22 pub key: String,
23 #[validate(length(min = 1, max = 25_000))]
24 pub value: String,
25 #[validate(length(max = 7_000), custom(function = validate_only_whitespaces))]
26 pub note: String,
27 pub project_ids: Option<Vec<Uuid>>,
28}
29
30pub(crate) async fn update_secret(
31 client: &Client,
32 input: &SecretPutRequest,
33) -> Result<SecretResponse, SecretsManagerError> {
34 input.validate()?;
35
36 let key_store = client.internal.get_key_store();
37 let key = SymmetricKeyId::Organization(input.organization_id);
38
39 let secret = {
40 let mut ctx = key_store.context();
41 Some(SecretUpdateRequestModel {
42 key: input.key.clone().trim().encrypt(&mut ctx, key)?.to_string(),
43 value: input.value.clone().encrypt(&mut ctx, key)?.to_string(),
44 note: input
45 .note
46 .clone()
47 .trim()
48 .encrypt(&mut ctx, key)?
49 .to_string(),
50 project_ids: input.project_ids.clone(),
51 access_policies_requests: None,
52 })
53 };
54
55 let config = client.internal.get_api_configurations().await;
56 let res =
57 bitwarden_api_api::apis::secrets_api::secrets_id_put(&config.api, input.id, secret).await?;
58
59 SecretResponse::process_response(res, &mut key_store.context())
60}
61
62#[cfg(test)]
63mod tests {
64 use super::*;
65
66 async fn update_secret(
67 key: Option<String>,
68 value: Option<String>,
69 note: Option<String>,
70 ) -> Result<SecretResponse, SecretsManagerError> {
71 let input = SecretPutRequest {
72 id: Uuid::new_v4(),
73 organization_id: Uuid::new_v4(),
74 key: key.unwrap_or_else(|| "test key".into()),
75 value: value.unwrap_or_else(|| "test value".into()),
76 note: note.unwrap_or_else(|| "test note".into()),
77 project_ids: Some(vec![Uuid::new_v4()]),
78 };
79
80 super::update_secret(&Client::new(None), &input).await
81 }
82
83 #[tokio::test]
84 async fn test_update_secret_request_key_empty_string() {
85 let response = update_secret(Some("".into()), None, None).await;
86 assert!(response.is_err());
87 assert_eq!(response.err().unwrap().to_string(), "key must not be empty");
88 }
89
90 #[tokio::test]
91 async fn test_update_secret_request_key_all_whitespaces_space() {
92 let response = update_secret(Some(" ".into()), None, None).await;
93 assert!(response.is_err());
94 assert_eq!(
95 response.err().unwrap().to_string(),
96 "key must not contain only whitespaces"
97 );
98 }
99
100 #[tokio::test]
101 async fn test_update_secret_request_key_all_whitespaces_tab() {
102 let response = update_secret(Some("\t".into()), None, None).await;
103 assert!(response.is_err());
104 assert_eq!(
105 response.err().unwrap().to_string(),
106 "key must not contain only whitespaces"
107 );
108 }
109
110 #[tokio::test]
111 async fn test_update_secret_request_key_all_whitespaces_newline() {
112 let response = update_secret(Some("\n".into()), None, None).await;
113 assert!(response.is_err());
114 assert_eq!(
115 response.err().unwrap().to_string(),
116 "key must not contain only whitespaces"
117 );
118 }
119
120 #[tokio::test]
121 async fn test_update_secret_request_key_all_whitespaces_combined() {
122 let response = update_secret(Some(" \t\n".into()), None, None).await;
123 assert!(response.is_err());
124 assert_eq!(
125 response.err().unwrap().to_string(),
126 "key must not contain only whitespaces"
127 );
128 }
129
130 #[tokio::test]
131 async fn test_update_secret_request_key_501_character_length() {
132 let response = update_secret(Some("a".repeat(501)), None, None).await;
133 assert!(response.is_err());
134 assert_eq!(
135 response.err().unwrap().to_string(),
136 "key must not exceed 500 characters in length"
137 );
138 }
139
140 #[tokio::test]
141 async fn test_update_secret_request_value_empty_string() {
142 let response = update_secret(None, Some("".into()), None).await;
143 assert!(response.is_err());
144 assert_eq!(
145 response.err().unwrap().to_string(),
146 "value must not be empty"
147 );
148 }
149
150 #[tokio::test]
151 async fn test_update_secret_request_value_25001_character_length() {
152 let response = update_secret(None, Some("a".repeat(25001)), None).await;
153 assert!(response.is_err());
154 assert_eq!(
155 response.err().unwrap().to_string(),
156 "value must not exceed 25000 characters in length"
157 );
158 }
159
160 #[tokio::test]
161 async fn test_update_secret_request_note_all_whitespaces_space() {
162 let response = update_secret(None, None, Some(" ".into())).await;
163 assert!(response.is_err());
164 assert_eq!(
165 response.err().unwrap().to_string(),
166 "note must not contain only whitespaces"
167 );
168 }
169
170 #[tokio::test]
171 async fn test_update_secret_request_note_all_whitespaces_tab() {
172 let response = update_secret(None, None, Some("\t".into())).await;
173 assert!(response.is_err());
174 assert_eq!(
175 response.err().unwrap().to_string(),
176 "note must not contain only whitespaces"
177 );
178 }
179
180 #[tokio::test]
181 async fn test_update_secret_request_note_all_whitespaces_newline() {
182 let response = update_secret(None, None, Some("\n".into())).await;
183 assert!(response.is_err());
184 assert_eq!(
185 response.err().unwrap().to_string(),
186 "note must not contain only whitespaces"
187 );
188 }
189
190 #[tokio::test]
191 async fn test_update_secret_request_note_all_whitespaces_combined() {
192 let response = update_secret(None, None, Some(" \t\n".into())).await;
193 assert!(response.is_err());
194 assert_eq!(
195 response.err().unwrap().to_string(),
196 "note must not contain only whitespaces"
197 );
198 }
199
200 #[tokio::test]
201 async fn test_update_secret_request_note_7001_character_length() {
202 let response = update_secret(None, None, Some("a".repeat(7001))).await;
203 assert!(response.is_err());
204 assert_eq!(
205 response.err().unwrap().to_string(),
206 "note must not exceed 7000 characters in length"
207 );
208 }
209}