1use crate::models::{Signer, SignerConfig, SignerRepoModel, SignerType};
13use serde::{Deserialize, Serialize};
14use utoipa::ToSchema;
15
16#[derive(Debug, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
19#[serde(untagged)]
20#[serde(rename_all = "lowercase")]
21pub enum SignerConfigResponse {
22 #[serde(rename = "plain")]
23 Vault {
24 address: String,
25 namespace: Option<String>,
26 key_name: String,
27 mount_point: Option<String>,
28 },
31 #[serde(rename = "vault_transit")]
32 VaultTransit {
33 key_name: String,
34 address: String,
35 namespace: Option<String>,
36 pubkey: String,
37 mount_point: Option<String>,
38 },
41 #[serde(rename = "aws_kms")]
42 AwsKms {
43 region: Option<String>,
44 key_id: String,
45 },
46 Turnkey {
47 api_public_key: String,
48 organization_id: String,
49 private_key_id: String,
50 public_key: String,
51 },
53 #[serde(rename = "google_cloud_kms")]
54 GoogleCloudKms {
55 service_account: GoogleCloudKmsSignerServiceAccountResponseConfig,
56 key: GoogleCloudKmsSignerKeyResponseConfig,
57 },
58 Plain {},
59}
60
61#[derive(Debug, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
62pub struct GoogleCloudKmsSignerServiceAccountResponseConfig {
63 pub project_id: String,
64 pub client_id: String,
65 pub auth_uri: String,
66 pub token_uri: String,
67 pub auth_provider_x509_cert_url: String,
68 pub client_x509_cert_url: String,
69 pub universe_domain: String,
70 }
74
75#[derive(Debug, Serialize, Deserialize, ToSchema, PartialEq, Eq)]
76pub struct GoogleCloudKmsSignerKeyResponseConfig {
77 pub location: String,
78 pub key_ring_id: String,
79 pub key_id: String,
80 pub key_version: u32,
81}
82
83impl From<SignerConfig> for SignerConfigResponse {
84 fn from(config: SignerConfig) -> Self {
85 match config {
86 SignerConfig::Local(_) => SignerConfigResponse::Plain {},
87 SignerConfig::Vault(c) => SignerConfigResponse::Vault {
88 address: c.address,
89 namespace: c.namespace,
90 key_name: c.key_name,
91 mount_point: c.mount_point,
92 },
93 SignerConfig::VaultTransit(c) => SignerConfigResponse::VaultTransit {
94 key_name: c.key_name,
95 address: c.address,
96 namespace: c.namespace,
97 pubkey: c.pubkey,
98 mount_point: c.mount_point,
99 },
100 SignerConfig::AwsKms(c) => SignerConfigResponse::AwsKms {
101 region: c.region,
102 key_id: c.key_id,
103 },
104 SignerConfig::Turnkey(c) => SignerConfigResponse::Turnkey {
105 api_public_key: c.api_public_key,
106 organization_id: c.organization_id,
107 private_key_id: c.private_key_id,
108 public_key: c.public_key,
109 },
110 SignerConfig::GoogleCloudKms(c) => SignerConfigResponse::GoogleCloudKms {
111 service_account: GoogleCloudKmsSignerServiceAccountResponseConfig {
112 project_id: c.service_account.project_id,
113 client_id: c.service_account.client_id,
114 auth_uri: c.service_account.auth_uri,
115 token_uri: c.service_account.token_uri,
116 auth_provider_x509_cert_url: c.service_account.auth_provider_x509_cert_url,
117 client_x509_cert_url: c.service_account.client_x509_cert_url,
118 universe_domain: c.service_account.universe_domain,
119 },
120 key: GoogleCloudKmsSignerKeyResponseConfig {
121 location: c.key.location,
122 key_ring_id: c.key.key_ring_id,
123 key_id: c.key.key_id,
124 key_version: c.key.key_version,
125 },
126 },
127 }
128 }
129}
130
131#[derive(Debug, Serialize, Deserialize, ToSchema)]
132pub struct SignerResponse {
133 pub id: String,
135 pub r#type: SignerType,
137 pub config: SignerConfigResponse,
139}
140
141impl From<SignerRepoModel> for SignerResponse {
142 fn from(repo_model: SignerRepoModel) -> Self {
143 let domain_signer = Signer::from(repo_model);
145
146 Self {
147 id: domain_signer.id.clone(),
148 r#type: domain_signer.signer_type(),
149 config: SignerConfigResponse::from(domain_signer.config),
150 }
151 }
152}
153
154impl From<Signer> for SignerResponse {
155 fn from(signer: Signer) -> Self {
156 Self {
157 id: signer.id.clone(),
158 r#type: signer.signer_type(),
159 config: SignerConfigResponse::from(signer.config),
160 }
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use crate::models::{LocalSignerConfigStorage, SignerConfigStorage};
168 use secrets::SecretVec;
169
170 #[test]
171 fn test_signer_response_from_repo_model() {
172 let repo_model = SignerRepoModel {
173 id: "test-signer".to_string(),
174 config: SignerConfigStorage::Local(LocalSignerConfigStorage {
175 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
176 }),
177 };
178
179 let response = SignerResponse::from(repo_model);
180
181 assert_eq!(response.id, "test-signer");
182 assert_eq!(response.r#type, SignerType::Local);
183 assert_eq!(response.config, SignerConfigResponse::Plain {});
184 }
185
186 #[test]
187 fn test_signer_response_from_domain_model() {
188 use crate::models::signer::{AwsKmsSignerConfig, SignerConfig};
189
190 let aws_config = AwsKmsSignerConfig {
191 key_id: "test-key-id".to_string(),
192 region: Some("us-east-1".to_string()),
193 };
194
195 let signer = crate::models::Signer::new(
196 "domain-signer".to_string(),
197 SignerConfig::AwsKms(aws_config),
198 );
199
200 let response = SignerResponse::from(signer);
201
202 assert_eq!(response.id, "domain-signer");
203 assert_eq!(response.r#type, SignerType::AwsKms);
204 assert_eq!(
205 response.config,
206 SignerConfigResponse::AwsKms {
207 region: Some("us-east-1".to_string()),
208 key_id: "test-key-id".to_string(),
209 }
210 );
211 }
212
213 #[test]
214 fn test_signer_type_mapping_from_config() {
215 let test_cases = vec![
216 (
217 SignerConfigStorage::Local(LocalSignerConfigStorage {
218 raw_key: SecretVec::new(32, |v| v.copy_from_slice(&[1; 32])),
219 }),
220 SignerType::Local,
221 SignerConfigResponse::Plain {},
222 ),
223 (
224 SignerConfigStorage::AwsKms(crate::models::AwsKmsSignerConfigStorage {
225 region: Some("us-east-1".to_string()),
226 key_id: "test-key".to_string(),
227 }),
228 SignerType::AwsKms,
229 SignerConfigResponse::AwsKms {
230 region: Some("us-east-1".to_string()),
231 key_id: "test-key".to_string(),
232 },
233 ),
234 ];
235
236 for (config, expected_type, expected_config) in test_cases {
237 let repo_model = SignerRepoModel {
238 id: "test".to_string(),
239 config,
240 };
241
242 let response = SignerResponse::from(repo_model);
243 assert_eq!(
244 response.r#type, expected_type,
245 "Type mapping failed for {:?}",
246 expected_type
247 );
248 assert_eq!(response.config, expected_config);
249 }
250 }
251
252 #[test]
253 fn test_response_serialization() {
254 let response = SignerResponse {
255 id: "test-signer".to_string(),
256 r#type: SignerType::Local,
257 config: SignerConfigResponse::Plain {},
258 };
259
260 let json = serde_json::to_string(&response).unwrap();
261 assert!(json.contains("\"id\":\"test-signer\""));
262 assert!(json.contains("\"type\":\"local\""));
263 }
264
265 #[test]
266 fn test_response_deserialization() {
267 let json = r#"{
268 "id": "test-signer",
269 "type": "aws_kms",
270 "config": {
271 "region": "us-east-1",
272 "key_id": "test-key-id"
273 }
274 }"#;
275
276 let response: SignerResponse = serde_json::from_str(json).unwrap();
277 assert_eq!(response.id, "test-signer");
278 assert_eq!(response.r#type, SignerType::AwsKms);
279 assert_eq!(
280 response.config,
281 SignerConfigResponse::AwsKms {
282 region: Some("us-east-1".to_string()),
283 key_id: "test-key-id".to_string(),
284 }
285 );
286 }
287
288 #[test]
289 fn test_response_deserialization_all_types() {
290 let json = r#"{"id": "test", "type": "google_cloud_kms", "config": {"service_account": {"project_id": "proj", "client_id": "cid", "auth_uri": "auth", "token_uri": "token", "auth_provider_x509_cert_url": "cert", "client_x509_cert_url": "client_cert", "universe_domain": "domain"}, "key": {"location": "loc", "key_ring_id": "ring", "key_id": "key", "key_version": 1}}}"#;
291
292 let response: SignerResponse = serde_json::from_str(json).unwrap();
293 assert_eq!(response.r#type, SignerType::GoogleCloudKms);
294 }
295}