mas_config/sections/
mod.rs

1// Copyright 2024, 2025 New Vector Ltd.
2// Copyright 2022-2024 The Matrix.org Foundation C.I.C.
3//
4// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5// Please see LICENSE files in the repository root for full details.
6
7use rand::Rng;
8use schemars::JsonSchema;
9use serde::{Deserialize, Serialize};
10
11mod account;
12mod branding;
13mod captcha;
14mod clients;
15mod database;
16mod email;
17mod experimental;
18mod http;
19mod matrix;
20mod passwords;
21mod policy;
22mod rate_limiting;
23mod secrets;
24mod telemetry;
25mod templates;
26mod upstream_oauth2;
27
28pub use self::{
29    account::AccountConfig,
30    branding::BrandingConfig,
31    captcha::{CaptchaConfig, CaptchaServiceKind},
32    clients::{ClientAuthMethodConfig, ClientConfig, ClientsConfig},
33    database::{DatabaseConfig, PgSslMode},
34    email::{EmailConfig, EmailSmtpMode, EmailTransportKind},
35    experimental::ExperimentalConfig,
36    http::{
37        BindConfig as HttpBindConfig, HttpConfig, ListenerConfig as HttpListenerConfig,
38        Resource as HttpResource, TlsConfig as HttpTlsConfig, UnixOrTcp,
39    },
40    matrix::{HomeserverKind, MatrixConfig},
41    passwords::{
42        Algorithm as PasswordAlgorithm, HashingScheme as PasswordHashingScheme, PasswordsConfig,
43    },
44    policy::PolicyConfig,
45    rate_limiting::RateLimitingConfig,
46    secrets::SecretsConfig,
47    telemetry::{
48        MetricsConfig, MetricsExporterKind, Propagator, TelemetryConfig, TracingConfig,
49        TracingExporterKind,
50    },
51    templates::TemplatesConfig,
52    upstream_oauth2::{
53        ClaimsImports as UpstreamOAuth2ClaimsImports, DiscoveryMode as UpstreamOAuth2DiscoveryMode,
54        EmailImportPreference as UpstreamOAuth2EmailImportPreference,
55        ImportAction as UpstreamOAuth2ImportAction,
56        OnBackchannelLogout as UpstreamOAuth2OnBackchannelLogout,
57        OnConflict as UpstreamOAuth2OnConflict, PkceMethod as UpstreamOAuth2PkceMethod,
58        Provider as UpstreamOAuth2Provider, ResponseMode as UpstreamOAuth2ResponseMode,
59        TokenAuthMethod as UpstreamOAuth2TokenAuthMethod, UpstreamOAuth2Config,
60    },
61};
62use crate::util::ConfigurationSection;
63
64/// Application configuration root
65#[derive(Debug, Serialize, Deserialize, JsonSchema)]
66pub struct RootConfig {
67    /// List of OAuth 2.0/OIDC clients config
68    #[serde(default, skip_serializing_if = "ClientsConfig::is_default")]
69    pub clients: ClientsConfig,
70
71    /// Configuration of the HTTP server
72    #[serde(default)]
73    pub http: HttpConfig,
74
75    /// Database connection configuration
76    #[serde(default)]
77    pub database: DatabaseConfig,
78
79    /// Configuration related to sending monitoring data
80    #[serde(default, skip_serializing_if = "TelemetryConfig::is_default")]
81    pub telemetry: TelemetryConfig,
82
83    /// Configuration related to templates
84    #[serde(default, skip_serializing_if = "TemplatesConfig::is_default")]
85    pub templates: TemplatesConfig,
86
87    /// Configuration related to sending emails
88    #[serde(default)]
89    pub email: EmailConfig,
90
91    /// Application secrets
92    pub secrets: SecretsConfig,
93
94    /// Configuration related to user passwords
95    #[serde(default)]
96    pub passwords: PasswordsConfig,
97
98    /// Configuration related to the homeserver
99    pub matrix: MatrixConfig,
100
101    /// Configuration related to the OPA policies
102    #[serde(default, skip_serializing_if = "PolicyConfig::is_default")]
103    pub policy: PolicyConfig,
104
105    /// Configuration related to limiting the rate of user actions to prevent
106    /// abuse
107    #[serde(default, skip_serializing_if = "RateLimitingConfig::is_default")]
108    pub rate_limiting: RateLimitingConfig,
109
110    /// Configuration related to upstream OAuth providers
111    #[serde(default, skip_serializing_if = "UpstreamOAuth2Config::is_default")]
112    pub upstream_oauth2: UpstreamOAuth2Config,
113
114    /// Configuration section for tweaking the branding of the service
115    #[serde(default, skip_serializing_if = "BrandingConfig::is_default")]
116    pub branding: BrandingConfig,
117
118    /// Configuration section to setup CAPTCHA protection on a few operations
119    #[serde(default, skip_serializing_if = "CaptchaConfig::is_default")]
120    pub captcha: CaptchaConfig,
121
122    /// Configuration section to configure features related to account
123    /// management
124    #[serde(default, skip_serializing_if = "AccountConfig::is_default")]
125    pub account: AccountConfig,
126
127    /// Experimental configuration options
128    #[serde(default, skip_serializing_if = "ExperimentalConfig::is_default")]
129    pub experimental: ExperimentalConfig,
130}
131
132impl ConfigurationSection for RootConfig {
133    fn validate(
134        &self,
135        figment: &figment::Figment,
136    ) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
137        self.clients.validate(figment)?;
138        self.http.validate(figment)?;
139        self.database.validate(figment)?;
140        self.telemetry.validate(figment)?;
141        self.templates.validate(figment)?;
142        self.email.validate(figment)?;
143        self.passwords.validate(figment)?;
144        self.secrets.validate(figment)?;
145        self.matrix.validate(figment)?;
146        self.policy.validate(figment)?;
147        self.rate_limiting.validate(figment)?;
148        self.upstream_oauth2.validate(figment)?;
149        self.branding.validate(figment)?;
150        self.captcha.validate(figment)?;
151        self.account.validate(figment)?;
152        self.experimental.validate(figment)?;
153
154        Ok(())
155    }
156}
157
158impl RootConfig {
159    /// Generate a new configuration with random secrets
160    ///
161    /// # Errors
162    ///
163    /// Returns an error if the secrets could not be generated
164    pub async fn generate<R>(mut rng: R) -> anyhow::Result<Self>
165    where
166        R: Rng + Send,
167    {
168        Ok(Self {
169            clients: ClientsConfig::default(),
170            http: HttpConfig::default(),
171            database: DatabaseConfig::default(),
172            telemetry: TelemetryConfig::default(),
173            templates: TemplatesConfig::default(),
174            email: EmailConfig::default(),
175            passwords: PasswordsConfig::default(),
176            secrets: SecretsConfig::generate(&mut rng).await?,
177            matrix: MatrixConfig::generate(&mut rng),
178            policy: PolicyConfig::default(),
179            rate_limiting: RateLimitingConfig::default(),
180            upstream_oauth2: UpstreamOAuth2Config::default(),
181            branding: BrandingConfig::default(),
182            captcha: CaptchaConfig::default(),
183            account: AccountConfig::default(),
184            experimental: ExperimentalConfig::default(),
185        })
186    }
187
188    /// Configuration used in tests
189    #[must_use]
190    pub fn test() -> Self {
191        Self {
192            clients: ClientsConfig::default(),
193            http: HttpConfig::default(),
194            database: DatabaseConfig::default(),
195            telemetry: TelemetryConfig::default(),
196            templates: TemplatesConfig::default(),
197            passwords: PasswordsConfig::default(),
198            email: EmailConfig::default(),
199            secrets: SecretsConfig::test(),
200            matrix: MatrixConfig::test(),
201            policy: PolicyConfig::default(),
202            rate_limiting: RateLimitingConfig::default(),
203            upstream_oauth2: UpstreamOAuth2Config::default(),
204            branding: BrandingConfig::default(),
205            captcha: CaptchaConfig::default(),
206            account: AccountConfig::default(),
207            experimental: ExperimentalConfig::default(),
208        }
209    }
210}
211
212/// Partial configuration actually used by the server
213#[allow(missing_docs)]
214#[derive(Debug, Deserialize)]
215pub struct AppConfig {
216    #[serde(default)]
217    pub http: HttpConfig,
218
219    #[serde(default)]
220    pub database: DatabaseConfig,
221
222    #[serde(default)]
223    pub templates: TemplatesConfig,
224
225    #[serde(default)]
226    pub email: EmailConfig,
227
228    pub secrets: SecretsConfig,
229
230    #[serde(default)]
231    pub passwords: PasswordsConfig,
232
233    pub matrix: MatrixConfig,
234
235    #[serde(default)]
236    pub policy: PolicyConfig,
237
238    #[serde(default)]
239    pub rate_limiting: RateLimitingConfig,
240
241    #[serde(default)]
242    pub branding: BrandingConfig,
243
244    #[serde(default)]
245    pub captcha: CaptchaConfig,
246
247    #[serde(default)]
248    pub account: AccountConfig,
249
250    #[serde(default)]
251    pub experimental: ExperimentalConfig,
252}
253
254impl ConfigurationSection for AppConfig {
255    fn validate(
256        &self,
257        figment: &figment::Figment,
258    ) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
259        self.http.validate(figment)?;
260        self.database.validate(figment)?;
261        self.templates.validate(figment)?;
262        self.email.validate(figment)?;
263        self.passwords.validate(figment)?;
264        self.secrets.validate(figment)?;
265        self.matrix.validate(figment)?;
266        self.policy.validate(figment)?;
267        self.rate_limiting.validate(figment)?;
268        self.branding.validate(figment)?;
269        self.captcha.validate(figment)?;
270        self.account.validate(figment)?;
271        self.experimental.validate(figment)?;
272
273        Ok(())
274    }
275}
276
277/// Partial config used by the `mas-cli config sync` command
278#[allow(missing_docs)]
279#[derive(Debug, Deserialize)]
280pub struct SyncConfig {
281    #[serde(default)]
282    pub database: DatabaseConfig,
283
284    pub secrets: SecretsConfig,
285
286    #[serde(default)]
287    pub clients: ClientsConfig,
288
289    #[serde(default)]
290    pub upstream_oauth2: UpstreamOAuth2Config,
291}
292
293impl ConfigurationSection for SyncConfig {
294    fn validate(
295        &self,
296        figment: &figment::Figment,
297    ) -> Result<(), Box<dyn std::error::Error + Send + Sync + 'static>> {
298        self.database.validate(figment)?;
299        self.secrets.validate(figment)?;
300        self.clients.validate(figment)?;
301        self.upstream_oauth2.validate(figment)?;
302
303        Ok(())
304    }
305}