From 3ae7960efbceb50140e6633b22c76f267369cb9e Mon Sep 17 00:00:00 2001 From: Your Name Date: Thu, 30 May 2024 15:58:38 +0000 Subject: [PATCH] Add sqlx --- Cargo.lock | 1 + crates/web-server/Cargo.toml | 3 + crates/web-server/src/auth/mod.rs | 71 ++++++++++++++++++++++ crates/web-server/src/auth/sign_in.rs | 86 ++++++++++++++++++++++++++- 4 files changed, 159 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 16ba134..d550285 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3732,6 +3732,7 @@ dependencies = [ "db", "lettre", "markup", + "reqwest", "serde", "sha2", "sqlx", diff --git a/crates/web-server/Cargo.toml b/crates/web-server/Cargo.toml index 0589b37..801f62b 100644 --- a/crates/web-server/Cargo.toml +++ b/crates/web-server/Cargo.toml @@ -19,6 +19,9 @@ tokio = { version = "1", features = ["rt-multi-thread"] } markup = "0" validator = { version = "0.12", features = ["derive"] } +# Use this for access to hcaptcha +reqwest = { version = "0", default-features = false, features = ["json", "rustls-tls"] } + # Used by the proxy url = "2.0" diff --git a/crates/web-server/src/auth/mod.rs b/crates/web-server/src/auth/mod.rs index dfd7bf9..5567372 100644 --- a/crates/web-server/src/auth/mod.rs +++ b/crates/web-server/src/auth/mod.rs @@ -1,7 +1,78 @@ pub mod sign_in; +use std::collections::HashSet; + use axum::Router; use axum_extra::routing::RouterExt; +use serde::{Deserialize, Deserializer}; pub fn routes() -> Router { Router::new().typed_get(sign_in::sign_in) } + +#[derive(PartialEq, Eq, Hash, Debug)] +pub enum Code { + MissingSecret, + InvalidSecret, + MissingResponse, + InvalidResponse, + BadRequest, + Unknown(String), +} + +impl<'de> Deserialize<'de> for Code { + fn deserialize(de: D) -> Result + where + D: Deserializer<'de>, + { + let code = String::deserialize(de)?; + Ok(match &*code { + "missing-input-secret" => Code::MissingSecret, + "invalid-input-secret" => Code::InvalidSecret, + "missing-input-response" => Code::MissingResponse, + "invalid-input-response" => Code::InvalidResponse, + "bad-request" => Code::BadRequest, + _ => Code::Unknown(code), + }) + } +} + +#[derive(Debug, Deserialize)] +pub struct RecaptchaResponse { + pub success: bool, + #[serde(rename = "error-codes")] + pub error_codes: Option>, +} + +/***pub async fn verify_hcaptcha( + hcaptcha_config: &Option, + response: &Option, +) -> bool { + if let Some(hcaptcha) = hcaptcha_config { + if let Some(resp) = response { + let mut url = Url::parse("https://hcaptcha.com/siteverify").unwrap(); + + let secret_key: &str = &hcaptcha.hcaptcha_secret_key; + + url.query_pairs_mut() + .extend_pairs(&[("secret", secret_key), ("response", resp)]); + + let response = reqwest::get(url).await; + + if let Ok(resp) = response { + let recaptcha_response = resp.json::().await; + + if let Ok(status) = recaptcha_response { + if status.success { + return true; + } else { + dbg!(status); + } + } + } + } + + return false; + } + + true +}**/ \ No newline at end of file diff --git a/crates/web-server/src/auth/sign_in.rs b/crates/web-server/src/auth/sign_in.rs index 066b8c6..7827203 100644 --- a/crates/web-server/src/auth/sign_in.rs +++ b/crates/web-server/src/auth/sign_in.rs @@ -1,3 +1,4 @@ + use super::super::errors::CustomError; use crate::config::Config; use crate::layouts; @@ -5,7 +6,6 @@ use crate::routes::auth::{self, ResetRequest, SignUp}; use crate::{components::forms, config}; use axum::extract::Extension; use axum::response::IntoResponse; -use db::Pool; use serde::{Deserialize, Serialize}; use validator::ValidationErrors; @@ -19,7 +19,6 @@ pub struct Login { pub async fn sign_in( auth::SignIn {}: auth::SignIn, - Extension(_pool): Extension, Extension(config): Extension, ) -> Result { let body = LoginPage { @@ -37,6 +36,89 @@ pub async fn sign_in( )) } + +/***pub async fn process_login( + auth::SignIn {}: auth::SignIn, + Extension(pool): Extension, + Extension(config): Extension, + identity: Identity, + Form(form): Form, +) -> Result { + let mut validation_errors = ValidationErrors::default(); + + let valid = super::verify_hcaptcha(&config.hcaptcha_config, &form.h_captcha_response).await; + + if valid { + let users = sqlx::query_as::<_, User>(&format!( + " + SELECT id, hashed_password FROM {} WHERE email = $1 + ", + config.user_table_name + )) + .bind(&form.email.to_lowercase()) + .fetch_all(pool.get_ref()) // -> Vec + .await?; + + if !users.is_empty() { + // Passwords must be normalised + let normalised_password = &form.password.nfkc().collect::(); + let valid = crate::encryption::verify_hash( + normalised_password, + &users[0].hashed_password, + config.use_bcrypt_instead_of_argon, + ) + .await?; + + if valid { + // Generate a session + + create_session(&config, pool, identity, users[0].id, None).await?; + + return Ok(HttpResponse::SeeOther() + .append_header((http::header::LOCATION, config.redirect_url.clone())) + .finish()); + } + } + + validation_errors.add( + "email", + ValidationError { + message: Some(Cow::from("Invalid email or password")), + code: Cow::from("0"), + params: Default::default(), + }, + ); + } else { + validation_errors.add( + "email", + ValidationError { + message: Some(Cow::from("Invalid hCaptcha")), + code: Cow::from("0"), + params: Default::default(), + }, + ); + } + + let login = Login { + email: form.email.clone(), + ..Default::default() + }; + + let body = LoginPage { + form: &login, + hcaptcha_config: &config.hcaptcha_config, + sign_up: SignUp {}.to_string(), + reset_password: ResetRequest {}.to_string(), + errors: &validation_errors, + }; + + Ok(layouts::session_layout( + "Login", + &body.to_string(), + config.hcaptcha_config.is_some(), + )) +}**/ + markup::define! { LoginPage<'a>(form: &'a Login, hcaptcha_config: &'a Option,