diff --git a/src/extractor.rs b/src/extractor.rs index b037f5c..63daeab 100644 --- a/src/extractor.rs +++ b/src/extractor.rs @@ -11,8 +11,6 @@ use crate::{model::user::User, state::AppState, token::Claims}; pub struct ExtractUserToken(pub Option); -pub const TOKEN_COOKIE_NAME: &'static str = "imgboard-token"; - #[async_trait] impl FromRequestParts for ExtractUserToken where @@ -24,8 +22,8 @@ where async fn from_request_parts(parts: &mut Parts, state: &S) -> Result { let state = match parts.extract_with_state::(state).await { Ok(s) => s, - Err(err) => { - error!("Getting state: {}", err); + Err(why) => { + error!("Getting state: {}", why); return Err((StatusCode::INTERNAL_SERVER_ERROR, "Internal server error")); } }; @@ -38,7 +36,7 @@ where } }; - match cookie.get(TOKEN_COOKIE_NAME) { + match cookie.get("imgboard-token") { Some(token) => { let claims = Claims::decode(token, &state.decoding_key).map_err(|e| { error!("Decoding token claims: {}", e); diff --git a/src/model/user.rs b/src/model/user.rs index b4f4cba..2d01ab9 100644 --- a/src/model/user.rs +++ b/src/model/user.rs @@ -49,17 +49,6 @@ impl From for Role { } } -impl Into for Role { - fn into(self) -> i32 { - match self { - Self::Viewer => 1, - Self::Editor => 2, - Self::Manager => 3, - Self::Admin => 4, - } - } -} - impl TryFrom<&str> for Role { type Error = eyre::Error; diff --git a/src/pages/user/mod.rs b/src/pages/user/mod.rs index 56177f9..4ff147d 100644 --- a/src/pages/user/mod.rs +++ b/src/pages/user/mod.rs @@ -1,9 +1,8 @@ use std::{collections::BTreeMap, sync::Arc}; use axum::{ - body::{BoxBody, Full}, + body::BoxBody, extract::{Path, State}, - http::header::{CONTENT_TYPE, SET_COOKIE}, response::{IntoResponse, Response}, Form, }; @@ -12,11 +11,10 @@ use sqlx::{query, query_as}; use tracing::{error, info}; use crate::{ - extractor::{ExtractUserToken, TOKEN_COOKIE_NAME}, + extractor::ExtractUserToken, model::user::{Role, User}, state::AppState, templates::{Page, PageData}, - token::Claims, }; pub async fn get_user( @@ -28,8 +26,8 @@ pub async fn get_user( .await { Ok(user) => user, - Err(err) => { - error!("Getting user: {}", err); + Err(why) => { + error!("Getting user: {}", why); return Page::new(state.hbs.render( "error.hbs", &PageData { @@ -61,24 +59,19 @@ pub async fn get_user_new( if user.role == Role::Admin { tracing::debug!("Authenticated user is admin"); - return Page::new(state.hbs.render( - "new_user.hbs", - &PageData { - authed_user: Some(user), - ..Default::default() - }, - )); + return Page::new( + state + .hbs + .render::("new_user.hbs", &Default::default()), + ); } tracing::debug!("Authenticated user is not an admin"); - return Page::new(state.hbs.render::( - "error.hbs", - &PageData { - authed_user: Some(user), - message: Some("You are not an admin".into()), - ..Default::default() - }, - )); + return Page::new( + state + .hbs + .render::("error.hbs", &Default::default()), + ); } tracing::debug!("There is no authenticated user"); @@ -89,8 +82,8 @@ pub async fn get_user_new( .await { Ok(r) => r.count.unwrap_or(0), - Err(err) => { - error!("Getting user count: {}", err); + Err(why) => { + error!("Getting user count: {}", why); return Page::new( state .hbs @@ -125,8 +118,8 @@ pub async fn post_user_new( .await { Ok(r) => r.count.unwrap_or(0), - Err(err) => { - error!("Getting user count: {}", err); + Err(why) => { + error!("Getting user count: {}", why); return Page::new(state.hbs.render( "error.hbs", @@ -182,8 +175,8 @@ pub async fn post_user_new( let hashed_password = match bcrypt::hash(form_data.password, 12) { Ok(hash) => hash, - Err(err) => { - error!("Hashing password: {}", err); + Err(why) => { + error!("Hashing password: {}", why); return Page::new(state.hbs.render( "error.hbs", @@ -196,24 +189,18 @@ pub async fn post_user_new( } }; - let role = match existing_user.is_some() { - true => Role::Viewer, - false => Role::Admin, - }; - let user = match query_as!( User, - r#"INSERT INTO users (username, password, role) VALUES ($1, $2, $3) RETURNING *"#, + r#"INSERT INTO users (username, password) VALUES ($1, $2) RETURNING *"#, form_data.username, hashed_password, - >::into(role), ) .fetch_one(&state.pool) .await { Ok(u) => u, - Err(err) => { - error!("Creating user: {}", err); + Err(why) => { + error!("Creating user: {}", why); return Page::new(state.hbs.render( "error.hbs", @@ -239,51 +226,11 @@ pub async fn post_user_new( .into_response(); } - // Else, create cookie - let token = match Claims::new(&user).encode(&state.encoding_key) { - Ok(token) => token, - Err(err) => { - error!("Encoding token: {}", err); - - return Page::new(state.hbs.render("error.hbs", &PageData{ - message: Some("Internal server error. Note: your account was created successfully, please log in manually.".into()), - ..Default::default() - })).into_response(); - } - }; - - let content = match state.hbs.render( - "new_user_confirm.hbs", + Page::new(state.hbs.render( + "new_user.hbs", &PageData { - authed_user: Some(user.clone()), - user: Some(user), - message: Some("Your account has been created!".into()), + ..Default::default() }, - ) { - Ok(content) => content, - Err(err) => { - error!("Rendering content: {}", err); - - return Page::new(state.hbs.render("error.hbs", &PageData{ - message: Some("Internal server error. Note: your account was created successfully, please log in manually.".into()), - ..Default::default() - })).into_response(); - } - }; - - return match Response::builder() - .header(SET_COOKIE, format!("{}={}", TOKEN_COOKIE_NAME, token)) - .header(CONTENT_TYPE, "text/html; charset=utf-8") - .body(Full::from(content)) - { - Ok(resp) => resp.into_response(), - Err(err) => { - error!("Building response: {}", err); - - return Page::new(state.hbs.render("error.hbs", &PageData{ - message: Some("Internal server error. Note: your account was created successfully, please log in manually.".into()), - ..Default::default() - })).into_response(); - } - }; + )) + .into_response() } diff --git a/src/templates.rs b/src/templates.rs index b3bc535..44dbb1c 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -1,6 +1,5 @@ use axum::{ - body::Full, - http::{header, Response, StatusCode}, + http::{header, StatusCode}, response::IntoResponse, }; use eyre::{Context, Result}; @@ -43,19 +42,10 @@ impl Page { impl IntoResponse for Page { fn into_response(self) -> axum::response::Response { match self.0 { - Ok(s) => Response::builder() - .header(header::CONTENT_TYPE, "text/html; charset=utf-8") - .body(Full::from(s)) - .unwrap() - .into_response(), - Err(err) => { - error!("Rendering page: {}", err); - - return Response::builder() - .status(StatusCode::INTERNAL_SERVER_ERROR) - .body(Full::from("Internal server error")) - .unwrap() - .into_response(); + Ok(s) => ([(header::CONTENT_TYPE, "text/html; charset=utf-8")], s).into_response(), + Err(why) => { + error!("Error rendering page: {}", why); + (StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response() } } } @@ -67,6 +57,4 @@ pub struct PageData { // An optional flash message. Not all templates will handle this. pub message: Option, - - pub authed_user: Option, } diff --git a/src/token.rs b/src/token.rs index b9df0d8..c78d045 100644 --- a/src/token.rs +++ b/src/token.rs @@ -1,4 +1,3 @@ -use chrono::{Duration, Utc}; use jsonwebtoken::{errors::Error, DecodingKey, EncodingKey, Header, Validation}; use serde::{Deserialize, Serialize}; @@ -13,13 +12,8 @@ pub struct Claims { impl Claims { pub fn new(user: &User) -> Self { - // Expire tokens after 90 days - // TODO: give tokens an ID to expire them earlier if necessary? Or a salt that's changed on - // password update - let now = Utc::now() + Duration::days(90); - Self { - exp: now.timestamp() as usize, + exp: 0, uid: user.id, role: user.role.into(), } diff --git a/templates/new_user_confirm.hbs b/templates/new_user_confirm.hbs index ed6fd27..33ee67b 100644 --- a/templates/new_user_confirm.hbs +++ b/templates/new_user_confirm.hbs @@ -6,8 +6,8 @@ {{/if}}
    -
  • ID: {{user.id}}
  • -
  • Username: {{user.username}}
  • +
  • ID: {{new_user.id}}
  • +
  • Username: {{new_user.username}}
{{/inline}} {{> root.hbs}}