Skip to content

Commit

Permalink
Support concurrent authenticated accounts (account switcher) (#578)
Browse files Browse the repository at this point in the history
* Sketch AccountSwitcher.

* Guard against adding an account multiple times.

* Use generics for AccountSwitcher, move to sdk.

* Bump unicode-width.

* Begin utoipa upgrade in server.

* Update comments for openapi paths.

* Remove unused utoipa config.

* Fix bug with address of NetworkAccount::new_unauthenticated().
  • Loading branch information
tmpfs authored Oct 28, 2024
1 parent 61baf96 commit cea80a8
Show file tree
Hide file tree
Showing 10 changed files with 182 additions and 64 deletions.
49 changes: 15 additions & 34 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions crates/net/src/account/mod.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Network aware account storage.
use crate::sdk::prelude::{Account, AccountSwitcher};

mod auto_merge;
#[cfg(feature = "files")]
Expand All @@ -9,6 +10,13 @@ mod network_account;
mod remote;
mod sync;

/// Account switcher for network-enabled accounts.
pub type NetworkAccountSwitcher = AccountSwitcher<
<NetworkAccount as Account>::Error,
<NetworkAccount as Account>::NetworkResult,
NetworkAccount,
>;

/// Information about a cancellation.
#[derive(Default, Debug, Clone, Hash, Eq, PartialEq)]
pub enum CancelReason {
Expand Down
2 changes: 1 addition & 1 deletion crates/net/src/account/network_account.rs
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ impl NetworkAccount {
LocalAccount::new_unauthenticated(address, data_dir).await?;

Ok(Self {
address: Default::default(),
address,
paths: account.paths(),
account: Arc::new(Mutex::new(account)),
remotes: Arc::new(RwLock::new(Default::default())),
Expand Down
119 changes: 119 additions & 0 deletions crates/sdk/src/account/account_switcher.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
use crate::{
account::{Account, LocalAccount},
prelude::Address,
};

/// Account switcher for local accounts.
pub type LocalAccountSwitcher = AccountSwitcher<
<LocalAccount as Account>::Error,
<LocalAccount as Account>::NetworkResult,
LocalAccount,
>;

/// Collection of accounts with a currently selected account.
///
/// Allows multiple accounts to be authenticated concurrently
/// so that integrations are able to operate on multiple accounts
/// provided they are authenticated.
pub struct AccountSwitcher<E, R, A: Account<Error = E, NetworkResult = R>> {
accounts: Vec<A>,
selected: Option<Address>,
}

impl<E, R, A: Account<Error = E, NetworkResult = R>>
AccountSwitcher<E, R, A>
{
/// Create an account switcher.
pub fn new() -> Self {
Self {
accounts: Default::default(),
selected: None,
}
}

/// Number of accounts.
pub fn len(&self) -> usize {
self.accounts.len()
}

/// Add an account if it does not already exist and make
/// it the selected account.
///
/// If the account already exists it is selected.
pub fn new_account(&mut self, account: A) -> bool {
let address = *account.address();
if self.add_account(account) {
self.selected = Some(address);
true
} else {
self.selected = Some(address);
false
}
}

/// Add an account to the collection if it does not already exist.
pub fn add_account(&mut self, account: A) -> bool {
if self.position(account.address()).is_none() {
self.accounts.push(account);
true
} else {
false
}
}

/// Remove an account from the collection if it exists.
pub fn remove_account(&mut self, address: &Address) -> bool {
if let Some(position) = self.position(address) {
self.accounts.remove(position);
if self.selected == Some(*address) {
self.selected = None;
}
true
} else {
false
}
}

/// Switch selected account.
///
/// If no account exists for the given address no change
/// is made to the current selection.
pub fn switch_account(&mut self, address: &Address) -> bool {
if self.position(address).is_some() {
self.selected = Some(*address);
true
} else {
false
}
}

/// Selected account.
pub fn selected_account(&self) -> Option<&A> {
if let Some(address) = &self.selected {
if let Some(index) = self.position(address) {
self.accounts.get(index)
} else {
None
}
} else {
None
}
}

/// Mutable selected account.
pub fn selected_account_mut(&mut self) -> Option<&mut A> {
if let Some(address) = &self.selected {
if let Some(index) = self.position(address) {
self.accounts.get_mut(index)
} else {
None
}
} else {
None
}
}

fn position(&self, address: &Address) -> Option<usize> {
self.accounts.iter().position(|a| a.address() == address)
}
}
2 changes: 2 additions & 0 deletions crates/sdk/src/account/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Create and manage local accounts.
mod account;
mod account_switcher;
#[cfg(feature = "archive")]
pub mod archive;
mod builder;
Expand All @@ -10,6 +11,7 @@ pub use account::{
FolderChange, FolderCreate, FolderDelete, LocalAccount, SecretChange,
SecretDelete, SecretInsert, SecretMove, SigninOptions,
};
pub use account_switcher::{AccountSwitcher, LocalAccountSwitcher};
pub use builder::{AccountBuilder, PrivateNewAccount};
pub use convert::CipherComparison;

Expand Down
4 changes: 2 additions & 2 deletions crates/server/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,8 @@ axum-extra = {version = "0.9", features = ["typed-header"] }
axum-macros = { version = "0.4" }
tower-http = { version = "0.6", features = ["cors", "trace"] }
tokio-stream = { version = "0.1" }
utoipa = { version = "4", features = ["uuid"] }
utoipa-rapidoc = { version = "4", features = ["axum"] }
utoipa = { version = "5", features = ["uuid"] }
utoipa-rapidoc = { version = "5", features = ["axum"] }
tokio = { version = "1", features = ["rt", "rt-multi-thread", "sync", "macros"] }

tokio-rustls-acme = { version = "0.4", features = ["axum"], optional = true }
Expand Down
8 changes: 5 additions & 3 deletions crates/server/src/api_docs.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::handlers::{account, files};
use utoipa::{openapi::security::*, Modify, OpenApi, ToSchema};

/*
#[derive(ToSchema)]
#[allow(dead_code)]
struct CreateSet(sos_protocol::CreateSet);
Expand All @@ -12,6 +13,7 @@ struct SyncStatus(sos_protocol::SyncStatus);
#[derive(ToSchema)]
#[allow(dead_code)]
struct SyncPacket(sos_protocol::SyncPacket);
*/

#[derive(OpenApi)]
#[openapi(
Expand Down Expand Up @@ -48,9 +50,9 @@ struct SyncPacket(sos_protocol::SyncPacket);
),
components(
schemas(
CreateSet,
SyncStatus,
SyncPacket,
// CreateSet,
// SyncStatus,
// SyncPacket,
),
),
)]
Expand Down
Loading

0 comments on commit cea80a8

Please sign in to comment.