1
0
mirror of https://github.com/matrix-org/matrix-authentication-service.git synced 2025-07-29 22:01:14 +03:00

Split the service in multiple crates

This commit is contained in:
Quentin Gliech
2021-09-16 14:43:56 +02:00
parent da91564bf9
commit a44e33931c
83 changed files with 311 additions and 174 deletions

74
Cargo.lock generated
View File

@ -1355,22 +1355,55 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d" checksum = "3e2e65a1a2e43cfcb47a895c4c8b10d1f4a61097f9f254f183aee60cad9c651d"
[[package]] [[package]]
name = "matchers" name = "mas-cli"
version = "0.0.1" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
dependencies = [ dependencies = [
"regex-automata", "anyhow",
"argon2",
"clap",
"dotenv",
"hyper",
"indoc",
"mas-config",
"mas-core",
"schemars",
"serde_yaml",
"tokio",
"tower",
"tower-http",
"tracing",
"tracing-subscriber",
"warp",
] ]
[[package]] [[package]]
name = "matches" name = "mas-config"
version = "0.1.9" version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f" "anyhow",
"async-trait",
"chrono",
"elliptic-curve",
"figment",
"indoc",
"jwt-compact",
"k256",
"pkcs8",
"rand 0.8.4",
"rsa",
"schemars",
"serde",
"serde_json",
"serde_with",
"sqlx",
"thiserror",
"tokio",
"tracing",
"url",
]
[[package]] [[package]]
name = "matrix-authentication-service" name = "mas-core"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
@ -1379,11 +1412,9 @@ dependencies = [
"bincode", "bincode",
"chacha20poly1305", "chacha20poly1305",
"chrono", "chrono",
"clap",
"cookie", "cookie",
"crc", "crc",
"data-encoding", "data-encoding",
"dotenv",
"elliptic-curve", "elliptic-curve",
"figment", "figment",
"futures-util", "futures-util",
@ -1393,6 +1424,7 @@ dependencies = [
"itertools", "itertools",
"jwt-compact", "jwt-compact",
"k256", "k256",
"mas-config",
"mime", "mime",
"oauth2-types", "oauth2-types",
"password-hash", "password-hash",
@ -1411,14 +1443,26 @@ dependencies = [
"thiserror", "thiserror",
"tokio", "tokio",
"tokio-stream", "tokio-stream",
"tower",
"tower-http",
"tracing", "tracing",
"tracing-subscriber",
"url", "url",
"warp", "warp",
] ]
[[package]]
name = "matchers"
version = "0.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f099785f7595cc4b4553a174ce30dd7589ef93391ff414dbb67f62392b9e0ce1"
dependencies = [
"regex-automata",
]
[[package]]
name = "matches"
version = "0.1.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
[[package]] [[package]]
name = "md-5" name = "md-5"
version = "0.9.1" version = "0.9.1"

View File

@ -1,8 +1,5 @@
[workspace] [workspace]
members = [ members = ["crates/*"]
"oauth2-types",
"matrix-authentication-service",
]
resolver = "2" resolver = "2"

27
crates/cli/Cargo.toml Normal file
View File

@ -0,0 +1,27 @@
[package]
name = "mas-cli"
version = "0.1.0"
authors = ["Quentin Gliech <quenting@element.io>"]
edition = "2018"
license = "Apache-2.0"
[dependencies]
tokio = { version = "1.11.0", features = ["full"] }
anyhow = "1.0.44"
clap = "3.0.0-beta.4"
tracing = "0.1.27"
tracing-subscriber = "0.2.22"
dotenv = "0.15.0"
schemars = { version = "0.8.3", features = ["url", "chrono"] }
tower = { version = "0.4.8", features = ["full"] }
tower-http = { version = "0.1.1", features = ["full"] }
hyper = { version = "0.14.12", features = ["full"] }
serde_yaml = "0.8.21"
warp = "0.3.1"
argon2 = { version = "0.3.1", features = ["password-hash"] }
mas-config = { path = "../config" }
mas-core = { path = "../core" }
[dev-dependencies]
indoc = "1.0.3"

View File

@ -13,11 +13,11 @@
// limitations under the License. // limitations under the License.
use clap::Clap; use clap::Clap;
use mas_config::{ConfigurationSection, RootConfig};
use schemars::schema_for; use schemars::schema_for;
use tracing::info; use tracing::info;
use super::RootCommand; use super::RootCommand;
use crate::config::{ConfigurationSection, RootConfig};
#[derive(Clap, Debug)] #[derive(Clap, Debug)]
pub(super) struct ConfigCommand { pub(super) struct ConfigCommand {

View File

@ -14,9 +14,10 @@
use anyhow::Context; use anyhow::Context;
use clap::Clap; use clap::Clap;
use mas_config::DatabaseConfig;
use mas_core::storage::MIGRATOR;
use super::RootCommand; use super::RootCommand;
use crate::{config::DatabaseConfig, storage::MIGRATOR};
#[derive(Clap, Debug)] #[derive(Clap, Debug)]
pub(super) struct DatabaseCommand { pub(super) struct DatabaseCommand {

View File

@ -12,18 +12,22 @@
// 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.
// Clippy seems confused by clap.rs derive macros #![forbid(unsafe_code)]
#![deny(clippy::all)]
#![warn(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::suspicious_else_formatting)] #![allow(clippy::suspicious_else_formatting)]
use std::path::PathBuf; use std::path::PathBuf;
use anyhow::Context; use anyhow::Context;
use clap::Clap; use clap::Clap;
use mas_config::ConfigurationSection;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
use self::{ use self::{
config::ConfigCommand, database::DatabaseCommand, manage::ManageCommand, server::ServerCommand, config::ConfigCommand, database::DatabaseCommand, manage::ManageCommand, server::ServerCommand,
}; };
use crate::config::ConfigurationSection;
mod config; mod config;
mod database; mod database;
@ -46,7 +50,7 @@ enum Subcommand {
} }
#[derive(Clap, Debug)] #[derive(Clap, Debug)]
pub struct RootCommand { struct RootCommand {
/// Path to the configuration file /// Path to the configuration file
#[clap(short, long, global = true, default_value = "config.yaml")] #[clap(short, long, global = true, default_value = "config.yaml")]
config: PathBuf, config: PathBuf,
@ -56,7 +60,7 @@ pub struct RootCommand {
} }
impl RootCommand { impl RootCommand {
pub async fn run(&self) -> anyhow::Result<()> { async fn run(&self) -> anyhow::Result<()> {
use Subcommand as S; use Subcommand as S;
match &self.subcommand { match &self.subcommand {
Some(S::Config(c)) => c.run(self).await, Some(S::Config(c)) => c.run(self).await,
@ -71,3 +75,29 @@ impl RootCommand {
T::load_from_file(&self.config).context("could not load configuration") T::load_from_file(&self.config).context("could not load configuration")
} }
} }
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load environment variables from .env files
if let Err(e) = dotenv::dotenv() {
// Display the error if it is something other than the .env file not existing
if !e.not_found() {
return Err(e).context("could not load .env file");
}
}
// Setup logging & tracing
let fmt_layer = tracing_subscriber::fmt::layer().with_writer(std::io::stderr);
let filter_layer = EnvFilter::try_from_default_env().or_else(|_| EnvFilter::try_new("info"))?;
let subscriber = Registry::default().with(filter_layer).with(fmt_layer);
subscriber
.try_init()
.context("could not initialize logging")?;
// Parse the CLI arguments
let opts = RootCommand::parse();
// And run the command
opts.run().await
}

View File

@ -14,10 +14,11 @@
use argon2::Argon2; use argon2::Argon2;
use clap::Clap; use clap::Clap;
use mas_config::DatabaseConfig;
use mas_core::storage::register_user;
use tracing::{info, warn}; use tracing::{info, warn};
use super::RootCommand; use super::RootCommand;
use crate::{config::DatabaseConfig, storage::register_user};
#[derive(Clap, Debug)] #[derive(Clap, Debug)]
pub(super) struct ManageCommand { pub(super) struct ManageCommand {

View File

@ -20,6 +20,11 @@ use std::{
use anyhow::Context; use anyhow::Context;
use clap::Clap; use clap::Clap;
use hyper::{header, Server}; use hyper::{header, Server};
use mas_config::RootConfig;
use mas_core::{
tasks::{self, TaskQueue},
templates::Templates,
};
use tower::{make::Shared, ServiceBuilder}; use tower::{make::Shared, ServiceBuilder};
use tower_http::{ use tower_http::{
compression::CompressionLayer, compression::CompressionLayer,
@ -29,11 +34,6 @@ use tower_http::{
}; };
use super::RootCommand; use super::RootCommand;
use crate::{
config::RootConfig,
tasks::{self, TaskQueue},
templates::Templates,
};
#[derive(Clap, Debug, Default)] #[derive(Clap, Debug, Default)]
pub(super) struct ServerCommand; pub(super) struct ServerCommand;
@ -52,7 +52,7 @@ impl ServerCommand {
let templates = Templates::load().context("could not load templates")?; let templates = Templates::load().context("could not load templates")?;
// Start the server // Start the server
let root = crate::handlers::root(&pool, &templates, &config); let root = mas_core::handlers::root(&pool, &templates, &config);
let queue = TaskQueue::default(); let queue = TaskQueue::default();
queue.recuring(Duration::from_secs(15), tasks::cleanup_expired(&pool)); queue.recuring(Duration::from_secs(15), tasks::cleanup_expired(&pool));

38
crates/config/Cargo.toml Normal file
View File

@ -0,0 +1,38 @@
[package]
name = "mas-config"
version = "0.1.0"
authors = ["Quentin Gliech <quenting@element.io>"]
edition = "2018"
license = "Apache-2.0"
[dependencies]
tokio = { version = "1.11.0", features = [] }
tracing = "0.1.27"
async-trait = "0.1.51"
thiserror = "1.0.29"
anyhow = "1.0.44"
schemars = { version = "0.8.3", features = ["url", "chrono"] }
figment = { version = "0.10.6", features = ["env", "yaml", "test"] }
chrono = { version = "0.4.19", features = ["serde"] }
url = { version = "2.2.2", features = ["serde"] }
serde = { version = "1.0.130", features = ["derive"] }
serde_with = { version = "1.10.0", features = ["hex", "chrono"] }
serde_json = "1.0.68"
sqlx = { version = "0.5.7", features = ["runtime-tokio-rustls", "postgres"] }
rand = "0.8.4"
rsa = "0.5.0"
k256 = "0.9.6"
pkcs8 = { version = "0.7.6", features = ["pem"] }
elliptic-curve = { version = "0.10.6", features = ["pem"] }
indoc = "1.0.3"
[dependencies.jwt-compact]
# Waiting on the next release because of the bump of the `rsa` dependency
git = "https://github.com/slowli/jwt-compact.git"
rev = "7a6dee6824c1d4e7c7f81019c9a968e5c9e44923"
features = ["rsa", "k256"]

View File

@ -42,4 +42,8 @@ impl ConfigurationSection<'_> for CookiesConfig {
secret: rand::random(), secret: rand::random(),
}) })
} }
fn test() -> Self {
Self { secret: [0xEA; 32] }
}
} }

View File

@ -52,6 +52,10 @@ impl ConfigurationSection<'_> for CsrfConfig {
async fn generate() -> anyhow::Result<Self> { async fn generate() -> anyhow::Result<Self> {
Ok(Self::default()) Ok(Self::default())
} }
fn test() -> Self {
Self::default()
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -19,12 +19,11 @@ use async_trait::async_trait;
use schemars::{gen::SchemaGenerator, schema::Schema, JsonSchema}; use schemars::{gen::SchemaGenerator, schema::Schema, 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::postgres::{PgConnectOptions, PgPool, PgPoolOptions};
postgres::{PgConnectOptions, PgPool, PgPoolOptions},
ConnectOptions,
};
use tracing::log::LevelFilter;
// FIXME
// use sqlx::ConnectOptions
// use tracing::log::LevelFilter;
use super::ConfigurationSection; use super::ConfigurationSection;
fn default_uri() -> String { fn default_uri() -> String {
@ -102,15 +101,16 @@ pub struct DatabaseConfig {
impl DatabaseConfig { impl DatabaseConfig {
#[tracing::instrument(err)] #[tracing::instrument(err)]
pub async fn connect(&self) -> anyhow::Result<PgPool> { pub async fn connect(&self) -> anyhow::Result<PgPool> {
let mut options = self let options = self
.uri .uri
.parse::<PgConnectOptions>() .parse::<PgConnectOptions>()
.context("invalid database URL")? .context("invalid database URL")?
.application_name("matrix-authentication-service"); .application_name("matrix-authentication-service");
options // FIXME
.log_statements(LevelFilter::Debug) // options
.log_slow_statements(LevelFilter::Warn, Duration::from_millis(100)); // .log_statements(LevelFilter::Debug)
// .log_slow_statements(LevelFilter::Warn, Duration::from_millis(100));
PgPoolOptions::new() PgPoolOptions::new()
.max_connections(self.max_connections) .max_connections(self.max_connections)
@ -133,6 +133,10 @@ impl ConfigurationSection<'_> for DatabaseConfig {
async fn generate() -> anyhow::Result<Self> { async fn generate() -> anyhow::Result<Self> {
Ok(Self::default()) Ok(Self::default())
} }
fn test() -> Self {
Self::default()
}
} }
#[cfg(test)] #[cfg(test)]

View File

@ -45,4 +45,8 @@ impl ConfigurationSection<'_> for HttpConfig {
async fn generate() -> anyhow::Result<Self> { async fn generate() -> anyhow::Result<Self> {
Ok(Self::default()) Ok(Self::default())
} }
fn test() -> Self {
Self::default()
}
} }

View File

@ -63,4 +63,14 @@ impl ConfigurationSection<'_> for RootConfig {
csrf: CsrfConfig::generate().await?, csrf: CsrfConfig::generate().await?,
}) })
} }
fn test() -> Self {
Self {
oauth2: OAuth2Config::test(),
http: HttpConfig::test(),
database: DatabaseConfig::test(),
cookies: CookiesConfig::test(),
csrf: CsrfConfig::test(),
}
}
} }

View File

@ -341,9 +341,49 @@ impl OAuth2Config {
.join(".well-known/openid-configuration") .join(".well-known/openid-configuration")
.expect("could not build discovery url") .expect("could not build discovery url")
} }
}
#[cfg(test)] #[async_trait]
pub fn test() -> Self { impl ConfigurationSection<'_> for OAuth2Config {
fn path() -> &'static str {
"oauth2"
}
#[tracing::instrument]
async fn generate() -> anyhow::Result<Self> {
info!("Generating keys...");
let span = tracing::info_span!("rsa");
let rsa_key = task::spawn_blocking(move || {
let _entered = span.enter();
let mut rng = rand::thread_rng();
let ret =
RsaPrivateKey::new(&mut rng, 2048).context("could not generate RSA private key");
info!("Done generating RSA key");
ret
})
.await
.context("could not join blocking task")??;
let span = tracing::info_span!("ecdsa");
let ecdsa_key = task::spawn_blocking(move || {
let _entered = span.enter();
let rng = rand::thread_rng();
let ret = k256::SecretKey::random(rng);
info!("Done generating ECDSA key");
ret
})
.await
.context("could not join blocking task")?;
Ok(Self {
issuer: default_oauth2_issuer(),
clients: Vec::new(),
keys: KeySet(vec![Key::from_rsa(rsa_key), Key::from_ecdsa(ecdsa_key)]),
})
}
fn test() -> Self {
let rsa_key = Key::from_rsa_pem(indoc::indoc! {r#" let rsa_key = Key::from_rsa_pem(indoc::indoc! {r#"
-----BEGIN PRIVATE KEY----- -----BEGIN PRIVATE KEY-----
MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAymS2RkeIZo7pUeEN MIIBVQIBADANBgkqhkiG9w0BAQEFAASCAT8wggE7AgEAAkEAymS2RkeIZo7pUeEN
@ -374,47 +414,6 @@ impl OAuth2Config {
} }
} }
#[async_trait]
impl ConfigurationSection<'_> for OAuth2Config {
fn path() -> &'static str {
"oauth2"
}
#[tracing::instrument]
async fn generate() -> anyhow::Result<Self> {
info!("Generating keys...");
let span = tracing::info_span!("rsa");
let rsa_key = task::spawn_blocking(move || {
let _entered = span.enter();
let mut rng = rand::thread_rng();
let ret =
RsaPrivateKey::new(&mut rng, 2048).context("could not generate RSA private key");
info!("Done generating RSA key");
ret
});
let span = tracing::info_span!("ecdsa");
let ecdsa_key = task::spawn_blocking(move || {
let _entered = span.enter();
let rng = rand::thread_rng();
let ret = k256::SecretKey::random(rng);
info!("Done generating ECDSA key");
ret
});
let (ecdsa_key, rsa_key) = tokio::join!(ecdsa_key, rsa_key);
let rsa_key = rsa_key.context("could not join blocking task")??;
let ecdsa_key = ecdsa_key.context("could not join blocking task")?;
Ok(Self {
issuer: default_oauth2_issuer(),
clients: Vec::new(),
keys: KeySet(vec![Key::from_rsa(rsa_key), Key::from_ecdsa(ecdsa_key)]),
})
}
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use figment::Jail; use figment::Jail;

View File

@ -66,4 +66,7 @@ pub trait ConfigurationSection<'a>: Sized + Deserialize<'a> + Serialize {
.merge(Yaml::file(path)) .merge(Yaml::file(path))
.extract_inner(Self::path()) .extract_inner(Self::path())
} }
/// Generate config used in unit tests
fn test() -> Self;
} }

View File

@ -1,5 +1,5 @@
[package] [package]
name = "matrix-authentication-service" name = "mas-core"
version = "0.1.0" version = "0.1.0"
authors = ["Quentin Gliech <quenting@element.io>"] authors = ["Quentin Gliech <quenting@element.io>"]
edition = "2018" edition = "2018"
@ -14,7 +14,6 @@ futures-util = "0.3.17"
# Logging and tracing # Logging and tracing
tracing = "0.1.27" tracing = "0.1.27"
tracing-subscriber = "0.2.22"
# Error management # Error management
thiserror = "1.0.29" thiserror = "1.0.29"
@ -22,8 +21,6 @@ anyhow = "1.0.44"
# Web server # Web server
warp = "0.3.1" warp = "0.3.1"
tower = { version = "0.4.8", features = ["full"] }
tower-http = { version = "0.1.1", features = ["full"] }
hyper = { version = "0.14.12", features = ["full"] } hyper = { version = "0.14.12", features = ["full"] }
# Template engine # Template engine
@ -40,10 +37,8 @@ serde_json = "1.0.68"
serde_urlencoded = "0.7.0" serde_urlencoded = "0.7.0"
# Argument & config parsing # Argument & config parsing
clap = "3.0.0-beta.4"
figment = { version = "0.10.6", features = ["env", "yaml", "test"] } figment = { version = "0.10.6", features = ["env", "yaml", "test"] }
schemars = { version = "0.8.3", features = ["url", "chrono"] } schemars = { version = "0.8.3", features = ["url", "chrono"] }
dotenv = "0.15.0"
# Password hashing # Password hashing
argon2 = { version = "0.3.1", features = ["password-hash"] } argon2 = { version = "0.3.1", features = ["password-hash"] }
@ -70,6 +65,7 @@ headers = "0.3.4"
cookie = "0.15.1" cookie = "0.15.1"
oauth2-types = { path = "../oauth2-types", features = ["sqlx_type"] } oauth2-types = { path = "../oauth2-types", features = ["sqlx_type"] }
mas-config = { path = "../config" }
[dependencies.jwt-compact] [dependencies.jwt-compact]
# Waiting on the next release because of the bump of the `rsa` dependency # Waiting on the next release because of the bump of the `rsa` dependency

View File

@ -93,7 +93,7 @@ pub struct ErroredForm<FieldType> {
} }
impl<T> ErroredForm<T> { impl<T> ErroredForm<T> {
pub fn new() -> Self { #[must_use] pub fn new() -> Self {
Self { Self {
form: Vec::new(), form: Vec::new(),
fields: Vec::new(), fields: Vec::new(),

View File

@ -28,11 +28,13 @@ pub enum ClientAuthentication {
} }
impl ClientAuthentication { impl ClientAuthentication {
#[must_use]
pub fn public(&self) -> bool { pub fn public(&self) -> bool {
matches!(self, &Self::None) matches!(self, &Self::None)
} }
} }
#[must_use]
pub fn with_client_auth<T: DeserializeOwned + Send + 'static>( pub fn with_client_auth<T: DeserializeOwned + Send + 'static>(
oauth2_config: &OAuth2Config, oauth2_config: &OAuth2Config,
) -> impl Filter<Extract = (ClientAuthentication, OAuth2ClientConfig, T), Error = Rejection> ) -> impl Filter<Extract = (ClientAuthentication, OAuth2ClientConfig, T), Error = Rejection>
@ -132,6 +134,8 @@ struct ClientAuthForm<T> {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use mas_config::ConfigurationSection;
use super::*; use super::*;
fn oauth2_config() -> OAuth2Config { fn oauth2_config() -> OAuth2Config {

View File

@ -69,7 +69,7 @@ impl EncryptedCookie {
} }
} }
pub fn maybe_encrypted<T>( #[must_use] pub fn maybe_encrypted<T>(
options: &CookiesConfig, options: &CookiesConfig,
) -> impl Filter<Extract = (Option<T>,), Error = Infallible> + Clone + Send + Sync + 'static ) -> impl Filter<Extract = (Option<T>,), Error = Infallible> + Clone + Send + Sync + 'static
where where
@ -83,7 +83,7 @@ where
}) })
} }
pub fn encrypted<T>( #[must_use] pub fn encrypted<T>(
options: &CookiesConfig, options: &CookiesConfig,
) -> impl Filter<Extract = (T,), Error = Rejection> + Clone + Send + Sync + 'static ) -> impl Filter<Extract = (T,), Error = Rejection> + Clone + Send + Sync + 'static
where where
@ -97,7 +97,7 @@ where
}) })
} }
pub fn with_cookie_saver( #[must_use] pub fn with_cookie_saver(
options: &CookiesConfig, options: &CookiesConfig,
) -> impl Filter<Extract = (EncryptedCookieSaver,), Error = Infallible> + Clone + Send + Sync + 'static ) -> impl Filter<Extract = (EncryptedCookieSaver,), Error = Infallible> + Clone + Send + Sync + 'static
{ {

View File

@ -66,7 +66,7 @@ impl CsrfToken {
} }
/// Get the value to include in HTML forms /// Get the value to include in HTML forms
pub fn form_value(&self) -> String { #[must_use] pub fn form_value(&self) -> String {
BASE64URL_NOPAD.encode(&self.token[..]) BASE64URL_NOPAD.encode(&self.token[..])
} }
@ -112,7 +112,7 @@ impl<T> CsrfForm<T> {
} }
} }
pub fn csrf_token( #[must_use] pub fn csrf_token(
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
) -> impl Filter<Extract = (CsrfToken,), Error = Rejection> + Clone + Send + Sync + 'static { ) -> impl Filter<Extract = (CsrfToken,), Error = Rejection> + Clone + Send + Sync + 'static {
super::cookies::encrypted(cookies_config).and_then(move |token: CsrfToken| async move { super::cookies::encrypted(cookies_config).and_then(move |token: CsrfToken| async move {
@ -121,7 +121,7 @@ pub fn csrf_token(
}) })
} }
pub fn updated_csrf_token( #[must_use] pub fn updated_csrf_token(
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
csrf_config: &CsrfConfig, csrf_config: &CsrfConfig,
) -> impl Filter<Extract = (CsrfToken,), Error = Rejection> + Clone + Send + Sync + 'static { ) -> impl Filter<Extract = (CsrfToken,), Error = Rejection> + Clone + Send + Sync + 'static {
@ -144,7 +144,7 @@ pub fn updated_csrf_token(
) )
} }
pub fn protected_form<T>( #[must_use] pub fn protected_form<T>(
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
) -> impl Filter<Extract = (T,), Error = Rejection> + Clone + Send + Sync + 'static ) -> impl Filter<Extract = (T,), Error = Rejection> + Clone + Send + Sync + 'static
where where

View File

@ -33,14 +33,14 @@ use crate::{
templates::Templates, templates::Templates,
}; };
pub fn with_templates( #[must_use] pub fn with_templates(
templates: &Templates, templates: &Templates,
) -> impl Filter<Extract = (Templates,), Error = Infallible> + Clone + Send + Sync + 'static { ) -> impl Filter<Extract = (Templates,), Error = Infallible> + Clone + Send + Sync + 'static {
let templates = templates.clone(); let templates = templates.clone();
warp::any().map(move || templates.clone()) warp::any().map(move || templates.clone())
} }
pub fn with_keys( #[must_use] pub fn with_keys(
oauth2_config: &OAuth2Config, oauth2_config: &OAuth2Config,
) -> impl Filter<Extract = (KeySet,), Error = Infallible> + Clone + Send + Sync + 'static { ) -> impl Filter<Extract = (KeySet,), Error = Infallible> + Clone + Send + Sync + 'static {
let keyset = oauth2_config.keys.clone(); let keyset = oauth2_config.keys.clone();

View File

@ -32,7 +32,7 @@ pub struct SessionCookie {
} }
impl SessionCookie { impl SessionCookie {
pub fn from_session_info(info: &SessionInfo) -> Self { #[must_use] pub fn from_session_info(info: &SessionInfo) -> Self {
Self { Self {
current: info.key(), current: info.key(),
} }
@ -52,7 +52,7 @@ impl EncryptableCookieValue for SessionCookie {
} }
} }
pub fn with_optional_session( #[must_use] pub fn with_optional_session(
pool: &PgPool, pool: &PgPool,
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
) -> impl Filter<Extract = (Option<SessionInfo>,), Error = Rejection> + Clone + Send + Sync + 'static ) -> impl Filter<Extract = (Option<SessionInfo>,), Error = Rejection> + Clone + Send + Sync + 'static
@ -71,7 +71,7 @@ pub fn with_optional_session(
) )
} }
pub fn with_session( #[must_use] pub fn with_session(
pool: &PgPool, pool: &PgPool,
cookies_config: &CookiesConfig, cookies_config: &CookiesConfig,
) -> impl Filter<Extract = (SessionInfo,), Error = Rejection> + Clone + Send + Sync + 'static { ) -> impl Filter<Extract = (SessionInfo,), Error = Rejection> + Clone + Send + Sync + 'static {

View File

@ -25,7 +25,7 @@ mod views;
use self::{health::filter as health, oauth2::filter as oauth2, views::filter as views}; use self::{health::filter as health, oauth2::filter as oauth2, views::filter as views};
pub fn root( #[must_use] pub fn root(
pool: &PgPool, pool: &PgPool,
templates: &Templates, templates: &Templates,
config: &RootConfig, config: &RootConfig,

31
crates/core/src/lib.rs Normal file
View File

@ -0,0 +1,31 @@
// Copyright 2021 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.
#![forbid(unsafe_code)]
#![deny(clippy::all)]
#![warn(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
#![allow(clippy::missing_panics_doc)]
#![allow(clippy::missing_errors_doc)]
#![allow(clippy::implicit_hasher)]
pub(crate) use mas_config as config;
pub mod errors;
pub mod filters;
pub mod handlers;
pub mod storage;
pub mod tasks;
pub mod templates;
pub mod tokens;

View File

@ -67,7 +67,7 @@ pub struct OAuth2AccessTokenLookup {
} }
impl OAuth2AccessTokenLookup { impl OAuth2AccessTokenLookup {
pub fn exp(&self) -> DateTime<Utc> { #[must_use] pub fn exp(&self) -> DateTime<Utc> {
self.created_at + Duration::seconds(i64::from(self.expires_after)) self.created_at + Duration::seconds(i64::from(self.expires_after))
} }
} }

View File

@ -103,7 +103,7 @@ impl OAuth2Session {
} }
} }
pub fn max_auth_time(&self) -> Option<DateTime<Utc>> { #[must_use] pub fn max_auth_time(&self) -> Option<DateTime<Utc>> {
self.max_age self.max_age
.map(|d| Duration::seconds(i64::from(d))) .map(|d| Duration::seconds(i64::from(d)))
.map(|d| self.created_at - d) .map(|d| self.created_at - d)

View File

@ -44,7 +44,7 @@ pub struct SessionInfo {
} }
impl SessionInfo { impl SessionInfo {
pub fn key(&self) -> i64 { #[must_use] pub fn key(&self) -> i64 {
self.id self.id
} }

View File

@ -38,6 +38,6 @@ impl Task for CleanupExpired {
} }
} }
pub fn cleanup_expired(pool: &Pool<Postgres>) -> impl Task + Clone { #[must_use] pub fn cleanup_expired(pool: &Pool<Postgres>) -> impl Task + Clone {
CleanupExpired(pool.clone()) CleanupExpired(pool.clone())
} }

View File

@ -212,7 +212,7 @@ pub struct IndexContext {
} }
impl IndexContext { impl IndexContext {
pub fn new(discovery_url: Url) -> Self { #[must_use] pub fn new(discovery_url: Url) -> Self {
Self { discovery_url } Self { discovery_url }
} }
} }
@ -230,7 +230,7 @@ pub struct LoginContext {
} }
impl LoginContext { impl LoginContext {
pub fn with_form_error(form: ErroredForm<LoginFormField>) -> Self { #[must_use] pub fn with_form_error(form: ErroredForm<LoginFormField>) -> Self {
Self { form } Self { form }
} }
} }
@ -267,22 +267,22 @@ pub struct ErrorContext {
} }
impl ErrorContext { impl ErrorContext {
pub fn new() -> Self { #[must_use] pub fn new() -> Self {
Self::default() Self::default()
} }
pub fn with_code(mut self, code: &'static str) -> Self { #[must_use] pub fn with_code(mut self, code: &'static str) -> Self {
self.code = Some(code); self.code = Some(code);
self self
} }
pub fn with_description(mut self, description: String) -> Self { #[must_use] pub fn with_description(mut self, description: String) -> Self {
self.description = Some(description); self.description = Some(description);
self self
} }
#[allow(dead_code)] #[allow(dead_code)]
pub fn with_details(mut self, details: String) -> Self { #[must_use] pub fn with_details(mut self, details: String) -> Self {
self.details = Some(details); self.details = Some(details);
self self
} }

View File

@ -1,60 +0,0 @@
// Copyright 2021 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.
#![forbid(unsafe_code)]
#![deny(clippy::all)]
#![warn(clippy::pedantic)]
#![allow(clippy::module_name_repetitions)]
use anyhow::Context;
use clap::Clap;
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
mod cli;
mod config;
mod errors;
mod filters;
mod handlers;
mod storage;
mod tasks;
mod templates;
mod tokens;
use self::cli::RootCommand;
#[tokio::main]
async fn main() -> anyhow::Result<()> {
// Load environment variables from .env files
if let Err(e) = dotenv::dotenv() {
// Display the error if it is something other than the .env file not existing
if !e.not_found() {
return Err(e).context("could not load .env file");
}
}
// Setup logging & tracing
let fmt_layer = tracing_subscriber::fmt::layer().with_writer(std::io::stderr);
let filter_layer = EnvFilter::try_from_default_env().or_else(|_| EnvFilter::try_new("info"))?;
let subscriber = Registry::default().with(filter_layer).with(fmt_layer);
subscriber
.try_init()
.context("could not initialize logging")?;
// Parse the CLI arguments
let opts = RootCommand::parse();
// And run the command
opts.run().await
}