1use handle_trait::Handle;
6
7use super::{
8 AccountSession,
9 errors::AccountError,
10 key_manager::AccountKeyManager,
11 types::{AccountCredentials, AccountInfo, AccountKey, AccountStatus, KeyStorage},
12};
13use crate::{
14 Database, Instance, Result,
15 auth::{
16 crypto::{PrivateKey, PublicKey, generate_keypair},
17 types::{AuthKey, Permission},
18 },
19 constants::{DATABASES, INSTANCE, USERS},
20 crdt::Doc,
21};
22
23pub(crate) async fn has_instance_admin(
30 users_db: &Database,
31 device_pubkey: &PublicKey,
32) -> Result<bool> {
33 let tx = users_db.new_transaction().await?;
34 let settings = tx.get_settings()?;
35 let auth = settings.auth_snapshot().await?;
36 let device_pubkey_str = device_pubkey.to_string();
37 for (pubkey_str, key) in auth.get_all_keys()? {
38 if pubkey_str == device_pubkey_str {
39 continue;
40 }
41 if matches!(key.permissions(), Permission::Admin(_)) {
42 return Ok(true);
43 }
44 }
45 Ok(false)
46}
47
48pub(crate) async fn grant_admin_on_system_dbs(
63 users_db: &Database,
64 databases_db: &Database,
65 pubkey: &PublicKey,
66) -> Result<()> {
67 for database in [users_db, databases_db] {
68 database
69 .with_transaction(|tx| async move {
70 let settings = tx.get_settings()?;
71 settings
72 .set_auth_key(pubkey, AuthKey::active(Some("admin"), Permission::Admin(0)))
73 .await
74 })
75 .await?;
76 }
77 Ok(())
78}
79
80pub async fn create_instance_database(
92 instance: &Instance,
93 device_signing_key: &PrivateKey,
94) -> Result<Database> {
95 let mut settings = Doc::new();
96 settings.set("name", INSTANCE);
97 settings.set("type", "system");
98 settings.set("description", "Instance configuration and management");
99
100 Database::create(instance, device_signing_key.clone(), settings).await
101}
102
103pub async fn create_accounts_database(
115 instance: &Instance,
116 device_signing_key: &PrivateKey,
117) -> Result<Database> {
118 let mut settings = Doc::new();
119 settings.set("name", USERS);
120 settings.set("type", "system");
121 settings.set("description", "Instance account administration");
122
123 Database::create(instance, device_signing_key.clone(), settings).await
124}
125
126pub async fn create_databases_tracking(
139 instance: &Instance,
140 device_signing_key: &PrivateKey,
141) -> Result<Database> {
142 let mut settings = Doc::new();
143 settings.set("name", DATABASES);
144 settings.set("type", "system");
145 settings.set("description", "Database tracking and registry");
146
147 Database::create(instance, device_signing_key.clone(), settings).await
148}
149
150pub async fn create_account(
176 users_db: &Database,
177 instance: &Instance,
178 username: impl AsRef<str>,
179 password: &str,
180) -> Result<(String, AccountInfo, crate::auth::crypto::PrivateKey)> {
181 let username = username.as_ref();
182 if super::local_logins::list(users_db)
185 .await?
186 .iter()
187 .any(|name| name == username)
188 {
189 return Err(AccountError::LoginNameAlreadyExists {
190 username: username.to_owned(),
191 }
192 .into());
193 }
194
195 let (user_private_key, user_public_key) = generate_keypair();
197 let returned_private_key = user_private_key.clone();
199
200 let mut user_db_settings = Doc::new();
202 user_db_settings.set("name", format!("_user_{username}"));
203 user_db_settings.set("type", "user");
204 user_db_settings.set("description", format!("Account database for {username}"));
205
206 let account_database =
207 Database::create(instance, user_private_key.clone(), user_db_settings).await?;
208 let account_id = account_database.root_id().clone();
209
210 let device_pubkey = instance.id();
214
215 let credentials = AccountCredentials {
217 root_key_id: user_public_key.clone(),
218 bundle: KeyStorage::seal(&account_id, &[user_private_key], password)?,
219 };
220
221 let account_info = AccountInfo {
223 username: username.to_string(),
224 account_id,
225 credentials,
226 created_at: instance.clock().now_secs(),
227 status: AccountStatus::Active,
228 };
229
230 let login_id = uuid::Uuid::new_v4().to_string();
231 super::local_logins::register(instance, &login_id, &account_info).await?;
232
233 if !has_instance_admin(users_db, &device_pubkey).await? {
240 let databases_db = instance.databases_db().await?;
241 grant_admin_on_system_dbs(users_db, &databases_db, &user_public_key).await?;
242 }
243
244 Ok((login_id, account_info, returned_private_key))
245}
246
247pub async fn login_account(
266 _users_db: &Database,
267 instance: &Instance,
268 username: impl AsRef<str>,
269 password: &str,
270) -> Result<super::AccountSession> {
271 let username = username.as_ref();
272
273 let (login_id, account_info) = super::local_logins::lookup(instance, username).await?;
274
275 let unlocked = account_info
276 .credentials
277 .unlock(&account_info.account_id, password)?;
278
279 let user = build_account_session(instance, &login_id, &account_info, unlocked).await?;
280
281 Ok(user)
282}
283
284pub(crate) async fn build_account_session(
292 instance: &Instance,
293 login_id: &str,
294 account_info: &AccountInfo,
295 unlocked: super::UnlockedCredentials,
296) -> Result<super::AccountSession> {
297 if unlocked.account_id() != &account_info.account_id {
299 return Err(AccountError::InvalidLocalState.into());
300 }
301 let root_signing_key = unlocked.signer(&account_info.credentials.root_key_id)?;
302 let account_database =
303 super::AccountSigner::new(account_info.account_id.clone(), root_signing_key)
304 .open_account(instance)
305 .await?
306 .database()
307 .clone();
308
309 let mut key_manager = None;
312 super::local_state::update(instance, login_id, |state| {
313 if state.credentials.is_none() {
314 state.credentials = Some(account_info.credentials.bundle.clone());
315 }
316 state
317 .keys
318 .entry(account_info.credentials.root_key_id.to_string())
319 .or_insert_with(|| AccountKey {
320 key_id: account_info.credentials.root_key_id.clone(),
321 display_name: Some("Root Key".to_string()),
322 created_at: account_info.created_at,
323 last_used: None,
324 is_default: true,
325 database_sigkeys: std::collections::HashMap::new(),
326 });
327 let current = unlocked.unlock_storage(
328 state
329 .credentials
330 .as_ref()
331 .ok_or(AccountError::InvalidLocalState)?,
332 )?;
333 current.signer(&account_info.credentials.root_key_id)?;
334 key_manager = Some(AccountKeyManager::new(
335 current,
336 state.keys.values().cloned().collect(),
337 )?);
338 state.last_login = Some(instance.clock().now_secs());
339 Ok(())
340 })
341 .await?;
342 let key_manager = key_manager.ok_or(AccountError::InvalidLocalState)?;
343
344 if let Err(e) = bootstrap_account_tree_if_needed(instance, &account_database).await {
350 tracing::debug!(
351 user = %account_info.username,
352 error = %e,
353 "user-tree first-login bootstrap skipped"
354 );
355 }
356
357 Ok(AccountSession::new(
358 login_id.to_string(),
359 account_info.clone(),
360 account_database,
361 instance.handle(),
362 key_manager,
363 ))
364}
365
366async fn bootstrap_account_tree_if_needed(
369 instance: &Instance,
370 account_database: &Database,
371) -> Result<()> {
372 let device_pubkey = instance.id();
373 if account_database
374 .get_settings()
375 .await?
376 .get_auth_key(&device_pubkey)
377 .await
378 .is_ok()
379 {
380 return Ok(());
381 }
382 account_database
383 .with_transaction(|tx| async move {
384 tx.get_settings()?
385 .set_auth_key(
386 &device_pubkey,
387 AuthKey::active(Some("device"), Permission::Read),
388 )
389 .await
390 })
391 .await?;
392 Ok(())
393}
394
395pub async fn lookup_account_record(
410 users_db: &Database,
411 username: impl AsRef<str>,
412) -> Result<(String, AccountInfo)> {
413 super::local_logins::lookup(&users_db.instance()?, username.as_ref()).await
414}
415
416pub async fn list_accounts(users_db: &Database) -> Result<Vec<String>> {
424 super::local_logins::list(users_db).await
425}
426
427#[cfg(test)]
428mod tests {
429 use super::*;
430 use crate::Instance;
431 use crate::backend::database::InMemory;
432 use crate::store::DocStore;
433 use crate::store::SettingsStore;
434
435 use std::sync::Arc;
436
437 async fn setup_instance() -> (Instance, PrivateKey, crate::account::AccountSession) {
446 use crate::clock::FixedClock;
447
448 let (instance, admin) = Instance::create_backend_with_clock(
449 Box::new(InMemory::new()),
450 Arc::new(FixedClock::default()),
451 crate::NewAccount::passwordless("admin"),
452 )
453 .await
454 .unwrap();
455
456 let device_key = instance.signing_key().unwrap().clone();
458
459 (instance, device_key, admin)
460 }
461
462 #[tokio::test]
463 async fn test_create_instance_database() {
464 let (instance, device_key, _admin) = setup_instance().await;
465
466 let instance_db = create_instance_database(&instance, &device_key)
467 .await
468 .unwrap();
469
470 assert!(!instance_db.root_id().to_string().is_empty());
472
473 let transaction = instance_db.new_transaction().await.unwrap();
475 let doc_store = transaction
476 .get_store::<DocStore>("_settings")
477 .await
478 .unwrap();
479 let name = doc_store.get_string("name").await.unwrap();
480 assert_eq!(name, INSTANCE);
481
482 let device_pubkey = instance.id();
484 let settings_store = SettingsStore::new(&transaction).unwrap();
485 let auth_settings = settings_store.auth_snapshot().await.unwrap();
486 let device_key = auth_settings.get_key_by_pubkey(&device_pubkey).unwrap();
487 assert_eq!(device_key.permissions(), &Permission::Admin(0));
488 assert_eq!(device_key.name(), None);
489 }
490
491 #[tokio::test]
492 async fn test_create_users_database() {
493 let (instance, device_key, _admin) = setup_instance().await;
494
495 let users_db = create_accounts_database(&instance, &device_key)
496 .await
497 .unwrap();
498
499 assert!(!users_db.root_id().to_string().is_empty());
501
502 let transaction = users_db.new_transaction().await.unwrap();
504 let doc_store = transaction
505 .get_store::<DocStore>("_settings")
506 .await
507 .unwrap();
508 let name = doc_store.get_string("name").await.unwrap();
509 assert_eq!(name, USERS);
510 }
511
512 #[tokio::test]
513 async fn test_create_databases_tracking() {
514 let (instance, device_key, _admin) = setup_instance().await;
515
516 let databases_db = create_databases_tracking(&instance, &device_key)
517 .await
518 .unwrap();
519
520 assert!(!databases_db.root_id().to_string().is_empty());
522
523 let transaction = databases_db.new_transaction().await.unwrap();
525 let doc_store = transaction
526 .get_store::<DocStore>("_settings")
527 .await
528 .unwrap();
529 let name = doc_store.get_string("name").await.unwrap();
530 assert_eq!(name, DATABASES);
531 }
532
533 #[tokio::test]
534 async fn test_system_databases_haveadmin_auth() {
535 let (instance, device_key, _admin) = setup_instance().await;
536
537 let users_db = create_accounts_database(&instance, &device_key)
538 .await
539 .unwrap();
540
541 let device_pubkey = instance.id();
543 let transaction = users_db.new_transaction().await.unwrap();
544 let settings_store = SettingsStore::new(&transaction).unwrap();
545 let auth_settings = settings_store.auth_snapshot().await.unwrap();
546 let device_key = auth_settings.get_key_by_pubkey(&device_pubkey).unwrap();
547
548 assert_eq!(device_key.permissions(), &Permission::Admin(0));
549 assert_eq!(device_key.name(), None);
550 }
551
552 #[tokio::test]
553 #[cfg_attr(miri, ignore)] async fn test_create_user() {
555 let (instance, device_key, _admin) = setup_instance().await;
556 let users_db = create_accounts_database(&instance, &device_key)
557 .await
558 .unwrap();
559
560 let (login_id, account_info, _) =
562 create_account(&users_db, &instance, "alice", "password123")
563 .await
564 .unwrap();
565
566 assert_eq!(account_info.username, "alice");
568 assert_eq!(account_info.status, AccountStatus::Active);
569
570 assert!(matches!(
571 account_info.credentials.bundle,
572 KeyStorage::Encrypted { .. }
573 ));
574 assert!(!login_id.is_empty());
575
576 let (stored_id, stored_user) = lookup_account_record(&users_db, &account_info.username)
578 .await
579 .unwrap();
580 assert_eq!(stored_id, login_id);
581 assert_eq!(stored_user.username, "alice");
582 }
583
584 #[tokio::test]
585 async fn test_create_user_passwordless() {
586 let (instance, device_key, _admin) = setup_instance().await;
587 let users_db = create_accounts_database(&instance, &device_key)
588 .await
589 .unwrap();
590
591 let (login_id, account_info, _) = create_account(&users_db, &instance, "bob", "")
593 .await
594 .unwrap();
595
596 assert_eq!(account_info.username, "bob");
598 assert_eq!(account_info.status, AccountStatus::Active);
599
600 assert!(matches!(
601 account_info.credentials.bundle,
602 KeyStorage::Encrypted { .. }
603 ));
604 assert!(!login_id.is_empty());
605
606 let (stored_id, stored_user) = lookup_account_record(&users_db, &account_info.username)
608 .await
609 .unwrap();
610 assert_eq!(stored_id, login_id);
611 assert_eq!(stored_user.username, "bob");
612 }
613
614 #[tokio::test]
615 #[cfg_attr(miri, ignore)] async fn test_create_duplicate_user() {
617 let (instance, device_key, _admin) = setup_instance().await;
618 let users_db = create_accounts_database(&instance, &device_key)
619 .await
620 .unwrap();
621
622 create_account(&users_db, &instance, "alice", "password123")
624 .await
625 .unwrap();
626
627 let result = create_account(&users_db, &instance, "alice", "password456").await;
629 assert!(result.is_err());
630 }
631
632 #[tokio::test]
633 #[cfg_attr(miri, ignore)] async fn test_login_user() {
635 let (instance, device_key, _admin) = setup_instance().await;
636 let users_db = create_accounts_database(&instance, &device_key)
637 .await
638 .unwrap();
639
640 create_account(&users_db, &instance, "bob", "bobpassword")
642 .await
643 .unwrap();
644
645 let user = login_account(&users_db, &instance, "bob", "bobpassword")
647 .await
648 .unwrap();
649
650 assert_eq!(user.username(), "bob");
652
653 let last_login = super::super::local_state::read(&instance, user.login_id())
655 .await
656 .unwrap()
657 .last_login
658 .unwrap();
659 assert!(last_login > 0);
660 }
661
662 #[tokio::test]
663 async fn test_login_user_passwordless() {
664 let (instance, device_key, _admin) = setup_instance().await;
665 let users_db = create_accounts_database(&instance, &device_key)
666 .await
667 .unwrap();
668
669 create_account(&users_db, &instance, "charlie", "")
671 .await
672 .unwrap();
673
674 let user = login_account(&users_db, &instance, "charlie", "")
676 .await
677 .unwrap();
678
679 assert_eq!(user.username(), "charlie");
681
682 let last_login = super::super::local_state::read(&instance, user.login_id())
684 .await
685 .unwrap()
686 .last_login
687 .unwrap();
688 assert!(last_login > 0);
689 }
690
691 #[tokio::test]
692 #[cfg_attr(miri, ignore)] async fn test_login_wrong_password() {
694 let (instance, device_key, _admin) = setup_instance().await;
695 let users_db = create_accounts_database(&instance, &device_key)
696 .await
697 .unwrap();
698
699 create_account(&users_db, &instance, "dave", "correct_password")
701 .await
702 .unwrap();
703
704 let result = login_account(&users_db, &instance, "dave", "wrong_password").await;
706 assert!(result.is_err());
707 }
708
709 #[tokio::test]
710 #[cfg_attr(miri, ignore)] async fn test_login_password_mismatch() {
712 let (instance, device_key, _admin) = setup_instance().await;
713 let users_db = create_accounts_database(&instance, &device_key)
714 .await
715 .unwrap();
716
717 create_account(&users_db, &instance, "eve", "")
719 .await
720 .unwrap();
721
722 let result = login_account(&users_db, &instance, "eve", "password").await;
724 assert!(result.is_err());
725
726 create_account(&users_db, &instance, "frank", "password")
728 .await
729 .unwrap();
730
731 let result = login_account(&users_db, &instance, "frank", "").await;
733 assert!(result.is_err());
734 }
735
736 #[tokio::test]
737 async fn test_login_nonexistent_user() {
738 let (instance, device_key, _admin) = setup_instance().await;
739 let users_db = create_accounts_database(&instance, &device_key)
740 .await
741 .unwrap();
742
743 let result = login_account(&users_db, &instance, "nonexistent", "password").await;
745 assert!(result.is_err());
746 }
747
748 #[tokio::test]
749 #[cfg_attr(miri, ignore)] async fn test_list_users() {
751 let (instance, device_key, _admin) = setup_instance().await;
752 let users_db = create_accounts_database(&instance, &device_key)
753 .await
754 .unwrap();
755
756 let users = list_accounts(&users_db).await.unwrap();
758 assert_eq!(users, ["admin"]);
759
760 create_account(&users_db, &instance, "alice", "pass1")
762 .await
763 .unwrap();
764 create_account(&users_db, &instance, "bob", "")
765 .await
766 .unwrap();
767 create_account(&users_db, &instance, "charlie", "pass3")
768 .await
769 .unwrap();
770
771 let users = list_accounts(&users_db).await.unwrap();
773 assert_eq!(users.len(), 4);
774 assert!(users.contains(&"alice".into()));
775 assert!(users.contains(&"bob".into()));
776 assert!(users.contains(&"charlie".into()));
777 }
778
779 async fn read_admin_permission(database: &Database, pubkey: &PublicKey) -> Option<Permission> {
782 let tx = database.new_transaction().await.unwrap();
783 let settings = SettingsStore::new(&tx).unwrap();
784 let auth = settings.auth_snapshot().await.unwrap();
785 auth.get_key_by_pubkey(pubkey)
786 .ok()
787 .map(|k| *k.permissions())
788 }
789
790 #[tokio::test]
793 async fn test_first_user_becomes_instance_admin() {
794 let (instance, _device_key, _admin) = setup_instance().await;
795
796 let admin_user = instance.login_account("admin", "").await.unwrap();
798
799 let users_db = instance.users_db().await.unwrap();
800 let databases_db = instance.databases_db().await.unwrap();
801
802 let admin_pubkey = admin_user.key_manager().get_default_key_id().unwrap();
803
804 assert_eq!(
805 read_admin_permission(&users_db, &admin_pubkey).await,
806 Some(Permission::Admin(0)),
807 "bootstrapped admin should be Admin(0) in _users"
808 );
809 assert_eq!(
810 read_admin_permission(&databases_db, &admin_pubkey).await,
811 Some(Permission::Admin(0)),
812 "bootstrapped admin should be Admin(0) in _databases"
813 );
814 }
815
816 #[tokio::test]
820 async fn test_subsequent_users_are_not_admin() {
821 let (instance, _device_key, admin) = setup_instance().await;
822
823 let admin_view = admin.admin().await.unwrap();
827 admin_view
828 .create_account(crate::NewAccount::passwordless("alice"))
829 .await
830 .unwrap();
831 let bob_uuid = admin_view
832 .create_account(crate::NewAccount::passwordless("bob"))
833 .await
834 .unwrap();
835
836 let users_db = instance.users_db().await.unwrap();
837 let databases_db = instance.databases_db().await.unwrap();
838
839 let (stored_id, bob_info) = lookup_account_record(&users_db, "bob").await.unwrap();
842 assert_eq!(stored_id, bob_uuid);
843 let bob_pubkey = bob_info.credentials.root_key_id.clone();
844
845 assert!(
846 read_admin_permission(&users_db, &bob_pubkey)
847 .await
848 .is_none(),
849 "second user must not have any entry in _users.auth_settings"
850 );
851 assert!(
852 read_admin_permission(&databases_db, &bob_pubkey)
853 .await
854 .is_none(),
855 "second user must not have any entry in _databases.auth_settings"
856 );
857 }
858
859 #[tokio::test]
862 async fn test_user_is_admin_query() {
863 let (instance, _device_key, admin) = setup_instance().await;
864
865 assert!(
867 admin.is_admin().await.unwrap(),
868 "bootstrapped admin must report is_admin = true"
869 );
870
871 admin
873 .admin()
874 .await
875 .unwrap()
876 .create_account(crate::NewAccount::passwordless("alice"))
877 .await
878 .unwrap();
879 let alice = instance.login_account("alice", "").await.unwrap();
880 assert!(
881 !alice.is_admin().await.unwrap(),
882 "newly created user must report is_admin = false"
883 );
884 }
885
886 #[tokio::test]
890 async fn test_admin_can_promote_user() {
891 let (instance, _device_key, admin) = setup_instance().await;
892
893 admin
894 .admin()
895 .await
896 .unwrap()
897 .create_account(crate::NewAccount::passwordless("bob"))
898 .await
899 .unwrap();
900 let bob = instance.login_account("bob", "").await.unwrap();
901 let bob_pubkey = bob.key_manager().get_default_key_id().unwrap();
902
903 assert!(
904 !bob.is_admin().await.unwrap(),
905 "precondition: bob starts as a non-admin"
906 );
907
908 admin
910 .admin()
911 .await
912 .unwrap()
913 .grant_instance_admin(&bob_pubkey)
914 .await
915 .unwrap();
916
917 let users_db = instance.users_db().await.unwrap();
918 let databases_db = instance.databases_db().await.unwrap();
919 assert_eq!(
920 read_admin_permission(&users_db, &bob_pubkey).await,
921 Some(Permission::Admin(0)),
922 "promoted user should be Admin(0) in _users"
923 );
924 assert_eq!(
925 read_admin_permission(&databases_db, &bob_pubkey).await,
926 Some(Permission::Admin(0)),
927 "promoted user should be Admin(0) in _databases"
928 );
929 assert!(
930 bob.is_admin().await.unwrap(),
931 "promoted user must now report is_admin = true"
932 );
933 }
934
935 #[tokio::test]
938 async fn test_non_admin_cannot_promote() {
939 let (instance, _device_key, admin) = setup_instance().await;
940
941 let admin_view = admin.admin().await.unwrap();
944 admin_view
945 .create_account(crate::NewAccount::passwordless("bob"))
946 .await
947 .unwrap();
948 admin_view
949 .create_account(crate::NewAccount::passwordless("charlie"))
950 .await
951 .unwrap();
952 let bob = instance.login_account("bob", "").await.unwrap();
953 let charlie = instance.login_account("charlie", "").await.unwrap();
954 let charlie_pubkey = charlie.key_manager().get_default_key_id().unwrap();
955
956 let err = bob
959 .admin()
960 .await
961 .expect_err("non-admin must not be able to promote");
962 assert!(
963 matches!(
964 &err,
965 crate::Error::Account(e)
966 if matches!(e.as_ref(), crate::account::AccountError::InsufficientPermissions)
967 ),
968 "expected InsufficientPermissions, got: {err:?}"
969 );
970
971 let users_db = instance.users_db().await.unwrap();
972 assert!(
973 read_admin_permission(&users_db, &charlie_pubkey)
974 .await
975 .is_none(),
976 "a failed promotion must not write to _users.auth_settings"
977 );
978 }
979
980 #[tokio::test]
984 async fn test_grant_instance_admin_idempotent_and_chains() {
985 let (instance, _device_key, admin) = setup_instance().await;
986
987 admin
988 .admin()
989 .await
990 .unwrap()
991 .create_account(crate::NewAccount::passwordless("bob"))
992 .await
993 .unwrap();
994 let bob = instance.login_account("bob", "").await.unwrap();
995 let bob_pubkey = bob.key_manager().get_default_key_id().unwrap();
996
997 admin
998 .admin()
999 .await
1000 .unwrap()
1001 .grant_instance_admin(&bob_pubkey)
1002 .await
1003 .unwrap();
1004 admin
1005 .admin()
1006 .await
1007 .unwrap()
1008 .grant_instance_admin(&bob_pubkey)
1009 .await
1010 .unwrap(); let users_db = instance.users_db().await.unwrap();
1013 assert_eq!(
1014 read_admin_permission(&users_db, &bob_pubkey).await,
1015 Some(Permission::Admin(0)),
1016 "re-granting an existing admin re-asserts the same entry"
1017 );
1018
1019 bob.admin()
1022 .await
1023 .unwrap()
1024 .create_account(crate::NewAccount::passwordless("charlie"))
1025 .await
1026 .unwrap();
1027 let charlie = instance.login_account("charlie", "").await.unwrap();
1028 let charlie_pubkey = charlie.key_manager().get_default_key_id().unwrap();
1029
1030 bob.admin()
1031 .await
1032 .unwrap()
1033 .grant_instance_admin(&charlie_pubkey)
1034 .await
1035 .unwrap();
1036
1037 let databases_db = instance.databases_db().await.unwrap();
1038 assert_eq!(
1039 read_admin_permission(&databases_db, &charlie_pubkey).await,
1040 Some(Permission::Admin(0)),
1041 "a promoted admin can promote further admins via their own key"
1042 );
1043 }
1044}