1use handle_trait::Handle;
6
7use super::{
8 User,
9 crypto::{derive_encryption_key, encrypt_private_key},
10 errors::UserError,
11 key_manager::UserKeyManager,
12 types::{KeyStorage, UserCredentials, UserInfo, UserKey, UserStatus},
13};
14use crate::{
15 Database, Instance, Result,
16 auth::{
17 crypto::{PrivateKey, PublicKey, generate_keypair},
18 types::{AuthKey, Permission},
19 },
20 constants::{DATABASES, INSTANCE, USERS},
21 crdt::Doc,
22 store::Table,
23};
24
25pub(crate) async fn has_instance_admin(
32 users_db: &Database,
33 device_pubkey: &PublicKey,
34) -> Result<bool> {
35 let tx = users_db.new_transaction().await?;
36 let settings = tx.get_settings()?;
37 let auth = settings.auth_snapshot().await?;
38 let device_pubkey_str = device_pubkey.to_string();
39 for (pubkey_str, key) in auth.get_all_keys()? {
40 if pubkey_str == device_pubkey_str {
41 continue;
42 }
43 if matches!(key.permissions(), Permission::Admin(_)) {
44 return Ok(true);
45 }
46 }
47 Ok(false)
48}
49
50pub(crate) async fn grant_admin_on_system_dbs(
65 users_db: &Database,
66 databases_db: &Database,
67 pubkey: &PublicKey,
68) -> Result<()> {
69 for database in [users_db, databases_db] {
70 database
71 .with_transaction(|tx| async move {
72 let settings = tx.get_settings()?;
73 settings
74 .set_auth_key(pubkey, AuthKey::active(Some("admin"), Permission::Admin(0)))
75 .await
76 })
77 .await?;
78 }
79 Ok(())
80}
81
82pub async fn create_instance_database(
94 instance: &Instance,
95 device_signing_key: &PrivateKey,
96) -> Result<Database> {
97 let mut settings = Doc::new();
98 settings.set("name", INSTANCE);
99 settings.set("type", "system");
100 settings.set("description", "Instance configuration and management");
101
102 Database::create(instance, device_signing_key.clone(), settings).await
103}
104
105pub async fn create_users_database(
117 instance: &Instance,
118 device_signing_key: &PrivateKey,
119) -> Result<Database> {
120 let mut settings = Doc::new();
121 settings.set("name", USERS);
122 settings.set("type", "system");
123 settings.set("description", "User directory database");
124
125 Database::create(instance, device_signing_key.clone(), settings).await
126}
127
128pub async fn create_databases_tracking(
141 instance: &Instance,
142 device_signing_key: &PrivateKey,
143) -> Result<Database> {
144 let mut settings = Doc::new();
145 settings.set("name", DATABASES);
146 settings.set("type", "system");
147 settings.set("description", "Database tracking and registry");
148
149 Database::create(instance, device_signing_key.clone(), settings).await
150}
151
152pub async fn create_user(
178 users_db: &Database,
179 instance: &Instance,
180 username: impl AsRef<str>,
181 password: Option<&str>,
182) -> Result<(String, UserInfo, crate::auth::crypto::PrivateKey)> {
183 let username = username.as_ref();
184 let users_table = users_db
193 .get_store_viewer::<Table<UserInfo>>("users")
194 .await?;
195 let existing = users_table.search(|u| u.username == username).await?;
196 if !existing.is_empty() {
197 return Err(UserError::UsernameAlreadyExists {
198 username: username.to_string(),
199 }
200 .into());
201 }
202
203 let (user_private_key, user_public_key) = generate_keypair();
205 let returned_private_key = user_private_key.clone();
209
210 let mut user_db_settings = Doc::new();
212 user_db_settings.set("name", format!("_user_{username}"));
213 user_db_settings.set("type", "user");
214 user_db_settings.set("description", format!("User database for {username}"));
215
216 let user_database =
217 Database::create(instance, user_private_key.clone(), user_db_settings).await?;
218 let user_database_id = user_database.root_id().clone();
219
220 let device_pubkey = instance.id();
234
235 let (root_key, password_salt) = match password {
237 Some(pwd) => {
238 let salt_string = super::crypto::generate_salt();
239 let encryption_key = derive_encryption_key(pwd, &salt_string)?;
240 let (ciphertext, nonce) = encrypt_private_key(&user_private_key, &encryption_key)?;
241 (
242 KeyStorage::Encrypted {
243 algorithm: "aes-256-gcm".to_string(),
244 ciphertext,
245 nonce,
246 },
247 Some(salt_string),
248 )
249 }
250 None => (
251 KeyStorage::Unencrypted {
252 key: user_private_key,
253 },
254 None,
255 ),
256 };
257
258 let credentials = UserCredentials {
259 root_key_id: user_public_key.clone(),
260 root_key,
261 password_salt,
262 };
263
264 let user_info = UserInfo {
266 username: username.to_string(),
267 user_database_id,
268 credentials,
269 created_at: instance.clock().now_secs(),
270 status: UserStatus::Active,
271 };
272
273 let tx = users_db.new_transaction().await?;
275 let users_table = tx.get_store::<Table<UserInfo>>("users").await?;
276 let user_uuid = users_table.insert(user_info.clone()).await?; tx.commit().await?;
278
279 if !has_instance_admin(users_db, &device_pubkey).await? {
286 let databases_db = instance.databases_db().await?;
287 grant_admin_on_system_dbs(users_db, &databases_db, &user_public_key).await?;
288 }
289
290 Ok((user_uuid, user_info, returned_private_key))
291}
292
293pub async fn login_user(
312 users_db: &Database,
313 instance: &Instance,
314 username: impl AsRef<str>,
315 password: Option<&str>,
316) -> Result<super::User> {
317 let username = username.as_ref();
318
319 let users_table = users_db
321 .get_store_viewer::<Table<UserInfo>>("users")
322 .await?;
323 let results = users_table.search(|u| u.username == username).await?;
324
325 let (user_uuid, user_info) = match results.len() {
327 0 => {
328 return Err(UserError::UserNotFound {
329 username: username.to_string(),
330 }
331 .into());
332 }
333 1 => results.into_iter().next().unwrap(),
334 count => {
335 return Err(UserError::DuplicateUsersDetected {
339 username: username.to_string(),
340 count,
341 }
342 .into());
343 }
344 };
345
346 if user_info.status != UserStatus::Active {
348 return Err(UserError::UserDisabled {
349 username: username.to_string(),
350 }
351 .into());
352 }
353
354 let creds = &user_info.credentials;
356 let is_passwordless = creds.password_salt.is_none();
357 match (password, is_passwordless) {
358 (Some(_), false) => {
359 }
362 (None, true) => {
363 }
365 (Some(_), true) => {
366 return Err(UserError::InvalidPassword.into());
368 }
369 (None, false) => {
370 return Err(UserError::PasswordRequired {
372 operation: "login for password-protected user".to_string(),
373 }
374 .into());
375 }
376 }
377
378 let root_signing_key = match (&creds.root_key, password) {
380 (
381 KeyStorage::Encrypted {
382 ciphertext, nonce, ..
383 },
384 Some(pwd),
385 ) => {
386 let salt = creds
387 .password_salt
388 .as_ref()
389 .ok_or_else(|| UserError::PasswordRequired {
390 operation: "decrypt keys for password-protected user".to_string(),
391 })?;
392 let encryption_key = derive_encryption_key(pwd, salt)?;
393 super::crypto::decrypt_private_key(ciphertext, nonce, &encryption_key)?
394 }
395 (KeyStorage::Unencrypted { key }, None) => key.clone(),
396 _ => return Err(UserError::InvalidPassword.into()),
397 };
398
399 let user =
400 build_user_session(instance, &user_uuid, &user_info, root_signing_key, password).await?;
401
402 let now = instance.clock().now_secs();
410 let user_uuid_ref = user_uuid.clone();
411 if let Err(e) = users_db
412 .with_transaction(|tx| async move {
413 let last_login_table = tx.get_store::<Table<i64>>("last_login").await?;
414 last_login_table.set(&user_uuid_ref, now).await
415 })
416 .await
417 {
418 tracing::debug!("skipping last_login update for {username}: {e}");
419 }
420
421 Ok(user)
422}
423
424pub(crate) async fn build_user_session(
440 instance: &Instance,
441 user_uuid: &str,
442 user_info: &UserInfo,
443 root_signing_key: crate::auth::crypto::PrivateKey,
444 password: Option<&str>,
445) -> Result<super::User> {
446 let user_database = Database::open(instance, &user_info.user_database_id)
448 .await?
449 .with_key(root_signing_key);
450
451 let keys_table = user_database
454 .get_store_viewer::<Table<UserKey>>("keys")
455 .await?;
456 let mut all_keys: Vec<UserKey> = keys_table
457 .search(|_| true)
458 .await?
459 .into_iter()
460 .map(|(_, key)| key)
461 .collect();
462
463 let root_in_table = all_keys
470 .iter()
471 .any(|k| k.key_id == user_info.credentials.root_key_id);
472 if !root_in_table {
473 all_keys.push(UserKey {
474 key_id: user_info.credentials.root_key_id.clone(),
475 storage: user_info.credentials.root_key.clone(),
476 display_name: Some("Root Key".to_string()),
477 created_at: user_info.created_at,
478 last_used: None,
479 is_default: true,
480 database_sigkeys: std::collections::HashMap::new(),
481 });
482 }
483
484 let key_manager = if let Some(pwd) = password {
485 let salt = user_info
486 .credentials
487 .password_salt
488 .as_ref()
489 .ok_or(UserError::InvalidPassword)?;
490 UserKeyManager::new(pwd, salt, all_keys)?
491 } else {
492 UserKeyManager::new_passwordless(all_keys)?
493 };
494
495 if let Err(e) =
501 bootstrap_user_tree_if_needed(instance, &user_database, user_info, root_in_table).await
502 {
503 tracing::debug!(
504 user = %user_info.username,
505 error = %e,
506 "user-tree first-login bootstrap skipped"
507 );
508 }
509
510 Ok(User::new(
511 user_uuid.to_string(),
512 user_info.clone(),
513 user_database,
514 instance.handle(),
515 key_manager,
516 ))
517}
518
519async fn bootstrap_user_tree_if_needed(
535 instance: &Instance,
536 user_database: &Database,
537 user_info: &UserInfo,
538 root_already_persisted: bool,
539) -> Result<()> {
540 let device_pubkey = instance.id();
541
542 let settings_viewer = user_database.get_settings().await?;
546 let device_already_granted = settings_viewer.get_auth_key(&device_pubkey).await.is_ok();
547
548 if root_already_persisted && device_already_granted {
549 return Ok(());
550 }
551
552 let root_user_key = if root_already_persisted {
554 None
555 } else {
556 Some(UserKey {
557 key_id: user_info.credentials.root_key_id.clone(),
558 storage: user_info.credentials.root_key.clone(),
559 display_name: Some("Root Key".to_string()),
560 created_at: user_info.created_at,
561 last_used: None,
562 is_default: true,
563 database_sigkeys: std::collections::HashMap::new(),
564 })
565 };
566 let device_grant = if device_already_granted {
567 None
568 } else {
569 Some((
570 device_pubkey,
571 AuthKey::active(Some("device"), Permission::Read),
572 ))
573 };
574
575 user_database
576 .with_transaction(|tx| async move {
577 if let Some(row) = root_user_key {
578 let keys_table = tx.get_store::<Table<UserKey>>("keys").await?;
579 keys_table.insert(row).await?;
580 }
581 if let Some((pubkey, auth_key)) = device_grant {
582 let settings = tx.get_settings()?;
583 settings.set_auth_key(&pubkey, auth_key).await?;
584 }
585 Ok(())
586 })
587 .await?;
588
589 Ok(())
590}
591
592pub async fn lookup_user_record(
608 users_db: &Database,
609 username: impl AsRef<str>,
610) -> Result<(String, UserInfo)> {
611 let username = username.as_ref();
612 let users_table = users_db
613 .get_store_viewer::<Table<UserInfo>>("users")
614 .await?;
615 let results = users_table.search(|u| u.username == username).await?;
616
617 let (user_uuid, user_info) = match results.len() {
618 0 => {
619 return Err(UserError::UserNotFound {
620 username: username.to_string(),
621 }
622 .into());
623 }
624 1 => results.into_iter().next().unwrap(),
625 count => {
626 return Err(UserError::DuplicateUsersDetected {
627 username: username.to_string(),
628 count,
629 }
630 .into());
631 }
632 };
633
634 if user_info.status != UserStatus::Active {
635 return Err(UserError::UserDisabled {
636 username: username.to_string(),
637 }
638 .into());
639 }
640
641 Ok((user_uuid, user_info))
642}
643
644pub async fn list_users(users_db: &Database) -> Result<Vec<String>> {
652 let users_table = users_db
653 .get_store_viewer::<Table<UserInfo>>("users")
654 .await?;
655 let users: Vec<UserInfo> = users_table
656 .search(|_| true)
657 .await? .into_iter()
659 .map(|(_, user)| user)
660 .collect();
661 Ok(users.into_iter().map(|u| u.username).collect())
662}
663
664#[cfg(test)]
665mod tests {
666 use super::*;
667 use crate::Instance;
668 use crate::backend::database::InMemory;
669 use crate::store::DocStore;
670 use crate::store::SettingsStore;
671
672 use std::sync::Arc;
673
674 async fn setup_instance() -> (Instance, PrivateKey, crate::user::User) {
683 use crate::clock::FixedClock;
684
685 let (instance, admin) = Instance::create_backend_with_clock(
686 Box::new(InMemory::new()),
687 Arc::new(FixedClock::default()),
688 crate::NewUser::passwordless("admin"),
689 )
690 .await
691 .unwrap();
692
693 let device_key = instance.signing_key().unwrap().clone();
695
696 (instance, device_key, admin)
697 }
698
699 #[tokio::test]
700 async fn test_create_instance_database() {
701 let (instance, device_key, _admin) = setup_instance().await;
702
703 let instance_db = create_instance_database(&instance, &device_key)
704 .await
705 .unwrap();
706
707 assert!(!instance_db.root_id().to_string().is_empty());
709
710 let transaction = instance_db.new_transaction().await.unwrap();
712 let doc_store = transaction
713 .get_store::<DocStore>("_settings")
714 .await
715 .unwrap();
716 let name = doc_store.get_string("name").await.unwrap();
717 assert_eq!(name, INSTANCE);
718
719 let device_pubkey = instance.id();
721 let settings_store = SettingsStore::new(&transaction).unwrap();
722 let auth_settings = settings_store.auth_snapshot().await.unwrap();
723 let device_key = auth_settings.get_key_by_pubkey(&device_pubkey).unwrap();
724 assert_eq!(device_key.permissions(), &Permission::Admin(0));
725 assert_eq!(device_key.name(), None);
726 }
727
728 #[tokio::test]
729 async fn test_create_users_database() {
730 let (instance, device_key, _admin) = setup_instance().await;
731
732 let users_db = create_users_database(&instance, &device_key).await.unwrap();
733
734 assert!(!users_db.root_id().to_string().is_empty());
736
737 let transaction = users_db.new_transaction().await.unwrap();
739 let doc_store = transaction
740 .get_store::<DocStore>("_settings")
741 .await
742 .unwrap();
743 let name = doc_store.get_string("name").await.unwrap();
744 assert_eq!(name, USERS);
745 }
746
747 #[tokio::test]
748 async fn test_create_databases_tracking() {
749 let (instance, device_key, _admin) = setup_instance().await;
750
751 let databases_db = create_databases_tracking(&instance, &device_key)
752 .await
753 .unwrap();
754
755 assert!(!databases_db.root_id().to_string().is_empty());
757
758 let transaction = databases_db.new_transaction().await.unwrap();
760 let doc_store = transaction
761 .get_store::<DocStore>("_settings")
762 .await
763 .unwrap();
764 let name = doc_store.get_string("name").await.unwrap();
765 assert_eq!(name, DATABASES);
766 }
767
768 #[tokio::test]
769 async fn test_system_databases_haveadmin_auth() {
770 let (instance, device_key, _admin) = setup_instance().await;
771
772 let users_db = create_users_database(&instance, &device_key).await.unwrap();
773
774 let device_pubkey = instance.id();
776 let transaction = users_db.new_transaction().await.unwrap();
777 let settings_store = SettingsStore::new(&transaction).unwrap();
778 let auth_settings = settings_store.auth_snapshot().await.unwrap();
779 let device_key = auth_settings.get_key_by_pubkey(&device_pubkey).unwrap();
780
781 assert_eq!(device_key.permissions(), &Permission::Admin(0));
782 assert_eq!(device_key.name(), None);
783 }
784
785 #[tokio::test]
786 #[cfg_attr(miri, ignore)] async fn test_create_user() {
788 let (instance, device_key, _admin) = setup_instance().await;
789 let users_db = create_users_database(&instance, &device_key).await.unwrap();
790
791 let (user_uuid, user_info, _) =
793 create_user(&users_db, &instance, "alice", Some("password123"))
794 .await
795 .unwrap();
796
797 assert_eq!(user_info.username, "alice");
799 assert_eq!(user_info.status, UserStatus::Active);
800 assert!(user_info.credentials.password_salt.is_some());
801 assert!(matches!(
802 user_info.credentials.root_key,
803 KeyStorage::Encrypted { .. }
804 ));
805 assert!(!user_uuid.is_empty());
806
807 let users_table = users_db
809 .get_store_viewer::<Table<UserInfo>>("users")
810 .await
811 .unwrap();
812 let stored_user = users_table.get(&user_uuid).await.unwrap();
813 assert_eq!(stored_user.username, "alice");
814 }
815
816 #[tokio::test]
817 async fn test_create_user_passwordless() {
818 let (instance, device_key, _admin) = setup_instance().await;
819 let users_db = create_users_database(&instance, &device_key).await.unwrap();
820
821 let (user_uuid, user_info, _) = create_user(&users_db, &instance, "bob", None)
823 .await
824 .unwrap();
825
826 assert_eq!(user_info.username, "bob");
828 assert_eq!(user_info.status, UserStatus::Active);
829 assert!(user_info.credentials.password_salt.is_none());
830 assert!(matches!(
831 user_info.credentials.root_key,
832 KeyStorage::Unencrypted { .. }
833 ));
834 assert!(!user_uuid.is_empty());
835
836 let users_table = users_db
838 .get_store_viewer::<Table<UserInfo>>("users")
839 .await
840 .unwrap();
841 let stored_user = users_table.get(&user_uuid).await.unwrap();
842 assert_eq!(stored_user.username, "bob");
843 }
844
845 #[tokio::test]
846 #[cfg_attr(miri, ignore)] async fn test_create_duplicate_user() {
848 let (instance, device_key, _admin) = setup_instance().await;
849 let users_db = create_users_database(&instance, &device_key).await.unwrap();
850
851 create_user(&users_db, &instance, "alice", Some("password123"))
853 .await
854 .unwrap();
855
856 let result = create_user(&users_db, &instance, "alice", Some("password456")).await;
858 assert!(result.is_err());
859 }
860
861 #[tokio::test]
862 #[cfg_attr(miri, ignore)] async fn test_login_user() {
864 let (instance, device_key, _admin) = setup_instance().await;
865 let users_db = create_users_database(&instance, &device_key).await.unwrap();
866
867 create_user(&users_db, &instance, "bob", Some("bobpassword"))
869 .await
870 .unwrap();
871
872 let user = login_user(&users_db, &instance, "bob", Some("bobpassword"))
874 .await
875 .unwrap();
876
877 assert_eq!(user.username(), "bob");
879
880 let last_login_table = users_db
882 .get_store_viewer::<Table<i64>>("last_login")
883 .await
884 .unwrap();
885 let last_login = last_login_table.get(user.user_uuid()).await.unwrap();
886 assert!(last_login > 0);
887 }
888
889 #[tokio::test]
890 async fn test_login_user_passwordless() {
891 let (instance, device_key, _admin) = setup_instance().await;
892 let users_db = create_users_database(&instance, &device_key).await.unwrap();
893
894 create_user(&users_db, &instance, "charlie", None)
896 .await
897 .unwrap();
898
899 let user = login_user(&users_db, &instance, "charlie", None)
901 .await
902 .unwrap();
903
904 assert_eq!(user.username(), "charlie");
906
907 let last_login_table = users_db
909 .get_store_viewer::<Table<i64>>("last_login")
910 .await
911 .unwrap();
912 let last_login = last_login_table.get(user.user_uuid()).await.unwrap();
913 assert!(last_login > 0);
914 }
915
916 #[tokio::test]
917 #[cfg_attr(miri, ignore)] async fn test_login_wrong_password() {
919 let (instance, device_key, _admin) = setup_instance().await;
920 let users_db = create_users_database(&instance, &device_key).await.unwrap();
921
922 create_user(&users_db, &instance, "dave", Some("correct_password"))
924 .await
925 .unwrap();
926
927 let result = login_user(&users_db, &instance, "dave", Some("wrong_password")).await;
929 assert!(result.is_err());
930 }
931
932 #[tokio::test]
933 #[cfg_attr(miri, ignore)] async fn test_login_password_mismatch() {
935 let (instance, device_key, _admin) = setup_instance().await;
936 let users_db = create_users_database(&instance, &device_key).await.unwrap();
937
938 create_user(&users_db, &instance, "eve", None)
940 .await
941 .unwrap();
942
943 let result = login_user(&users_db, &instance, "eve", Some("password")).await;
945 assert!(result.is_err());
946
947 create_user(&users_db, &instance, "frank", Some("password"))
949 .await
950 .unwrap();
951
952 let result = login_user(&users_db, &instance, "frank", None).await;
954 assert!(result.is_err());
955 }
956
957 #[tokio::test]
958 async fn test_login_nonexistent_user() {
959 let (instance, device_key, _admin) = setup_instance().await;
960 let users_db = create_users_database(&instance, &device_key).await.unwrap();
961
962 let result = login_user(&users_db, &instance, "nonexistent", Some("password")).await;
964 assert!(result.is_err());
965 }
966
967 #[tokio::test]
968 #[cfg_attr(miri, ignore)] async fn test_list_users() {
970 let (instance, device_key, _admin) = setup_instance().await;
971 let users_db = create_users_database(&instance, &device_key).await.unwrap();
972
973 let users = list_users(&users_db).await.unwrap();
975 assert_eq!(users.len(), 0);
976
977 create_user(&users_db, &instance, "alice", Some("pass1"))
979 .await
980 .unwrap();
981 create_user(&users_db, &instance, "bob", None)
982 .await
983 .unwrap();
984 create_user(&users_db, &instance, "charlie", Some("pass3"))
985 .await
986 .unwrap();
987
988 let users = list_users(&users_db).await.unwrap();
990 assert_eq!(users.len(), 3);
991 assert!(users.contains(&"alice".into()));
992 assert!(users.contains(&"bob".into()));
993 assert!(users.contains(&"charlie".into()));
994 }
995
996 async fn read_admin_permission(database: &Database, pubkey: &PublicKey) -> Option<Permission> {
999 let tx = database.new_transaction().await.unwrap();
1000 let settings = SettingsStore::new(&tx).unwrap();
1001 let auth = settings.auth_snapshot().await.unwrap();
1002 auth.get_key_by_pubkey(pubkey)
1003 .ok()
1004 .map(|k| *k.permissions())
1005 }
1006
1007 #[tokio::test]
1010 async fn test_first_user_becomes_instance_admin() {
1011 let (instance, _device_key, _admin) = setup_instance().await;
1012
1013 let admin_user = instance.login_user("admin", None).await.unwrap();
1015
1016 let users_db = instance.users_db().await.unwrap();
1017 let databases_db = instance.databases_db().await.unwrap();
1018
1019 let admin_pubkey = admin_user.key_manager().get_default_key_id().unwrap();
1020
1021 assert_eq!(
1022 read_admin_permission(&users_db, &admin_pubkey).await,
1023 Some(Permission::Admin(0)),
1024 "bootstrapped admin should be Admin(0) in _users"
1025 );
1026 assert_eq!(
1027 read_admin_permission(&databases_db, &admin_pubkey).await,
1028 Some(Permission::Admin(0)),
1029 "bootstrapped admin should be Admin(0) in _databases"
1030 );
1031 }
1032
1033 #[tokio::test]
1037 async fn test_subsequent_users_are_not_admin() {
1038 let (instance, _device_key, admin) = setup_instance().await;
1039
1040 let admin_view = admin.admin().await.unwrap();
1044 admin_view
1045 .create_user(crate::NewUser::passwordless("alice"))
1046 .await
1047 .unwrap();
1048 let bob_uuid = admin_view
1049 .create_user(crate::NewUser::passwordless("bob"))
1050 .await
1051 .unwrap();
1052
1053 let users_db = instance.users_db().await.unwrap();
1054 let databases_db = instance.databases_db().await.unwrap();
1055
1056 let users_table = users_db
1061 .get_store_viewer::<Table<UserInfo>>("users")
1062 .await
1063 .unwrap();
1064 let bob_info = users_table.get(&bob_uuid).await.unwrap();
1065 let bob_pubkey = bob_info.credentials.root_key_id.clone();
1066
1067 assert!(
1068 read_admin_permission(&users_db, &bob_pubkey)
1069 .await
1070 .is_none(),
1071 "second user must not have any entry in _users.auth_settings"
1072 );
1073 assert!(
1074 read_admin_permission(&databases_db, &bob_pubkey)
1075 .await
1076 .is_none(),
1077 "second user must not have any entry in _databases.auth_settings"
1078 );
1079 }
1080
1081 #[tokio::test]
1084 async fn test_user_is_admin_query() {
1085 let (instance, _device_key, admin) = setup_instance().await;
1086
1087 assert!(
1089 admin.is_admin().await.unwrap(),
1090 "bootstrapped admin must report is_admin = true"
1091 );
1092
1093 admin
1095 .admin()
1096 .await
1097 .unwrap()
1098 .create_user(crate::NewUser::passwordless("alice"))
1099 .await
1100 .unwrap();
1101 let alice = instance.login_user("alice", None).await.unwrap();
1102 assert!(
1103 !alice.is_admin().await.unwrap(),
1104 "newly created user must report is_admin = false"
1105 );
1106 }
1107
1108 #[tokio::test]
1112 async fn test_admin_can_promote_user() {
1113 let (instance, _device_key, admin) = setup_instance().await;
1114
1115 admin
1116 .admin()
1117 .await
1118 .unwrap()
1119 .create_user(crate::NewUser::passwordless("bob"))
1120 .await
1121 .unwrap();
1122 let bob = instance.login_user("bob", None).await.unwrap();
1123 let bob_pubkey = bob.key_manager().get_default_key_id().unwrap();
1124
1125 assert!(
1126 !bob.is_admin().await.unwrap(),
1127 "precondition: bob starts as a non-admin"
1128 );
1129
1130 admin
1132 .admin()
1133 .await
1134 .unwrap()
1135 .grant_instance_admin(&bob_pubkey)
1136 .await
1137 .unwrap();
1138
1139 let users_db = instance.users_db().await.unwrap();
1140 let databases_db = instance.databases_db().await.unwrap();
1141 assert_eq!(
1142 read_admin_permission(&users_db, &bob_pubkey).await,
1143 Some(Permission::Admin(0)),
1144 "promoted user should be Admin(0) in _users"
1145 );
1146 assert_eq!(
1147 read_admin_permission(&databases_db, &bob_pubkey).await,
1148 Some(Permission::Admin(0)),
1149 "promoted user should be Admin(0) in _databases"
1150 );
1151 assert!(
1152 bob.is_admin().await.unwrap(),
1153 "promoted user must now report is_admin = true"
1154 );
1155 }
1156
1157 #[tokio::test]
1160 async fn test_non_admin_cannot_promote() {
1161 let (instance, _device_key, admin) = setup_instance().await;
1162
1163 let admin_view = admin.admin().await.unwrap();
1166 admin_view
1167 .create_user(crate::NewUser::passwordless("bob"))
1168 .await
1169 .unwrap();
1170 admin_view
1171 .create_user(crate::NewUser::passwordless("charlie"))
1172 .await
1173 .unwrap();
1174 let bob = instance.login_user("bob", None).await.unwrap();
1175 let charlie = instance.login_user("charlie", None).await.unwrap();
1176 let charlie_pubkey = charlie.key_manager().get_default_key_id().unwrap();
1177
1178 let err = bob
1181 .admin()
1182 .await
1183 .expect_err("non-admin must not be able to promote");
1184 assert!(
1185 matches!(
1186 &err,
1187 crate::Error::User(e)
1188 if matches!(e.as_ref(), crate::user::UserError::InsufficientPermissions)
1189 ),
1190 "expected InsufficientPermissions, got: {err:?}"
1191 );
1192
1193 let users_db = instance.users_db().await.unwrap();
1194 assert!(
1195 read_admin_permission(&users_db, &charlie_pubkey)
1196 .await
1197 .is_none(),
1198 "a failed promotion must not write to _users.auth_settings"
1199 );
1200 }
1201
1202 #[tokio::test]
1206 async fn test_grant_instance_admin_idempotent_and_chains() {
1207 let (instance, _device_key, admin) = setup_instance().await;
1208
1209 admin
1210 .admin()
1211 .await
1212 .unwrap()
1213 .create_user(crate::NewUser::passwordless("bob"))
1214 .await
1215 .unwrap();
1216 let bob = instance.login_user("bob", None).await.unwrap();
1217 let bob_pubkey = bob.key_manager().get_default_key_id().unwrap();
1218
1219 admin
1220 .admin()
1221 .await
1222 .unwrap()
1223 .grant_instance_admin(&bob_pubkey)
1224 .await
1225 .unwrap();
1226 admin
1227 .admin()
1228 .await
1229 .unwrap()
1230 .grant_instance_admin(&bob_pubkey)
1231 .await
1232 .unwrap(); let users_db = instance.users_db().await.unwrap();
1235 assert_eq!(
1236 read_admin_permission(&users_db, &bob_pubkey).await,
1237 Some(Permission::Admin(0)),
1238 "re-granting an existing admin re-asserts the same entry"
1239 );
1240
1241 bob.admin()
1244 .await
1245 .unwrap()
1246 .create_user(crate::NewUser::passwordless("charlie"))
1247 .await
1248 .unwrap();
1249 let charlie = instance.login_user("charlie", None).await.unwrap();
1250 let charlie_pubkey = charlie.key_manager().get_default_key_id().unwrap();
1251
1252 bob.admin()
1253 .await
1254 .unwrap()
1255 .grant_instance_admin(&charlie_pubkey)
1256 .await
1257 .unwrap();
1258
1259 let databases_db = instance.databases_db().await.unwrap();
1260 assert_eq!(
1261 read_admin_permission(&databases_db, &charlie_pubkey).await,
1262 Some(Permission::Admin(0)),
1263 "a promoted admin can promote further admins via their own key"
1264 );
1265 }
1266}