Skip to main content

neuron_secret_gcp/
lib.rs

1#![deny(missing_docs)]
2//! Stub secret resolver for GCP Secret Manager.
3//!
4//! This crate provides the correct trait impl shape for a GCP Secret Manager resolver.
5//! The actual GCP SDK integration is not implemented — all resolve calls return
6//! `SecretError::BackendError`.
7
8use async_trait::async_trait;
9use layer0::secret::SecretSource;
10use neuron_auth::AuthProvider;
11use neuron_secret::{SecretError, SecretLease, SecretResolver};
12use std::sync::Arc;
13
14/// Stub resolver for GCP Secret Manager.
15pub struct GcpResolver {
16    _auth: Arc<dyn AuthProvider>,
17}
18
19impl GcpResolver {
20    /// Create a new GCP resolver (stub).
21    pub fn new(auth: Arc<dyn AuthProvider>) -> Self {
22        Self { _auth: auth }
23    }
24}
25
26#[async_trait]
27impl SecretResolver for GcpResolver {
28    async fn resolve(&self, source: &SecretSource) -> Result<SecretLease, SecretError> {
29        match source {
30            SecretSource::GcpSecretManager { project, secret_id } => {
31                Err(SecretError::BackendError(format!(
32                    "GcpResolver is a stub — would resolve {project}/{secret_id}"
33                )))
34            }
35            _ => Err(SecretError::NoResolver("gcp".into())),
36        }
37    }
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43    use neuron_auth::{AuthError, AuthRequest, AuthToken};
44
45    struct StubAuth;
46    #[async_trait]
47    impl AuthProvider for StubAuth {
48        async fn provide(&self, _request: &AuthRequest) -> Result<AuthToken, AuthError> {
49            Ok(AuthToken::permanent(b"stub".to_vec()))
50        }
51    }
52
53    fn _assert_send_sync<T: Send + Sync>() {}
54
55    #[test]
56    fn object_safety() {
57        _assert_send_sync::<Box<dyn SecretResolver>>();
58        _assert_send_sync::<Arc<dyn SecretResolver>>();
59        let auth: Arc<dyn AuthProvider> = Arc::new(StubAuth);
60        let _: Arc<dyn SecretResolver> = Arc::new(GcpResolver::new(auth));
61    }
62
63    #[tokio::test]
64    async fn matches_gcp_source() {
65        let auth: Arc<dyn AuthProvider> = Arc::new(StubAuth);
66        let resolver = GcpResolver::new(auth);
67        let source = SecretSource::GcpSecretManager {
68            project: "my-project".into(),
69            secret_id: "api-key".into(),
70        };
71        let err = resolver.resolve(&source).await.unwrap_err();
72        assert!(matches!(err, SecretError::BackendError(_)));
73        assert!(err.to_string().contains("stub"));
74    }
75
76    #[tokio::test]
77    async fn rejects_wrong_source() {
78        let auth: Arc<dyn AuthProvider> = Arc::new(StubAuth);
79        let resolver = GcpResolver::new(auth);
80        let source = SecretSource::Vault {
81            mount: "secret".into(),
82            path: "data/key".into(),
83        };
84        let err = resolver.resolve(&source).await.unwrap_err();
85        assert!(matches!(err, SecretError::NoResolver(_)));
86    }
87}