bitwarden_sm/secrets/
create.rs1use bitwarden_api_api::models::SecretCreateRequestModel;
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#[allow(missing_docs)]
15#[derive(Serialize, Deserialize, Debug, JsonSchema, Validate)]
16#[serde(rename_all = "camelCase", deny_unknown_fields)]
17pub struct SecretCreateRequest {
18 pub organization_id: Uuid,
20
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
28 pub project_ids: Option<Vec<Uuid>>,
30}
31
32pub(crate) async fn create_secret(
33 client: &Client,
34 input: &SecretCreateRequest,
35) -> Result<SecretResponse, SecretsManagerError> {
36 input.validate()?;
37
38 let key_store = client.internal.get_key_store();
39 let key = SymmetricKeyId::Organization(input.organization_id);
40
41 let secret = {
42 let mut ctx = key_store.context();
43 Some(SecretCreateRequestModel {
44 key: input.key.clone().trim().encrypt(&mut ctx, key)?.to_string(),
45 value: input.value.clone().encrypt(&mut ctx, key)?.to_string(),
46 note: input
47 .note
48 .clone()
49 .trim()
50 .encrypt(&mut ctx, key)?
51 .to_string(),
52 project_ids: input.project_ids.clone(),
53 access_policies_requests: None,
54 })
55 };
56
57 let config = client.internal.get_api_configurations().await;
58 let res = bitwarden_api_api::apis::secrets_api::organizations_organization_id_secrets_post(
59 &config.api,
60 input.organization_id,
61 secret,
62 )
63 .await?;
64
65 SecretResponse::process_response(res, &mut key_store.context())
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 async fn create_secret(
73 key: Option<String>,
74 value: Option<String>,
75 note: Option<String>,
76 ) -> Result<SecretResponse, SecretsManagerError> {
77 let input = SecretCreateRequest {
78 organization_id: Uuid::new_v4(),
79 key: key.unwrap_or_else(|| "test key".into()),
80 value: value.unwrap_or_else(|| "test value".into()),
81 note: note.unwrap_or_else(|| "test note".into()),
82 project_ids: Some(vec![Uuid::new_v4()]),
83 };
84
85 super::create_secret(&Client::new(None), &input).await
86 }
87
88 #[tokio::test]
89 async fn test_create_secret_request_key_empty_string() {
90 let response = create_secret(Some("".into()), None, None).await;
91 assert!(response.is_err());
92 assert_eq!(response.err().unwrap().to_string(), "key must not be empty");
93 }
94
95 #[tokio::test]
96 async fn test_create_secret_request_key_all_whitespaces_space() {
97 let response = create_secret(Some(" ".into()), None, None).await;
98 assert!(response.is_err());
99 assert_eq!(
100 response.err().unwrap().to_string(),
101 "key must not contain only whitespaces"
102 );
103 }
104
105 #[tokio::test]
106 async fn test_create_secret_request_key_all_whitespaces_tab() {
107 let response = create_secret(Some("\t".into()), None, None).await;
108 assert!(response.is_err());
109 assert_eq!(
110 response.err().unwrap().to_string(),
111 "key must not contain only whitespaces"
112 );
113 }
114
115 #[tokio::test]
116 async fn test_create_secret_request_key_all_whitespaces_newline() {
117 let response = create_secret(Some("\n".into()), None, None).await;
118 assert!(response.is_err());
119 assert_eq!(
120 response.err().unwrap().to_string(),
121 "key must not contain only whitespaces"
122 );
123 }
124
125 #[tokio::test]
126 async fn test_create_secret_request_key_all_whitespaces_combined() {
127 let response = create_secret(Some(" \t\n".into()), None, None).await;
128 assert!(response.is_err());
129 assert_eq!(
130 response.err().unwrap().to_string(),
131 "key must not contain only whitespaces"
132 );
133 }
134
135 #[tokio::test]
136 async fn test_create_secret_request_key_501_character_length() {
137 let response = create_secret(Some("a".repeat(501)), None, None).await;
138 assert!(response.is_err());
139 assert_eq!(
140 response.err().unwrap().to_string(),
141 "key must not exceed 500 characters in length"
142 );
143 }
144
145 #[tokio::test]
146 async fn test_create_secret_request_value_empty_string() {
147 let response = create_secret(None, Some("".into()), None).await;
148 assert!(response.is_err());
149 assert_eq!(
150 response.err().unwrap().to_string(),
151 "value must not be empty"
152 );
153 }
154
155 #[tokio::test]
156 async fn test_create_secret_request_value_25001_character_length() {
157 let response = create_secret(None, Some("a".repeat(25001)), None).await;
158 assert!(response.is_err());
159 assert_eq!(
160 response.err().unwrap().to_string(),
161 "value must not exceed 25000 characters in length"
162 );
163 }
164
165 #[tokio::test]
166 async fn test_create_secret_request_note_all_whitespaces_space() {
167 let response = create_secret(None, None, Some(" ".into())).await;
168 assert!(response.is_err());
169 assert_eq!(
170 response.err().unwrap().to_string(),
171 "note must not contain only whitespaces"
172 );
173 }
174
175 #[tokio::test]
176 async fn test_create_secret_request_note_all_whitespaces_tab() {
177 let response = create_secret(None, None, Some("\t".into())).await;
178 assert!(response.is_err());
179 assert_eq!(
180 response.err().unwrap().to_string(),
181 "note must not contain only whitespaces"
182 );
183 }
184
185 #[tokio::test]
186 async fn test_create_secret_request_note_all_whitespaces_newline() {
187 let response = create_secret(None, None, Some("\n".into())).await;
188 assert!(response.is_err());
189 assert_eq!(
190 response.err().unwrap().to_string(),
191 "note must not contain only whitespaces"
192 );
193 }
194
195 #[tokio::test]
196 async fn test_create_secret_request_note_all_whitespaces_combined() {
197 let response = create_secret(None, None, Some(" \t\n".into())).await;
198 assert!(response.is_err());
199 assert_eq!(
200 response.err().unwrap().to_string(),
201 "note must not contain only whitespaces"
202 );
203 }
204
205 #[tokio::test]
206 async fn test_create_secret_request_note_7001_character_length() {
207 let response = create_secret(None, None, Some("a".repeat(7001))).await;
208 assert!(response.is_err());
209 assert_eq!(
210 response.err().unwrap().to_string(),
211 "note must not exceed 7000 characters in length"
212 );
213 }
214}