Skip to main content

agent_secrets/
lib.rs

1// Copyright AGNTCY Contributors (https://github.com/agntcy)
2// SPDX-License-Identifier: Apache-2.0
3
4pub mod agent;
5pub mod auth;
6pub mod memory;
7pub mod platform;
8pub mod policy;
9pub mod session;
10
11use std::fmt;
12
13pub use agent::AgentSecretAccess;
14pub use auth::AgentVerifier;
15pub use memory::SecretBytes;
16pub use policy::SecretPolicy;
17pub use session::SessionContext;
18
19#[cfg(feature = "onepassword")]
20pub use platform::onepassword::OnePasswordStore;
21
22#[derive(Debug)]
23pub enum SecretError {
24    NotSupported,
25    NotAuthorized,
26    InvalidInput,
27    StorageFailure,
28}
29
30impl fmt::Display for SecretError {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        let msg = match self {
33            SecretError::NotSupported => "operation not supported",
34            SecretError::NotAuthorized => "not authorized",
35            SecretError::InvalidInput => "invalid input",
36            SecretError::StorageFailure => "storage failure",
37        };
38        f.write_str(msg)
39    }
40}
41
42impl std::error::Error for SecretError {}
43
44pub type SecretResult<T> = Result<T, SecretError>;
45
46pub trait SecretStore: Send + Sync {
47    fn put(&self, key: &str, secret: &[u8], policy: SecretPolicy) -> SecretResult<()>;
48    fn get(&self, key: &str) -> SecretResult<SecretBytes>;
49    fn delete(&self, key: &str) -> SecretResult<()>;
50    fn list_keys(&self) -> SecretResult<Vec<String>>;
51}
52
53pub fn default_store() -> Box<dyn SecretStore> {
54    platform::default_store()
55}
56
57#[cfg(test)]
58mod tests {
59    use std::collections::HashMap;
60    use std::sync::Mutex;
61
62    use super::*;
63    use crate::agent::AgentSecretAccess;
64    use crate::auth::AgentVerifier;
65
66    struct AllowVerifier;
67
68    impl AgentVerifier for AllowVerifier {
69        fn verify(&self, _session: &SessionContext) -> SecretResult<()> {
70            Ok(())
71        }
72    }
73
74    struct DenyVerifier;
75
76    impl AgentVerifier for DenyVerifier {
77        fn verify(&self, _session: &SessionContext) -> SecretResult<()> {
78            Err(SecretError::NotAuthorized)
79        }
80    }
81
82    struct MemoryStore {
83        entries: Mutex<HashMap<String, Vec<u8>>>,
84    }
85
86    impl MemoryStore {
87        fn new() -> Self {
88            Self {
89                entries: Mutex::new(HashMap::new()),
90            }
91        }
92    }
93
94    impl SecretStore for MemoryStore {
95        fn put(&self, key: &str, secret: &[u8], _policy: SecretPolicy) -> SecretResult<()> {
96            let mut guard = self.entries.lock().map_err(|_| SecretError::StorageFailure)?;
97            guard.insert(key.to_string(), secret.to_vec());
98            Ok(())
99        }
100
101        fn get(&self, key: &str) -> SecretResult<SecretBytes> {
102            let guard = self.entries.lock().map_err(|_| SecretError::StorageFailure)?;
103            let value = guard
104                .get(key)
105                .ok_or(SecretError::InvalidInput)?
106                .clone();
107            Ok(SecretBytes::new(value))
108        }
109
110        fn delete(&self, key: &str) -> SecretResult<()> {
111            let mut guard = self.entries.lock().map_err(|_| SecretError::StorageFailure)?;
112            guard.remove(key);
113            Ok(())
114        }
115
116        fn list_keys(&self) -> SecretResult<Vec<String>> {
117            let guard = self.entries.lock().map_err(|_| SecretError::StorageFailure)?;
118            Ok(guard.keys().cloned().collect())
119        }
120    }
121
122    #[test]
123    fn agent_access_allows_put_get_delete_when_verified() {
124        let store = MemoryStore::new();
125        let verifier = AllowVerifier;
126        let access = AgentSecretAccess::new(&store, &verifier);
127        let session = SessionContext::new("agent", "session");
128
129        access
130            .put_for_session(&session, "key", b"value", SecretPolicy::default())
131            .unwrap();
132
133        let secret = access.get_for_session(&session, "key").unwrap();
134        let got = secret.expose(|bytes| bytes.to_vec());
135        assert_eq!(got, b"value");
136
137        access.delete_for_session(&session, "key").unwrap();
138    }
139
140    #[test]
141    fn agent_access_denies_when_verifier_rejects() {
142        let store = MemoryStore::new();
143        let verifier = DenyVerifier;
144        let access = AgentSecretAccess::new(&store, &verifier);
145        let session = SessionContext::new("agent", "session");
146
147        let err = access
148            .put_for_session(&session, "key", b"value", SecretPolicy::default())
149            .unwrap_err();
150        assert!(matches!(err, SecretError::NotAuthorized));
151    }
152
153    #[test]
154    fn secret_error_display_formats_messages() {
155        assert_eq!(SecretError::NotSupported.to_string(), "operation not supported");
156        assert_eq!(SecretError::NotAuthorized.to_string(), "not authorized");
157        assert_eq!(SecretError::InvalidInput.to_string(), "invalid input");
158        assert_eq!(SecretError::StorageFailure.to_string(), "storage failure");
159    }
160
161    #[test]
162    fn default_store_constructs_a_backend() {
163        let _store = default_store();
164    }
165
166    #[test]
167    fn memory_store_lists_inserted_keys() {
168        let store = MemoryStore::new();
169        store
170            .put("alpha", b"one", SecretPolicy::default())
171            .expect("put alpha");
172        store
173            .put("beta", b"two", SecretPolicy::default())
174            .expect("put beta");
175
176        let mut keys = store.list_keys().expect("list keys");
177        keys.sort();
178
179        assert_eq!(keys, vec!["alpha".to_string(), "beta".to_string()]);
180    }
181}