diff --git a/Cargo.lock b/Cargo.lock index c04ab28..4fcb01b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -103,9 +103,9 @@ checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a" [[package]] name = "axum" -version = "0.4.4" +version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "310a147401c66e79fc78636e4db63ac68cd6acb9ece056de806ea173a15bce32" +checksum = "1dbbc81d15ddf33148615b778836b525dbae4e0731710294b2c484e80c4858f7" dependencies = [ "async-trait", "axum-core", @@ -1053,8 +1053,10 @@ dependencies = [ "once_cell", "quoted_printable", "regex", + "serde", "tokio", "tokio-native-tls", + "tracing", ] [[package]] @@ -1403,9 +1405,9 @@ dependencies = [ [[package]] name = "oso" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3130188f8467dd6d904e692f3d31a3aa6e63902449a1b02d056e8e2633b863d1" +checksum = "c527b33eaa5534f8a486051827f963dbc73de1a787b90e32adccce625613ca8c" dependencies = [ "impl-trait-for-tuples", "lazy_static", @@ -1420,9 +1422,9 @@ dependencies = [ [[package]] name = "oso-derive" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b03f10ad1ba555f82c0ccfe497010beccd873651604cb69999ba8c570d312fda" +checksum = "b6d027db8ed6ca7b074615f9c9dff2b4f01c352448c2d54b82087575ee5a58e8" dependencies = [ "quote", "syn", @@ -1567,9 +1569,9 @@ checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe" [[package]] name = "polar-core" -version = "0.25.1" +version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b469dacc2c69faed6b71972dac8fb9fbddf18ae22ecce48f97a4643e58860fa3" +checksum = "fc2a1ed15352e3d6ca0575212741cbcd8db208bcca90ed8c92fb1c292f08f9d4" dependencies = [ "indoc", "js-sys", @@ -1577,6 +1579,7 @@ dependencies = [ "lalrpop-util", "serde", "serde_derive", + "strum_macros", "wasm-bindgen", ] @@ -1914,18 +1917,18 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2cf9235533494ea2ddcdb794665461814781c53f19d87b76e571a1c35acbad2b" +checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.135" +version = "1.0.136" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8dcde03d87d4c973c04be249e7d8f0b35db1c848c487bd43032808e59dd8328d" +checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9" dependencies = [ "proc-macro2", "quote", @@ -2179,6 +2182,19 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "strum_macros" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5bb0dc7ee9c15cea6199cde9a127fa16a4c5819af85395457ad72d68edc85a38" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn", +] + [[package]] name = "subtle" version = "2.4.1" @@ -2377,9 +2393,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c" [[package]] name = "tokio" -version = "1.15.0" +version = "1.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838" +checksum = "0c27a64b625de6d309e8c57716ba93021dccf1b3b5c97edd6d3dd2d2135afc0a" dependencies = [ "bytes", "libc", @@ -2458,9 +2474,9 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03650267ad175b51c47d02ed9547fc7d4ba2c7e5cb76df0bed67edd1825ae297" +checksum = "33ed53e1fd082268841975cb39587fa9fca9a7a30da84f97818ef667c97f2872" dependencies = [ "bitflags", "bytes", @@ -2489,9 +2505,9 @@ checksum = "360dfd1d6d30e05fda32ace2c8c70e9c0a9da713275777f5a4dbb8a1893930c6" [[package]] name = "tracing" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "375a639232caf30edfc78e8d89b2d4c375515393e7af7e16f01cd96917fb2105" +checksum = "2d8d93354fe2a8e50d5953f5ae2e47a3fc2ef03292e7ea46e3cc38f549525fb9" dependencies = [ "cfg-if", "log", @@ -2502,9 +2518,9 @@ dependencies = [ [[package]] name = "tracing-attributes" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4f480b8f81512e825f337ad51e94c1eb5d3bbdf2b363dcd01e2b19a9ffe3f8e" +checksum = "8276d9a4a3a558d7b7ad5303ad50b53d58264641b82914b7ada36bd762e7a716" dependencies = [ "proc-macro2", "quote", @@ -2513,11 +2529,12 @@ dependencies = [ [[package]] name = "tracing-core" -version = "0.1.21" +version = "0.1.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f4ed65637b8390770814083d20756f87bfa2c21bf2f110babdc5438351746e4" +checksum = "03cfcb51380632a72d3111cb8d3447a8d908e577d31beeac006f836383d29a23" dependencies = [ "lazy_static", + "valuable", ] [[package]] @@ -2533,9 +2550,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.6" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77be66445c4eeebb934a7340f227bfe7b338173d3f8c00a60a5a58005c9faecf" +checksum = "74786ce43333fcf51efe947aed9718fbe46d5c7328ec3f1029e818083966d9aa" dependencies = [ "ansi_term", "sharded-slab", @@ -2689,6 +2706,12 @@ dependencies = [ "syn", ] +[[package]] +name = "valuable" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" + [[package]] name = "vcpkg" version = "0.2.15" diff --git a/Cargo.toml b/Cargo.toml index 7a59034..dc0b27e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ edition = "2018" [dependencies] anyhow = "1.0.53" -axum = "0.4.4" +axum = "0.4.5" bincode = "1.3.3" chrono = "0.4.19" dotenv = "0.15.0" @@ -16,8 +16,14 @@ http = "0.2.6" http-body = "0.4.4" hyper = { version = "0.14.16", features = ["full"] } lazy_static = "1.4.0" +lettre = { version = "0.10.0-rc.4", features = [ + "tokio1", + "tokio1-native-tls", + "tracing", + "serde", +] } libreauth = "0.14.1" -oso = { version = "0.25.1", features = ["derive", "uuid-07"] } +oso = { version = "0.26.0", features = ["derive", "uuid-07"] } oxide-auth = "0.5.1" oxide-auth-axum = "0.1.0" rand = "0.8.4" @@ -27,7 +33,7 @@ redis = { version = "0.21.5", features = [ "connection-manager", ], default-features = false } regex = "1.5.4" -serde = { version = "1.0.135", features = ["derive"] } +serde = { version = "1.0.136", features = ["derive"] } serde_json = "1.0.78" sqlx = { version = "0.5.10", features = [ "runtime-tokio-native-tls", @@ -39,22 +45,11 @@ sqlx = { version = "0.5.10", features = [ ] } tantivy = "0.16.1" thiserror = "1.0.30" -tokio = { version = "1.15.0", features = ["rt-multi-thread", "macros", "sync"] } +tokio = { version = "1.16.1", features = ["rt-multi-thread", "macros", "sync"] } tower = "0.4.11" -tower-http = { version = "0.2.1", features = ["add-extension", "trace"] } -tracing = "0.1.29" -tracing-subscriber = "0.3.6" +tower-http = { version = "0.2.2", features = ["add-extension", "trace"] } +tracing = "0.1.30" +tracing-subscriber = "0.3.8" ulid = { version = "0.5.0", features = ["serde", "uuid"] } uuid = { version = "0.8.2", features = ["serde", "v4"] } validator = { version = "0.14.0", features = ["derive"] } - -[dependencies.lettre] -version = "0.10.0-rc.4" -features = [ - "builder", - "hostname", - "tokio1", - "smtp-transport", - "tokio1-native-tls", -] -default-features = false diff --git a/src/error.rs b/src/error.rs index 8582f8e..9656705 100644 --- a/src/error.rs +++ b/src/error.rs @@ -5,7 +5,7 @@ use axum::{ use thiserror::Error; // dost thou know of the pepeloni -const INTERNAL_SERVER_ERROR_MESSAGE: &str = "aah the pepeloni"; +const INTERNAL_SERVER_ERROR_MESSAGE: &str = "ahh the pepeloni"; /// Any possible errors #[derive(Debug, Error)] diff --git a/src/handlers/user.rs b/src/handlers/user.rs index b63ed34..76a00ff 100644 --- a/src/handlers/user.rs +++ b/src/handlers/user.rs @@ -1,7 +1,6 @@ use anyhow::format_err; use axum::{body::Body, extract::Extension}; use http::{Response, StatusCode}; -use lettre::{Message, Transport}; use redis::AsyncCommands; use serde::Deserialize; use std::sync::Arc; @@ -14,7 +13,7 @@ use crate::error::MixiniError; use crate::handlers::{ValidatedForm, RE_PASSWORD, RE_USERNAME}; use crate::models::User; use crate::server::State; -use crate::utils::{generate_redis_key, pass::HASHER}; +use crate::utils::{generate_redis_key, mail::send_email_verification_request, pass::HASHER}; const VERIFY_KEY_PREFIX: &str = "verify:"; const VERIFY_EXPIRY_SECONDS: usize = 86400; @@ -138,21 +137,7 @@ pub(crate) async fn create_verify_entry( .set_ex(&key, user.id.to_string(), VERIFY_EXPIRY_SECONDS) .await?; - let mail = Message::builder() - .from( - std::env::var("SMTP_EMAIL") - .unwrap() - .parse() - .expect("SMTP_EMAIL key is invalid"), - ) - .to(user.email.parse().unwrap()) - .subject("Your Mixini email verification") - .body(format!( - "Your Mixini verification key is {}. Note that it will expire in 24 hours.", - key - ))?; - - state.mailer.send(&mail)?; + send_email_verification_request(&state.mailsender, user.email, key).await?; Ok(Response::builder() .status(StatusCode::OK) diff --git a/src/models/user.rs b/src/models/user.rs index 90a07c4..04ddab9 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -9,8 +9,8 @@ pub(crate) struct User { pub(crate) created_at: DateTime, pub(crate) updated_at: DateTime, pub(crate) name: String, - /// The password in hashed PHC form, as represented in the database pub(crate) email: String, + /// The password in hashed PHC form, as represented in the database pub(crate) password: String, #[polar(attribute)] pub(crate) verified: bool, diff --git a/src/server.rs b/src/server.rs index 42dc3af..f7b53bf 100644 --- a/src/server.rs +++ b/src/server.rs @@ -5,7 +5,7 @@ use axum::{ }; use lettre::{ transport::smtp::authentication::{Credentials, Mechanism}, - SmtpTransport, + AsyncSmtpTransport, Tokio1Executor, }; use oso::{Oso, PolarClass}; use sqlx::PgPool; @@ -21,7 +21,7 @@ pub(crate) struct State { pub(crate) oso: Arc>, pub(crate) db_pool: PgPool, pub(crate) redis_manager: redis::aio::ConnectionManager, - pub(crate) mailer: SmtpTransport, + pub(crate) mailsender: AsyncSmtpTransport, } impl State { @@ -32,21 +32,22 @@ impl State { let redis_manager = redis::Client::open(std::env::var("REDIS_URL")?)? .get_tokio_connection_manager() .await?; - let mailer = SmtpTransport::starttls_relay(&std::env::var("SMTP_SERVER")?)? - // Add credentials for authentication - .credentials(Credentials::new( - std::env::var("SMTP_USERNAME")?, - std::env::var("SMTP_PASSWORD")?, - )) - // Configure expected authentication mechanism - .authentication(vec![Mechanism::Plain]) - .build(); + let mailsender = + AsyncSmtpTransport::::relay(&std::env::var("SMTP_SERVER")?)? + // Add credentials for authentication + .credentials(Credentials::new( + std::env::var("SMTP_USERNAME")?, + std::env::var("SMTP_PASSWORD")?, + )) + // Configure expected authentication mechanism + .authentication(vec![Mechanism::Plain]) + .build(); Ok(State { oso, db_pool, redis_manager, - mailer, + mailsender, }) } } diff --git a/src/utils/mail.rs b/src/utils/mail.rs new file mode 100644 index 0000000..d2d28fc --- /dev/null +++ b/src/utils/mail.rs @@ -0,0 +1,34 @@ +//! Handles email stuff. + +use lazy_static::lazy_static; +use lettre::{ + message::Mailbox, transport::smtp::response::Response, AsyncSmtpTransport, AsyncTransport, + Message, Tokio1Executor, +}; + +use crate::error::MixiniError; + +lazy_static! { + pub(crate) static ref SMTP_EMAIL: Mailbox = std::env::var("SMTP_EMAIL") + .unwrap() + .parse() + .expect("SMTP_EMAIL key is invalid"); +} + +pub(crate) async fn send_email_verification_request( + mailsender: &AsyncSmtpTransport, + email: String, + key: String, +) -> Result { + let email = email.parse().expect("somehow not verified?"); + let mail = Message::builder() + .from(SMTP_EMAIL.clone()) + .to(email) + .subject("Your Mixini email verification") + .body(format!( + "Your Mixini verification key is {}. Note that it will expire in 24 hours.", + key + ))?; + + Ok(mailsender.send(mail).await?) +} diff --git a/src/utils/mod.rs b/src/utils/mod.rs index b5e3def..1055379 100644 --- a/src/utils/mod.rs +++ b/src/utils/mod.rs @@ -2,6 +2,7 @@ use rand::distributions::Alphanumeric; use rand::{thread_rng, Rng}; +pub(crate) mod mail; pub(crate) mod pass; /// Generate random key for use in Redis given the prefix diff --git a/src/utils/pass.rs b/src/utils/pass.rs index 422042b..059e3f5 100644 --- a/src/utils/pass.rs +++ b/src/utils/pass.rs @@ -8,7 +8,7 @@ pub(crate) const PWD_SCHEME_VERSION: usize = 1; // If the Hasher changes, make sure to increment PWD_SCHEME_VERSION lazy_static! { - pub static ref HASHER: Hasher = { + pub(crate) static ref HASHER: Hasher = { HashBuilder::new() .algorithm(PWD_ALGORITHM) .version(PWD_SCHEME_VERSION)