Development Documentation (main branch) - For stable release docs, see docs.rs/eidetica

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