Compare commits

...
27 changed files with 419 additions and 44 deletions
Generated
+163 -30
View File
@@ -1086,7 +1086,7 @@ dependencies = [
name = "buckets"
version = "0.0.1"
dependencies = [
"crockford",
"crockford_deprecated",
"errors",
]
@@ -1104,9 +1104,9 @@ checksum = "aaa3a8d9a1ca92e282c96a32d6511b695d7d994d1d102ba85d279f9b2756947f"
[[package]]
name = "byteorder"
version = "1.4.3"
version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b"
[[package]]
name = "bytes"
@@ -1222,6 +1222,16 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "chumsky"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23170228b96236b5a7299057ac284a321457700bc8c41a4476052f0f4ba5349d"
dependencies = [
"hashbrown",
"stacker",
]
[[package]]
name = "cidr-utils"
version = "0.5.11"
@@ -1517,6 +1527,12 @@ dependencies = [
[[package]]
name = "crockford"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4a39e5d3a228d65251f96d188da6fb36f3944137c776953077ecc6f41498b19"
[[package]]
name = "crockford_deprecated"
version = "0.0.1"
dependencies = [
"rand 0.8.5",
@@ -2011,7 +2027,7 @@ dependencies = [
"concurrency",
"config",
"container_common",
"crockford",
"crockford_deprecated",
"dotenv",
"easyenv",
"enums",
@@ -2075,7 +2091,7 @@ dependencies = [
"easyenv",
"errors",
"futures 0.1.31",
"futures 0.3.26",
"futures 0.3.28",
"futures-util",
"hostname",
"http_server_common",
@@ -2113,6 +2129,30 @@ dependencies = [
"serde",
]
[[package]]
name = "email"
version = "0.1.0"
dependencies = [
"errors",
"lettre",
]
[[package]]
name = "email-encoding"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbfb21b9878cf7a348dcb8559109aabc0ec40d69924bd706fa5149846c4fef75"
dependencies = [
"base64 0.21.0",
"memchr",
]
[[package]]
name = "email_address"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2153bd83ebc09db15bcbdc3e2194d901804952e3dc96967e1cd3b0c5c32d112"
[[package]]
name = "encoding_rs"
version = "0.8.28"
@@ -2143,7 +2183,7 @@ dependencies = [
"serde_json",
"sqlx",
"sqlx-core",
"strum",
"strum 0.24.1",
"toml",
]
@@ -2232,6 +2272,12 @@ dependencies = [
"instant",
]
[[package]]
name = "fastrand"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25cbce373ec4653f1a01a31e8a5e5ec0c622dc27ff9c4e6606eefef5cbbed4a5"
[[package]]
name = "fdeflate"
version = "0.3.0"
@@ -2356,9 +2402,9 @@ checksum = "3a471a38ef8ed83cd6e40aa59c1ffe17db6855c18e3604d9c4ed8c08ebc28678"
[[package]]
name = "futures"
version = "0.3.26"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13e2792b0ff0340399d58445b88fd9770e3489eff258a4cbc1523418f12abf84"
checksum = "23342abe12aba583913b2e62f22225ff9c950774065e4bfb61a19cd9770fec40"
dependencies = [
"futures-channel",
"futures-core",
@@ -2387,9 +2433,9 @@ checksum = "4bca583b7e26f571124fe5b7561d49cb2868d79116cfa0eefce955557c6fee8c"
[[package]]
name = "futures-executor"
version = "0.3.26"
version = "0.3.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e8de0a35a6ab97ec8869e32a2473f4b1324459e14c29275d14b10cb1fd19b50e"
checksum = "ccecee823288125bd88b4d7f565c9e58e41858e47ab72e8ea2d64e93624386e0"
dependencies = [
"futures-core",
"futures-task",
@@ -2420,7 +2466,7 @@ version = "1.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7694489acd39452c77daa48516b894c153f192c3578d5a839b62c58099fcbf48"
dependencies = [
"fastrand",
"fastrand 1.7.0",
"futures-core",
"futures-io",
"memchr",
@@ -2637,9 +2683,9 @@ dependencies = [
[[package]]
name = "heck"
version = "0.4.0"
version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2540771e65fc8cb83cd6e8a237f70c319bd5c29f78ed1084ba5d50eeac86f7f9"
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
dependencies = [
"unicode-segmentation",
]
@@ -3239,6 +3285,31 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
[[package]]
name = "lettre"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d47084ad58f99c26816d174702f60e873f861fcef3f9bd6075b4ad2dd72d07d5"
dependencies = [
"base64 0.21.0",
"chumsky",
"email-encoding",
"email_address",
"fastrand 2.0.1",
"futures-util",
"hostname",
"httpdate 1.0.1",
"idna 0.4.0",
"mime",
"native-tls",
"nom",
"once_cell",
"quoted_printable",
"socket2 0.5.5",
"tokio 1.25.0",
"url",
]
[[package]]
name = "lexical-sort"
version = "0.3.1"
@@ -3259,9 +3330,9 @@ dependencies = [
[[package]]
name = "libc"
version = "0.2.141"
version = "0.2.149"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5"
checksum = "a08173bc88b7955d1b3145aa561539096c421ac8debde8cbc3612ec635fee29b"
[[package]]
name = "libm"
@@ -3714,7 +3785,7 @@ dependencies = [
"anyhow",
"env_logger 0.7.1",
"flate2",
"futures 0.3.26",
"futures 0.3.28",
"hyper 0.14.24",
"hyper-tls",
"log",
@@ -4288,6 +4359,15 @@ dependencies = [
"unicode-ident",
]
[[package]]
name = "psm"
version = "0.1.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5787f7cda34e3033a72192c018bc5883100330f362ef279a8cbccfce8bb4e874"
dependencies = [
"cc",
]
[[package]]
name = "pulldown-cmark"
version = "0.8.0"
@@ -4344,6 +4424,12 @@ dependencies = [
"proc-macro2",
]
[[package]]
name = "quoted_printable"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79ec282e887b434b68c18fe5c121d38e72a5cf35119b59e54ec5b992ea9c8eb0"
[[package]]
name = "r2d2"
version = "0.8.9"
@@ -4803,7 +4889,7 @@ dependencies = [
"base64 0.13.1",
"bytes 1.3.0",
"cfg-if 1.0.0",
"futures 0.3.26",
"futures 0.3.28",
"hex",
"hmac 0.12.1",
"http",
@@ -5120,7 +5206,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e56dd856803e253c8f298af3f4d7eb0ae5e23a737252cd90bb4f3b435033b2d"
dependencies = [
"dashmap",
"futures 0.3.26",
"futures 0.3.28",
"lazy_static",
"log",
"parking_lot 0.12.1",
@@ -5303,6 +5389,16 @@ dependencies = [
"winapi 0.3.9",
]
[[package]]
name = "socket2"
version = "0.5.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5fac59a5cb5dd637972e5fca70daf0523c9067fcdc4842f053dae04a18f8e9"
dependencies = [
"libc",
"windows-sys 0.48.0",
]
[[package]]
name = "speculoos"
version = "0.9.0"
@@ -5417,7 +5513,7 @@ checksum = "b850fa514dc11f2ee85be9d055c512aa866746adfacd1cb42d867d68e6a5b0d9"
dependencies = [
"dotenvy",
"either",
"heck 0.4.0",
"heck 0.4.1",
"hex",
"once_cell",
"proc-macro2",
@@ -5457,6 +5553,19 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
[[package]]
name = "stacker"
version = "0.1.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c886bd4480155fd3ef527d45e9ac8dd7118a898a46530b7b94c3e21866259fce"
dependencies = [
"cc",
"cfg-if 1.0.0",
"libc",
"psm",
"winapi 0.3.9",
]
[[package]]
name = "standback"
version = "0.2.17"
@@ -5557,7 +5666,7 @@ dependencies = [
"enums",
"errors",
"futures 0.1.31",
"futures 0.3.26",
"futures 0.3.28",
"futures-core",
"futures-util",
"google_drive_common",
@@ -5686,22 +5795,41 @@ version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "063e6045c0e62079840579a7e47a355ae92f60eb74daaf156fb1e84ba164e63f"
dependencies = [
"strum_macros",
"strum_macros 0.24.3",
]
[[package]]
name = "strum"
version = "0.25.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125"
[[package]]
name = "strum_macros"
version = "0.24.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e385be0d24f186b4ce2f9982191e7101bb737312ad61c1f2f984f34bcf85d59"
dependencies = [
"heck 0.4.0",
"heck 0.4.1",
"proc-macro2",
"quote",
"rustversion",
"syn 1.0.107",
]
[[package]]
name = "strum_macros"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0"
dependencies = [
"heck 0.4.1",
"proc-macro2",
"quote",
"rustversion",
"syn 2.0.26",
]
[[package]]
name = "subprocess"
version = "0.2.7"
@@ -5981,7 +6109,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5cdb1ef4eaeeaddc8fbd371e5017057064af0911902ef36b39801f67cc6d79e4"
dependencies = [
"cfg-if 1.0.0",
"fastrand",
"fastrand 1.7.0",
"libc",
"redox_syscall 0.2.13",
"remove_dir_all",
@@ -6174,7 +6302,7 @@ dependencies = [
"serde_json",
"sqlx",
"sqlx-core",
"strum",
"strum 0.24.1",
"toml",
]
@@ -6469,7 +6597,7 @@ dependencies = [
"async-trait",
"cfg-if 1.0.0",
"enum-as-inner",
"futures 0.3.26",
"futures 0.3.28",
"idna 0.2.3",
"lazy_static",
"log",
@@ -6487,7 +6615,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "710f593b371175db53a26d0b38ed2978fafb9e9e8d3868b1acd753ea18df0ceb"
dependencies = [
"cfg-if 0.1.10",
"futures 0.3.26",
"futures 0.3.28",
"ipconfig",
"lazy_static",
"log",
@@ -6633,7 +6761,7 @@ dependencies = [
"dotenv",
"easyenv",
"futures 0.1.31",
"futures 0.3.26",
"futures 0.3.28",
"futures-timer",
"futures-util",
"hex",
@@ -6684,7 +6812,7 @@ dependencies = [
"async-trait",
"chrono",
"displaydoc",
"futures 0.3.26",
"futures 0.3.28",
"http",
"once_cell",
"reqwest",
@@ -6873,10 +7001,13 @@ dependencies = [
"anyhow",
"base64 0.21.0",
"bcrypt",
"byteorder",
"chrono",
"container_common",
"cookies",
"crockford",
"dotenv",
"email",
"enums",
"errors",
"hmac 0.10.1",
@@ -6899,6 +7030,8 @@ dependencies = [
"sha2 0.9.5",
"sqlx",
"sqlx_mysql_helpers",
"strum 0.25.0",
"strum_macros 0.25.3",
"time 0.2.26",
"tokens",
"toml",
@@ -7153,7 +7286,7 @@ dependencies = [
"serde",
"serde_derive",
"serde_json",
"strum",
"strum 0.24.1",
"toml",
]
@@ -7202,7 +7335,7 @@ dependencies = [
"easyenv",
"errors",
"futures 0.1.31",
"futures 0.3.26",
"futures 0.3.28",
"futures-timer",
"futures-util",
"hex",
+2 -1
View File
@@ -26,12 +26,13 @@ members = [
"crates/lib/caching/redis_caching",
"crates/lib/concurrency",
"crates/lib/cookies",
"crates/lib/crockford",
"crates/lib/crockford_deprecated",
"crates/lib/datetimes",
"crates/lib/deprecated/config", # TODO(bt, 2023-02-13): Deprecate or repurpose
"crates/lib/deprecated/container_common", # TODO(bt, 2023-02-13): Pull apart and rehome pieces
"crates/lib/deprecated/http_server_common", # TODO(bt, 2023-03-20): Move useful pieces to actix_helpers
"crates/lib/easyenv", # NB: Vendored version of https://github.com/echelon/easyenv.rs that we'll improve.
"crates/lib/email",
"crates/lib/errors",
"crates/lib/files/archives",
"crates/lib/files/filesys",
+1 -1
View File
@@ -15,7 +15,7 @@ path = "src/lib.rs"
[dependencies]
# Internal
crockford = { path = "../../lib/crockford" }
crockford_deprecated = { path = "../../lib/crockford_deprecated" }
errors = { path = "../../lib/errors" }
# External
@@ -1,4 +1,4 @@
use crockford::crockford_entropy_lower;
use crockford_deprecated::crockford_entropy_lower;
use crate::public::public_path::PublicPath;
use crate::util::hashed_directory_path_long_string::hashed_directory_path_long_string;
@@ -1,6 +1,6 @@
use std::path::PathBuf;
use crockford::crockford_entropy_lower;
use crockford_deprecated::crockford_entropy_lower;
use crate::public::media_files::directory::MediaFileBucketDirectory;
use crate::public::public_path::PublicPath;
@@ -1,4 +1,4 @@
use crockford::crockford_entropy_lower;
use crockford_deprecated::crockford_entropy_lower;
use crate::public::public_path::PublicPath;
use crate::util::hashed_directory_path_long_string::hashed_directory_path_long_string;
@@ -1,6 +1,6 @@
use std::path::PathBuf;
use crockford::crockford_entropy_lower;
use crockford_deprecated::crockford_entropy_lower;
use crate::public::media_uploads::directory::MediaUploadDirectory;
use crate::public::public_path::PublicPath;
@@ -1,6 +1,6 @@
use std::path::PathBuf;
use crockford::crockford_entropy_lower;
use crockford_deprecated::crockford_entropy_lower;
use crate::public::public_path::PublicPath;
use crate::public::voice_conversion_results::directory::VoiceConversionResultDirectory;
+2
View File
@@ -23,6 +23,7 @@ pub(crate) enum EntityType {
MediaUpload,
ModelCategory,
NewsStory, // NB: aichatbot / sqlite
PasswordReset,
TtsModel,
TtsRenderTask, // NB: aichatbot / sqlite
User,
@@ -47,6 +48,7 @@ impl EntityType {
Self::MediaUpload => "mu_",
Self::ModelCategory => "CAT:", // NB: Old-style prefix, do not use for future tokens.
Self::NewsStory => "news_story_",
Self::PasswordReset => "pw_reset_",
Self::TtsModel => "TM:", // NB: Old-style prefix, do not use for future tokens.
Self::TtsRenderTask => "tts_task_",
Self::User => "U:", // NB: Old-style prefix, do not use for future tokens.
+1
View File
@@ -6,6 +6,7 @@ pub mod generic_inference_jobs;
pub mod media_files;
pub mod media_uploads;
pub mod model_categories;
pub mod password_reset;
pub mod sqlite;
pub mod tts_models;
pub mod users;
@@ -0,0 +1,14 @@
use std::fmt::Debug;
use serde::Deserialize;
use serde::Serialize;
use crate::prefixes::EntityType;
/// The primary key for `password_reset`s
#[derive(Clone, PartialEq, Eq, sqlx::Type, Debug, Serialize, Deserialize)]
#[sqlx(transparent)]
pub struct PasswordResetToken(pub String);
impl_string_token!(PasswordResetToken);
impl_crockford_generator!(PasswordResetToken, 32usize, EntityType::PasswordReset, CrockfordUpper);
@@ -1,5 +1,5 @@
[package]
name = "crockford"
name = "crockford_deprecated"
edition = "2018"
version = "0.0.1"
authors = [
@@ -11,7 +11,7 @@ publish = false
[lib]
# NB: Public "crockford" crate is non-sensically only for encoding and decoding u64s (wtf?)
# Our crate is for generating entropy and crockford encoding it.
name = "crockford"
name = "crockford_deprecated"
path = "src/lib.rs"
[dependencies]
+14
View File
@@ -0,0 +1,14 @@
[package]
name = "email"
version = "0.1.0"
edition = "2021"
[lib]
path = "src/lib.rs"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
errors = { path = "../errors" }
lettre = "0.11.0"
+32
View File
@@ -0,0 +1,32 @@
use errors::{AnyhowResult, AnyhowError, anyhow};
use lettre::message::header::ContentType;
use lettre::transport::smtp::authentication::Credentials;
use lettre::{Message, SmtpTransport, Transport};
pub async fn send_password_reset(email_address: &str, secret_key: String) -> AnyhowResult<()> {
let email = Message::builder()
.from("Support <support@storyteller.ai>".parse().unwrap())
.to( email_address.parse().unwrap())
.subject("Your password reset")
.header(ContentType::TEXT_PLAIN)
.body(format!("Here you go! {secret_key}"))
.unwrap();
let creds = Credentials::new("smtp_username".to_owned(), "smtp_password".to_owned());
// Open a remote connection to gmail
let mailer = SmtpTransport::relay("smtp.gmail.com")
.unwrap()
.credentials(creds)
.build();
// Send the email
match mailer.send(&email) {
Ok(_) => println!("Email sent successfully!"),
Err(e) => panic!("Could not send email: {e:?}"),
}
Ok(())
}
@@ -0,0 +1,33 @@
use anyhow::Context;
use sqlx::MySqlPool;
use errors::AnyhowResult;
use tokens::tokens::password_reset::PasswordResetToken;
use super::lookup_user_for_login_result::UserRecordForLogin;
pub async fn create_password_reset(pool: &MySqlPool, user: &UserRecordForLogin, ip_address: String, secret_key: String) -> AnyhowResult<()> {
let token = PasswordResetToken::generate();
//TODO: ip_address_redemption nullable
// Definitely remove the empty string default as well!
sqlx::query!(
r#"
INSERT INTO password_resets
(token, user_token, secret_key, current_password_version, ip_address_creation, ip_address_redemption, expires_at)
VALUES (?, ?, ?, ?, ?, '', NOW() + INTERVAL 3 hour);
"#,
token,
user.token,
secret_key,
user.password_version,
ip_address
)
.execute(pool)
.await
.context("inserting password reset")
.map(|_| ())
}
@@ -14,7 +14,8 @@ SELECT
username,
email_address,
password_hash,
is_banned
is_banned,
password_version
FROM users
WHERE email_address = ?
LIMIT 1
@@ -15,7 +15,8 @@ SELECT
username,
email_address,
password_hash,
is_banned
is_banned,
password_version
FROM users
WHERE username = ?
LIMIT 1
@@ -8,4 +8,5 @@ pub struct UserRecordForLogin {
pub email_address: String,
pub password_hash: Vec<u8>,
pub is_banned: i8,
pub password_version: i32,
}
@@ -1,4 +1,5 @@
pub mod create_account;
pub mod create_password_reset_request;
pub mod get_user_token_by_username;
pub mod list_users_query_builder;
pub mod lookup_user_for_login_by_email;
+1 -1
View File
@@ -20,7 +20,7 @@ cloud_storage = { path = "../../../lib/api_clients/cloud_storage" }
concurrency = { path = "../../../lib/concurrency" }
config = { path = "../../../lib/deprecated/config" }
container_common = { path = "../../../lib/deprecated/container_common" }
crockford = { path = "../../../lib/crockford" }
crockford_deprecated = { path = "../../../lib/crockford_deprecated" }
easyenv = { path = "../../../lib/easyenv" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
@@ -8,7 +8,7 @@ use container_common::filesystem::check_file_exists::check_file_exists;
use container_common::filesystem::safe_delete_possible_temp_file::safe_delete_possible_temp_file;
use container_common::filesystem::safe_delete_temp_directory::safe_delete_temp_directory;
use container_common::filesystem::safe_delete_temp_file::safe_delete_temp_file;
use crockford::crockford_entropy_lower;
use crockford_deprecated::crockford_entropy_lower;
use enums::by_table::voice_conversion_models::voice_conversion_model_type::VoiceConversionModelType;
use enums::common::visibility::Visibility;
use errors::AnyhowResult;
@@ -183,7 +183,7 @@ pub async fn process_rvc_v2_model<'a, 'b>(
//#[cfg(test)]
//mod tests {
// use crockford::crockford_entropy_lower;
// use crockford_deprecated::crockford_entropy_lower;
//
// #[test]
// fn temp() {
+5
View File
@@ -17,6 +17,7 @@ path = "src/lib.rs"
# Internal
container_common = { path = "../../../lib/deprecated/container_common" }
cookies = { path = "../../../lib/cookies" }
email = { path = "../../../lib/email" }
enums = { path = "../../../_types/enums" }
errors = { path = "../../../lib/errors" }
http_server_common = { path = "../../../lib/deprecated/http_server_common" }
@@ -65,3 +66,7 @@ sqlx = { version = "0.6.2", features = [ "mysql", "runtime-actix-native-tls", "o
# TODO: Shouldn't really be a dependency, but need to add caching for the DDoS attack.
# Redis Database
r2d2_redis = "0.14.0"
crockford = "1.1.2"
byteorder = "1.5.0"
strum = "0.25.0"
strum_macros = "0.25.3"
@@ -7,6 +7,7 @@ use actix_web::error::Error;
use crate::endpoints::create_account_handler::create_account_handler;
use crate::endpoints::login_handler::login_handler;
use crate::endpoints::logout_handler::logout_handler;
use crate::endpoints::password_reset_handler::password_reset_request_handler;
use crate::endpoints::session_info_handler::session_info_handler;
// NB: This does not include the user edit endpoints since FakeYou's API mounts them alongside other things.
@@ -64,4 +65,9 @@ pub fn add_suggested_api_v1_account_creation_and_session_routes<T, B> (app: App<
.route(web::get().to(session_info_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
.service(
web::resource("/v1/request-reset")
.route(web::post().to(password_reset_request_handler))
.route(web::head().to(|| HttpResponse::Ok()))
)
}
@@ -3,4 +3,5 @@ pub mod edit_profile_handler;
pub mod get_profile_handler;
pub mod login_handler;
pub mod logout_handler;
pub mod password_reset_handler;
pub mod session_info_handler;
@@ -0,0 +1,129 @@
/*
~ B R A I N S T O R M ~
- redeem reset request
- needs a way to identify the user because only the tuple is unique (user + key)
username or email address
- provide new password
*/
use std::fmt::Display;
use actix_web::http::StatusCode;
use actix_web::{HttpRequest, HttpResponse, web, ResponseError};
use byteorder::ByteOrder;
use http_server_common::request::get_request_ip::get_request_ip;
use http_server_common::response::serialize_as_json_error::serialize_as_json_error;
use log::warn;
use mysql_queries::queries::users::user::create_password_reset_request::create_password_reset;
use mysql_queries::queries::users::user::lookup_user_for_login_by_email::lookup_user_for_login_by_email;
use mysql_queries::queries::users::user::lookup_user_for_login_by_username::lookup_user_for_login_by_username;
use rand::RngCore;
use serde::Deserialize;
use sqlx::MySqlPool;
use strum_macros::Display;
/// This can be reused in login requests in the future!
#[derive(Deserialize)]
pub enum UserLogin {
Email(String),
Username(String),
}
#[derive(Deserialize)]
pub struct PasswordResetRequest {
login: UserLogin,
}
#[derive(Serialize)]
pub struct PasswordResetResponse {}
#[derive(Serialize, Debug, Display)]
pub enum PasswordResetRequestError {
NoSuchUser,
Internal,
}
#[derive(Serialize, Debug)]
pub struct PasswordResetErrorResponse {
kind: PasswordResetRequestError,
}
impl Display for PasswordResetErrorResponse {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:?}", self)
}
}
impl From<PasswordResetRequestError> for PasswordResetErrorResponse {
fn from(value: PasswordResetRequestError) -> Self {
Self { kind: value }
}
}
impl From<errors::AnyhowError> for PasswordResetErrorResponse {
fn from(value: errors::AnyhowError) -> Self {
log::error!("Internal error: {value}");
Self { kind: PasswordResetRequestError::Internal }
}
}
impl ResponseError for PasswordResetErrorResponse {
//TODO: Yknow, clean this up and stuff
fn status_code(&self) -> StatusCode {
StatusCode::INTERNAL_SERVER_ERROR
}
fn error_response(&self) -> HttpResponse {
serialize_as_json_error(self)
}
}
///
/// Non-authenticated!
///
/// - create password reset request
/// - sends email?
/// - inserts record and stuff
pub async fn password_reset_request_handler(
http_request: HttpRequest,
request: web::Json<PasswordResetRequest>,
// session_cookie_manager: web::Data<SessionCookieManager>,
mysql_pool: web::Data<MySqlPool>,
) -> Result<HttpResponse, PasswordResetErrorResponse> {
let ip_address = get_request_ip(&http_request);
let user = match &request.login {
UserLogin::Email(email) => lookup_user_for_login_by_email(&email, &mysql_pool).await,
UserLogin::Username(username) => lookup_user_for_login_by_username(&username, &mysql_pool).await,
}.map_err(|e| {
warn!("Password reset user lookup: {:?}", e);
PasswordResetRequestError::NoSuchUser
//TODO: This could be anything, not necessarily a lookup. The name is misleading 🤷🏻
})?;
let mut rng = rand::thread_rng();
let mut secret_key_bytes = [0u8;40];
// Has to fit within (4 / 3) * string length (32 chars),
// but also be a multiple of 8 so we can read them as `u64`s
// (for Crockford)
rng.fill_bytes(&mut secret_key_bytes);
let mut secret_key = String::new();
secret_key_bytes.chunks_exact(8)
.map(|chunk| byteorder::LittleEndian::read_u64(chunk))
.for_each(|chunk| crockford::encode_into::<String>(chunk, &mut secret_key));
secret_key.truncate(32);
//TODO: Handle banned users, they shouldn't be able to do this
create_password_reset(&mysql_pool, &user, ip_address, secret_key.clone()).await
.map_err(|err| {
log::error!("AAAAAHHH! {:?}", err);
err
})?;
email::send_password_reset(&user.email_address, secret_key).await?;
Ok(HttpResponse::Ok().finish())
}