Allow configuring the connection to the homeserver to be read-only.
This commit is contained in:
@@ -9,13 +9,13 @@ use std::{sync::Arc, time::Duration};
|
||||
use anyhow::Context;
|
||||
use mas_config::{
|
||||
AccountConfig, BrandingConfig, CaptchaConfig, DatabaseConfig, EmailConfig, EmailSmtpMode,
|
||||
EmailTransportKind, ExperimentalConfig, MatrixConfig, PasswordsConfig, PolicyConfig,
|
||||
TemplatesConfig,
|
||||
EmailTransportKind, ExperimentalConfig, HomeserverKind, MatrixConfig, PasswordsConfig,
|
||||
PolicyConfig, TemplatesConfig,
|
||||
};
|
||||
use mas_data_model::{SessionExpirationConfig, SiteConfig};
|
||||
use mas_email::{MailTransport, Mailer};
|
||||
use mas_handlers::passwords::PasswordManager;
|
||||
use mas_matrix::HomeserverConnection;
|
||||
use mas_matrix::{HomeserverConnection, ReadOnlyHomeserverConnection};
|
||||
use mas_matrix_synapse::SynapseConnection;
|
||||
use mas_policy::PolicyFactory;
|
||||
use mas_router::UrlBuilder;
|
||||
@@ -354,12 +354,24 @@ pub fn homeserver_connection_from_config(
|
||||
config: &MatrixConfig,
|
||||
http_client: reqwest::Client,
|
||||
) -> Arc<dyn HomeserverConnection> {
|
||||
Arc::new(SynapseConnection::new(
|
||||
config.homeserver.clone(),
|
||||
config.endpoint.clone(),
|
||||
config.secret.clone(),
|
||||
http_client,
|
||||
))
|
||||
match config.kind {
|
||||
HomeserverKind::Synapse => Arc::new(SynapseConnection::new(
|
||||
config.homeserver.clone(),
|
||||
config.endpoint.clone(),
|
||||
config.secret.clone(),
|
||||
http_client,
|
||||
)),
|
||||
HomeserverKind::SynapseReadOnly => {
|
||||
let connection = SynapseConnection::new(
|
||||
config.homeserver.clone(),
|
||||
config.endpoint.clone(),
|
||||
config.secret.clone(),
|
||||
http_client,
|
||||
);
|
||||
let readonly = ReadOnlyHomeserverConnection::new(connection);
|
||||
Arc::new(readonly)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
||||
@@ -23,10 +23,29 @@ fn default_endpoint() -> Url {
|
||||
Url::parse("http://localhost:8008/").unwrap()
|
||||
}
|
||||
|
||||
/// The kind of homeserver it is.
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema, Default)]
|
||||
#[serde(rename_all = "snake_case")]
|
||||
pub enum HomeserverKind {
|
||||
/// Homeserver is Synapse
|
||||
#[default]
|
||||
Synapse,
|
||||
|
||||
/// Homeserver is Synapse, in read-only mode
|
||||
///
|
||||
/// This is meant for testing rolling out Matrix Authentication Service with
|
||||
/// no risk of writing data to the homeserver.
|
||||
SynapseReadOnly,
|
||||
}
|
||||
|
||||
/// Configuration related to the Matrix homeserver
|
||||
#[serde_as]
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||
pub struct MatrixConfig {
|
||||
/// The kind of homeserver it is.
|
||||
#[serde(default)]
|
||||
pub kind: HomeserverKind,
|
||||
|
||||
/// The server name of the homeserver.
|
||||
#[serde(default = "default_homeserver")]
|
||||
pub homeserver: String,
|
||||
@@ -49,6 +68,7 @@ impl MatrixConfig {
|
||||
R: Rng + Send,
|
||||
{
|
||||
Self {
|
||||
kind: HomeserverKind::default(),
|
||||
homeserver: default_homeserver(),
|
||||
secret: Alphanumeric.sample_string(&mut rng, 32),
|
||||
endpoint: default_endpoint(),
|
||||
@@ -57,6 +77,7 @@ impl MatrixConfig {
|
||||
|
||||
pub(crate) fn test() -> Self {
|
||||
Self {
|
||||
kind: HomeserverKind::default(),
|
||||
homeserver: default_homeserver(),
|
||||
secret: "test".to_owned(),
|
||||
endpoint: default_endpoint(),
|
||||
|
||||
@@ -37,7 +37,7 @@ pub use self::{
|
||||
BindConfig as HttpBindConfig, HttpConfig, ListenerConfig as HttpListenerConfig,
|
||||
Resource as HttpResource, TlsConfig as HttpTlsConfig, UnixOrTcp,
|
||||
},
|
||||
matrix::MatrixConfig,
|
||||
matrix::{HomeserverKind, MatrixConfig},
|
||||
passwords::{Algorithm as PasswordAlgorithm, PasswordsConfig},
|
||||
policy::PolicyConfig,
|
||||
rate_limiting::RateLimitingConfig,
|
||||
|
||||
@@ -5,12 +5,15 @@
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
|
||||
mod mock;
|
||||
mod readonly;
|
||||
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
|
||||
use ruma_common::UserId;
|
||||
|
||||
pub use self::mock::HomeserverConnection as MockHomeserverConnection;
|
||||
pub use self::{
|
||||
mock::HomeserverConnection as MockHomeserverConnection, readonly::ReadOnlyHomeserverConnection,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct MatrixUser {
|
||||
|
||||
78
crates/matrix/src/readonly.rs
Normal file
78
crates/matrix/src/readonly.rs
Normal file
@@ -0,0 +1,78 @@
|
||||
// Copyright 2025 New Vector Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Please see LICENSE in the repository root for full details.
|
||||
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::{HomeserverConnection, MatrixUser, ProvisionRequest};
|
||||
|
||||
/// A wrapper around a [`HomeserverConnection`] that only allows read
|
||||
/// operations.
|
||||
pub struct ReadOnlyHomeserverConnection<C> {
|
||||
inner: C,
|
||||
}
|
||||
|
||||
impl<C> ReadOnlyHomeserverConnection<C> {
|
||||
pub fn new(inner: C) -> Self
|
||||
where
|
||||
C: HomeserverConnection,
|
||||
{
|
||||
Self { inner }
|
||||
}
|
||||
}
|
||||
|
||||
#[async_trait::async_trait]
|
||||
impl<C: HomeserverConnection> HomeserverConnection for ReadOnlyHomeserverConnection<C> {
|
||||
fn homeserver(&self) -> &str {
|
||||
self.inner.homeserver()
|
||||
}
|
||||
|
||||
async fn query_user(&self, mxid: &str) -> Result<MatrixUser, anyhow::Error> {
|
||||
self.inner.query_user(mxid).await
|
||||
}
|
||||
|
||||
async fn provision_user(&self, _request: &ProvisionRequest) -> Result<bool, anyhow::Error> {
|
||||
anyhow::bail!("Provisioning is not supported in read-only mode");
|
||||
}
|
||||
|
||||
async fn is_localpart_available(&self, localpart: &str) -> Result<bool, anyhow::Error> {
|
||||
self.inner.is_localpart_available(localpart).await
|
||||
}
|
||||
|
||||
async fn create_device(&self, _mxid: &str, _device_id: &str) -> Result<(), anyhow::Error> {
|
||||
anyhow::bail!("Device creation is not supported in read-only mode");
|
||||
}
|
||||
|
||||
async fn delete_device(&self, _mxid: &str, _device_id: &str) -> Result<(), anyhow::Error> {
|
||||
anyhow::bail!("Device deletion is not supported in read-only mode");
|
||||
}
|
||||
|
||||
async fn sync_devices(
|
||||
&self,
|
||||
_mxid: &str,
|
||||
_devices: HashSet<String>,
|
||||
) -> Result<(), anyhow::Error> {
|
||||
anyhow::bail!("Device synchronization is not supported in read-only mode");
|
||||
}
|
||||
|
||||
async fn delete_user(&self, _mxid: &str, _erase: bool) -> Result<(), anyhow::Error> {
|
||||
anyhow::bail!("User deletion is not supported in read-only mode");
|
||||
}
|
||||
|
||||
async fn reactivate_user(&self, _mxid: &str) -> Result<(), anyhow::Error> {
|
||||
anyhow::bail!("User reactivation is not supported in read-only mode");
|
||||
}
|
||||
|
||||
async fn set_displayname(&self, _mxid: &str, _displayname: &str) -> Result<(), anyhow::Error> {
|
||||
anyhow::bail!("User displayname update is not supported in read-only mode");
|
||||
}
|
||||
|
||||
async fn unset_displayname(&self, _mxid: &str) -> Result<(), anyhow::Error> {
|
||||
anyhow::bail!("User displayname update is not supported in read-only mode");
|
||||
}
|
||||
|
||||
async fn allow_cross_signing_reset(&self, _mxid: &str) -> Result<(), anyhow::Error> {
|
||||
anyhow::bail!("Allowing cross-signing reset is not supported in read-only mode");
|
||||
}
|
||||
}
|
||||
@@ -1612,6 +1612,15 @@
|
||||
"secret"
|
||||
],
|
||||
"properties": {
|
||||
"kind": {
|
||||
"description": "The kind of homeserver it is.",
|
||||
"default": "synapse",
|
||||
"allOf": [
|
||||
{
|
||||
"$ref": "#/definitions/HomeserverKind"
|
||||
}
|
||||
]
|
||||
},
|
||||
"homeserver": {
|
||||
"description": "The server name of the homeserver.",
|
||||
"default": "localhost:8008",
|
||||
@@ -1629,6 +1638,25 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"HomeserverKind": {
|
||||
"description": "The kind of homeserver it is.",
|
||||
"oneOf": [
|
||||
{
|
||||
"description": "Homeserver is Synapse",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"synapse"
|
||||
]
|
||||
},
|
||||
{
|
||||
"description": "Homeserver is Synapse, in read-only mode\n\nThis is meant for testing rolling out Matrix Authentication Service with no risk of writing data to the homeserver.",
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"synapse_read_only"
|
||||
]
|
||||
}
|
||||
]
|
||||
},
|
||||
"PolicyConfig": {
|
||||
"description": "Application secrets",
|
||||
"type": "object",
|
||||
|
||||
Reference in New Issue
Block a user