1use handle_trait::Handle;
6
7use super::{
8 User,
9 crypto::{derive_encryption_key, encrypt_private_key, hash_password},
10 errors::UserError,
11 key_manager::UserKeyManager,
12 types::{KeyStorage, UserInfo, UserKey, UserStatus},
13};
14use crate::{
15 Database, Instance, Result,
16 auth::{
17 crypto::{PrivateKey, generate_keypair},
18 types::{AuthKey, Permission},
19 },
20 constants::{DATABASES, INSTANCE, USERS},
21 crdt::Doc,
22 database::DatabaseKey,
23 store::Table,
24};
25
26pub async fn create_instance_database(
38 instance: &Instance,
39 device_signing_key: &PrivateKey,
40) -> Result<Database> {
41 let mut settings = Doc::new();
42 settings.set("name", INSTANCE);
43 settings.set("type", "system");
44 settings.set("description", "Instance configuration and management");
45
46 Database::create(instance, device_signing_key.clone(), settings).await
47}
48
49pub async fn create_users_database(
61 instance: &Instance,
62 device_signing_key: &PrivateKey,
63) -> Result<Database> {
64 let mut settings = Doc::new();
65 settings.set("name", USERS);
66 settings.set("type", "system");
67 settings.set("description", "User directory database");
68
69 Database::create(instance, device_signing_key.clone(), settings).await
70}
71
72pub async fn create_databases_tracking(
85 instance: &Instance,
86 device_signing_key: &PrivateKey,
87) -> Result<Database> {
88 let mut settings = Doc::new();
89 settings.set("name", DATABASES);
90 settings.set("type", "system");
91 settings.set("description", "Database tracking and registry");
92
93 Database::create(instance, device_signing_key.clone(), settings).await
94}
95
96pub async fn create_user(
113 users_db: &Database,
114 instance: &Instance,
115 username: impl AsRef<str>,
116 password: Option<&str>,
117) -> Result<(String, UserInfo)> {
118 let username = username.as_ref();
119 let users_table = users_db
128 .get_store_viewer::<Table<UserInfo>>("users")
129 .await?;
130 let existing = users_table.search(|u| u.username == username).await?;
131 if !existing.is_empty() {
132 return Err(UserError::UsernameAlreadyExists {
133 username: username.to_string(),
134 }
135 .into());
136 }
137
138 let (password_hash, password_salt) = match password {
140 Some(pwd) => {
141 let (hash, salt) = hash_password(pwd)?;
142 (Some(hash), Some(salt))
143 }
144 None => (None, None),
145 };
146
147 let (user_private_key, user_public_key) = generate_keypair();
149 let mut user_db_settings = Doc::new();
151 user_db_settings.set("name", format!("_user_{username}"));
152 user_db_settings.set("type", "user");
153 user_db_settings.set("description", format!("User database for {username}"));
154
155 let device_private_key = instance.device_key().clone();
157
158 let user_database = Database::create(instance, device_private_key, user_db_settings).await?;
161 let user_database_id = user_database.root_id().clone();
162
163 let txn = user_database.new_transaction().await?;
166 let settings = txn.get_settings()?;
167 settings
168 .set_auth_key(
169 &user_public_key,
170 AuthKey::active(Some("user"), Permission::Admin(0)),
171 )
172 .await?;
173 txn.commit().await?;
174
175 let user_key = match (password, &password_salt) {
177 (Some(pwd), Some(salt)) => {
178 let encryption_key = derive_encryption_key(pwd, salt)?;
180 let (ciphertext, nonce) = encrypt_private_key(&user_private_key, &encryption_key)?;
181
182 UserKey {
183 key_id: user_public_key.clone(),
184 storage: KeyStorage::Encrypted {
185 algorithm: "aes-256-gcm".to_string(),
186 ciphertext,
187 nonce,
188 },
189 display_name: Some("Default Key".to_string()),
190 created_at: instance.clock().now_secs(),
191 last_used: None,
192 is_default: true, database_sigkeys: std::collections::HashMap::new(),
194 }
195 }
196 _ => {
197 UserKey {
199 key_id: user_public_key.clone(),
200 storage: KeyStorage::Unencrypted {
201 key: user_private_key,
202 },
203 display_name: Some("Default Key".to_string()),
204 created_at: instance.clock().now_secs(),
205 last_used: None,
206 is_default: true, database_sigkeys: std::collections::HashMap::new(),
208 }
209 }
210 };
211
212 let tx = user_database.new_transaction().await?;
213 let keys_table = tx.get_store::<Table<UserKey>>("keys").await?;
214 keys_table.insert(user_key).await?;
215 tx.commit().await?;
216
217 let user_info = UserInfo {
219 username: username.to_string(),
220 user_database_id,
221 password_hash,
222 password_salt,
223 created_at: instance.clock().now_secs(),
224 status: UserStatus::Active,
225 };
226
227 let tx = users_db.new_transaction().await?;
229 let users_table = tx.get_store::<Table<UserInfo>>("users").await?;
230 let user_uuid = users_table.insert(user_info.clone()).await?; tx.commit().await?;
232
233 Ok((user_uuid, user_info))
234}
235
236pub async fn login_user(
255 users_db: &Database,
256 instance: &Instance,
257 username: impl AsRef<str>,
258 password: Option<&str>,
259) -> Result<super::User> {
260 let username = username.as_ref();
261
262 let users_table = users_db
264 .get_store_viewer::<Table<UserInfo>>("users")
265 .await?;
266 let results = users_table.search(|u| u.username == username).await?;
267
268 let (user_uuid, user_info) = match results.len() {
270 0 => {
271 return Err(UserError::UserNotFound {
272 username: username.to_string(),
273 }
274 .into());
275 }
276 1 => results.into_iter().next().unwrap(),
277 count => {
278 return Err(UserError::DuplicateUsersDetected {
282 username: username.to_string(),
283 count,
284 }
285 .into());
286 }
287 };
288
289 if user_info.status != UserStatus::Active {
291 return Err(UserError::UserDisabled {
292 username: username.to_string(),
293 }
294 .into());
295 }
296
297 let is_passwordless = user_info.password_hash.is_none();
299 match (password, is_passwordless) {
300 (Some(pwd), false) => {
301 let password_hash = user_info.password_hash.as_ref().unwrap();
303 super::crypto::verify_password(pwd, password_hash)?;
304 }
305 (None, true) => {
306 }
308 (Some(_), true) => {
309 return Err(UserError::InvalidPassword.into());
311 }
312 (None, false) => {
313 return Err(UserError::PasswordRequired {
315 operation: "login for password-protected user".to_string(),
316 }
317 .into());
318 }
319 }
320
321 let temp_user_database =
323 Database::open_unauthenticated(user_info.user_database_id.clone(), instance)?;
324
325 let keys_table = temp_user_database
327 .get_store_viewer::<Table<UserKey>>("keys")
328 .await?;
329 let keys: Vec<UserKey> = keys_table
330 .search(|_| true)
331 .await? .into_iter()
333 .map(|(_, key)| key)
334 .collect();
335
336 let key_manager = if let Some(pwd) = password {
338 let password_salt =
340 user_info
341 .password_salt
342 .as_ref()
343 .ok_or_else(|| UserError::PasswordRequired {
344 operation: "decrypt keys for password-protected user".to_string(),
345 })?;
346 UserKeyManager::new(pwd, password_salt, keys)?
347 } else {
348 UserKeyManager::new_passwordless(keys)?
350 };
351
352 let default_key_id = key_manager
356 .get_default_key_id()
357 .ok_or(UserError::NoKeysAvailable)?;
358 let default_signing_key = key_manager
359 .get_signing_key(&default_key_id)
360 .ok_or_else(|| UserError::KeyNotFound {
361 key_id: default_key_id.to_string(),
362 })?
363 .clone();
364
365 let user_database = Database::open(
366 instance.handle(),
367 &user_info.user_database_id,
368 DatabaseKey::new(default_signing_key),
369 )
370 .await?;
371
372 let tx = users_db.new_transaction().await?;
375 let last_login_table = tx.get_store::<Table<i64>>("last_login").await?;
376 last_login_table
377 .set(&user_uuid, instance.clock().now_secs())
378 .await?;
379 tx.commit().await?;
380
381 Ok(User::new(
383 user_uuid,
384 user_info,
385 user_database,
386 instance.handle(),
387 key_manager,
388 ))
389}
390
391pub async fn list_users(users_db: &Database) -> Result<Vec<String>> {
399 let users_table = users_db
400 .get_store_viewer::<Table<UserInfo>>("users")
401 .await?;
402 let users: Vec<UserInfo> = users_table
403 .search(|_| true)
404 .await? .into_iter()
406 .map(|(_, user)| user)
407 .collect();
408 Ok(users.into_iter().map(|u| u.username).collect())
409}
410
411#[cfg(test)]
412mod tests {
413 use super::*;
414 use crate::Instance;
415 use crate::backend::database::InMemory;
416 use crate::store::DocStore;
417 use crate::store::SettingsStore;
418
419 use std::sync::Arc;
420
421 async fn setup_instance() -> (Instance, PrivateKey) {
425 use crate::clock::FixedClock;
426
427 let backend = Arc::new(InMemory::new());
428
429 let instance = Instance::create_internal(backend, Arc::new(FixedClock::default()))
431 .await
432 .unwrap();
433
434 let device_key = instance.device_key().clone();
436
437 (instance, device_key)
438 }
439
440 #[tokio::test]
441 async fn test_create_instance_database() {
442 let (instance, device_key) = setup_instance().await;
443
444 let instance_db = create_instance_database(&instance, &device_key)
445 .await
446 .unwrap();
447
448 assert!(!instance_db.root_id().to_string().is_empty());
450
451 let transaction = instance_db.new_transaction().await.unwrap();
453 let doc_store = transaction
454 .get_store::<DocStore>("_settings")
455 .await
456 .unwrap();
457 let name = doc_store.get_string("name").await.unwrap();
458 assert_eq!(name, INSTANCE);
459
460 let device_pubkey = instance.device_key().public_key();
462 let settings_store = SettingsStore::new(&transaction).unwrap();
463 let auth_settings = settings_store.auth_snapshot().await.unwrap();
464 let device_key = auth_settings.get_key_by_pubkey(&device_pubkey).unwrap();
465 assert_eq!(device_key.permissions(), &Permission::Admin(0));
466 assert_eq!(device_key.name(), None);
467 }
468
469 #[tokio::test]
470 async fn test_create_users_database() {
471 let (instance, device_key) = setup_instance().await;
472
473 let users_db = create_users_database(&instance, &device_key).await.unwrap();
474
475 assert!(!users_db.root_id().to_string().is_empty());
477
478 let transaction = users_db.new_transaction().await.unwrap();
480 let doc_store = transaction
481 .get_store::<DocStore>("_settings")
482 .await
483 .unwrap();
484 let name = doc_store.get_string("name").await.unwrap();
485 assert_eq!(name, USERS);
486 }
487
488 #[tokio::test]
489 async fn test_create_databases_tracking() {
490 let (instance, device_key) = setup_instance().await;
491
492 let databases_db = create_databases_tracking(&instance, &device_key)
493 .await
494 .unwrap();
495
496 assert!(!databases_db.root_id().to_string().is_empty());
498
499 let transaction = databases_db.new_transaction().await.unwrap();
501 let doc_store = transaction
502 .get_store::<DocStore>("_settings")
503 .await
504 .unwrap();
505 let name = doc_store.get_string("name").await.unwrap();
506 assert_eq!(name, DATABASES);
507 }
508
509 #[tokio::test]
510 async fn test_system_databases_haveadmin_auth() {
511 let (instance, device_key) = setup_instance().await;
512
513 let users_db = create_users_database(&instance, &device_key).await.unwrap();
514
515 let device_pubkey = instance.device_key().public_key();
517 let transaction = users_db.new_transaction().await.unwrap();
518 let settings_store = SettingsStore::new(&transaction).unwrap();
519 let auth_settings = settings_store.auth_snapshot().await.unwrap();
520 let device_key = auth_settings.get_key_by_pubkey(&device_pubkey).unwrap();
521
522 assert_eq!(device_key.permissions(), &Permission::Admin(0));
523 assert_eq!(device_key.name(), None);
524 }
525
526 #[tokio::test]
527 #[cfg_attr(miri, ignore)] async fn test_create_user() {
529 let (instance, device_key) = setup_instance().await;
530 let users_db = create_users_database(&instance, &device_key).await.unwrap();
531
532 let (user_uuid, user_info) =
534 create_user(&users_db, &instance, "alice", Some("password123"))
535 .await
536 .unwrap();
537
538 assert_eq!(user_info.username, "alice");
540 assert_eq!(user_info.status, UserStatus::Active);
541 assert!(user_info.password_hash.is_some());
542 assert!(user_info.password_salt.is_some());
543 assert!(!user_uuid.is_empty());
544
545 let users_table = users_db
547 .get_store_viewer::<Table<UserInfo>>("users")
548 .await
549 .unwrap();
550 let stored_user = users_table.get(&user_uuid).await.unwrap();
551 assert_eq!(stored_user.username, "alice");
552 }
553
554 #[tokio::test]
555 async fn test_create_user_passwordless() {
556 let (instance, device_key) = setup_instance().await;
557 let users_db = create_users_database(&instance, &device_key).await.unwrap();
558
559 let (user_uuid, user_info) = create_user(&users_db, &instance, "bob", None)
561 .await
562 .unwrap();
563
564 assert_eq!(user_info.username, "bob");
566 assert_eq!(user_info.status, UserStatus::Active);
567 assert!(user_info.password_hash.is_none());
568 assert!(user_info.password_salt.is_none());
569 assert!(!user_uuid.is_empty());
570
571 let users_table = users_db
573 .get_store_viewer::<Table<UserInfo>>("users")
574 .await
575 .unwrap();
576 let stored_user = users_table.get(&user_uuid).await.unwrap();
577 assert_eq!(stored_user.username, "bob");
578 }
579
580 #[tokio::test]
581 #[cfg_attr(miri, ignore)] async fn test_create_duplicate_user() {
583 let (instance, device_key) = setup_instance().await;
584 let users_db = create_users_database(&instance, &device_key).await.unwrap();
585
586 create_user(&users_db, &instance, "alice", Some("password123"))
588 .await
589 .unwrap();
590
591 let result = create_user(&users_db, &instance, "alice", Some("password456")).await;
593 assert!(result.is_err());
594 }
595
596 #[tokio::test]
597 #[cfg_attr(miri, ignore)] async fn test_login_user() {
599 let (instance, device_key) = setup_instance().await;
600 let users_db = create_users_database(&instance, &device_key).await.unwrap();
601
602 create_user(&users_db, &instance, "bob", Some("bobpassword"))
604 .await
605 .unwrap();
606
607 let user = login_user(&users_db, &instance, "bob", Some("bobpassword"))
609 .await
610 .unwrap();
611
612 assert_eq!(user.username(), "bob");
614
615 let last_login_table = users_db
617 .get_store_viewer::<Table<i64>>("last_login")
618 .await
619 .unwrap();
620 let last_login = last_login_table.get(user.user_uuid()).await.unwrap();
621 assert!(last_login > 0);
622 }
623
624 #[tokio::test]
625 async fn test_login_user_passwordless() {
626 let (instance, device_key) = setup_instance().await;
627 let users_db = create_users_database(&instance, &device_key).await.unwrap();
628
629 create_user(&users_db, &instance, "charlie", None)
631 .await
632 .unwrap();
633
634 let user = login_user(&users_db, &instance, "charlie", None)
636 .await
637 .unwrap();
638
639 assert_eq!(user.username(), "charlie");
641
642 let last_login_table = users_db
644 .get_store_viewer::<Table<i64>>("last_login")
645 .await
646 .unwrap();
647 let last_login = last_login_table.get(user.user_uuid()).await.unwrap();
648 assert!(last_login > 0);
649 }
650
651 #[tokio::test]
652 #[cfg_attr(miri, ignore)] async fn test_login_wrong_password() {
654 let (instance, device_key) = setup_instance().await;
655 let users_db = create_users_database(&instance, &device_key).await.unwrap();
656
657 create_user(&users_db, &instance, "dave", Some("correct_password"))
659 .await
660 .unwrap();
661
662 let result = login_user(&users_db, &instance, "dave", Some("wrong_password")).await;
664 assert!(result.is_err());
665 }
666
667 #[tokio::test]
668 #[cfg_attr(miri, ignore)] async fn test_login_password_mismatch() {
670 let (instance, device_key) = setup_instance().await;
671 let users_db = create_users_database(&instance, &device_key).await.unwrap();
672
673 create_user(&users_db, &instance, "eve", None)
675 .await
676 .unwrap();
677
678 let result = login_user(&users_db, &instance, "eve", Some("password")).await;
680 assert!(result.is_err());
681
682 create_user(&users_db, &instance, "frank", Some("password"))
684 .await
685 .unwrap();
686
687 let result = login_user(&users_db, &instance, "frank", None).await;
689 assert!(result.is_err());
690 }
691
692 #[tokio::test]
693 async fn test_login_nonexistent_user() {
694 let (instance, device_key) = setup_instance().await;
695 let users_db = create_users_database(&instance, &device_key).await.unwrap();
696
697 let result = login_user(&users_db, &instance, "nonexistent", Some("password")).await;
699 assert!(result.is_err());
700 }
701
702 #[tokio::test]
703 #[cfg_attr(miri, ignore)] async fn test_list_users() {
705 let (instance, device_key) = setup_instance().await;
706 let users_db = create_users_database(&instance, &device_key).await.unwrap();
707
708 let users = list_users(&users_db).await.unwrap();
710 assert_eq!(users.len(), 0);
711
712 create_user(&users_db, &instance, "alice", Some("pass1"))
714 .await
715 .unwrap();
716 create_user(&users_db, &instance, "bob", None)
717 .await
718 .unwrap();
719 create_user(&users_db, &instance, "charlie", Some("pass3"))
720 .await
721 .unwrap();
722
723 let users = list_users(&users_db).await.unwrap();
725 assert_eq!(users.len(), 3);
726 assert!(users.contains(&"alice".into()));
727 assert!(users.contains(&"bob".into()));
728 assert!(users.contains(&"charlie".into()));
729 }
730}