Skip to content

Commit 7e1c825

Browse files
authored
Allow configuring a 'read-only' connection to the homeserver (#4145)
2 parents dd928c4 + d0f1cf4 commit 7e1c825

File tree

30 files changed

+330
-163
lines changed

30 files changed

+330
-163
lines changed

crates/cli/src/app_state.rs

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@ use mas_handlers::{
1515
};
1616
use mas_i18n::Translator;
1717
use mas_keystore::{Encrypter, Keystore};
18-
use mas_matrix::BoxHomeserverConnection;
19-
use mas_matrix_synapse::SynapseConnection;
18+
use mas_matrix::HomeserverConnection;
2019
use mas_policy::{Policy, PolicyFactory};
2120
use mas_router::UrlBuilder;
2221
use mas_storage::{BoxClock, BoxRepository, BoxRng, SystemClock};
@@ -37,7 +36,7 @@ pub struct AppState {
3736
pub cookie_manager: CookieManager,
3837
pub encrypter: Encrypter,
3938
pub url_builder: UrlBuilder,
40-
pub homeserver_connection: SynapseConnection,
39+
pub homeserver_connection: Arc<dyn HomeserverConnection>,
4140
pub policy_factory: Arc<PolicyFactory>,
4241
pub graphql_schema: GraphQLSchema,
4342
pub http_client: reqwest::Client,
@@ -204,9 +203,9 @@ impl FromRef<AppState> for Limiter {
204203
}
205204
}
206205

207-
impl FromRef<AppState> for BoxHomeserverConnection {
206+
impl FromRef<AppState> for Arc<dyn HomeserverConnection> {
208207
fn from_ref(input: &AppState) -> Self {
209-
Box::new(input.homeserver_connection.clone())
208+
Arc::clone(&input.homeserver_connection)
210209
}
211210
}
212211

crates/cli/src/commands/manage.rs

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@ use mas_config::{
1717
use mas_data_model::{Device, TokenType, Ulid, UpstreamOAuthProvider, User};
1818
use mas_email::Address;
1919
use mas_matrix::HomeserverConnection;
20-
use mas_matrix_synapse::SynapseConnection;
2120
use mas_storage::{
2221
Clock, RepositoryAccess, SystemClock,
2322
compat::{CompatAccessTokenRepository, CompatSessionFilter, CompatSessionRepository},
@@ -33,7 +32,10 @@ use rand::{RngCore, SeedableRng};
3332
use sqlx::{Acquire, types::Uuid};
3433
use tracing::{error, info, info_span, warn};
3534

36-
use crate::util::{database_connection_from_config, password_manager_from_config};
35+
use crate::util::{
36+
database_connection_from_config, homeserver_connection_from_config,
37+
password_manager_from_config,
38+
};
3739

3840
const USER_ATTRIBUTES_HEADING: &str = "User attributes";
3941

@@ -491,12 +493,7 @@ impl Options {
491493
let matrix_config = MatrixConfig::extract(figment)?;
492494

493495
let password_manager = password_manager_from_config(&password_config).await?;
494-
let homeserver = SynapseConnection::new(
495-
matrix_config.homeserver,
496-
matrix_config.endpoint,
497-
matrix_config.secret,
498-
http_client,
499-
);
496+
let homeserver = homeserver_connection_from_config(&matrix_config, http_client);
500497
let mut conn = database_connection_from_config(&database_config).await?;
501498
let txn = conn.begin().await?;
502499
let mut repo = PgRepository::from_conn(txn);
@@ -746,7 +743,7 @@ impl std::fmt::Display for HumanReadable<&UpstreamOAuthProvider> {
746743
async fn check_and_normalize_username<'a>(
747744
localpart_or_mxid: &'a str,
748745
repo: &mut dyn RepositoryAccess<Error = DatabaseError>,
749-
homeserver: &SynapseConnection,
746+
homeserver: &dyn HomeserverConnection,
750747
) -> anyhow::Result<&'a str> {
751748
// XXX: this is a very basic MXID to localpart conversion
752749
// Strip any leading '@'
@@ -828,7 +825,7 @@ impl UserCreationRequest<'_> {
828825
}
829826

830827
/// Show the user creation request in a human-readable format
831-
fn show(&self, term: &Term, homeserver: &SynapseConnection) -> std::io::Result<()> {
828+
fn show(&self, term: &Term, homeserver: &dyn HomeserverConnection) -> std::io::Result<()> {
832829
let value_style = Style::new().green();
833830
let key_style = Style::new().bold();
834831
let warning_style = Style::new().italic().red().bright();

crates/cli/src/commands/server.rs

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ use mas_config::{
1515
};
1616
use mas_handlers::{ActivityTracker, CookieManager, Limiter, MetadataCache};
1717
use mas_listener::server::Server;
18-
use mas_matrix_synapse::SynapseConnection;
1918
use mas_router::UrlBuilder;
2019
use mas_storage::SystemClock;
2120
use mas_storage_pg::MIGRATOR;
@@ -26,9 +25,9 @@ use crate::{
2625
app_state::AppState,
2726
lifecycle::LifecycleManager,
2827
util::{
29-
database_pool_from_config, mailer_from_config, password_manager_from_config,
30-
policy_factory_from_config, site_config_from_config, templates_from_config,
31-
test_mailer_in_background,
28+
database_pool_from_config, homeserver_connection_from_config, mailer_from_config,
29+
password_manager_from_config, policy_factory_from_config, site_config_from_config,
30+
templates_from_config, test_mailer_in_background,
3231
},
3332
};
3433

@@ -153,12 +152,8 @@ impl Options {
153152

154153
let http_client = mas_http::reqwest_client();
155154

156-
let homeserver_connection = SynapseConnection::new(
157-
config.matrix.homeserver.clone(),
158-
config.matrix.endpoint.clone(),
159-
config.matrix.secret.clone(),
160-
http_client.clone(),
161-
);
155+
let homeserver_connection =
156+
homeserver_connection_from_config(&config.matrix, http_client.clone());
162157

163158
if !self.no_worker {
164159
let mailer = mailer_from_config(&config.email, &templates)?;

crates/cli/src/commands/worker.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@ use std::{process::ExitCode, time::Duration};
99
use clap::Parser;
1010
use figment::Figment;
1111
use mas_config::{AppConfig, ConfigurationSection};
12-
use mas_matrix_synapse::SynapseConnection;
1312
use mas_router::UrlBuilder;
1413
use tracing::{info, info_span};
1514

1615
use crate::{
1716
lifecycle::LifecycleManager,
1817
util::{
19-
database_pool_from_config, mailer_from_config, site_config_from_config,
20-
templates_from_config, test_mailer_in_background,
18+
database_pool_from_config, homeserver_connection_from_config, mailer_from_config,
19+
site_config_from_config, templates_from_config, test_mailer_in_background,
2120
},
2221
};
2322

@@ -58,12 +57,7 @@ impl Options {
5857
test_mailer_in_background(&mailer, Duration::from_secs(30));
5958

6059
let http_client = mas_http::reqwest_client();
61-
let conn = SynapseConnection::new(
62-
config.matrix.homeserver.clone(),
63-
config.matrix.endpoint.clone(),
64-
config.matrix.secret.clone(),
65-
http_client,
66-
);
60+
let conn = homeserver_connection_from_config(&config.matrix, http_client);
6761

6862
drop(config);
6963

crates/cli/src/util.rs

Lines changed: 31 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,19 @@
44
// SPDX-License-Identifier: AGPL-3.0-only
55
// Please see LICENSE in the repository root for full details.
66

7-
use std::time::Duration;
7+
use std::{sync::Arc, time::Duration};
88

99
use anyhow::Context;
1010
use mas_config::{
1111
AccountConfig, BrandingConfig, CaptchaConfig, DatabaseConfig, EmailConfig, EmailSmtpMode,
12-
EmailTransportKind, ExperimentalConfig, MatrixConfig, PasswordsConfig, PolicyConfig,
13-
TemplatesConfig,
12+
EmailTransportKind, ExperimentalConfig, HomeserverKind, MatrixConfig, PasswordsConfig,
13+
PolicyConfig, TemplatesConfig,
1414
};
1515
use mas_data_model::{SessionExpirationConfig, SiteConfig};
1616
use mas_email::{MailTransport, Mailer};
1717
use mas_handlers::passwords::PasswordManager;
18+
use mas_matrix::{HomeserverConnection, ReadOnlyHomeserverConnection};
19+
use mas_matrix_synapse::SynapseConnection;
1820
use mas_policy::PolicyFactory;
1921
use mas_router::UrlBuilder;
2022
use mas_templates::{SiteConfigExt, TemplateLoadingError, Templates};
@@ -346,6 +348,32 @@ pub async fn database_connection_from_config(
346348
.context("could not connect to the database")
347349
}
348350

351+
/// Create a clonable, type-erased [`HomeserverConnection`] from the
352+
/// configuration
353+
pub fn homeserver_connection_from_config(
354+
config: &MatrixConfig,
355+
http_client: reqwest::Client,
356+
) -> Arc<dyn HomeserverConnection> {
357+
match config.kind {
358+
HomeserverKind::Synapse => Arc::new(SynapseConnection::new(
359+
config.homeserver.clone(),
360+
config.endpoint.clone(),
361+
config.secret.clone(),
362+
http_client,
363+
)),
364+
HomeserverKind::SynapseReadOnly => {
365+
let connection = SynapseConnection::new(
366+
config.homeserver.clone(),
367+
config.endpoint.clone(),
368+
config.secret.clone(),
369+
http_client,
370+
);
371+
let readonly = ReadOnlyHomeserverConnection::new(connection);
372+
Arc::new(readonly)
373+
}
374+
}
375+
}
376+
349377
#[cfg(test)]
350378
mod tests {
351379
use rand::SeedableRng;

crates/config/src/sections/matrix.rs

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,29 @@ fn default_endpoint() -> Url {
2323
Url::parse("http://localhost:8008/").unwrap()
2424
}
2525

26+
/// The kind of homeserver it is.
27+
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
28+
#[serde(rename_all = "snake_case")]
29+
pub enum HomeserverKind {
30+
/// Homeserver is Synapse
31+
#[default]
32+
Synapse,
33+
34+
/// Homeserver is Synapse, in read-only mode
35+
///
36+
/// This is meant for testing rolling out Matrix Authentication Service with
37+
/// no risk of writing data to the homeserver.
38+
SynapseReadOnly,
39+
}
40+
2641
/// Configuration related to the Matrix homeserver
2742
#[serde_as]
2843
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
2944
pub struct MatrixConfig {
45+
/// The kind of homeserver it is.
46+
#[serde(default)]
47+
pub kind: HomeserverKind,
48+
3049
/// The server name of the homeserver.
3150
#[serde(default = "default_homeserver")]
3251
pub homeserver: String,
@@ -49,6 +68,7 @@ impl MatrixConfig {
4968
R: Rng + Send,
5069
{
5170
Self {
71+
kind: HomeserverKind::default(),
5272
homeserver: default_homeserver(),
5373
secret: Alphanumeric.sample_string(&mut rng, 32),
5474
endpoint: default_endpoint(),
@@ -57,6 +77,7 @@ impl MatrixConfig {
5777

5878
pub(crate) fn test() -> Self {
5979
Self {
80+
kind: HomeserverKind::default(),
6081
homeserver: default_homeserver(),
6182
secret: "test".to_owned(),
6283
endpoint: default_endpoint(),

crates/config/src/sections/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ pub use self::{
3737
BindConfig as HttpBindConfig, HttpConfig, ListenerConfig as HttpListenerConfig,
3838
Resource as HttpResource, TlsConfig as HttpTlsConfig, UnixOrTcp,
3939
},
40-
matrix::MatrixConfig,
40+
matrix::{HomeserverKind, MatrixConfig},
4141
passwords::{Algorithm as PasswordAlgorithm, PasswordsConfig},
4242
policy::PolicyConfig,
4343
rate_limiting::RateLimitingConfig,

crates/handlers/src/admin/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
// SPDX-License-Identifier: AGPL-3.0-only
55
// Please see LICENSE in the repository root for full details.
66

7+
use std::sync::Arc;
8+
79
use aide::{
810
axum::ApiRouter,
911
openapi::{OAuth2Flow, OAuth2Flows, OpenApi, SecurityScheme, Server, Tag},
@@ -19,7 +21,7 @@ use hyper::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE};
1921
use indexmap::IndexMap;
2022
use mas_axum_utils::FancyError;
2123
use mas_http::CorsLayerExt;
22-
use mas_matrix::BoxHomeserverConnection;
24+
use mas_matrix::HomeserverConnection;
2325
use mas_router::{
2426
ApiDoc, ApiDocCallback, OAuth2AuthorizationEndpoint, OAuth2TokenEndpoint, Route, SimpleRoute,
2527
UrlBuilder,
@@ -107,7 +109,7 @@ fn finish(t: TransformOpenApi) -> TransformOpenApi {
107109
pub fn router<S>() -> (OpenApi, Router<S>)
108110
where
109111
S: Clone + Send + Sync + 'static,
110-
BoxHomeserverConnection: FromRef<S>,
112+
Arc<dyn HomeserverConnection>: FromRef<S>,
111113
PasswordManager: FromRef<S>,
112114
BoxRng: FromRequestParts<S>,
113115
CallContext: FromRequestParts<S>,

crates/handlers/src/admin/v1/mod.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
// SPDX-License-Identifier: AGPL-3.0-only
55
// Please see LICENSE in the repository root for full details.
66

7+
use std::sync::Arc;
8+
79
use aide::axum::{
810
ApiRouter,
911
routing::{get_with, post_with},
1012
};
1113
use axum::extract::{FromRef, FromRequestParts};
12-
use mas_matrix::BoxHomeserverConnection;
14+
use mas_matrix::HomeserverConnection;
1315
use mas_storage::BoxRng;
1416

1517
use super::call_context::CallContext;
@@ -25,7 +27,7 @@ mod users;
2527
pub fn router<S>() -> ApiRouter<S>
2628
where
2729
S: Clone + Send + Sync + 'static,
28-
BoxHomeserverConnection: FromRef<S>,
30+
Arc<dyn HomeserverConnection>: FromRef<S>,
2931
PasswordManager: FromRef<S>,
3032
BoxRng: FromRequestParts<S>,
3133
CallContext: FromRequestParts<S>,

crates/handlers/src/admin/v1/users/add.rs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@
44
// SPDX-License-Identifier: AGPL-3.0-only
55
// Please see LICENSE in the repository root for full details.
66

7+
use std::sync::Arc;
8+
79
use aide::{NoApi, OperationIo, transform::TransformOperation};
810
use axum::{Json, extract::State, response::IntoResponse};
911
use hyper::StatusCode;
10-
use mas_matrix::BoxHomeserverConnection;
12+
use mas_matrix::HomeserverConnection;
1113
use mas_storage::{
1214
BoxRng,
1315
queue::{ProvisionUserJob, QueueJobRepositoryExt as _},
@@ -135,7 +137,7 @@ pub async fn handler(
135137
mut repo, clock, ..
136138
}: CallContext,
137139
NoApi(mut rng): NoApi<BoxRng>,
138-
State(homeserver): State<BoxHomeserverConnection>,
140+
State(homeserver): State<Arc<dyn HomeserverConnection>>,
139141
Json(params): Json<Request>,
140142
) -> Result<(StatusCode, Json<SingleResponse<User>>), RouteError> {
141143
if repo.user().exists(&params.username).await? {

0 commit comments

Comments
 (0)