-
-
Notifications
You must be signed in to change notification settings - Fork 9
Expand file tree
/
Copy pathlib.rs
More file actions
146 lines (138 loc) · 6.13 KB
/
Copy pathlib.rs
File metadata and controls
146 lines (138 loc) · 6.13 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
//! McpMux Storage Layer
//!
//! SQLite database with field-level encryption for sensitive data.
//!
//! # Architecture
//!
//! ```text
//! ┌──────────────────────────────────────────────────────┐
//! │ Application │
//! ├──────────────────────────────────────────────────────┤
//! │ Repository Traits │
//! │ (SpaceRepository, CredentialRepository, etc.) │
//! ├──────────────────────────────────────────────────────┤
//! │ SQLite Implementations │
//! │ (SqliteSpaceRepository, SqliteCredentialRepo) │
//! ├──────────────────────────────────────────────────────┤
//! │ FieldEncryptor (AES-256-GCM) │
//! │ (Encrypts tokens/credentials) │
//! ├──────────────────────────────────────────────────────┤
//! │ DpapiKeyProvider (Windows) / KeychainKeyProvider │
//! │ (DPAPI file storage / OS Keychain) │
//! ├──────────────────────────────────────────────────────┤
//! │ Database │
//! │ (SQLite) │
//! └──────────────────────────────────────────────────────┘
//! ```
//!
//! # Usage
//!
//! ```rust,ignore
//! use mcpmux_storage::{
//! Database, SqliteSpaceRepository, SqliteCredentialRepository,
//! FieldEncryptor, MasterKeyProvider,
//! };
//! use std::sync::Arc;
//! use tokio::sync::Mutex;
//!
//! // Get master key (DPAPI on Windows, OS Keychain elsewhere)
//! let key_provider = mcpmux_storage::create_key_provider(&data_dir)?;
//! let master_key = key_provider.get_or_create_key()?;
//!
//! // Open database
//! let db = Database::open(&path)?;
//! let db = Arc::new(Mutex::new(db));
//!
//! // Create encryptor for sensitive fields
//! let encryptor = Arc::new(FieldEncryptor::new(&master_key)?);
//!
//! // Create repositories
//! let space_repo = SqliteSpaceRepository::new(db.clone());
//! let credential_repo = SqliteCredentialRepository::new(db.clone(), encryptor);
//! ```
pub mod crypto;
mod database;
pub mod keychain;
#[cfg(windows)]
pub mod keychain_dpapi;
#[cfg(not(windows))]
pub mod keychain_file;
mod repositories;
pub use crypto::{generate_master_key, FieldEncryptor, KEY_SIZE};
pub use database::Database;
pub use keychain::{
generate_jwt_secret, JwtSecretProvider, KeychainJwtSecretProvider, KeychainKeyProvider,
MasterKeyProvider, JWT_SECRET_SIZE,
};
#[cfg(windows)]
pub use keychain_dpapi::{DpapiJwtSecretProvider, DpapiKeyProvider};
#[cfg(not(windows))]
pub use keychain_file::{FileJwtSecretProvider, FileKeyProvider};
pub use repositories::*;
/// Default database file name.
pub const DATABASE_FILE: &str = "mcpmux.db";
/// Get the default database path for the current platform.
pub fn default_database_path() -> Option<std::path::PathBuf> {
dirs::data_local_dir().map(|p| p.join("mcpmux").join(DATABASE_FILE))
}
/// Create the platform-appropriate master key provider.
///
/// - **Windows**: Uses DPAPI file-based storage (key not visible in Credential Manager UI).
/// Also migrates existing keys from Credential Manager on first use.
/// - **macOS/Linux**: Uses the OS keychain (Keychain / Secret Service).
pub fn create_key_provider(
data_dir: &std::path::Path,
) -> anyhow::Result<Box<dyn MasterKeyProvider>> {
#[cfg(windows)]
{
// Migrate any existing keys from Credential Manager to DPAPI files
if let Err(e) = keychain_dpapi::migrate_from_credential_manager(data_dir) {
tracing::warn!("Credential Manager migration encountered an error: {}", e);
}
Ok(Box::new(DpapiKeyProvider::new(data_dir)?))
}
#[cfg(not(windows))]
{
// Try OS keychain first, fall back to file-based storage if unavailable
match KeychainKeyProvider::new() {
Ok(provider) => match provider.get_or_create_key() {
Ok(_) => return Ok(Box::new(provider)),
Err(e) => tracing::warn!(
"OS keychain unavailable ({e}), using file-based key storage. \
For better security, install gnome-keyring or another Secret Service provider."
),
},
Err(e) => {
tracing::warn!("OS keychain unavailable ({e}), using file-based key storage.")
}
}
Ok(Box::new(FileKeyProvider::new(data_dir)?))
}
}
/// Create the platform-appropriate JWT secret provider.
///
/// - **Windows**: Uses DPAPI file-based storage.
/// - **macOS/Linux**: Uses the OS keychain, with file-based fallback if unavailable.
pub fn create_jwt_secret_provider(
data_dir: &std::path::Path,
) -> anyhow::Result<Box<dyn JwtSecretProvider>> {
#[cfg(windows)]
{
Ok(Box::new(DpapiJwtSecretProvider::new(data_dir)?))
}
#[cfg(not(windows))]
{
match KeychainJwtSecretProvider::new() {
Ok(provider) => match provider.get_or_create_secret() {
Ok(_) => return Ok(Box::new(provider)),
Err(e) => tracing::warn!(
"OS keychain unavailable for JWT secret ({e}), using file-based storage."
),
},
Err(e) => tracing::warn!(
"OS keychain unavailable for JWT secret ({e}), using file-based storage."
),
}
Ok(Box::new(FileJwtSecretProvider::new(data_dir)?))
}
}