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