From ee9490982895ce31be306a425e3a30f460dfcb05 Mon Sep 17 00:00:00 2001 From: Doug Date: Thu, 16 Jun 2022 18:03:58 +0100 Subject: [PATCH 1/2] Add creation of an unauthenticated client to FFI and login types. --- bindings/matrix-sdk-ffi/Cargo.toml | 2 ++ bindings/matrix-sdk-ffi/src/api.udl | 13 ++++++++++- bindings/matrix-sdk-ffi/src/client.rs | 17 ++++++++++++++ bindings/matrix-sdk-ffi/src/lib.rs | 33 +++++++++++++++++++++++++++ crates/matrix-sdk/src/client/mod.rs | 14 ++++++++++++ 5 files changed, 78 insertions(+), 1 deletion(-) diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index fc6c3e0c6a9..e3f1a7d1f2c 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -24,6 +24,7 @@ futures-util = { version = "0.3.17", default-features = false } matrix-sdk = { path = "../../crates/matrix-sdk", features = ["experimental-timeline", "markdown"] } once_cell = "1.10.0" parking_lot = "0.12.0" +ruma = "0.6.1" sanitize-filename-reader-friendly = "2.2.1" serde = { version = "1", features = ["derive"] } serde_json = { version = "1" } @@ -34,3 +35,4 @@ tracing = "0.1.32" # keep in sync with uniffi dependency in matrix-sdk-crypto-ffi, and uniffi_bindgen in ffi CI job uniffi = "0.18.0" uniffi_macros = "0.18.0" +url = "2.2.2" diff --git a/bindings/matrix-sdk-ffi/src/api.udl b/bindings/matrix-sdk-ffi/src/api.udl index a2af0c03229..ebad0adb00e 100644 --- a/bindings/matrix-sdk-ffi/src/api.udl +++ b/bindings/matrix-sdk-ffi/src/api.udl @@ -1,6 +1,9 @@ namespace sdk { [Throws=ClientError] - Client login_new_client(string base_path, string username, string password); + Client new_client(string homeserver); + + [Throws=ClientError] + Client discover_client(string domain); [Throws=ClientError] Client guest_client(string base_path, string homeserver); @@ -8,6 +11,9 @@ namespace sdk { [Throws=ClientError] Client login_with_token(string base_path, string restore_token); + [Throws=ClientError] + Client login_new_client(string base_path, string username, string password); + MediaSource media_source_from_url(string url); MessageEventContent message_event_content_from_markdown(string md); string gen_transaction_id(); @@ -24,6 +30,11 @@ callback interface ClientDelegate { interface Client { void set_delegate(ClientDelegate? delegate); + + [Throws=ClientError] + boolean supports_password_login(); + + string homeserver(); void start_sync(); diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index b8b7e056a79..7dfdacbb5a0 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -14,6 +14,7 @@ use matrix_sdk::{ Client as MatrixClient, LoopCtrl, }; use parking_lot::RwLock; +use ruma::api::client::session::get_login_types; use super::{room::Room, ClientState, RestoreToken, RUNTIME}; @@ -48,6 +49,22 @@ impl Client { *self.delegate.write() = delegate; } + 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| match login_type { + get_login_types::v3::LoginType::Password(_) => true, + _ => false, + }); + Ok(supports_password) + }) + } + + /// The homeserver address of this client. + pub fn homeserver(&self) -> String { + RUNTIME.block_on(async move { self.client.homeserver().await.to_string() }) + } + pub fn start_sync(&self) { let client = self.client.clone(); let state = self.state.clone(); diff --git a/bindings/matrix-sdk-ffi/src/lib.rs b/bindings/matrix-sdk-ffi/src/lib.rs index 0d92c4d3679..ee96ce3173f 100644 --- a/bindings/matrix-sdk-ffi/src/lib.rs +++ b/bindings/matrix-sdk-ffi/src/lib.rs @@ -13,10 +13,12 @@ use std::{fs, path, sync::Arc}; use client::Client; use matrix_sdk::{store::make_store_config, Client as MatrixClient, ClientBuilder, Session}; use once_cell::sync::Lazy; +use ruma::ServerName; use sanitize_filename_reader_friendly::sanitize; use serde::{Deserialize, Serialize}; use tokio::runtime::Runtime; pub use uniffi_api::*; +use url::Url; pub static RUNTIME: Lazy = Lazy::new(|| Runtime::new().expect("Can't start Tokio runtime")); @@ -25,6 +27,37 @@ pub use matrix_sdk::ruma::{api::client::account::register, UserId}; pub use self::{backward_stream::*, client::*, messages::*, room::*}; +/// Create a new [`Client`] that will use the given homeserver. +/// +/// # Arguments +/// +/// * `homeserver` - The homeserver's URL as a string. +pub fn new_client(homeserver: String) -> anyhow::Result> { + RUNTIME.block_on(async move { + let homeserver_url = Url::parse(&homeserver)?; + let client = MatrixClient::new(homeserver_url).await?; + let state = ClientState::default(); + + Ok(Arc::new(Client::new(client, state))) + }) +} + +/// Create a new [`Client`] that will discover a homeserver for the given +/// domain. +/// +/// # Arguments +/// +/// * `domain` - The domain the client should use for discovery. +pub fn discover_client(domain: String) -> anyhow::Result> { + RUNTIME.block_on(async move { + let server_name = ServerName::parse(domain).unwrap(); + let client = MatrixClient::discover(&server_name).await?; + let state = ClientState::default(); + + Ok(Arc::new(Client::new(client, state))) + }) +} + pub fn guest_client(base_path: String, homeurl: String) -> anyhow::Result> { let builder = new_client_builder(base_path, homeurl.clone())?.homeserver_url(&homeurl); let mut guest_registration = register::v3::Request::new(); diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index b905252e059..f7b4d7488a0 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -192,6 +192,20 @@ impl Client { .map_err(ClientBuildError::assert_valid_builder_args) } + /// Create a new [`Client`] that will discover a homeserver from the given + /// server name. + /// + /// # Arguments + /// + /// * `server_name` - The server name the client should use for discovery. + pub async fn discover(server_name: &ServerName) -> Result { + Self::builder() + .server_name(server_name) + .build() + .await + .map_err(ClientBuildError::assert_valid_builder_args) + } + /// Create a new [`ClientBuilder`]. pub fn builder() -> ClientBuilder { ClientBuilder::new() From 9ce9a8018ea7cb11487115363fd1696676569689 Mon Sep 17 00:00:00 2001 From: Doug Date: Fri, 17 Jun 2022 17:15:38 +0100 Subject: [PATCH 2/2] Add (fake) support for OIDC detection to FFI. Uses the identity server whilst waiting on https://github.com/ruma/ruma/pull/1193 --- bindings/matrix-sdk-ffi/Cargo.toml | 1 - bindings/matrix-sdk-ffi/src/api.udl | 2 ++ bindings/matrix-sdk-ffi/src/client.rs | 8 +++++++- bindings/matrix-sdk-ffi/src/lib.rs | 5 +++-- crates/matrix-sdk/src/client/builder.rs | 7 +++++++ crates/matrix-sdk/src/client/mod.rs | 10 ++++++++++ 6 files changed, 29 insertions(+), 4 deletions(-) diff --git a/bindings/matrix-sdk-ffi/Cargo.toml b/bindings/matrix-sdk-ffi/Cargo.toml index e3f1a7d1f2c..d4a648a188e 100644 --- a/bindings/matrix-sdk-ffi/Cargo.toml +++ b/bindings/matrix-sdk-ffi/Cargo.toml @@ -24,7 +24,6 @@ futures-util = { version = "0.3.17", default-features = false } matrix-sdk = { path = "../../crates/matrix-sdk", features = ["experimental-timeline", "markdown"] } once_cell = "1.10.0" parking_lot = "0.12.0" -ruma = "0.6.1" sanitize-filename-reader-friendly = "2.2.1" serde = { version = "1", features = ["derive"] } serde_json = { version = "1" } diff --git a/bindings/matrix-sdk-ffi/src/api.udl b/bindings/matrix-sdk-ffi/src/api.udl index ebad0adb00e..a236b26df5f 100644 --- a/bindings/matrix-sdk-ffi/src/api.udl +++ b/bindings/matrix-sdk-ffi/src/api.udl @@ -35,6 +35,8 @@ interface Client { boolean supports_password_login(); string homeserver(); + + string? authentication_server(); void start_sync(); diff --git a/bindings/matrix-sdk-ffi/src/client.rs b/bindings/matrix-sdk-ffi/src/client.rs index 7dfdacbb5a0..4863aec7f57 100644 --- a/bindings/matrix-sdk-ffi/src/client.rs +++ b/bindings/matrix-sdk-ffi/src/client.rs @@ -6,6 +6,7 @@ use matrix_sdk::{ ruma::{ api::client::{ filter::{FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter}, + session::get_login_types, sync::sync_events::v3::Filter, }, events::room::MediaSource, @@ -14,7 +15,6 @@ use matrix_sdk::{ Client as MatrixClient, LoopCtrl, }; use parking_lot::RwLock; -use ruma::api::client::session::get_login_types; use super::{room::Room, ClientState, RestoreToken, RUNTIME}; @@ -65,6 +65,12 @@ impl Client { RUNTIME.block_on(async move { self.client.homeserver().await.to_string() }) } + pub fn authentication_server(&self) -> Option { + RUNTIME.block_on(async move { + self.client.authentication_server().await.map(|server| server.to_string()) + }) + } + pub fn start_sync(&self) { let client = self.client.clone(); let state = self.state.clone(); diff --git a/bindings/matrix-sdk-ffi/src/lib.rs b/bindings/matrix-sdk-ffi/src/lib.rs index ee96ce3173f..909d7b9a469 100644 --- a/bindings/matrix-sdk-ffi/src/lib.rs +++ b/bindings/matrix-sdk-ffi/src/lib.rs @@ -11,9 +11,10 @@ mod uniffi_api; use std::{fs, path, sync::Arc}; use client::Client; -use matrix_sdk::{store::make_store_config, Client as MatrixClient, ClientBuilder, Session}; +use matrix_sdk::{ + ruma::ServerName, store::make_store_config, Client as MatrixClient, ClientBuilder, Session, +}; use once_cell::sync::Lazy; -use ruma::ServerName; use sanitize_filename_reader_friendly::sanitize; use serde::{Deserialize, Serialize}; use tokio::runtime::Runtime; diff --git a/crates/matrix-sdk/src/client/builder.rs b/crates/matrix-sdk/src/client/builder.rs index 41705380ac6..ffcf30b636f 100644 --- a/crates/matrix-sdk/src/client/builder.rs +++ b/crates/matrix-sdk/src/client/builder.rs @@ -295,6 +295,7 @@ impl ClientBuilder { let base_client = BaseClient::with_store_config(self.store_config); let http_client = HttpClient::new(inner_http_client.clone(), self.request_config); + let mut authentication_server: Option = None; let homeserver = match homeserver_cfg { HomeserverConfig::Url(url) => url, HomeserverConfig::ServerName(server_name) => { @@ -313,14 +314,20 @@ impl ClientBuilder { err => ClientBuildError::Http(err), })?; + if let Some(base_url) = well_known.identity_server.map(|server| server.base_url) { + authentication_server = Url::parse(&base_url).ok(); + }; + well_known.homeserver.base_url } }; let homeserver = RwLock::new(Url::parse(&homeserver)?); + let authentication_server = authentication_server.map(|server| RwLock::new(server)); let inner = Arc::new(ClientInner { homeserver, + authentication_server, http_client, base_client, server_versions: OnceCell::new_with(self.server_versions), diff --git a/crates/matrix-sdk/src/client/mod.rs b/crates/matrix-sdk/src/client/mod.rs index f7b4d7488a0..c896320b9e9 100644 --- a/crates/matrix-sdk/src/client/mod.rs +++ b/crates/matrix-sdk/src/client/mod.rs @@ -135,6 +135,8 @@ pub struct Client { pub(crate) struct ClientInner { /// The URL of the homeserver to connect to. homeserver: RwLock, + /// The URL of the authentication server to connect to. + authentication_server: Option>, /// The underlying HTTP client. http_client: HttpClient, /// User session data. @@ -286,6 +288,14 @@ impl Client { self.inner.homeserver.read().await.clone() } + /// The authentication server of the client. + pub async fn authentication_server(&self) -> Option { + if let Some(server) = &self.inner.authentication_server { + return Some(server.read().await.clone()); + } + return None; + } + /// Get the user id of the current owner of the client. pub fn user_id(&self) -> Option<&UserId> { self.session().map(|s| s.user_id.as_ref())