You've already forked authentication-service
mirror of
https://github.com/matrix-org/matrix-authentication-service.git
synced 2025-08-06 06:02:40 +03:00
Improve the configuration schema
This commit is contained in:
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use mas_config::{ConfigurationSection, RootConfig};
|
use mas_config::{ConfigurationSection, RootConfig};
|
||||||
use schemars::schema_for;
|
use schemars::gen::SchemaSettings;
|
||||||
use tracing::info;
|
use tracing::info;
|
||||||
|
|
||||||
use super::RootCommand;
|
use super::RootCommand;
|
||||||
@@ -52,7 +52,12 @@ impl ConfigCommand {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
SC::Schema => {
|
SC::Schema => {
|
||||||
let schema = schema_for!(RootConfig);
|
let settings = SchemaSettings::draft07().with(|s| {
|
||||||
|
s.option_nullable = false;
|
||||||
|
s.option_add_null_type = false;
|
||||||
|
});
|
||||||
|
let gen = settings.into_generator();
|
||||||
|
let schema = gen.into_root_schema_for::<RootConfig>();
|
||||||
|
|
||||||
serde_yaml::to_writer(std::io::stdout(), &schema)?;
|
serde_yaml::to_writer(std::io::stdout(), &schema)?;
|
||||||
|
|
||||||
|
@@ -13,20 +13,26 @@
|
|||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::serde_as;
|
use serde_with::serde_as;
|
||||||
|
|
||||||
use super::ConfigurationSection;
|
use super::ConfigurationSection;
|
||||||
|
|
||||||
fn secret_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn example_secret() -> &'static str {
|
||||||
String::json_schema(gen)
|
"0000111122223333444455556666777788889999aaaabbbbccccddddeeeeffff"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cookies-related configuration
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct CookiesConfig {
|
pub struct CookiesConfig {
|
||||||
#[schemars(schema_with = "secret_schema")]
|
/// Encryption key for secure cookies
|
||||||
|
#[schemars(
|
||||||
|
with = "String",
|
||||||
|
regex(pattern = r"[0-9a-fA-F]{64}"),
|
||||||
|
example = "example_secret"
|
||||||
|
)]
|
||||||
#[serde_as(as = "serde_with::hex::Hex")]
|
#[serde_as(as = "serde_with::hex::Hex")]
|
||||||
pub secret: [u8; 32],
|
pub secret: [u8; 32],
|
||||||
}
|
}
|
||||||
|
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use chrono::Duration;
|
use chrono::Duration;
|
||||||
use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::serde_as;
|
use serde_with::serde_as;
|
||||||
|
|
||||||
@@ -24,14 +24,12 @@ fn default_ttl() -> Duration {
|
|||||||
Duration::hours(1)
|
Duration::hours(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ttl_schema(gen: &mut SchemaGenerator) -> Schema {
|
/// Configuration related to Cross-Site Request Forgery protections
|
||||||
u64::json_schema(gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Clone, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct CsrfConfig {
|
pub struct CsrfConfig {
|
||||||
#[schemars(schema_with = "ttl_schema")]
|
/// Time-to-live of a CSRF token in seconds
|
||||||
|
#[schemars(with = "u64", range(min = 60, max = 86400))]
|
||||||
#[serde(default = "default_ttl")]
|
#[serde(default = "default_ttl")]
|
||||||
#[serde_as(as = "serde_with::DurationSeconds<i64>")]
|
#[serde_as(as = "serde_with::DurationSeconds<i64>")]
|
||||||
pub ttl: Duration,
|
pub ttl: Duration,
|
||||||
|
@@ -12,11 +12,11 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
use std::{path::PathBuf, time::Duration};
|
use std::{num::NonZeroU32, path::PathBuf, time::Duration};
|
||||||
|
|
||||||
use anyhow::Context;
|
use anyhow::Context;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_with::{serde_as, skip_serializing_none};
|
use serde_with::{serde_as, skip_serializing_none};
|
||||||
use sqlx::{
|
use sqlx::{
|
||||||
@@ -26,9 +26,14 @@ use sqlx::{
|
|||||||
use tracing::log::LevelFilter;
|
use tracing::log::LevelFilter;
|
||||||
|
|
||||||
use super::ConfigurationSection;
|
use super::ConfigurationSection;
|
||||||
|
use crate::schema;
|
||||||
|
|
||||||
fn default_max_connections() -> u32 {
|
fn default_connection_string() -> String {
|
||||||
10
|
"postgresql://".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_max_connections() -> NonZeroU32 {
|
||||||
|
NonZeroU32::new(10).unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn default_connect_timeout() -> Duration {
|
fn default_connect_timeout() -> Duration {
|
||||||
@@ -58,31 +63,38 @@ impl Default for DatabaseConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn duration_schema(gen: &mut SchemaGenerator) -> Schema {
|
|
||||||
Option::<u64>::json_schema(gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn optional_duration_schema(gen: &mut SchemaGenerator) -> Schema {
|
|
||||||
u64::json_schema(gen)
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema, PartialEq)]
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
enum ConnectConfig {
|
enum ConnectConfig {
|
||||||
Uri {
|
Uri {
|
||||||
|
/// Connection URI
|
||||||
|
#[schemars(url, default = "default_connection_string")]
|
||||||
uri: String,
|
uri: String,
|
||||||
},
|
},
|
||||||
Options {
|
Options {
|
||||||
|
/// Name of host to connect to
|
||||||
|
#[schemars(schema_with = "schema::hostname")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
host: Option<String>,
|
host: Option<String>,
|
||||||
|
|
||||||
|
/// Port number to connect at the server host
|
||||||
|
#[schemars(schema_with = "schema::port")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
port: Option<u16>,
|
port: Option<u16>,
|
||||||
|
|
||||||
|
/// Directory containing the UNIX socket to connect to
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
socket: Option<PathBuf>,
|
socket: Option<PathBuf>,
|
||||||
|
|
||||||
|
/// PostgreSQL user name to connect as
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
username: Option<String>,
|
username: Option<String>,
|
||||||
|
|
||||||
|
/// Password to be used if the server demands password authentication
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
password: Option<String>,
|
password: Option<String>,
|
||||||
|
|
||||||
|
/// The database name
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
database: Option<String>,
|
database: Option<String>,
|
||||||
/* TODO
|
/* TODO
|
||||||
@@ -141,41 +153,49 @@ impl TryInto<PgConnectOptions> for &ConnectConfig {
|
|||||||
impl Default for ConnectConfig {
|
impl Default for ConnectConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self::Uri {
|
Self::Uri {
|
||||||
uri: "postgresql://".to_string(),
|
uri: default_connection_string(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Database connection configuration
|
||||||
#[serde_as]
|
#[serde_as]
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct DatabaseConfig {
|
pub struct DatabaseConfig {
|
||||||
|
/// Options related to how to connect to the database
|
||||||
#[serde(default, flatten)]
|
#[serde(default, flatten)]
|
||||||
options: ConnectConfig,
|
options: ConnectConfig,
|
||||||
|
|
||||||
|
/// Set the maximum number of connections the pool should maintain
|
||||||
#[serde(default = "default_max_connections")]
|
#[serde(default = "default_max_connections")]
|
||||||
max_connections: u32,
|
max_connections: NonZeroU32,
|
||||||
|
|
||||||
|
/// Set the minimum number of connections the pool should maintain
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
min_connections: u32,
|
min_connections: u32,
|
||||||
|
|
||||||
#[schemars(schema_with = "duration_schema")]
|
/// Set the amount of time to attempt connecting to the database
|
||||||
|
#[schemars(with = "u64")]
|
||||||
#[serde(default = "default_connect_timeout")]
|
#[serde(default = "default_connect_timeout")]
|
||||||
#[serde_as(as = "serde_with::DurationSeconds<u64>")]
|
#[serde_as(as = "serde_with::DurationSeconds<u64>")]
|
||||||
connect_timeout: Duration,
|
connect_timeout: Duration,
|
||||||
|
|
||||||
#[schemars(schema_with = "optional_duration_schema")]
|
/// Set a maximum idle duration for individual connections
|
||||||
|
#[schemars(with = "Option<u64>")]
|
||||||
#[serde(default = "default_idle_timeout")]
|
#[serde(default = "default_idle_timeout")]
|
||||||
#[serde_as(as = "Option<serde_with::DurationSeconds<u64>>")]
|
#[serde_as(as = "Option<serde_with::DurationSeconds<u64>>")]
|
||||||
idle_timeout: Option<Duration>,
|
idle_timeout: Option<Duration>,
|
||||||
|
|
||||||
#[schemars(schema_with = "optional_duration_schema")]
|
/// Set the maximum lifetime of individual connections
|
||||||
|
#[schemars(with = "u64")]
|
||||||
#[serde(default = "default_max_lifetime")]
|
#[serde(default = "default_max_lifetime")]
|
||||||
#[serde_as(as = "Option<serde_with::DurationSeconds<u64>>")]
|
#[serde_as(as = "Option<serde_with::DurationSeconds<u64>>")]
|
||||||
max_lifetime: Option<Duration>,
|
max_lifetime: Option<Duration>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DatabaseConfig {
|
impl DatabaseConfig {
|
||||||
|
/// Connect to the database
|
||||||
#[tracing::instrument(err, skip_all)]
|
#[tracing::instrument(err, skip_all)]
|
||||||
pub async fn connect(&self) -> anyhow::Result<PgPool> {
|
pub async fn connect(&self) -> anyhow::Result<PgPool> {
|
||||||
let mut options: PgConnectOptions = (&self.options)
|
let mut options: PgConnectOptions = (&self.options)
|
||||||
@@ -187,7 +207,7 @@ impl DatabaseConfig {
|
|||||||
.log_slow_statements(LevelFilter::Warn, Duration::from_millis(100));
|
.log_slow_statements(LevelFilter::Warn, Duration::from_millis(100));
|
||||||
|
|
||||||
PgPoolOptions::new()
|
PgPoolOptions::new()
|
||||||
.max_connections(self.max_connections)
|
.max_connections(self.max_connections.into())
|
||||||
.min_connections(self.min_connections)
|
.min_connections(self.min_connections)
|
||||||
.connect_timeout(self.connect_timeout)
|
.connect_timeout(self.connect_timeout)
|
||||||
.idle_timeout(self.idle_timeout)
|
.idle_timeout(self.idle_timeout)
|
||||||
|
@@ -12,46 +12,90 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
use std::num::NonZeroU16;
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use lettre::{message::Mailbox, Address};
|
use lettre::{message::Mailbox, Address};
|
||||||
use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema};
|
use schemars::{
|
||||||
|
gen::SchemaGenerator,
|
||||||
|
schema::{InstanceType, Schema, SchemaObject},
|
||||||
|
JsonSchema,
|
||||||
|
};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use super::ConfigurationSection;
|
use super::ConfigurationSection;
|
||||||
|
|
||||||
fn mailbox_schema(gen: &mut SchemaGenerator) -> Schema {
|
fn mailbox_schema(_gen: &mut SchemaGenerator) -> Schema {
|
||||||
// TODO: proper email schema
|
Schema::Object(SchemaObject {
|
||||||
String::json_schema(gen)
|
instance_type: Some(InstanceType::String.into()),
|
||||||
|
format: Some("email".to_string()),
|
||||||
|
..SchemaObject::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hostname_schema(_gen: &mut SchemaGenerator) -> Schema {
|
||||||
|
Schema::Object(SchemaObject {
|
||||||
|
instance_type: Some(InstanceType::String.into()),
|
||||||
|
format: Some("hostname".to_string()),
|
||||||
|
..SchemaObject::default()
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct Credentials {
|
pub struct Credentials {
|
||||||
|
/// Username for use to authenticate when connecting to the SMTP server
|
||||||
pub username: String,
|
pub username: String,
|
||||||
|
|
||||||
|
/// Password for use to authenticate when connecting to the SMTP server
|
||||||
pub password: String,
|
pub password: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Encryption mode to use
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
pub enum EmailSmtpMode {
|
pub enum EmailSmtpMode {
|
||||||
|
/// Plain text
|
||||||
Plain,
|
Plain,
|
||||||
|
/// StartTLS (starts as plain text then upgrade to TLS)
|
||||||
StartTls,
|
StartTls,
|
||||||
|
/// TLS
|
||||||
Tls,
|
Tls,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// What backend should be used when sending emails
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(tag = "transport", rename_all = "snake_case")]
|
#[serde(tag = "transport", rename_all = "snake_case")]
|
||||||
pub enum EmailTransportConfig {
|
pub enum EmailTransportConfig {
|
||||||
|
/// Don't send emails anywhere
|
||||||
Blackhole,
|
Blackhole,
|
||||||
|
|
||||||
|
/// Send emails via an SMTP relay
|
||||||
Smtp {
|
Smtp {
|
||||||
|
/// Connection mode to the relay
|
||||||
mode: EmailSmtpMode,
|
mode: EmailSmtpMode,
|
||||||
|
|
||||||
|
/// Hostname to connect to
|
||||||
|
#[schemars(schema_with = "hostname_schema")]
|
||||||
hostname: String,
|
hostname: String,
|
||||||
|
|
||||||
#[serde(default)]
|
/// Port to connect to. Default is 25 for plain, 465 for TLS and 587 for
|
||||||
port: Option<u16>,
|
/// StartTLS
|
||||||
|
#[serde(default, skip_serializing_if = "Option::is_none")]
|
||||||
|
port: Option<NonZeroU16>,
|
||||||
|
|
||||||
|
/// Set of credentials to use
|
||||||
#[serde(flatten, default)]
|
#[serde(flatten, default)]
|
||||||
credentials: Option<Credentials>,
|
credentials: Option<Credentials>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Send emails by calling sendmail
|
||||||
|
Sendmail {
|
||||||
|
/// Command to execute
|
||||||
|
#[serde(default = "default_sendmail_command")]
|
||||||
|
command: String,
|
||||||
|
},
|
||||||
|
|
||||||
|
/// Send emails via the AWS SESv2 API
|
||||||
AwsSes,
|
AwsSes,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,25 +105,38 @@ impl Default for EmailTransportConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn default_email() -> Mailbox {
|
||||||
|
let address = Address::new("root", "localhost").unwrap();
|
||||||
|
Mailbox::new(Some("Authentication Service".to_string()), address)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_sendmail_command() -> String {
|
||||||
|
"sendmail".to_string()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration related to sending emails
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct EmailConfig {
|
pub struct EmailConfig {
|
||||||
|
/// Email address to use as From when sending emails
|
||||||
|
#[serde(default = "default_email")]
|
||||||
#[schemars(schema_with = "mailbox_schema")]
|
#[schemars(schema_with = "mailbox_schema")]
|
||||||
pub from: Mailbox,
|
pub from: Mailbox,
|
||||||
|
|
||||||
|
/// Email address to use as Reply-To when sending emails
|
||||||
|
#[serde(default = "default_email")]
|
||||||
#[schemars(schema_with = "mailbox_schema")]
|
#[schemars(schema_with = "mailbox_schema")]
|
||||||
pub reply_to: Mailbox,
|
pub reply_to: Mailbox,
|
||||||
|
|
||||||
#[serde(flatten)]
|
/// What backend should be used when sending emails
|
||||||
|
#[serde(flatten, default)]
|
||||||
pub transport: EmailTransportConfig,
|
pub transport: EmailTransportConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for EmailConfig {
|
impl Default for EmailConfig {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
let address = Address::new("root", "localhost").unwrap();
|
|
||||||
let mailbox = Mailbox::new(Some("Authentication Service".to_string()), address);
|
|
||||||
Self {
|
Self {
|
||||||
from: mailbox.clone(),
|
from: default_email(),
|
||||||
reply_to: mailbox,
|
reply_to: default_email(),
|
||||||
transport: EmailTransportConfig::Blackhole,
|
transport: EmailTransportConfig::Blackhole,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -24,11 +24,34 @@ fn default_http_address() -> String {
|
|||||||
"[::]:8080".into()
|
"[::]:8080".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn http_address_example_1() -> &'static str {
|
||||||
|
"[::1]:8080"
|
||||||
|
}
|
||||||
|
fn http_address_example_2() -> &'static str {
|
||||||
|
"[::]:8080"
|
||||||
|
}
|
||||||
|
fn http_address_example_3() -> &'static str {
|
||||||
|
"127.0.0.1:8080"
|
||||||
|
}
|
||||||
|
fn http_address_example_4() -> &'static str {
|
||||||
|
"0.0.0.0:8080"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Configuration related to the web server
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct HttpConfig {
|
pub struct HttpConfig {
|
||||||
|
/// IP and port the server should listen to
|
||||||
|
#[schemars(
|
||||||
|
example = "http_address_example_1",
|
||||||
|
example = "http_address_example_2",
|
||||||
|
example = "http_address_example_3",
|
||||||
|
example = "http_address_example_4"
|
||||||
|
)]
|
||||||
#[serde(default = "default_http_address")]
|
#[serde(default = "default_http_address")]
|
||||||
pub address: String,
|
pub address: String,
|
||||||
|
|
||||||
|
/// Path from which to serve static files. If not specified, it will serve
|
||||||
|
/// the static files embedded in the server binary
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub web_root: Option<PathBuf>,
|
pub web_root: Option<PathBuf>,
|
||||||
}
|
}
|
||||||
|
@@ -20,6 +20,8 @@
|
|||||||
#![allow(clippy::missing_panics_doc)]
|
#![allow(clippy::missing_panics_doc)]
|
||||||
#![allow(clippy::missing_errors_doc)]
|
#![allow(clippy::missing_errors_doc)]
|
||||||
|
|
||||||
|
//! Application configuration logic
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use schemars::JsonSchema;
|
use schemars::JsonSchema;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
@@ -30,6 +32,7 @@ mod database;
|
|||||||
mod email;
|
mod email;
|
||||||
mod http;
|
mod http;
|
||||||
mod oauth2;
|
mod oauth2;
|
||||||
|
pub(crate) mod schema;
|
||||||
mod telemetry;
|
mod telemetry;
|
||||||
mod templates;
|
mod templates;
|
||||||
mod util;
|
mod util;
|
||||||
@@ -49,27 +52,36 @@ pub use self::{
|
|||||||
util::ConfigurationSection,
|
util::ConfigurationSection,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Application configuration root
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct RootConfig {
|
pub struct RootConfig {
|
||||||
|
/// Configuration related to OAuth 2.0/OIDC operations
|
||||||
pub oauth2: OAuth2Config,
|
pub oauth2: OAuth2Config,
|
||||||
|
|
||||||
|
/// Configuration of the HTTP server
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub http: HttpConfig,
|
pub http: HttpConfig,
|
||||||
|
|
||||||
|
/// Database connection configuration
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub database: DatabaseConfig,
|
pub database: DatabaseConfig,
|
||||||
|
|
||||||
|
/// Configuration related to cookies
|
||||||
pub cookies: CookiesConfig,
|
pub cookies: CookiesConfig,
|
||||||
|
|
||||||
|
/// Configuration related to sending monitoring data
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub telemetry: TelemetryConfig,
|
pub telemetry: TelemetryConfig,
|
||||||
|
|
||||||
|
/// Configuration related to templates
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub templates: TemplatesConfig,
|
pub templates: TemplatesConfig,
|
||||||
|
|
||||||
|
/// Configuration related to Cross-Site Request Forgery protections
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub csrf: CsrfConfig,
|
pub csrf: CsrfConfig,
|
||||||
|
|
||||||
|
/// Configuration related to sending emails
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub email: EmailConfig,
|
pub email: EmailConfig,
|
||||||
}
|
}
|
||||||
|
38
crates/config/src/schema.rs
Normal file
38
crates/config/src/schema.rs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Copyright 2022 The Matrix.org Foundation C.I.C.
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
use schemars::{
|
||||||
|
gen::SchemaGenerator,
|
||||||
|
schema::{InstanceType, NumberValidation, Schema, SchemaObject},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn port(_gen: &mut SchemaGenerator) -> Schema {
|
||||||
|
Schema::Object(SchemaObject {
|
||||||
|
instance_type: Some(InstanceType::Integer.into()),
|
||||||
|
number: Some(Box::new(NumberValidation {
|
||||||
|
minimum: Some(1.0),
|
||||||
|
maximum: Some(65535.0),
|
||||||
|
..NumberValidation::default()
|
||||||
|
})),
|
||||||
|
..SchemaObject::default()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn hostname(_gen: &mut SchemaGenerator) -> Schema {
|
||||||
|
Schema::Object(SchemaObject {
|
||||||
|
instance_type: Some(InstanceType::String.into()),
|
||||||
|
format: Some("hostname".to_string()),
|
||||||
|
..SchemaObject::default()
|
||||||
|
})
|
||||||
|
}
|
@@ -22,32 +22,72 @@ use url::Url;
|
|||||||
|
|
||||||
use super::ConfigurationSection;
|
use super::ConfigurationSection;
|
||||||
|
|
||||||
|
/// Propagation format for incoming and outgoing requests
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, JsonSchema)]
|
||||||
#[serde(rename_all = "lowercase")]
|
#[serde(rename_all = "lowercase")]
|
||||||
#[non_exhaustive]
|
#[non_exhaustive]
|
||||||
pub enum Propagator {
|
pub enum Propagator {
|
||||||
|
/// Propagate according to the W3C Trace Context specification
|
||||||
TraceContext,
|
TraceContext,
|
||||||
|
|
||||||
|
/// Propagate according to the W3C Baggage specification
|
||||||
Baggage,
|
Baggage,
|
||||||
|
|
||||||
|
/// Propagate trace context with Jaeger compatible headers
|
||||||
Jaeger,
|
Jaeger,
|
||||||
|
|
||||||
|
/// Propagate trace context with Zipkin compatible headers (single `b3`
|
||||||
|
/// header variant)
|
||||||
B3,
|
B3,
|
||||||
|
|
||||||
|
/// Propagate trace context with Zipkin compatible headers (multiple
|
||||||
|
/// `x-b3-*` headers variant)
|
||||||
B3Multi,
|
B3Multi,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn otlp_endpoint_example() -> &'static str {
|
||||||
|
"https://localhost:4317"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn jaeger_agent_endpoint_example() -> &'static str {
|
||||||
|
"127.0.0.1:6831"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn zipkin_collector_endpoint_example() -> &'static str {
|
||||||
|
"http://127.0.0.1:9411/api/v2/spans"
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Exporter to use when exporting traces
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(tag = "exporter", rename_all = "lowercase")]
|
#[serde(tag = "exporter", rename_all = "lowercase")]
|
||||||
pub enum TracingExporterConfig {
|
pub enum TracingExporterConfig {
|
||||||
|
/// Don't export traces
|
||||||
None,
|
None,
|
||||||
|
|
||||||
|
/// Export traces to the standard output. Only useful for debugging
|
||||||
Stdout,
|
Stdout,
|
||||||
|
|
||||||
|
/// Export traces to an OpenTelemetry protocol compatible endpoint
|
||||||
Otlp {
|
Otlp {
|
||||||
|
/// OTLP compatible endpoint
|
||||||
|
#[schemars(url, example = "otlp_endpoint_example")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
endpoint: Option<Url>,
|
endpoint: Option<Url>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Export traces to a Jaeger agent
|
||||||
Jaeger {
|
Jaeger {
|
||||||
|
/// Jaeger agent endpoint
|
||||||
|
#[schemars(example = "jaeger_agent_endpoint_example")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
agent_endpoint: Option<SocketAddr>,
|
agent_endpoint: Option<SocketAddr>,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/// Export traces to a Zipkin collector
|
||||||
Zipkin {
|
Zipkin {
|
||||||
|
/// Zipkin collector endpoint
|
||||||
|
#[schemars(url, example = "zipkin_collector_endpoint_example")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
collector_endpoint: Option<Url>,
|
collector_endpoint: Option<Url>,
|
||||||
},
|
},
|
||||||
@@ -59,23 +99,34 @@ impl Default for TracingExporterConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configuration related to exporting traces
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct TracingConfig {
|
pub struct TracingConfig {
|
||||||
|
/// Exporter to use when exporting traces
|
||||||
#[serde(default, flatten)]
|
#[serde(default, flatten)]
|
||||||
pub exporter: TracingExporterConfig,
|
pub exporter: TracingExporterConfig,
|
||||||
|
|
||||||
|
/// List of propagation formats to use for incoming and outgoing requests
|
||||||
pub propagators: Vec<Propagator>,
|
pub propagators: Vec<Propagator>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Exporter to use when exporting metrics
|
||||||
#[skip_serializing_none]
|
#[skip_serializing_none]
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Serialize, Deserialize, JsonSchema)]
|
||||||
#[serde(tag = "exporter", rename_all = "lowercase")]
|
#[serde(tag = "exporter", rename_all = "lowercase")]
|
||||||
pub enum MetricsExporterConfig {
|
pub enum MetricsExporterConfig {
|
||||||
|
/// Don't export metrics
|
||||||
None,
|
None,
|
||||||
|
|
||||||
|
/// Export metrics to stdout. Only useful for debugging
|
||||||
Stdout,
|
Stdout,
|
||||||
|
|
||||||
|
/// Export metrics to an OpenTelemetry protocol compatible endpoint
|
||||||
Otlp {
|
Otlp {
|
||||||
|
/// OTLP compatible endpoint
|
||||||
|
#[schemars(url, example = "otlp_endpoint_example")]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
endpoint: Option<url::Url>,
|
endpoint: Option<Url>,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,17 +136,22 @@ impl Default for MetricsExporterConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configuration related to exporting metrics
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct MetricsConfig {
|
pub struct MetricsConfig {
|
||||||
|
/// Exporter to use when exporting metrics
|
||||||
#[serde(default, flatten)]
|
#[serde(default, flatten)]
|
||||||
pub exporter: MetricsExporterConfig,
|
pub exporter: MetricsExporterConfig,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configuration related to sending monitoring data
|
||||||
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
|
||||||
pub struct TelemetryConfig {
|
pub struct TelemetryConfig {
|
||||||
|
/// Configuration related to exporting traces
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub tracing: TracingConfig,
|
pub tracing: TracingConfig,
|
||||||
|
|
||||||
|
/// Configuration related to exporting metrics
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub metrics: MetricsConfig,
|
pub metrics: MetricsConfig,
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,7 @@ fn default_builtin() -> bool {
|
|||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Configuration related to templates
|
||||||
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
#[derive(Debug, Serialize, Deserialize, JsonSchema, Clone)]
|
||||||
pub struct TemplatesConfig {
|
pub struct TemplatesConfig {
|
||||||
/// Path to the folder that holds the custom templates
|
/// Path to the folder that holds the custom templates
|
||||||
|
@@ -19,4 +19,4 @@ aws-config = "0.5.2"
|
|||||||
[dependencies.lettre]
|
[dependencies.lettre]
|
||||||
version = "0.10.0-rc.4"
|
version = "0.10.0-rc.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
features = ["tokio1-rustls-tls", "hostname", "builder", "tracing", "pool", "smtp-transport"]
|
features = ["tokio1-rustls-tls", "hostname", "builder", "tracing", "pool", "smtp-transport", "sendmail-transport"]
|
||||||
|
@@ -17,7 +17,10 @@ use std::sync::Arc;
|
|||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use lettre::{
|
use lettre::{
|
||||||
address::Envelope,
|
address::Envelope,
|
||||||
transport::smtp::{authentication::Credentials, AsyncSmtpTransport},
|
transport::{
|
||||||
|
sendmail::AsyncSendmailTransport,
|
||||||
|
smtp::{authentication::Credentials, AsyncSmtpTransport},
|
||||||
|
},
|
||||||
AsyncTransport, Tokio1Executor,
|
AsyncTransport, Tokio1Executor,
|
||||||
};
|
};
|
||||||
use mas_config::{EmailSmtpMode, EmailTransportConfig};
|
use mas_config::{EmailSmtpMode, EmailTransportConfig};
|
||||||
@@ -32,6 +35,7 @@ pub struct Transport {
|
|||||||
enum TransportInner {
|
enum TransportInner {
|
||||||
Blackhole,
|
Blackhole,
|
||||||
Smtp(AsyncSmtpTransport<Tokio1Executor>),
|
Smtp(AsyncSmtpTransport<Tokio1Executor>),
|
||||||
|
Sendmail(AsyncSendmailTransport<Tokio1Executor>),
|
||||||
AwsSes(aws_ses::Transport),
|
AwsSes(aws_ses::Transport),
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -63,11 +67,14 @@ impl Transport {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if let Some(port) = port {
|
if let Some(port) = port {
|
||||||
t = t.port(*port);
|
t = t.port((*port).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
TransportInner::Smtp(t.build())
|
TransportInner::Smtp(t.build())
|
||||||
}
|
}
|
||||||
|
EmailTransportConfig::Sendmail { command } => {
|
||||||
|
TransportInner::Sendmail(AsyncSendmailTransport::new_with_command(command))
|
||||||
|
}
|
||||||
EmailTransportConfig::AwsSes => {
|
EmailTransportConfig::AwsSes => {
|
||||||
TransportInner::AwsSes(aws_ses::Transport::from_env().await)
|
TransportInner::AwsSes(aws_ses::Transport::from_env().await)
|
||||||
}
|
}
|
||||||
@@ -84,6 +91,7 @@ impl Transport {
|
|||||||
TransportInner::Smtp(t) => {
|
TransportInner::Smtp(t) => {
|
||||||
t.test_connection().await?;
|
t.test_connection().await?;
|
||||||
}
|
}
|
||||||
|
&TransportInner::Sendmail(_) => {}
|
||||||
TransportInner::AwsSes(_) => {}
|
TransportInner::AwsSes(_) => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +121,9 @@ impl AsyncTransport for Transport {
|
|||||||
TransportInner::Smtp(t) => {
|
TransportInner::Smtp(t) => {
|
||||||
t.send_raw(envelope, email).await?;
|
t.send_raw(envelope, email).await?;
|
||||||
}
|
}
|
||||||
|
TransportInner::Sendmail(t) => {
|
||||||
|
t.send_raw(envelope, email).await?;
|
||||||
|
}
|
||||||
TransportInner::AwsSes(t) => {
|
TransportInner::AwsSes(t) => {
|
||||||
t.send_raw(envelope, email).await?;
|
t.send_raw(envelope, email).await?;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user