Use typed parameters instead of a hashmap

This commit is contained in:
Greg Burri 2025-05-21 20:18:31 +02:00
parent 084f7ef445
commit 817ef3d727
2 changed files with 129 additions and 150 deletions

View file

@ -110,7 +110,6 @@ pub async fn dev_panel(
#[derive(Deserialize)] #[derive(Deserialize)]
pub struct LogsParams { pub struct LogsParams {
#[serde(default)]
pub log_file: String, pub log_file: String,
} }

View file

@ -31,6 +31,11 @@ use crate::{
const VALIDATION_TOKEN_KEY: &str = "validation_token"; const VALIDATION_TOKEN_KEY: &str = "validation_token";
#[derive(Deserialize)]
pub struct ValidationTokenParams {
validation_token: String,
}
/// SIGN UP /// /// SIGN UP ///
#[debug_handler] #[debug_handler]
@ -205,7 +210,7 @@ pub async fn sign_up_validation(
State(connection): State<db::Connection>, State(connection): State<db::Connection>,
Extension(context): Extension<Context>, Extension(context): Extension<Context>,
ConnectInfo(addr): ConnectInfo<SocketAddr>, ConnectInfo(addr): ConnectInfo<SocketAddr>,
Query(query): Query<HashMap<String, String>>, Query(params): Query<ValidationTokenParams>,
headers: HeaderMap, headers: HeaderMap,
) -> Result<(CookieJar, impl IntoResponse)> { ) -> Result<(CookieJar, impl IntoResponse)> {
let mut jar = CookieJar::from_headers(&headers); let mut jar = CookieJar::from_headers(&headers);
@ -223,73 +228,62 @@ pub async fn sign_up_validation(
)); ));
} }
let (client_ip, client_user_agent) = utils::get_ip_and_user_agent(&headers, addr); let (client_ip, client_user_agent) = utils::get_ip_and_user_agent(&headers, addr);
match query.get(VALIDATION_TOKEN_KEY) {
// 'validation_token' exists only when a user tries to validate a new account. match connection
Some(token) => { .validation(
match connection &params.validation_token,
.validation( Duration::seconds(consts::VALIDATION_TOKEN_DURATION),
token, &client_ip,
Duration::seconds(consts::VALIDATION_TOKEN_DURATION), &client_user_agent,
&client_ip, )
&client_user_agent, .await?
) {
.await? db::user::ValidationResult::Ok(token, user_id) => {
{ let cookie = Cookie::build((consts::COOKIE_AUTH_TOKEN_NAME, token))
db::user::ValidationResult::Ok(token, user_id) => { .secure(true)
let cookie = Cookie::build((consts::COOKIE_AUTH_TOKEN_NAME, token)) .same_site(cookie::SameSite::Strict);
.secure(true) jar = jar.add(cookie);
.same_site(cookie::SameSite::Strict); let user = connection.load_user(user_id).await?;
jar = jar.add(cookie);
let user = connection.load_user(user_id).await?;
Ok((
jar,
Html(
MessageTemplate::new(
context.tr.t(Sentence::SignUpEmailValidationSuccess),
Context { user, ..context },
)
.render()?,
),
))
}
db::user::ValidationResult::ValidationExpired => {
warn!("Unable to validate: validation expired. Token: {}", token);
Ok((
jar,
Html(
MessageTemplate::new(
context.tr.t(Sentence::SignUpValidationExpired),
context,
)
.render()?,
),
))
}
db::user::ValidationResult::UnknownUser => {
warn!("Unable to validate: unknown user. Token: {}", token);
Ok((
jar,
Html(
MessageTemplate::new(
context.tr.t(Sentence::SignUpValidationErrorTryAgain),
context,
)
.render()?,
),
))
}
}
}
None => {
warn!("Unable to validate: no token provided");
Ok(( Ok((
jar, jar,
Html( Html(
MessageTemplate::new(context.tr.t(Sentence::ValidationError), context) MessageTemplate::new(
context.tr.t(Sentence::SignUpEmailValidationSuccess),
Context { user, ..context },
)
.render()?,
),
))
}
db::user::ValidationResult::ValidationExpired => {
warn!(
"Unable to validate: validation expired. Token: {}",
&params.validation_token
);
Ok((
jar,
Html(
MessageTemplate::new(context.tr.t(Sentence::SignUpValidationExpired), context)
.render()?, .render()?,
), ),
)) ))
} }
db::user::ValidationResult::UnknownUser => {
warn!(
"Unable to validate: unknown user. Token: {}",
&params.validation_token
);
Ok((
jar,
Html(
MessageTemplate::new(
context.tr.t(Sentence::SignUpValidationErrorTryAgain),
context,
)
.render()?,
),
))
}
} }
} }
@ -560,38 +554,35 @@ pub async fn ask_reset_password_post(
} }
} }
#[derive(Deserialize)]
pub struct ResetPasswordGetParams {
reset_token: String,
}
#[debug_handler] #[debug_handler]
pub async fn reset_password_get( pub async fn reset_password_get(
State(connection): State<db::Connection>, State(connection): State<db::Connection>,
Extension(context): Extension<Context>, Extension(context): Extension<Context>,
Query(query): Query<HashMap<String, String>>, Query(params): Query<ResetPasswordGetParams>,
) -> Result<Response> { ) -> Result<Response> {
if let Some(reset_token) = query.get("reset_token") { // Check if the token is valid.
// Check if the token is valid. if connection
if connection .is_reset_password_token_valid(
.is_reset_password_token_valid( &params.reset_token,
reset_token, Duration::seconds(consts::VALIDATION_PASSWORD_RESET_TOKEN_DURATION),
Duration::seconds(consts::VALIDATION_PASSWORD_RESET_TOKEN_DURATION), )
) .await?
.await? {
{ Ok(Html(
Ok(Html( ResetPasswordTemplate {
ResetPasswordTemplate { context,
context, reset_token: &params.reset_token,
reset_token, message: "",
message: "", message_password: "",
message_password: "", }
} .render()?,
.render()?, )
) .into_response())
.into_response())
} else {
Ok(Html(
MessageTemplate::new(context.tr.t(Sentence::AskResetTokenMissing), context)
.render()?,
)
.into_response())
}
} else { } else {
Ok(Html( Ok(Html(
MessageTemplate::new(context.tr.t(Sentence::AskResetTokenMissing), context).render()?, MessageTemplate::new(context.tr.t(Sentence::AskResetTokenMissing), context).render()?,
@ -948,7 +939,7 @@ pub async fn email_revalidation(
State(connection): State<db::Connection>, State(connection): State<db::Connection>,
Extension(context): Extension<Context>, Extension(context): Extension<Context>,
ConnectInfo(addr): ConnectInfo<SocketAddr>, ConnectInfo(addr): ConnectInfo<SocketAddr>,
Query(query): Query<HashMap<String, String>>, Query(params): Query<ValidationTokenParams>,
headers: HeaderMap, headers: HeaderMap,
) -> Result<(CookieJar, impl IntoResponse)> { ) -> Result<(CookieJar, impl IntoResponse)> {
let mut jar = CookieJar::from_headers(&headers); let mut jar = CookieJar::from_headers(&headers);
@ -962,66 +953,55 @@ pub async fn email_revalidation(
)); ));
} }
let (client_ip, client_user_agent) = utils::get_ip_and_user_agent(&headers, addr); let (client_ip, client_user_agent) = utils::get_ip_and_user_agent(&headers, addr);
match query.get(VALIDATION_TOKEN_KEY) {
// 'validation_token' exists only when a user must validate a new email. match connection
Some(token) => { .validation(
match connection &params.validation_token,
.validation( Duration::seconds(consts::VALIDATION_TOKEN_DURATION),
token, &client_ip,
Duration::seconds(consts::VALIDATION_TOKEN_DURATION), &client_user_agent,
&client_ip, )
&client_user_agent, .await?
) {
.await? db::user::ValidationResult::Ok(token, user_id) => {
{ let cookie = Cookie::build((consts::COOKIE_AUTH_TOKEN_NAME, token))
db::user::ValidationResult::Ok(token, user_id) => { .secure(true)
let cookie = Cookie::build((consts::COOKIE_AUTH_TOKEN_NAME, token)) .same_site(cookie::SameSite::Strict);
.secure(true) jar = jar.add(cookie);
.same_site(cookie::SameSite::Strict); let user = connection.load_user(user_id).await?;
jar = jar.add(cookie); Ok((
let user = connection.load_user(user_id).await?; jar,
Ok(( Html(
jar, MessageTemplate::new(
Html( context.tr.t(Sentence::ValidationSuccessful),
MessageTemplate::new( Context { user, ..context },
context.tr.t(Sentence::ValidationSuccessful), )
Context { user, ..context }, .render()?,
) ),
.render()?, ))
), }
)) error @ db::user::ValidationResult::ValidationExpired => {
} error!("Token: {}: {}", &params.validation_token, error);
error @ db::user::ValidationResult::ValidationExpired => { Ok((
error!("Token: {}: {}", token, error); jar,
Ok(( Html(
jar, MessageTemplate::new(context.tr.t(Sentence::ValidationExpired), context)
Html( .render()?,
MessageTemplate::new( ),
context.tr.t(Sentence::ValidationExpired), ))
context, }
) error @ db::user::ValidationResult::UnknownUser => {
.render()?, error!("(email={}): {}", &params.validation_token, error);
), Ok((
)) jar,
} Html(
error @ db::user::ValidationResult::UnknownUser => { MessageTemplate::new(
error!("(email={}): {}", token, error); context.tr.t(Sentence::ValidationErrorTryToSignUpAgain),
Ok(( context,
jar, )
Html( .render()?,
MessageTemplate::new( ),
context.tr.t(Sentence::ValidationErrorTryToSignUpAgain), ))
context,
)
.render()?,
),
))
}
}
} }
None => Ok((
jar,
Html(MessageTemplate::new(context.tr.t(Sentence::ValidationError), context).render()?),
)),
} }
} }