diff --git a/crates/cli/src/commands/config.rs b/crates/cli/src/commands/config.rs index 9bd13dd6..3dfab39d 100644 --- a/crates/cli/src/commands/config.rs +++ b/crates/cli/src/commands/config.rs @@ -15,7 +15,7 @@ use std::collections::HashSet; use clap::Parser; -use mas_config::{ConfigurationSection, RootConfig}; +use mas_config::{ConfigurationSection, RootConfig, SyncConfig}; use mas_storage::{ upstream_oauth2::UpstreamOAuthProviderRepository, Repository, RepositoryAccess, SystemClock, }; @@ -142,7 +142,7 @@ async fn sync(root: &super::Options, prune: bool, dry_run: bool) -> anyhow::Resu let mut rng = rand_chacha::ChaChaRng::from_entropy(); let clock = SystemClock::default(); - let config: RootConfig = root.load_config()?; + let config: SyncConfig = root.load_config()?; let encrypter = config.secrets.encrypter(); let pool = database_from_config(&config.database).await?; let mut repo = PgRepository::from_pool(&pool).await?.boxed(); diff --git a/crates/cli/src/commands/mod.rs b/crates/cli/src/commands/mod.rs index 6f27124a..1a726664 100644 --- a/crates/cli/src/commands/mod.rs +++ b/crates/cli/src/commands/mod.rs @@ -74,7 +74,7 @@ impl Options { } } - pub fn load_config<'de, T: ConfigurationSection<'de>>(&self) -> anyhow::Result { + pub fn load_config(&self) -> anyhow::Result { let configs = if self.config.is_empty() { // Read the MAS_CONFIG environment variable std::env::var("MAS_CONFIG") diff --git a/crates/cli/src/commands/server.rs b/crates/cli/src/commands/server.rs index bced9e98..a4a2a052 100644 --- a/crates/cli/src/commands/server.rs +++ b/crates/cli/src/commands/server.rs @@ -17,7 +17,7 @@ use std::{sync::Arc, time::Duration}; use anyhow::Context; use clap::Parser; use itertools::Itertools; -use mas_config::RootConfig; +use mas_config::AppConfig; use mas_handlers::{AppState, HttpClientFactory, MatrixHomeserver}; use mas_listener::{server::Server, shutdown::ShutdownStream}; use mas_matrix_synapse::SynapseConnection; @@ -54,7 +54,7 @@ impl Options { #[allow(clippy::too_many_lines)] pub async fn run(self, root: &super::Options) -> anyhow::Result<()> { let span = info_span!("cli.run.init").entered(); - let config: RootConfig = root.load_config()?; + let config: AppConfig = root.load_config()?; // Connect to the database info!("Connecting to the database"); diff --git a/crates/cli/src/commands/worker.rs b/crates/cli/src/commands/worker.rs index 1a993cf0..5b131a74 100644 --- a/crates/cli/src/commands/worker.rs +++ b/crates/cli/src/commands/worker.rs @@ -13,7 +13,7 @@ // limitations under the License. use clap::Parser; -use mas_config::RootConfig; +use mas_config::AppConfig; use mas_handlers::HttpClientFactory; use mas_matrix_synapse::SynapseConnection; use mas_router::UrlBuilder; @@ -31,7 +31,7 @@ pub(super) struct Options {} impl Options { pub async fn run(self, root: &super::Options) -> anyhow::Result<()> { let span = info_span!("cli.worker.init").entered(); - let config: RootConfig = root.load_config()?; + let config: AppConfig = root.load_config()?; // Connect to the database info!("Connecting to the database"); diff --git a/crates/config/src/sections/clients.rs b/crates/config/src/sections/clients.rs index bcaed40b..837f8c91 100644 --- a/crates/config/src/sections/clients.rs +++ b/crates/config/src/sections/clients.rs @@ -171,7 +171,7 @@ impl DerefMut for ClientsConfig { } #[async_trait] -impl ConfigurationSection<'_> for ClientsConfig { +impl ConfigurationSection for ClientsConfig { fn path() -> &'static str { "clients" } diff --git a/crates/config/src/sections/csrf.rs b/crates/config/src/sections/csrf.rs index ec7eeba6..e82df2c4 100644 --- a/crates/config/src/sections/csrf.rs +++ b/crates/config/src/sections/csrf.rs @@ -43,7 +43,7 @@ impl Default for CsrfConfig { } #[async_trait] -impl ConfigurationSection<'_> for CsrfConfig { +impl ConfigurationSection for CsrfConfig { fn path() -> &'static str { "csrf" } diff --git a/crates/config/src/sections/database.rs b/crates/config/src/sections/database.rs index bc84c1b9..127193a8 100644 --- a/crates/config/src/sections/database.rs +++ b/crates/config/src/sections/database.rs @@ -145,7 +145,7 @@ pub struct DatabaseConfig { } #[async_trait] -impl ConfigurationSection<'_> for DatabaseConfig { +impl ConfigurationSection for DatabaseConfig { fn path() -> &'static str { "database" } diff --git a/crates/config/src/sections/email.rs b/crates/config/src/sections/email.rs index 8b74c21d..efeacd7d 100644 --- a/crates/config/src/sections/email.rs +++ b/crates/config/src/sections/email.rs @@ -124,7 +124,7 @@ impl Default for EmailConfig { } #[async_trait] -impl ConfigurationSection<'_> for EmailConfig { +impl ConfigurationSection for EmailConfig { fn path() -> &'static str { "email" } diff --git a/crates/config/src/sections/http.rs b/crates/config/src/sections/http.rs index d5e54a1e..35507853 100644 --- a/crates/config/src/sections/http.rs +++ b/crates/config/src/sections/http.rs @@ -370,7 +370,7 @@ impl Default for HttpConfig { } #[async_trait] -impl ConfigurationSection<'_> for HttpConfig { +impl ConfigurationSection for HttpConfig { fn path() -> &'static str { "http" } diff --git a/crates/config/src/sections/matrix.rs b/crates/config/src/sections/matrix.rs index 6f807498..e5718e4c 100644 --- a/crates/config/src/sections/matrix.rs +++ b/crates/config/src/sections/matrix.rs @@ -49,7 +49,7 @@ pub struct MatrixConfig { } #[async_trait] -impl ConfigurationSection<'_> for MatrixConfig { +impl ConfigurationSection for MatrixConfig { fn path() -> &'static str { "matrix" } diff --git a/crates/config/src/sections/mod.rs b/crates/config/src/sections/mod.rs index 49a2dc68..5cb1dd7b 100644 --- a/crates/config/src/sections/mod.rs +++ b/crates/config/src/sections/mod.rs @@ -106,7 +106,7 @@ pub struct RootConfig { } #[async_trait] -impl ConfigurationSection<'_> for RootConfig { +impl ConfigurationSection for RootConfig { fn path() -> &'static str { "" } @@ -148,3 +148,115 @@ impl ConfigurationSection<'_> for RootConfig { } } } + +/// Partial configuration actually used by the server +#[allow(missing_docs)] +#[derive(Debug, Deserialize, Serialize)] +pub struct AppConfig { + #[serde(default)] + pub http: HttpConfig, + + #[serde(default)] + pub database: DatabaseConfig, + + #[serde(default)] + pub templates: TemplatesConfig, + + #[serde(default)] + pub csrf: CsrfConfig, + + #[serde(default)] + pub email: EmailConfig, + + pub secrets: SecretsConfig, + + #[serde(default)] + pub passwords: PasswordsConfig, + + pub matrix: MatrixConfig, + + #[serde(default)] + pub policy: PolicyConfig, +} + +#[async_trait] +impl ConfigurationSection for AppConfig { + fn path() -> &'static str { + "" + } + + async fn generate(mut rng: R) -> anyhow::Result + where + R: Rng + Send, + { + Ok(Self { + http: HttpConfig::generate(&mut rng).await?, + database: DatabaseConfig::generate(&mut rng).await?, + templates: TemplatesConfig::generate(&mut rng).await?, + csrf: CsrfConfig::generate(&mut rng).await?, + email: EmailConfig::generate(&mut rng).await?, + passwords: PasswordsConfig::generate(&mut rng).await?, + secrets: SecretsConfig::generate(&mut rng).await?, + matrix: MatrixConfig::generate(&mut rng).await?, + policy: PolicyConfig::generate(&mut rng).await?, + }) + } + + fn test() -> Self { + Self { + http: HttpConfig::test(), + database: DatabaseConfig::test(), + templates: TemplatesConfig::test(), + passwords: PasswordsConfig::test(), + csrf: CsrfConfig::test(), + email: EmailConfig::test(), + secrets: SecretsConfig::test(), + matrix: MatrixConfig::test(), + policy: PolicyConfig::test(), + } + } +} + +/// Partial config used by the `mas-cli config sync` command +#[allow(missing_docs)] +#[derive(Debug, Deserialize, Serialize)] +pub struct SyncConfig { + #[serde(default)] + pub database: DatabaseConfig, + + pub secrets: SecretsConfig, + + #[serde(default)] + pub clients: ClientsConfig, + + #[serde(default)] + pub upstream_oauth2: UpstreamOAuth2Config, +} + +#[async_trait] +impl ConfigurationSection for SyncConfig { + fn path() -> &'static str { + "" + } + + async fn generate(mut rng: R) -> anyhow::Result + where + R: Rng + Send, + { + Ok(Self { + database: DatabaseConfig::generate(&mut rng).await?, + secrets: SecretsConfig::generate(&mut rng).await?, + clients: ClientsConfig::generate(&mut rng).await?, + upstream_oauth2: UpstreamOAuth2Config::generate(&mut rng).await?, + }) + } + + fn test() -> Self { + Self { + database: DatabaseConfig::test(), + secrets: SecretsConfig::test(), + clients: ClientsConfig::test(), + upstream_oauth2: UpstreamOAuth2Config::test(), + } + } +} diff --git a/crates/config/src/sections/passwords.rs b/crates/config/src/sections/passwords.rs index f92db589..9a6cf59e 100644 --- a/crates/config/src/sections/passwords.rs +++ b/crates/config/src/sections/passwords.rs @@ -56,7 +56,7 @@ impl Default for PasswordsConfig { } #[async_trait] -impl ConfigurationSection<'_> for PasswordsConfig { +impl ConfigurationSection for PasswordsConfig { fn path() -> &'static str { "passwords" } diff --git a/crates/config/src/sections/policy.rs b/crates/config/src/sections/policy.rs index 4eccf059..3ac10254 100644 --- a/crates/config/src/sections/policy.rs +++ b/crates/config/src/sections/policy.rs @@ -82,7 +82,7 @@ impl Default for PolicyConfig { } #[async_trait] -impl ConfigurationSection<'_> for PolicyConfig { +impl ConfigurationSection for PolicyConfig { fn path() -> &'static str { "policy" } diff --git a/crates/config/src/sections/secrets.rs b/crates/config/src/sections/secrets.rs index afe624f4..05eedee3 100644 --- a/crates/config/src/sections/secrets.rs +++ b/crates/config/src/sections/secrets.rs @@ -138,7 +138,7 @@ impl SecretsConfig { } #[async_trait] -impl ConfigurationSection<'_> for SecretsConfig { +impl ConfigurationSection for SecretsConfig { fn path() -> &'static str { "secrets" } diff --git a/crates/config/src/sections/telemetry.rs b/crates/config/src/sections/telemetry.rs index cc87850b..10407eb0 100644 --- a/crates/config/src/sections/telemetry.rs +++ b/crates/config/src/sections/telemetry.rs @@ -287,7 +287,7 @@ pub struct TelemetryConfig { } #[async_trait] -impl ConfigurationSection<'_> for TelemetryConfig { +impl ConfigurationSection for TelemetryConfig { fn path() -> &'static str { "telemetry" } diff --git a/crates/config/src/sections/templates.rs b/crates/config/src/sections/templates.rs index 23ebfa38..9cdfee54 100644 --- a/crates/config/src/sections/templates.rs +++ b/crates/config/src/sections/templates.rs @@ -48,7 +48,7 @@ impl Default for TemplatesConfig { } #[async_trait] -impl ConfigurationSection<'_> for TemplatesConfig { +impl ConfigurationSection for TemplatesConfig { fn path() -> &'static str { "templates" } diff --git a/crates/config/src/sections/upstream_oauth2.rs b/crates/config/src/sections/upstream_oauth2.rs index a50c5034..9fa025db 100644 --- a/crates/config/src/sections/upstream_oauth2.rs +++ b/crates/config/src/sections/upstream_oauth2.rs @@ -32,7 +32,7 @@ pub struct UpstreamOAuth2Config { } #[async_trait] -impl<'a> ConfigurationSection<'a> for UpstreamOAuth2Config { +impl ConfigurationSection for UpstreamOAuth2Config { fn path() -> &'static str { "upstream_oauth2" } diff --git a/crates/config/src/util.rs b/crates/config/src/util.rs index f3bbb23f..9c5bcc8e 100644 --- a/crates/config/src/util.rs +++ b/crates/config/src/util.rs @@ -21,12 +21,12 @@ use figment::{ Figment, Profile, }; use rand::Rng; -use serde::{Deserialize, Serialize}; +use serde::{de::DeserializeOwned, Serialize}; #[async_trait] /// Trait implemented by all configuration section to help loading specific part /// of the config and generate the sample config. -pub trait ConfigurationSection<'a>: Sized + Deserialize<'a> + Serialize { +pub trait ConfigurationSection: Sized + DeserializeOwned + Serialize { /// Specify where this section should live relative to the root. fn path() -> &'static str; @@ -39,7 +39,7 @@ pub trait ConfigurationSection<'a>: Sized + Deserialize<'a> + Serialize { /// variables. /// /// This is what backs the `config generate` subcommand, allowing to - /// programatically generate a configuration file, e.g. + /// programmatically generate a configuration file, e.g. /// /// ```sh /// export MAS_OAUTH2_ISSUER=https://example.com/