You've already forked authentication-service
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:
27
crates/cli/Cargo.toml
Normal file
27
crates/cli/Cargo.toml
Normal 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"
|
75
crates/cli/src/config.rs
Normal file
75
crates/cli/src/config.rs
Normal file
@ -0,0 +1,75 @@
|
||||
// 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.
|
||||
|
||||
use clap::Clap;
|
||||
use mas_config::{ConfigurationSection, RootConfig};
|
||||
use schemars::schema_for;
|
||||
use tracing::info;
|
||||
|
||||
use super::RootCommand;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub(super) struct ConfigCommand {
|
||||
#[clap(subcommand)]
|
||||
subcommand: ConfigSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum ConfigSubcommand {
|
||||
/// Dump the current config as YAML
|
||||
Dump,
|
||||
|
||||
/// Print the JSON Schema that validates configuration files
|
||||
Schema,
|
||||
|
||||
/// Check a config file
|
||||
Check,
|
||||
|
||||
/// Generate a new config file
|
||||
Generate,
|
||||
}
|
||||
|
||||
impl ConfigCommand {
|
||||
pub async fn run(&self, root: &RootCommand) -> anyhow::Result<()> {
|
||||
use ConfigSubcommand as SC;
|
||||
match &self.subcommand {
|
||||
SC::Dump => {
|
||||
let config: RootConfig = root.load_config()?;
|
||||
|
||||
serde_yaml::to_writer(std::io::stdout(), &config)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
SC::Schema => {
|
||||
let schema = schema_for!(RootConfig);
|
||||
|
||||
serde_yaml::to_writer(std::io::stdout(), &schema)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
SC::Check => {
|
||||
let _config: RootConfig = root.load_config()?;
|
||||
info!(path = ?root.config, "Configuration file looks good");
|
||||
Ok(())
|
||||
}
|
||||
SC::Generate => {
|
||||
let config = RootConfig::load_and_generate().await?;
|
||||
|
||||
serde_yaml::to_writer(std::io::stdout(), &config)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
47
crates/cli/src/database.rs
Normal file
47
crates/cli/src/database.rs
Normal file
@ -0,0 +1,47 @@
|
||||
// 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.
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::Clap;
|
||||
use mas_config::DatabaseConfig;
|
||||
use mas_core::storage::MIGRATOR;
|
||||
|
||||
use super::RootCommand;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub(super) struct DatabaseCommand {
|
||||
#[clap(subcommand)]
|
||||
subcommand: DatabaseSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum DatabaseSubcommand {
|
||||
/// Run database migrations
|
||||
Migrate,
|
||||
}
|
||||
|
||||
impl DatabaseCommand {
|
||||
pub async fn run(&self, root: &RootCommand) -> anyhow::Result<()> {
|
||||
let config: DatabaseConfig = root.load_config()?;
|
||||
let pool = config.connect().await?;
|
||||
|
||||
// Run pending migrations
|
||||
MIGRATOR
|
||||
.run(&pool)
|
||||
.await
|
||||
.context("could not run migrations")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
103
crates/cli/src/main.rs
Normal file
103
crates/cli/src/main.rs
Normal file
@ -0,0 +1,103 @@
|
||||
// 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::suspicious_else_formatting)]
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::Clap;
|
||||
use mas_config::ConfigurationSection;
|
||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt, EnvFilter, Registry};
|
||||
|
||||
use self::{
|
||||
config::ConfigCommand, database::DatabaseCommand, manage::ManageCommand, server::ServerCommand,
|
||||
};
|
||||
|
||||
mod config;
|
||||
mod database;
|
||||
mod manage;
|
||||
mod server;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum Subcommand {
|
||||
/// Configuration-related commands
|
||||
Config(ConfigCommand),
|
||||
|
||||
/// Manage the database
|
||||
Database(DatabaseCommand),
|
||||
|
||||
/// Runs the web server
|
||||
Server(ServerCommand),
|
||||
|
||||
/// Manage the instance
|
||||
Manage(ManageCommand),
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
struct RootCommand {
|
||||
/// Path to the configuration file
|
||||
#[clap(short, long, global = true, default_value = "config.yaml")]
|
||||
config: PathBuf,
|
||||
|
||||
#[clap(subcommand)]
|
||||
subcommand: Option<Subcommand>,
|
||||
}
|
||||
|
||||
impl RootCommand {
|
||||
async fn run(&self) -> anyhow::Result<()> {
|
||||
use Subcommand as S;
|
||||
match &self.subcommand {
|
||||
Some(S::Config(c)) => c.run(self).await,
|
||||
Some(S::Database(c)) => c.run(self).await,
|
||||
Some(S::Server(c)) => c.run(self).await,
|
||||
Some(S::Manage(c)) => c.run(self).await,
|
||||
None => ServerCommand::default().run(self).await,
|
||||
}
|
||||
}
|
||||
|
||||
fn load_config<'de, T: ConfigurationSection<'de>>(&self) -> anyhow::Result<T> {
|
||||
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
|
||||
}
|
59
crates/cli/src/manage.rs
Normal file
59
crates/cli/src/manage.rs
Normal file
@ -0,0 +1,59 @@
|
||||
// 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.
|
||||
|
||||
use argon2::Argon2;
|
||||
use clap::Clap;
|
||||
use mas_config::DatabaseConfig;
|
||||
use mas_core::storage::register_user;
|
||||
use tracing::{info, warn};
|
||||
|
||||
use super::RootCommand;
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
pub(super) struct ManageCommand {
|
||||
#[clap(subcommand)]
|
||||
subcommand: ManageSubcommand,
|
||||
}
|
||||
|
||||
#[derive(Clap, Debug)]
|
||||
enum ManageSubcommand {
|
||||
/// Register a new user
|
||||
Register { username: String, password: String },
|
||||
|
||||
/// List active users
|
||||
Users,
|
||||
}
|
||||
|
||||
impl ManageCommand {
|
||||
pub async fn run(&self, root: &RootCommand) -> anyhow::Result<()> {
|
||||
use ManageSubcommand as SC;
|
||||
match &self.subcommand {
|
||||
SC::Register { username, password } => {
|
||||
let config: DatabaseConfig = root.load_config()?;
|
||||
let pool = config.connect().await?;
|
||||
let hasher = Argon2::default();
|
||||
|
||||
let user = register_user(&pool, hasher, username, password).await?;
|
||||
info!(?user, "User registered");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
SC::Users => {
|
||||
warn!("Not implemented yet");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
93
crates/cli/src/server.rs
Normal file
93
crates/cli/src/server.rs
Normal file
@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
|
||||
use std::{
|
||||
net::{SocketAddr, TcpListener},
|
||||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::Context;
|
||||
use clap::Clap;
|
||||
use hyper::{header, Server};
|
||||
use mas_config::RootConfig;
|
||||
use mas_core::{
|
||||
tasks::{self, TaskQueue},
|
||||
templates::Templates,
|
||||
};
|
||||
use tower::{make::Shared, ServiceBuilder};
|
||||
use tower_http::{
|
||||
compression::CompressionLayer,
|
||||
sensitive_headers::SetSensitiveHeadersLayer,
|
||||
trace::{DefaultMakeSpan, DefaultOnResponse, TraceLayer},
|
||||
LatencyUnit,
|
||||
};
|
||||
|
||||
use super::RootCommand;
|
||||
|
||||
#[derive(Clap, Debug, Default)]
|
||||
pub(super) struct ServerCommand;
|
||||
|
||||
impl ServerCommand {
|
||||
pub async fn run(&self, root: &RootCommand) -> anyhow::Result<()> {
|
||||
let config: RootConfig = root.load_config()?;
|
||||
|
||||
let addr: SocketAddr = config.http.address.parse()?;
|
||||
let listener = TcpListener::bind(addr)?;
|
||||
|
||||
// Connect to the database
|
||||
let pool = config.database.connect().await?;
|
||||
|
||||
// Load and compile the templates
|
||||
let templates = Templates::load().context("could not load templates")?;
|
||||
|
||||
// Start the server
|
||||
let root = mas_core::handlers::root(&pool, &templates, &config);
|
||||
|
||||
let queue = TaskQueue::default();
|
||||
queue.recuring(Duration::from_secs(15), tasks::cleanup_expired(&pool));
|
||||
queue.start();
|
||||
|
||||
let warp_service = warp::service(root);
|
||||
|
||||
let service = ServiceBuilder::new()
|
||||
// Add high level tracing/logging to all requests
|
||||
.layer(
|
||||
TraceLayer::new_for_http()
|
||||
.make_span_with(DefaultMakeSpan::new().include_headers(true))
|
||||
.on_response(
|
||||
DefaultOnResponse::new()
|
||||
.include_headers(true)
|
||||
.latency_unit(LatencyUnit::Micros),
|
||||
),
|
||||
)
|
||||
// Set a timeout
|
||||
.timeout(Duration::from_secs(10))
|
||||
// Compress responses
|
||||
.layer(CompressionLayer::new())
|
||||
// Mark the `Authorization` and `Cookie` headers as sensitive so it doesn't show in logs
|
||||
.layer(SetSensitiveHeadersLayer::new(vec![
|
||||
header::AUTHORIZATION,
|
||||
header::COOKIE,
|
||||
]))
|
||||
.service(warp_service);
|
||||
|
||||
tracing::info!("Listening on http://{}", listener.local_addr().unwrap());
|
||||
|
||||
Server::from_tcp(listener)?
|
||||
.serve(Shared::new(service))
|
||||
.await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user