eidetica/user/types.rs
1//! Core data types for the user system
2
3use std::collections::HashMap;
4
5use serde::{Deserialize, Serialize};
6
7use crate::{
8 auth::{
9 SigKey,
10 crypto::{PrivateKey, PublicKey},
11 },
12 entry::ID,
13};
14
15/// Credentials for unlocking a user's root signing key.
16///
17/// For password-protected users, contains the salt (for Argon2id key derivation)
18/// and the encrypted root key (AES-256-GCM). Decryption failure IS password
19/// verification — no separate password hash is needed.
20/// For passwordless users, contains the unencrypted root key directly.
21#[derive(Clone, Debug, Serialize, Deserialize)]
22pub struct UserCredentials {
23 /// Public key (available without decryption)
24 pub root_key_id: PublicKey,
25
26 /// Encrypted or unencrypted root private key
27 pub root_key: KeyStorage,
28
29 /// Salt for Argon2id key derivation (None for passwordless)
30 pub password_salt: Option<String>,
31}
32
33/// User information stored in _users database
34///
35/// Users are stored in a Table with auto-generated UUID primary keys.
36/// The username field is used for login and must be unique.
37#[derive(Clone, Debug, Serialize, Deserialize)]
38pub struct UserInfo {
39 /// Unique username (login identifier)
40 pub username: String,
41
42 /// ID of the user's private database
43 pub user_database_id: ID,
44
45 /// Credentials containing the user's root signing key
46 pub credentials: UserCredentials,
47
48 /// User account creation timestamp (Unix timestamp)
49 pub created_at: i64,
50
51 /// Account status
52 pub status: UserStatus,
53}
54
55/// User account status
56#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
57pub enum UserStatus {
58 Active,
59 Disabled,
60 Locked,
61}
62
63/// User profile stored in user's private database
64#[derive(Clone, Debug, Serialize, Deserialize)]
65pub struct UserProfile {
66 /// Username
67 pub username: String,
68
69 /// Display name
70 pub display_name: Option<String>,
71
72 /// Email or other contact info
73 pub contact_info: Option<String>,
74
75 /// User preferences
76 pub preferences: UserPreferences,
77}
78
79/// User-specific preferences
80#[derive(Clone, Debug, Serialize, Deserialize, Default)]
81pub struct UserPreferences {
82 /// Default sync tracked databases
83 pub default_sync_enabled: bool,
84
85 /// Other user-specific settings
86 pub properties: HashMap<String, String>,
87}
88
89/// How a user's private key is stored
90///
91/// Encrypted keys store AES-256-GCM ciphertext of a prefixed-string-encoded `PrivateKey`.
92/// Unencrypted keys store the `PrivateKey` directly (passwordless users only).
93#[derive(Clone, Debug, Serialize, Deserialize)]
94#[serde(tag = "type", rename_all = "lowercase")]
95pub enum KeyStorage {
96 /// Key is encrypted with a password-derived key (AES-256-GCM)
97 Encrypted {
98 /// Encryption algorithm identifier
99 algorithm: String,
100 /// Encrypted prefixed-string-encoded PrivateKey
101 ciphertext: Vec<u8>,
102 /// Encryption nonce/IV (12 bytes for AES-GCM)
103 nonce: Vec<u8>,
104 },
105 /// Key is stored unencrypted (passwordless users only)
106 Unencrypted {
107 /// PrivateKey stored directly — serde carries the signing algorithm tag
108 key: PrivateKey,
109 },
110}
111
112/// User's private key with database mappings
113///
114/// Keys can be either encrypted (for password-protected users) or
115/// unencrypted (for passwordless single-user mode).
116#[derive(Clone, Debug, Serialize, Deserialize)]
117pub struct UserKey {
118 /// Key identifier (the public key)
119 pub key_id: PublicKey,
120
121 /// Key storage (encrypted ciphertext or plaintext PrivateKey)
122 pub storage: KeyStorage,
123
124 /// Display name for this key
125 pub display_name: Option<String>,
126
127 /// When this key was created (Unix timestamp)
128 pub created_at: i64,
129
130 /// Last time this key was used (Unix timestamp)
131 pub last_used: Option<i64>,
132
133 /// Whether this is the user's default key, which has admin access on the user's DB
134 /// Only one key should be marked as default at a time
135 pub is_default: bool,
136
137 /// Database-specific SigKey mappings
138 /// Maps: Database ID → SigKey identity for that database
139 ///
140 /// `None` = default identity (pubkey derived from this key's `key_id`)
141 /// `Some(sigkey)` = non-default identity (global wildcard, name-based, delegation)
142 pub database_sigkeys: HashMap<ID, Option<SigKey>>,
143}
144
145/// A database tracked by a user.
146///
147/// Stored in the user's private database "databases" Table.
148/// Records which databases the user has added to their list, along with
149/// which key to use and sync preferences.
150#[derive(Clone, Debug, Serialize, Deserialize)]
151pub struct TrackedDatabase {
152 /// Database ID
153 pub database_id: ID,
154
155 /// Which user key to use for this database
156 pub key_id: PublicKey,
157
158 /// Sync preferences for this database
159 pub sync_settings: SyncSettings,
160}
161
162/// Synchronization settings for a database
163///
164/// Per-user-per-database sync configuration.
165/// Different users may have different sync preferences for the same database.
166///
167/// # Construction
168///
169/// Use factory methods for common configurations:
170/// - `SyncSettings::disabled()` — sync disabled (this is the `Default`)
171/// - `SyncSettings::enabled()` — sync enabled, no sync on commit
172/// - `SyncSettings::on_commit()` — sync enabled with sync on commit on every transaction
173///
174/// Chain `.with_interval()` to override the periodic sync interval:
175/// ```
176/// # use eidetica::user::types::SyncSettings;
177/// let settings = SyncSettings::enabled().with_interval(60);
178/// ```
179#[derive(Clone, Debug, Serialize, Deserialize)]
180pub struct SyncSettings {
181 /// Whether user wants to sync this database
182 pub sync_enabled: bool,
183
184 /// Sync on commit
185 /// Whether to sync after every commit
186 pub sync_on_commit: bool,
187
188 /// Sync interval in seconds (for periodic sync).
189 /// `None` uses the background sync default interval.
190 pub interval_seconds: Option<u64>,
191
192 /// Additional sync configuration
193 pub properties: HashMap<String, String>,
194}
195
196impl Default for SyncSettings {
197 fn default() -> Self {
198 Self::disabled()
199 }
200}
201
202impl SyncSettings {
203 /// Sync disabled. This is the `Default`.
204 pub fn disabled() -> Self {
205 Self {
206 sync_enabled: false,
207 sync_on_commit: false,
208 interval_seconds: None,
209 properties: HashMap::new(),
210 }
211 }
212
213 /// Sync enabled, no sync on commit.
214 pub fn enabled() -> Self {
215 Self {
216 sync_enabled: true,
217 ..Self::disabled()
218 }
219 }
220
221 /// Sync enabled with sync on commit on every transaction.
222 pub fn on_commit() -> Self {
223 Self {
224 sync_enabled: true,
225 sync_on_commit: true,
226 ..Self::disabled()
227 }
228 }
229
230 /// Set periodic sync interval in seconds.
231 pub fn with_interval(mut self, seconds: u64) -> Self {
232 self.interval_seconds = Some(seconds);
233 self
234 }
235}
236
237/// Database tracking information in _databases table
238#[derive(Clone, Debug, Serialize, Deserialize)]
239pub struct DatabaseTracking {
240 /// Database ID
241 pub database_id: ID,
242
243 /// Cached database name (for quick lookup)
244 pub name: Option<String>,
245
246 /// User UUIDs who have this database in their preferences
247 /// (stores internal UUIDs, not usernames)
248 pub users: Vec<String>,
249
250 /// Database creation time (Unix timestamp)
251 pub created_at: i64,
252
253 /// Last modification time (Unix timestamp)
254 pub last_modified: i64,
255
256 /// Additional metadata
257 pub metadata: HashMap<String, String>,
258}