From a10a26a68d7870f421c93958925b8d326a2a6c19 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 19 Jul 2022 11:36:34 +0100 Subject: [PATCH 1/3] Tidy up authentication service. Add HomeserverLoginDetails --- bindings/matrix-sdk-ffi/src/api.udl | 21 +++-- .../src/authentication_service.rs | 79 +++++++++++-------- 2 files changed, 57 insertions(+), 43 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/api.udl b/bindings/matrix-sdk-ffi/src/api.udl index 80a24aa20..5ccb81863 100644 --- a/bindings/matrix-sdk-ffi/src/api.udl +++ b/bindings/matrix-sdk-ffi/src/api.udl @@ -156,24 +156,21 @@ interface MediaSource { [Error] enum AuthenticationError { - "ClientMissing", - "Generic", + "ClientMissing", + "Generic", +}; + +interface HomeserverLoginDetails { + string url(); + string? authentication_issuer(); + boolean supports_password_login(); }; interface AuthenticationService { constructor(string base_path); [Throws=AuthenticationError] - string homeserver(); - - [Throws=AuthenticationError] - string? authentication_issuer(); - - [Throws=AuthenticationError] - boolean supports_password_login(); - - [Throws=AuthenticationError] - void use_server(string server_name); + HomeserverLoginDetails configure_homeserver(string server_name); [Throws=AuthenticationError] Client login(string username, string password); diff --git a/bindings/matrix-sdk-ffi/src/authentication_service.rs b/bindings/matrix-sdk-ffi/src/authentication_service.rs index 7ed148d82..da52f9fc1 100644 --- a/bindings/matrix-sdk-ffi/src/authentication_service.rs +++ b/bindings/matrix-sdk-ffi/src/authentication_service.rs @@ -23,42 +23,42 @@ impl From for AuthenticationError { } } +pub struct HomeserverLoginDetails { + url: String, + authentication_issuer: Option, + supports_password_login: bool, +} + +impl HomeserverLoginDetails { + /// The URL of the currently configured homeserver. + pub fn url(&self) -> String { + self.url.clone() + } + + /// The OIDC Provider that is trusted by the homeserver. `None` when + /// not configured. + pub fn authentication_issuer(&self) -> Option { + self.authentication_issuer.clone() + } + + /// Whether the current homeserver supports the password login flow. + pub fn supports_password_login(&self) -> bool { + self.supports_password_login + } +} + impl AuthenticationService { /// Creates a new service to authenticate a user with. pub fn new(base_path: String) -> Self { AuthenticationService { base_path, client: RwLock::new(None) } } - /// The currently configured homeserver. - pub fn homeserver(&self) -> Result { - self.client - .read() - .as_ref() - .ok_or(AuthenticationError::ClientMissing) - .map(|client| client.homeserver()) - } - - /// The OIDC Provider that is trusted by the homeserver. `None` when - /// not configured. - pub fn authentication_issuer(&self) -> Result, AuthenticationError> { - self.client - .read() - .as_ref() - .ok_or(AuthenticationError::ClientMissing) - .map(|client| client.authentication_issuer()) - } - - /// Whether the current homeserver supports the password login flow. - pub fn supports_password_login(&self) -> Result { - self.client - .read() - .as_ref() - .ok_or(AuthenticationError::ClientMissing) - .and_then(|client| client.supports_password_login().map_err(AuthenticationError::from)) - } - - /// Updates the server to authenticate with the specified homeserver. - pub fn use_server(&self, server_name: String) -> Result<(), AuthenticationError> { + /// Updates the service to authenticate with the homeserver for the + /// specified address. + pub fn configure_homeserver( + &self, + server_name: String, + ) -> Result, AuthenticationError> { // Construct a username as the builder currently requires one. let username = format!("@auth:{}", server_name); let client = Arc::new(ClientBuilder::new()) @@ -67,8 +67,10 @@ impl AuthenticationService { .build() .map_err(AuthenticationError::from)?; + let details = self.details_from_client(&client)?; + *self.client.write() = Some(client); - Ok(()) + Ok(Arc::new(details)) } /// Performs a password login using the current homeserver. @@ -97,4 +99,19 @@ impl AuthenticationService { None => Err(AuthenticationError::ClientMissing), } } + + fn details_from_client( + &self, + client: &Arc, + ) -> Result { + let homeserver = client.homeserver(); + let authentication_issuer = client.authentication_issuer(); + let supports_password_login = + client.supports_password_login().map_err(AuthenticationError::from)?; + Ok(HomeserverLoginDetails { + url: homeserver, + authentication_issuer, + supports_password_login, + }) + } } From 93d879f356a26b52975a86214e9d49b3b60d6972 Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 19 Jul 2022 12:22:14 +0100 Subject: [PATCH 2/3] Add homeserver_details property. --- bindings/matrix-sdk-ffi/src/api.udl | 4 +++- .../src/authentication_service.rs | 23 +++++++++++++------ 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/api.udl b/bindings/matrix-sdk-ffi/src/api.udl index 5ccb81863..2c705695f 100644 --- a/bindings/matrix-sdk-ffi/src/api.udl +++ b/bindings/matrix-sdk-ffi/src/api.udl @@ -169,8 +169,10 @@ interface HomeserverLoginDetails { interface AuthenticationService { constructor(string base_path); + HomeserverLoginDetails? homeserver_details(); + [Throws=AuthenticationError] - HomeserverLoginDetails configure_homeserver(string server_name); + void configure_homeserver(string server_name); [Throws=AuthenticationError] Client login(string username, string password); diff --git a/bindings/matrix-sdk-ffi/src/authentication_service.rs b/bindings/matrix-sdk-ffi/src/authentication_service.rs index da52f9fc1..afda831d3 100644 --- a/bindings/matrix-sdk-ffi/src/authentication_service.rs +++ b/bindings/matrix-sdk-ffi/src/authentication_service.rs @@ -7,6 +7,7 @@ use super::{client::Client, client_builder::ClientBuilder}; pub struct AuthenticationService { base_path: String, client: RwLock>>, + homeserver_details: RwLock>>, } #[derive(Debug, thiserror::Error)] @@ -50,15 +51,20 @@ impl HomeserverLoginDetails { impl AuthenticationService { /// Creates a new service to authenticate a user with. pub fn new(base_path: String) -> Self { - AuthenticationService { base_path, client: RwLock::new(None) } + AuthenticationService { + base_path, + client: RwLock::new(None), + homeserver_details: RwLock::new(None), + } + } + + pub fn homeserver_details(&self) -> Option> { + self.homeserver_details.read().clone() } /// Updates the service to authenticate with the homeserver for the /// specified address. - pub fn configure_homeserver( - &self, - server_name: String, - ) -> Result, AuthenticationError> { + pub fn configure_homeserver(&self, server_name: String) -> Result<(), AuthenticationError> { // Construct a username as the builder currently requires one. let username = format!("@auth:{}", server_name); let client = Arc::new(ClientBuilder::new()) @@ -67,10 +73,12 @@ impl AuthenticationService { .build() .map_err(AuthenticationError::from)?; - let details = self.details_from_client(&client)?; + let details = Arc::new(self.details_from_client(&client)?); *self.client.write() = Some(client); - Ok(Arc::new(details)) + *self.homeserver_details.write() = Some(details); + + Ok(()) } /// Performs a password login using the current homeserver. @@ -100,6 +108,7 @@ impl AuthenticationService { } } + /// Get the homeserver login details from a client. fn details_from_client( &self, client: &Arc, From 77f7dbbbc805e69c13e64dab1e93e01bbb12c7cb Mon Sep 17 00:00:00 2001 From: Doug Date: Tue, 19 Jul 2022 16:16:03 +0100 Subject: [PATCH 3/3] Support server URLs. Join homeserver details futures. --- .../src/authentication_service.rs | 50 +++++++++++-------- bindings/matrix-sdk-ffi/src/client.rs | 27 +++++----- 2 files changed, 44 insertions(+), 33 deletions(-) diff --git a/bindings/matrix-sdk-ffi/src/authentication_service.rs b/bindings/matrix-sdk-ffi/src/authentication_service.rs index afda831d3..0c3df8f12 100644 --- a/bindings/matrix-sdk-ffi/src/authentication_service.rs +++ b/bindings/matrix-sdk-ffi/src/authentication_service.rs @@ -1,8 +1,9 @@ use std::sync::Arc; +use futures_util::future::join3; use parking_lot::RwLock; -use super::{client::Client, client_builder::ClientBuilder}; +use super::{client::Client, client_builder::ClientBuilder, RUNTIME}; pub struct AuthenticationService { base_path: String, @@ -67,18 +68,24 @@ impl AuthenticationService { pub fn configure_homeserver(&self, server_name: String) -> Result<(), AuthenticationError> { // Construct a username as the builder currently requires one. let username = format!("@auth:{}", server_name); - let client = Arc::new(ClientBuilder::new()) - .base_path(self.base_path.clone()) - .username(username) - .build() - .map_err(AuthenticationError::from)?; - let details = Arc::new(self.details_from_client(&client)?); + let mut builder = + Arc::new(ClientBuilder::new()).base_path(self.base_path.clone()).username(username); - *self.client.write() = Some(client); - *self.homeserver_details.write() = Some(details); + if server_name.starts_with("http://") || server_name.starts_with("https://") { + builder = builder.homeserver_url(server_name) + } - Ok(()) + let client = builder.build().map_err(AuthenticationError::from)?; + + RUNTIME.block_on(async move { + let details = Arc::new(self.details_from_client(&client).await?); + + *self.client.write() = Some(client); + *self.homeserver_details.write() = Some(details); + + Ok(()) + }) } /// Performs a password login using the current homeserver. @@ -109,18 +116,21 @@ impl AuthenticationService { } /// Get the homeserver login details from a client. - fn details_from_client( + async fn details_from_client( &self, client: &Arc, ) -> Result { - let homeserver = client.homeserver(); - let authentication_issuer = client.authentication_issuer(); - let supports_password_login = - client.supports_password_login().map_err(AuthenticationError::from)?; - Ok(HomeserverLoginDetails { - url: homeserver, - authentication_issuer, - supports_password_login, - }) + let login_details = join3( + client.async_homeserver(), + client.authentication_issuer(), + client.supports_password_login(), + ) + .await; + + let url = login_details.0; + let authentication_issuer = login_details.1; + let supports_password_login = login_details.2.map_err(AuthenticationError::from)?; + + Ok(HomeserverLoginDetails { url, authentication_issuer, supports_password_login }) } } diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index 0dda08d72..35538cb84 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -74,26 +74,27 @@ impl Client { /// The homeserver this client is configured to use. pub fn homeserver(&self) -> String { - RUNTIME.block_on(async move { self.client.homeserver().await.to_string() }) + RUNTIME.block_on(async move { self.async_homeserver().await }) + } + + pub async fn async_homeserver(&self) -> String { + self.client.homeserver().await.to_string() } /// The OIDC Provider that is trusted by the homeserver. `None` when /// not configured. - pub fn authentication_issuer(&self) -> Option { - RUNTIME.block_on(async move { - self.client.authentication_issuer().await.map(|server| server.to_string()) - }) + pub async fn authentication_issuer(&self) -> Option { + self.client.authentication_issuer().await.map(|server| server.to_string()) } /// Whether or not the client's homeserver supports the password login flow. - pub fn supports_password_login(&self) -> anyhow::Result { - RUNTIME.block_on(async move { - let login_types = self.client.get_login_types().await?; - let supports_password = login_types.flows.iter().any(|login_type| { - matches!(login_type, get_login_types::v3::LoginType::Password(_)) - }); - Ok(supports_password) - }) + pub async fn supports_password_login(&self) -> anyhow::Result { + let login_types = self.client.get_login_types().await?; + let supports_password = login_types + .flows + .iter() + .any(|login_type| matches!(login_type, get_login_types::v3::LoginType::Password(_))); + Ok(supports_password) } pub fn start_sync(&self) {