Log errors in the user module
This commit is contained in:
parent
b0f0633338
commit
7b9df97a32
6 changed files with 199 additions and 118 deletions
|
|
@ -38,6 +38,8 @@ pub const COOKIE_LANG_NAME: &str = "lang";
|
|||
/// (cookie authentication, password reset, validation token).
|
||||
pub const TOKEN_SIZE: usize = 32;
|
||||
|
||||
pub const EMAIL_ADDRESS: &str = "recipes@recipes.gburri.org";
|
||||
|
||||
/// When sending a validation email,
|
||||
/// the server has this duration to wait for a response from the SMTP server.
|
||||
pub const SEND_EMAIL_TIMEOUT: Duration = Duration::from_secs(60);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
use chrono::{Duration, prelude::*};
|
||||
use rand::distr::{Alphanumeric, SampleString};
|
||||
use sqlx::Sqlite;
|
||||
use strum_macros::Display;
|
||||
|
||||
use super::{Connection, DBError, Result};
|
||||
use crate::{
|
||||
|
|
@ -9,27 +10,27 @@ use crate::{
|
|||
hash::{hash, verify_password},
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Display)]
|
||||
pub enum SignUpResult {
|
||||
UserAlreadyExists,
|
||||
UserCreatedWaitingForValidation(String), // Validation token.
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Display)]
|
||||
pub enum UpdateUserResult {
|
||||
EmailAlreadyTaken,
|
||||
UserUpdatedWaitingForRevalidation(String), // Validation token.
|
||||
Ok,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Display)]
|
||||
pub enum ValidationResult {
|
||||
UnknownUser,
|
||||
ValidationExpired,
|
||||
Ok(String, i64), // Returns token and user id.
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Display)]
|
||||
pub enum SignInResult {
|
||||
UserNotFound,
|
||||
WrongPassword,
|
||||
|
|
@ -37,20 +38,20 @@ pub enum SignInResult {
|
|||
Ok(String, i64), // Returns token and user id.
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Display)]
|
||||
pub enum AuthenticationResult {
|
||||
NotValidToken,
|
||||
Ok(i64), // Returns user id.
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Display)]
|
||||
pub enum GetTokenResetPasswordResult {
|
||||
PasswordAlreadyReset,
|
||||
EmailUnknown,
|
||||
Ok(String),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
#[derive(Debug, Display)]
|
||||
pub enum ResetPasswordResult {
|
||||
ResetTokenExpired,
|
||||
Ok,
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ pub async fn send_email(
|
|||
) -> Result<(), Error> {
|
||||
let email = Message::builder()
|
||||
.message_id(None)
|
||||
.from("recipes@recipes.gburri.org".parse()?)
|
||||
.from(consts::EMAIL_ADDRESS.parse()?)
|
||||
.to(email.parse()?)
|
||||
.subject(title)
|
||||
.body(message.to_string())?;
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ const TRACING_DISPLAY_THREAD: bool = false;
|
|||
|
||||
#[derive(Clone)]
|
||||
pub struct Log {
|
||||
guard: Arc<WorkerGuard>,
|
||||
_guard: Arc<WorkerGuard>,
|
||||
directory: PathBuf,
|
||||
}
|
||||
|
||||
|
|
@ -69,7 +69,7 @@ impl Log {
|
|||
.init();
|
||||
|
||||
Log {
|
||||
guard: Arc::new(guard),
|
||||
_guard: Arc::new(guard),
|
||||
directory: directory.as_ref().to_path_buf(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ use axum_extra::extract::{
|
|||
use chrono::Duration;
|
||||
use lettre::Address;
|
||||
use serde::Deserialize;
|
||||
use strum_macros::Display;
|
||||
use tracing::{Level, event};
|
||||
|
||||
use crate::{
|
||||
|
|
@ -23,6 +24,8 @@ use crate::{
|
|||
translation::Sentence, utils,
|
||||
};
|
||||
|
||||
const VALIDATION_TOKEN_KEY: &str = "validation_token";
|
||||
|
||||
/// SIGN UP ///
|
||||
|
||||
#[debug_handler]
|
||||
|
|
@ -62,6 +65,7 @@ pub struct SignUpFormData {
|
|||
password_2: String,
|
||||
}
|
||||
|
||||
#[derive(Display)]
|
||||
enum SignUpError {
|
||||
InvalidEmail,
|
||||
PasswordsNotEqual,
|
||||
|
|
@ -84,6 +88,13 @@ pub async fn sign_up_post(
|
|||
form_data: &SignUpFormData,
|
||||
context: Context,
|
||||
) -> Result<Response> {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Sign up] Unable to sign up with email {}: {}",
|
||||
form_data.email,
|
||||
error
|
||||
);
|
||||
|
||||
let invalid_password_mess = &context.tr.tp(
|
||||
Sentence::InvalidPassword,
|
||||
&[Box::new(common::consts::MIN_PASSWORD_SIZE)],
|
||||
|
|
@ -160,8 +171,8 @@ pub async fn sign_up_post(
|
|||
&context.tr.tp(
|
||||
Sentence::SignUpFollowEmailLink,
|
||||
&[Box::new(format!(
|
||||
"{}/validation?validation_token={}",
|
||||
url, token
|
||||
"{}/validation?{}={}",
|
||||
url, VALIDATION_TOKEN_KEY, token
|
||||
))],
|
||||
),
|
||||
&config.smtp_relay_address,
|
||||
|
|
@ -179,16 +190,10 @@ pub async fn sign_up_post(
|
|||
.render()?,
|
||||
)
|
||||
.into_response()),
|
||||
Err(_) => {
|
||||
// error!("Email validation error: {}", error); // TODO: log
|
||||
error_response(SignUpError::UnableSendEmail, &form_data, context)
|
||||
}
|
||||
Err(_) => error_response(SignUpError::UnableSendEmail, &form_data, context),
|
||||
}
|
||||
}
|
||||
Err(_) => {
|
||||
// error!("Signup database error: {}", error); // TODO: log
|
||||
error_response(SignUpError::DatabaseError, &form_data, context)
|
||||
}
|
||||
Err(_) => error_response(SignUpError::DatabaseError, &form_data, context),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -201,7 +206,12 @@ pub async fn sign_up_validation(
|
|||
headers: HeaderMap,
|
||||
) -> Result<(CookieJar, impl IntoResponse)> {
|
||||
let mut jar = CookieJar::from_headers(&headers);
|
||||
if context.user.is_some() {
|
||||
if let Some(ref user) = context.user {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Sign up] Unable to validate: user already logged. Email: {}",
|
||||
user.email
|
||||
);
|
||||
return Ok((
|
||||
jar,
|
||||
Html(
|
||||
|
|
@ -215,7 +225,7 @@ pub async fn sign_up_validation(
|
|||
));
|
||||
}
|
||||
let (client_ip, client_user_agent) = utils::get_ip_and_user_agent(&headers, addr);
|
||||
match query.get("validation_token") {
|
||||
match query.get(VALIDATION_TOKEN_KEY) {
|
||||
// 'validation_token' exists only when a user tries to validate a new account.
|
||||
Some(token) => {
|
||||
match connection
|
||||
|
|
@ -244,41 +254,61 @@ pub async fn sign_up_validation(
|
|||
),
|
||||
))
|
||||
}
|
||||
db::user::ValidationResult::ValidationExpired => Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::SignUpValidationExpired),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
)),
|
||||
db::user::ValidationResult::UnknownUser => Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::SignUpValidationErrorTryAgain),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
)),
|
||||
db::user::ValidationResult::ValidationExpired => {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Sign up] Unable to validate: validation expired. Token: {}",
|
||||
token
|
||||
);
|
||||
Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::SignUpValidationExpired),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
))
|
||||
}
|
||||
db::user::ValidationResult::UnknownUser => {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Sign up] Unable to validate: unknown user. Token: {}",
|
||||
token
|
||||
);
|
||||
Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::SignUpValidationErrorTryAgain),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::ValidationError),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
)),
|
||||
None => {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Sign up] Unable to validate: no token provided"
|
||||
);
|
||||
Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::ValidationError),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -322,30 +352,46 @@ pub async fn sign_in_post(
|
|||
)
|
||||
.await?
|
||||
{
|
||||
db::user::SignInResult::AccountNotValidated => Ok((
|
||||
jar,
|
||||
Html(
|
||||
SignInFormTemplate {
|
||||
email: &form_data.email,
|
||||
message: context.tr.t(Sentence::AccountMustBeValidatedFirst),
|
||||
context,
|
||||
}
|
||||
.render()?,
|
||||
)
|
||||
.into_response(),
|
||||
)),
|
||||
db::user::SignInResult::UserNotFound | db::user::SignInResult::WrongPassword => Ok((
|
||||
jar,
|
||||
Html(
|
||||
SignInFormTemplate {
|
||||
email: &form_data.email,
|
||||
message: context.tr.t(Sentence::WrongEmailOrPassword),
|
||||
context,
|
||||
}
|
||||
.render()?,
|
||||
)
|
||||
.into_response(),
|
||||
)),
|
||||
error @ db::user::SignInResult::AccountNotValidated => {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Sign in] Account not validated, email: {}: {}",
|
||||
form_data.email,
|
||||
error
|
||||
);
|
||||
Ok((
|
||||
jar,
|
||||
Html(
|
||||
SignInFormTemplate {
|
||||
email: &form_data.email,
|
||||
message: context.tr.t(Sentence::AccountMustBeValidatedFirst),
|
||||
context,
|
||||
}
|
||||
.render()?,
|
||||
)
|
||||
.into_response(),
|
||||
))
|
||||
}
|
||||
error @ (db::user::SignInResult::UserNotFound | db::user::SignInResult::WrongPassword) => {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Sign in] Email: {}: {}",
|
||||
form_data.email,
|
||||
error
|
||||
);
|
||||
Ok((
|
||||
jar,
|
||||
Html(
|
||||
SignInFormTemplate {
|
||||
email: &form_data.email,
|
||||
message: context.tr.t(Sentence::WrongEmailOrPassword),
|
||||
context,
|
||||
}
|
||||
.render()?,
|
||||
)
|
||||
.into_response(),
|
||||
))
|
||||
}
|
||||
db::user::SignInResult::Ok(token, _user_id) => {
|
||||
let cookie = Cookie::build((consts::COOKIE_AUTH_TOKEN_NAME, token))
|
||||
.same_site(cookie::SameSite::Strict);
|
||||
|
|
@ -586,6 +632,7 @@ pub struct ResetPasswordForm {
|
|||
reset_token: String,
|
||||
}
|
||||
|
||||
#[derive(Display)]
|
||||
enum ResetPasswordError {
|
||||
PasswordsNotEqual,
|
||||
InvalidPassword,
|
||||
|
|
@ -604,6 +651,16 @@ pub async fn reset_password_post(
|
|||
form_data: &ResetPasswordForm,
|
||||
context: Context,
|
||||
) -> Result<Response> {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Reset password] Email: {}: {}",
|
||||
if let Some(ref user) = context.user {
|
||||
&user.email
|
||||
} else {
|
||||
"<Unknown user>"
|
||||
},
|
||||
error
|
||||
);
|
||||
let reset_password_mess = &context.tr.tp(
|
||||
Sentence::InvalidPassword,
|
||||
&[Box::new(common::consts::MIN_PASSWORD_SIZE)],
|
||||
|
|
@ -700,6 +757,7 @@ pub struct EditUserForm {
|
|||
password_2: String,
|
||||
}
|
||||
|
||||
#[derive(Display)]
|
||||
enum ProfileUpdateError {
|
||||
InvalidEmail,
|
||||
EmailAlreadyTaken,
|
||||
|
|
@ -709,7 +767,6 @@ enum ProfileUpdateError {
|
|||
UnableSendEmail,
|
||||
}
|
||||
|
||||
// TODO: A lot of code are similar to 'sign_up_post', maybe find a way to factorize some.
|
||||
#[debug_handler(state = AppState)]
|
||||
pub async fn edit_user_post(
|
||||
Host(host): Host,
|
||||
|
|
@ -718,17 +775,22 @@ pub async fn edit_user_post(
|
|||
Extension(context): Extension<Context>,
|
||||
Form(form_data): Form<EditUserForm>,
|
||||
) -> Result<Response> {
|
||||
event!(
|
||||
Level::DEBUG,
|
||||
"First day of the week: {:?}",
|
||||
form_data.first_day_of_the_week
|
||||
);
|
||||
if let Some(ref user) = context.user {
|
||||
fn error_response(
|
||||
error: ProfileUpdateError,
|
||||
form_data: &EditUserForm,
|
||||
context: Context,
|
||||
) -> Result<Response> {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Edit user] Email: {}: {}",
|
||||
if let Some(ref user) = context.user {
|
||||
&user.email
|
||||
} else {
|
||||
"<Unknown user>"
|
||||
},
|
||||
error
|
||||
);
|
||||
let invalid_password_mess = &context.tr.tp(
|
||||
Sentence::InvalidPassword,
|
||||
&[Box::new(common::consts::MIN_PASSWORD_SIZE)],
|
||||
|
|
@ -810,8 +872,8 @@ pub async fn edit_user_post(
|
|||
&context.tr.tp(
|
||||
Sentence::ProfileFollowEmailLink,
|
||||
&[Box::new(format!(
|
||||
"{}/revalidation?validation_token={}",
|
||||
url, token
|
||||
"{}/revalidation?{}={}",
|
||||
url, VALIDATION_TOKEN_KEY, token
|
||||
))],
|
||||
),
|
||||
&config.smtp_relay_address,
|
||||
|
|
@ -888,7 +950,7 @@ pub async fn email_revalidation(
|
|||
));
|
||||
}
|
||||
let (client_ip, client_user_agent) = utils::get_ip_and_user_agent(&headers, addr);
|
||||
match query.get("validation_token") {
|
||||
match query.get(VALIDATION_TOKEN_KEY) {
|
||||
// 'validation_token' exists only when a user must validate a new email.
|
||||
Some(token) => {
|
||||
match connection
|
||||
|
|
@ -917,28 +979,44 @@ pub async fn email_revalidation(
|
|||
),
|
||||
))
|
||||
}
|
||||
db::user::ValidationResult::ValidationExpired => Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::ValidationExpired),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
)),
|
||||
db::user::ValidationResult::UnknownUser => Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::ValidationErrorTryToSignUpAgain),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
)),
|
||||
error @ db::user::ValidationResult::ValidationExpired => {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Email revalidation] Token: {}: {}",
|
||||
token,
|
||||
error
|
||||
);
|
||||
Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::ValidationExpired),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
))
|
||||
}
|
||||
error @ db::user::ValidationResult::UnknownUser => {
|
||||
event!(
|
||||
Level::WARN,
|
||||
"[Email revalidation] Email: {}: {}",
|
||||
token,
|
||||
error
|
||||
);
|
||||
Ok((
|
||||
jar,
|
||||
Html(
|
||||
MessageTemplate::new_with_user(
|
||||
context.tr.t(Sentence::ValidationErrorTryToSignUpAgain),
|
||||
context.tr,
|
||||
context.user,
|
||||
)
|
||||
.render()?,
|
||||
),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
None => Ok((
|
||||
|
|
|
|||
|
|
@ -51,14 +51,14 @@ pub fn main() -> Result<(), JsValue> {
|
|||
.unwrap_or(chrono::Weekday::Mon);
|
||||
|
||||
match path[..] {
|
||||
["recipe", "edit", id] => {
|
||||
let id = id.parse::<i64>().unwrap(); // TODO: remove unwrap.
|
||||
pages::recipe_edit::setup_page(id)
|
||||
}
|
||||
["recipe", "view", id] => {
|
||||
let id = id.parse::<i64>().unwrap(); // TODO: remove unwrap.
|
||||
pages::recipe_view::setup_page(id, is_user_logged, first_day_of_the_week)
|
||||
}
|
||||
["recipe", "edit", id] => match id.parse::<i64>() {
|
||||
Ok(id) => pages::recipe_edit::setup_page(id),
|
||||
Err(error) => log!(format!("Error parsing recipe id: {}", error)),
|
||||
},
|
||||
["recipe", "view", id] => match id.parse::<i64>() {
|
||||
Ok(id) => pages::recipe_view::setup_page(id, is_user_logged, first_day_of_the_week),
|
||||
Err(error) => log!(format!("Error parsing recipe id: {}", error)),
|
||||
},
|
||||
["dev_panel"] => pages::dev_panel::setup_page(),
|
||||
// Home.
|
||||
[""] => pages::home::setup_page(is_user_logged, first_day_of_the_week),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue