openzeppelin_relayer/models/signer/
repository.rs

1//! Repository layer models and data persistence for signers.
2//!
3//! This module provides the data layer representation of signers, including:
4//!
5//! - **Repository Models**: Data structures optimized for storage and retrieval
6//! - **Data Conversions**: Mapping between domain objects and repository representations
7//! - **Persistence Logic**: Storage-specific validation and constraints
8//!
9//! Acts as the bridge between the domain layer and actual data storage implementations
10//! (in-memory, Redis, etc.), ensuring consistent data representation across repositories.
11//!
12
13use crate::{
14    models::{
15        signer::{
16            AwsKmsSignerConfig, GoogleCloudKmsSignerConfig, GoogleCloudKmsSignerKeyConfig,
17            GoogleCloudKmsSignerServiceAccountConfig, LocalSignerConfig, Signer, SignerConfig,
18            SignerValidationError, TurnkeySignerConfig, VaultSignerConfig,
19            VaultTransitSignerConfig,
20        },
21        SecretString,
22    },
23    utils::{
24        deserialize_secret_string, deserialize_secret_vec, serialize_secret_string,
25        serialize_secret_vec,
26    },
27};
28use secrets::SecretVec;
29use serde::{Deserialize, Serialize};
30/// Repository model for signer storage and retrieval
31#[derive(Debug, Clone, Serialize, Deserialize)]
32pub struct SignerRepoModel {
33    pub id: String,
34    pub config: SignerConfigStorage,
35}
36
37#[derive(Debug, Clone, Serialize, Deserialize)]
38pub enum SignerConfigStorage {
39    Local(LocalSignerConfigStorage),
40    Vault(VaultSignerConfigStorage),
41    VaultTransit(VaultTransitSignerConfigStorage),
42    AwsKms(AwsKmsSignerConfigStorage),
43    Turnkey(TurnkeySignerConfigStorage),
44    GoogleCloudKms(GoogleCloudKmsSignerConfigStorage),
45}
46
47/// Local signer configuration for storage (with base64 encoding)
48#[derive(Debug, Clone, Serialize, Deserialize)]
49pub struct LocalSignerConfigStorage {
50    #[serde(
51        serialize_with = "serialize_secret_vec",
52        deserialize_with = "deserialize_secret_vec"
53    )]
54    pub raw_key: SecretVec<u8>,
55}
56
57impl From<LocalSignerConfig> for LocalSignerConfigStorage {
58    fn from(config: LocalSignerConfig) -> Self {
59        Self {
60            raw_key: config.raw_key,
61        }
62    }
63}
64
65impl From<LocalSignerConfigStorage> for LocalSignerConfig {
66    fn from(storage: LocalSignerConfigStorage) -> Self {
67        Self {
68            raw_key: storage.raw_key,
69        }
70    }
71}
72
73/// Storage representations for other signer types (these are simpler as they don't contain secrets that need encoding)
74#[derive(Debug, Clone, Serialize, Deserialize)]
75pub struct AwsKmsSignerConfigStorage {
76    pub region: Option<String>,
77    pub key_id: String,
78}
79
80#[derive(Debug, Clone, Serialize, Deserialize)]
81pub struct VaultSignerConfigStorage {
82    pub address: String,
83    pub namespace: Option<String>,
84    #[serde(
85        serialize_with = "serialize_secret_string",
86        deserialize_with = "deserialize_secret_string"
87    )]
88    pub role_id: SecretString,
89    #[serde(
90        serialize_with = "serialize_secret_string",
91        deserialize_with = "deserialize_secret_string"
92    )]
93    pub secret_id: SecretString,
94    pub key_name: String,
95    pub mount_point: Option<String>,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
99pub struct VaultTransitSignerConfigStorage {
100    pub key_name: String,
101    pub address: String,
102    pub namespace: Option<String>,
103    #[serde(
104        serialize_with = "serialize_secret_string",
105        deserialize_with = "deserialize_secret_string"
106    )]
107    pub role_id: SecretString,
108    #[serde(
109        serialize_with = "serialize_secret_string",
110        deserialize_with = "deserialize_secret_string"
111    )]
112    pub secret_id: SecretString,
113    pub pubkey: String,
114    pub mount_point: Option<String>,
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
118pub struct TurnkeySignerConfigStorage {
119    pub api_public_key: String,
120    #[serde(
121        serialize_with = "serialize_secret_string",
122        deserialize_with = "deserialize_secret_string"
123    )]
124    pub api_private_key: SecretString,
125    pub organization_id: String,
126    pub private_key_id: String,
127    pub public_key: String,
128}
129
130#[derive(Debug, Clone, Serialize, Deserialize)]
131pub struct GoogleCloudKmsSignerServiceAccountConfigStorage {
132    #[serde(
133        serialize_with = "serialize_secret_string",
134        deserialize_with = "deserialize_secret_string"
135    )]
136    pub private_key: SecretString,
137    #[serde(
138        serialize_with = "serialize_secret_string",
139        deserialize_with = "deserialize_secret_string"
140    )]
141    pub private_key_id: SecretString,
142    pub project_id: String,
143    #[serde(
144        serialize_with = "serialize_secret_string",
145        deserialize_with = "deserialize_secret_string"
146    )]
147    pub client_email: SecretString,
148    pub client_id: String,
149    pub auth_uri: String,
150    pub token_uri: String,
151    pub auth_provider_x509_cert_url: String,
152    pub client_x509_cert_url: String,
153    pub universe_domain: String,
154}
155
156#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct GoogleCloudKmsSignerKeyConfigStorage {
158    pub location: String,
159    pub key_ring_id: String,
160    pub key_id: String,
161    pub key_version: u32,
162}
163
164#[derive(Debug, Clone, Serialize, Deserialize)]
165pub struct GoogleCloudKmsSignerConfigStorage {
166    pub service_account: GoogleCloudKmsSignerServiceAccountConfigStorage,
167    pub key: GoogleCloudKmsSignerKeyConfigStorage,
168}
169
170/// Convert from domain model to repository model
171impl From<Signer> for SignerRepoModel {
172    fn from(signer: Signer) -> Self {
173        Self {
174            id: signer.id,
175            config: signer.config.into(),
176        }
177    }
178}
179
180/// Convert from repository model to domain model
181impl From<SignerRepoModel> for Signer {
182    fn from(repo_model: SignerRepoModel) -> Self {
183        Self {
184            id: repo_model.id,
185            config: repo_model.config.into(),
186        }
187    }
188}
189
190impl From<AwsKmsSignerConfig> for AwsKmsSignerConfigStorage {
191    fn from(config: AwsKmsSignerConfig) -> Self {
192        Self {
193            region: config.region,
194            key_id: config.key_id,
195        }
196    }
197}
198
199impl From<AwsKmsSignerConfigStorage> for AwsKmsSignerConfig {
200    fn from(storage: AwsKmsSignerConfigStorage) -> Self {
201        Self {
202            region: storage.region,
203            key_id: storage.key_id,
204        }
205    }
206}
207
208impl From<VaultSignerConfig> for VaultSignerConfigStorage {
209    fn from(config: VaultSignerConfig) -> Self {
210        Self {
211            address: config.address,
212            namespace: config.namespace,
213            role_id: config.role_id,
214            secret_id: config.secret_id,
215            key_name: config.key_name,
216            mount_point: config.mount_point,
217        }
218    }
219}
220
221impl From<VaultSignerConfigStorage> for VaultSignerConfig {
222    fn from(storage: VaultSignerConfigStorage) -> Self {
223        Self {
224            address: storage.address,
225            namespace: storage.namespace,
226            role_id: storage.role_id,
227            secret_id: storage.secret_id,
228            key_name: storage.key_name,
229            mount_point: storage.mount_point,
230        }
231    }
232}
233
234impl From<VaultTransitSignerConfig> for VaultTransitSignerConfigStorage {
235    fn from(config: VaultTransitSignerConfig) -> Self {
236        Self {
237            key_name: config.key_name,
238            address: config.address,
239            namespace: config.namespace,
240            role_id: config.role_id,
241            secret_id: config.secret_id,
242            pubkey: config.pubkey,
243            mount_point: config.mount_point,
244        }
245    }
246}
247
248impl From<VaultTransitSignerConfigStorage> for VaultTransitSignerConfig {
249    fn from(storage: VaultTransitSignerConfigStorage) -> Self {
250        Self {
251            key_name: storage.key_name,
252            address: storage.address,
253            namespace: storage.namespace,
254            role_id: storage.role_id,
255            secret_id: storage.secret_id,
256            pubkey: storage.pubkey,
257            mount_point: storage.mount_point,
258        }
259    }
260}
261
262impl From<TurnkeySignerConfig> for TurnkeySignerConfigStorage {
263    fn from(config: TurnkeySignerConfig) -> Self {
264        Self {
265            api_public_key: config.api_public_key,
266            api_private_key: config.api_private_key,
267            organization_id: config.organization_id,
268            private_key_id: config.private_key_id,
269            public_key: config.public_key,
270        }
271    }
272}
273
274impl From<TurnkeySignerConfigStorage> for TurnkeySignerConfig {
275    fn from(storage: TurnkeySignerConfigStorage) -> Self {
276        Self {
277            api_public_key: storage.api_public_key,
278            api_private_key: storage.api_private_key,
279            organization_id: storage.organization_id,
280            private_key_id: storage.private_key_id,
281            public_key: storage.public_key,
282        }
283    }
284}
285
286impl From<GoogleCloudKmsSignerConfig> for GoogleCloudKmsSignerConfigStorage {
287    fn from(config: GoogleCloudKmsSignerConfig) -> Self {
288        Self {
289            service_account: config.service_account.into(),
290            key: config.key.into(),
291        }
292    }
293}
294
295impl From<GoogleCloudKmsSignerConfigStorage> for GoogleCloudKmsSignerConfig {
296    fn from(storage: GoogleCloudKmsSignerConfigStorage) -> Self {
297        Self {
298            service_account: storage.service_account.into(),
299            key: storage.key.into(),
300        }
301    }
302}
303
304impl From<GoogleCloudKmsSignerServiceAccountConfig>
305    for GoogleCloudKmsSignerServiceAccountConfigStorage
306{
307    fn from(config: GoogleCloudKmsSignerServiceAccountConfig) -> Self {
308        Self {
309            private_key: config.private_key,
310            private_key_id: config.private_key_id,
311            project_id: config.project_id,
312            client_email: config.client_email,
313            client_id: config.client_id,
314            auth_uri: config.auth_uri,
315            token_uri: config.token_uri,
316            auth_provider_x509_cert_url: config.auth_provider_x509_cert_url,
317            client_x509_cert_url: config.client_x509_cert_url,
318            universe_domain: config.universe_domain,
319        }
320    }
321}
322
323impl From<GoogleCloudKmsSignerServiceAccountConfigStorage>
324    for GoogleCloudKmsSignerServiceAccountConfig
325{
326    fn from(storage: GoogleCloudKmsSignerServiceAccountConfigStorage) -> Self {
327        Self {
328            private_key: storage.private_key,
329            private_key_id: storage.private_key_id,
330            project_id: storage.project_id,
331            client_email: storage.client_email,
332            client_id: storage.client_id,
333            auth_uri: storage.auth_uri,
334            token_uri: storage.token_uri,
335            auth_provider_x509_cert_url: storage.auth_provider_x509_cert_url,
336            client_x509_cert_url: storage.client_x509_cert_url,
337            universe_domain: storage.universe_domain,
338        }
339    }
340}
341
342impl From<GoogleCloudKmsSignerKeyConfig> for GoogleCloudKmsSignerKeyConfigStorage {
343    fn from(config: GoogleCloudKmsSignerKeyConfig) -> Self {
344        Self {
345            location: config.location,
346            key_ring_id: config.key_ring_id,
347            key_id: config.key_id,
348            key_version: config.key_version,
349        }
350    }
351}
352
353impl From<GoogleCloudKmsSignerKeyConfigStorage> for GoogleCloudKmsSignerKeyConfig {
354    fn from(storage: GoogleCloudKmsSignerKeyConfigStorage) -> Self {
355        Self {
356            location: storage.location,
357            key_ring_id: storage.key_ring_id,
358            key_id: storage.key_id,
359            key_version: storage.key_version,
360        }
361    }
362}
363
364impl SignerRepoModel {
365    /// Validates the repository model using core validation logic
366    pub fn validate(&self) -> Result<(), SignerValidationError> {
367        let core_signer = Signer::from(self.clone());
368        core_signer.validate()
369    }
370}
371
372impl From<SignerConfig> for SignerConfigStorage {
373    fn from(config: SignerConfig) -> Self {
374        match config {
375            SignerConfig::Local(local) => SignerConfigStorage::Local(local.into()),
376            SignerConfig::Vault(vault) => SignerConfigStorage::Vault(vault.into()),
377            SignerConfig::VaultTransit(vault_transit) => {
378                SignerConfigStorage::VaultTransit(vault_transit.into())
379            }
380            SignerConfig::AwsKms(aws_kms) => SignerConfigStorage::AwsKms(aws_kms.into()),
381            SignerConfig::Turnkey(turnkey) => SignerConfigStorage::Turnkey(turnkey.into()),
382            SignerConfig::GoogleCloudKms(gcp) => SignerConfigStorage::GoogleCloudKms(gcp.into()),
383        }
384    }
385}
386
387impl From<SignerConfigStorage> for SignerConfig {
388    fn from(storage: SignerConfigStorage) -> Self {
389        match storage {
390            SignerConfigStorage::Local(local) => SignerConfig::Local(local.into()),
391            SignerConfigStorage::Vault(vault) => SignerConfig::Vault(vault.into()),
392            SignerConfigStorage::VaultTransit(vault_transit) => {
393                SignerConfig::VaultTransit(vault_transit.into())
394            }
395            SignerConfigStorage::AwsKms(aws_kms) => SignerConfig::AwsKms(aws_kms.into()),
396            SignerConfigStorage::Turnkey(turnkey) => SignerConfig::Turnkey(turnkey.into()),
397            SignerConfigStorage::GoogleCloudKms(gcp) => SignerConfig::GoogleCloudKms(gcp.into()),
398        }
399    }
400}
401
402impl SignerConfigStorage {
403    /// Get local signer config, returns error if not a local signer
404    pub fn get_local(&self) -> Option<&LocalSignerConfigStorage> {
405        match self {
406            Self::Local(config) => Some(config),
407            _ => None,
408        }
409    }
410
411    /// Get vault transit signer config, returns error if not a vault transit signer
412    pub fn get_vault_transit(&self) -> Option<&VaultTransitSignerConfigStorage> {
413        match self {
414            Self::VaultTransit(config) => Some(config),
415            _ => None,
416        }
417    }
418
419    /// Get vault signer config, returns error if not a vault signer
420    pub fn get_vault(&self) -> Option<&VaultSignerConfigStorage> {
421        match self {
422            Self::Vault(config) => Some(config),
423            _ => None,
424        }
425    }
426
427    /// Get turnkey signer config, returns error if not a turnkey signer
428    pub fn get_turnkey(&self) -> Option<&TurnkeySignerConfigStorage> {
429        match self {
430            Self::Turnkey(config) => Some(config),
431            _ => None,
432        }
433    }
434
435    /// Get google cloud kms signer config, returns error if not a google cloud kms signer
436    pub fn get_google_cloud_kms(&self) -> Option<&GoogleCloudKmsSignerConfigStorage> {
437        match self {
438            Self::GoogleCloudKms(config) => Some(config),
439            _ => None,
440        }
441    }
442
443    /// Get aws kms signer config, returns error if not an aws kms signer
444    pub fn get_aws_kms(&self) -> Option<&AwsKmsSignerConfigStorage> {
445        match self {
446            Self::AwsKms(config) => Some(config),
447            _ => None,
448        }
449    }
450}
451
452#[cfg(test)]
453mod tests {
454    use super::*;
455    use crate::models::signer::{LocalSignerConfig, SignerConfig};
456    use secrets::SecretVec;
457
458    #[test]
459    fn test_from_core_signer() {
460        let config = LocalSignerConfig {
461            raw_key: SecretVec::new(32, |v| v.fill(1)),
462        };
463
464        let core =
465            crate::models::signer::Signer::new("test-id".to_string(), SignerConfig::Local(config));
466
467        let repo_model = SignerRepoModel::from(core);
468        assert_eq!(repo_model.id, "test-id");
469        assert!(matches!(repo_model.config, SignerConfigStorage::Local(_)));
470    }
471
472    #[test]
473    fn test_to_core_signer() {
474        use crate::models::signer::AwsKmsSignerConfigStorage;
475
476        let domain_config = AwsKmsSignerConfigStorage {
477            region: Some("us-east-1".to_string()),
478            key_id: "test-key".to_string(),
479        };
480
481        let repo_model = SignerRepoModel {
482            id: "test-id".to_string(),
483            config: SignerConfigStorage::AwsKms(domain_config),
484        };
485
486        let core = Signer::from(repo_model);
487        assert_eq!(core.id, "test-id");
488        assert_eq!(
489            core.signer_type(),
490            crate::models::signer::SignerType::AwsKms
491        );
492    }
493
494    #[test]
495    fn test_validation() {
496        use secrets::SecretVec;
497
498        let domain_config = LocalSignerConfig {
499            raw_key: SecretVec::new(32, |v| v.fill(1)),
500        };
501        // Convert to storage config properly
502        let storage_config = LocalSignerConfigStorage::from(domain_config);
503
504        let repo_model = SignerRepoModel {
505            id: "test-id".to_string(),
506            config: SignerConfigStorage::Local(storage_config),
507        };
508
509        assert!(repo_model.validate().is_ok());
510    }
511
512    #[test]
513    fn test_local_config_storage_conversion() {
514        let domain_config = LocalSignerConfig {
515            raw_key: SecretVec::new(4, |v| v.copy_from_slice(&[1, 2, 3, 4])),
516        };
517
518        let storage_config = LocalSignerConfigStorage::from(domain_config.clone());
519        let converted_back = LocalSignerConfig::from(storage_config);
520
521        // Compare the actual secret data
522        let original_data = domain_config.raw_key.borrow();
523        let converted_data = converted_back.raw_key.borrow();
524        assert_eq!(*original_data, *converted_data);
525    }
526}