Admin API to expose a few configuration values

This commit is contained in:
Quentin Gliech
2025-09-12 17:46:53 +02:00
parent 7c4cf27a52
commit ca9aeb42c0
5 changed files with 210 additions and 2 deletions

View File

@@ -20,7 +20,7 @@ use axum::{
use hyper::header::{ACCEPT, AUTHORIZATION, CONTENT_TYPE};
use indexmap::IndexMap;
use mas_axum_utils::InternalError;
use mas_data_model::BoxRng;
use mas_data_model::{BoxRng, SiteConfig};
use mas_http::CorsLayerExt;
use mas_matrix::HomeserverConnection;
use mas_policy::PolicyFactory;
@@ -43,6 +43,11 @@ use crate::passwords::PasswordManager;
fn finish(t: TransformOpenApi) -> TransformOpenApi {
t.title("Matrix Authentication Service admin API")
.tag(Tag {
name: "server".to_owned(),
description: Some("Information about the server".to_owned()),
..Tag::default()
})
.tag(Tag {
name: "compat-session".to_owned(),
description: Some("Manage compatibility sessions from legacy clients".to_owned()),
@@ -153,6 +158,7 @@ where
Templates: FromRef<S>,
UrlBuilder: FromRef<S>,
Arc<PolicyFactory>: FromRef<S>,
SiteConfig: FromRef<S>,
{
// We *always* want to explicitly set the possible responses, beacuse the
// infered ones are not necessarily correct

View File

@@ -11,7 +11,7 @@ use aide::axum::{
routing::{get_with, post_with},
};
use axum::extract::{FromRef, FromRequestParts};
use mas_data_model::BoxRng;
use mas_data_model::{BoxRng, SiteConfig};
use mas_matrix::HomeserverConnection;
use mas_policy::PolicyFactory;
@@ -21,6 +21,7 @@ use crate::passwords::PasswordManager;
mod compat_sessions;
mod oauth2_sessions;
mod policy_data;
mod site_config;
mod upstream_oauth_links;
mod user_emails;
mod user_registration_tokens;
@@ -32,11 +33,16 @@ where
S: Clone + Send + Sync + 'static,
Arc<dyn HomeserverConnection>: FromRef<S>,
PasswordManager: FromRef<S>,
SiteConfig: FromRef<S>,
Arc<PolicyFactory>: FromRef<S>,
BoxRng: FromRequestParts<S>,
CallContext: FromRequestParts<S>,
{
ApiRouter::<S>::new()
.api_route(
"/site-config",
get_with(self::site_config::handler, self::site_config::doc),
)
.api_route(
"/compat-sessions",
get_with(self::compat_sessions::list, self::compat_sessions::list_doc),

View File

@@ -0,0 +1,92 @@
// Copyright 2025 New Vector Ltd.
//
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
// Please see LICENSE files in the repository root for full details.
use aide::transform::TransformOperation;
use axum::{Json, extract::State};
use schemars::JsonSchema;
use serde::Serialize;
use crate::admin::call_context::CallContext;
#[allow(clippy::struct_excessive_bools)]
#[derive(Serialize, JsonSchema)]
pub struct SiteConfig {
/// The Matrix server name for which this instance is configured
server_name: String,
/// Whether password login is enabled.
pub password_login_enabled: bool,
/// Whether password registration is enabled.
pub password_registration_enabled: bool,
/// Whether registration tokens are required for password registrations.
pub registration_token_required: bool,
/// Whether users can change their email.
pub email_change_allowed: bool,
/// Whether users can change their display name.
pub displayname_change_allowed: bool,
/// Whether users can change their password.
pub password_change_allowed: bool,
/// Whether users can recover their account via email.
pub account_recovery_allowed: bool,
/// Whether users can delete their own account.
pub account_deactivation_allowed: bool,
/// Whether CAPTCHA during registration is enabled.
pub captcha_enabled: bool,
/// Minimum password complexity, between 0 and 4.
/// This is a score from zxcvbn.
#[schemars(range(min = 0, max = 4))]
pub minimum_password_complexity: u8,
}
pub fn doc(operation: TransformOperation) -> TransformOperation {
operation
.id("siteConfig")
.tag("server")
.summary("Get informations about the configuration of this MAS instance")
.response_with::<200, Json<SiteConfig>, _>(|t| {
t.example(SiteConfig {
server_name: "example.com".to_owned(),
password_login_enabled: true,
password_registration_enabled: true,
registration_token_required: true,
email_change_allowed: true,
displayname_change_allowed: true,
password_change_allowed: true,
account_recovery_allowed: true,
account_deactivation_allowed: true,
captcha_enabled: true,
minimum_password_complexity: 3,
})
})
}
#[tracing::instrument(name = "handler.admin.v1.site_config", skip_all)]
pub async fn handler(
_: CallContext,
State(site_config): State<mas_data_model::SiteConfig>,
) -> Json<SiteConfig> {
Json(SiteConfig {
server_name: site_config.server_name,
password_login_enabled: site_config.password_login_enabled,
password_registration_enabled: site_config.password_registration_enabled,
registration_token_required: site_config.registration_token_required,
email_change_allowed: site_config.email_change_allowed,
displayname_change_allowed: site_config.displayname_change_allowed,
password_change_allowed: site_config.password_change_allowed,
account_recovery_allowed: site_config.account_recovery_allowed,
account_deactivation_allowed: site_config.account_deactivation_allowed,
captcha_enabled: site_config.captcha.is_some(),
minimum_password_complexity: site_config.minimum_password_complexity,
})
}

View File

@@ -59,6 +59,7 @@ impl_from_ref!(Arc<dyn mas_matrix::HomeserverConnection>);
impl_from_ref!(mas_keystore::Keystore);
impl_from_ref!(mas_handlers::passwords::PasswordManager);
impl_from_ref!(Arc<mas_policy::PolicyFactory>);
impl_from_ref!(mas_data_model::SiteConfig);
fn main() -> Result<(), Box<dyn std::error::Error>> {
let (mut api, _) = mas_handlers::admin_api_router::<DummyState>();

View File

@@ -16,6 +16,40 @@
}
],
"paths": {
"/api/admin/v1/site-config": {
"get": {
"tags": [
"server"
],
"summary": "Get informations about the configuration of this MAS instance",
"operationId": "siteConfig",
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/SiteConfig"
},
"example": {
"server_name": "example.com",
"password_login_enabled": true,
"password_registration_enabled": true,
"registration_token_required": true,
"email_change_allowed": true,
"displayname_change_allowed": true,
"password_change_allowed": true,
"account_recovery_allowed": true,
"account_deactivation_allowed": true,
"captcha_enabled": true,
"minimum_password_complexity": 3
}
}
}
}
}
}
},
"/api/admin/v1/compat-sessions": {
"get": {
"tags": [
@@ -3186,6 +3220,71 @@
}
},
"schemas": {
"SiteConfig": {
"type": "object",
"required": [
"account_deactivation_allowed",
"account_recovery_allowed",
"captcha_enabled",
"displayname_change_allowed",
"email_change_allowed",
"minimum_password_complexity",
"password_change_allowed",
"password_login_enabled",
"password_registration_enabled",
"registration_token_required",
"server_name"
],
"properties": {
"server_name": {
"description": "The Matrix server name for which this instance is configured",
"type": "string"
},
"password_login_enabled": {
"description": "Whether password login is enabled.",
"type": "boolean"
},
"password_registration_enabled": {
"description": "Whether password registration is enabled.",
"type": "boolean"
},
"registration_token_required": {
"description": "Whether registration tokens are required for password registrations.",
"type": "boolean"
},
"email_change_allowed": {
"description": "Whether users can change their email.",
"type": "boolean"
},
"displayname_change_allowed": {
"description": "Whether users can change their display name.",
"type": "boolean"
},
"password_change_allowed": {
"description": "Whether users can change their password.",
"type": "boolean"
},
"account_recovery_allowed": {
"description": "Whether users can recover their account via email.",
"type": "boolean"
},
"account_deactivation_allowed": {
"description": "Whether users can delete their own account.",
"type": "boolean"
},
"captcha_enabled": {
"description": "Whether CAPTCHA during registration is enabled.",
"type": "boolean"
},
"minimum_password_complexity": {
"description": "Minimum password complexity, between 0 and 4. This is a score from zxcvbn.",
"type": "integer",
"format": "uint8",
"maximum": 4.0,
"minimum": 0.0
}
}
},
"PaginationParams": {
"type": "object",
"properties": {
@@ -4586,6 +4685,10 @@
}
],
"tags": [
{
"name": "server",
"description": "Information about the server"
},
{
"name": "compat-session",
"description": "Manage compatibility sessions from legacy clients"