openclaw_node/auth/
credentials.rs1use napi::bindgen_prelude::*;
4use napi_derive::napi;
5use std::path::PathBuf;
6use std::sync::Arc;
7use tokio::sync::RwLock;
8
9use openclaw_core::secrets::CredentialStore as RustCredentialStore;
10
11use super::api_key::NodeApiKey;
12use crate::error::OpenClawError;
13
14#[napi]
34pub struct CredentialStore {
35 inner: Arc<RwLock<RustCredentialStore>>,
36}
37
38#[napi]
39impl CredentialStore {
40 #[napi(constructor)]
54 pub fn new(encryption_key_hex: String, store_path: String) -> Result<Self> {
55 let key_bytes = hex::decode(&encryption_key_hex)
56 .map_err(|e| OpenClawError::new("INVALID_KEY", format!("Invalid hex key: {e}")))?;
57
58 if key_bytes.len() != 32 {
59 return Err(OpenClawError::new(
60 "INVALID_KEY",
61 format!(
62 "Encryption key must be 32 bytes (64 hex chars), got {}",
63 key_bytes.len()
64 ),
65 )
66 .into());
67 }
68
69 let mut key = [0u8; 32];
70 key.copy_from_slice(&key_bytes);
71
72 let store = RustCredentialStore::new(key, PathBuf::from(store_path));
73
74 Ok(Self {
75 inner: Arc::new(RwLock::new(store)),
76 })
77 }
78
79 #[napi]
84 pub async fn store(&self, name: String, api_key: &NodeApiKey) -> Result<()> {
85 let store = self.inner.read().await;
86 store
87 .store(&name, &api_key.inner)
88 .map_err(|e| OpenClawError::from_credential_error(e).into())
89 }
90
91 #[napi]
95 pub async fn load(&self, name: String) -> Result<NodeApiKey> {
96 let store = self.inner.read().await;
97 let key = store
98 .load(&name)
99 .map_err(OpenClawError::from_credential_error)?;
100 Ok(NodeApiKey { inner: key })
101 }
102
103 #[napi]
105 pub async fn delete(&self, name: String) -> Result<()> {
106 let store = self.inner.read().await;
107 store
108 .delete(&name)
109 .map_err(|e| OpenClawError::from_credential_error(e).into())
110 }
111
112 #[napi]
114 pub async fn list(&self) -> Result<Vec<String>> {
115 let store = self.inner.read().await;
116 store
117 .list()
118 .map_err(|e| OpenClawError::from_credential_error(e).into())
119 }
120
121 #[napi]
123 pub async fn exists(&self, name: String) -> Result<bool> {
124 let names = self.list().await?;
125 Ok(names.contains(&name))
126 }
127}