Compare commits
No commits in common. "dd17a68cf221ae6c173618499fdc19b00799c3df" and "99246773efca83551c5cc21fd65fb7a0cea64263" have entirely different histories.
dd17a68cf2
...
99246773ef
6 changed files with 39 additions and 123 deletions
|
@ -11,8 +11,6 @@ use crate::{model::user::User, state::AppState, token::Claims};
|
||||||
|
|
||||||
pub struct ExtractUserToken(pub Option<User>);
|
pub struct ExtractUserToken(pub Option<User>);
|
||||||
|
|
||||||
pub const TOKEN_COOKIE_NAME: &'static str = "imgboard-token";
|
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl<S> FromRequestParts<S> for ExtractUserToken
|
impl<S> FromRequestParts<S> for ExtractUserToken
|
||||||
where
|
where
|
||||||
|
@ -24,8 +22,8 @@ where
|
||||||
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
async fn from_request_parts(parts: &mut Parts, state: &S) -> Result<Self, Self::Rejection> {
|
||||||
let state = match parts.extract_with_state::<AppState, _>(state).await {
|
let state = match parts.extract_with_state::<AppState, _>(state).await {
|
||||||
Ok(s) => s,
|
Ok(s) => s,
|
||||||
Err(err) => {
|
Err(why) => {
|
||||||
error!("Getting state: {}", err);
|
error!("Getting state: {}", why);
|
||||||
return Err((StatusCode::INTERNAL_SERVER_ERROR, "Internal server error"));
|
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) => {
|
Some(token) => {
|
||||||
let claims = Claims::decode(token, &state.decoding_key).map_err(|e| {
|
let claims = Claims::decode(token, &state.decoding_key).map_err(|e| {
|
||||||
error!("Decoding token claims: {}", e);
|
error!("Decoding token claims: {}", e);
|
||||||
|
|
|
@ -49,17 +49,6 @@ impl From<i32> for Role {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Into<i32> 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 {
|
impl TryFrom<&str> for Role {
|
||||||
type Error = eyre::Error;
|
type Error = eyre::Error;
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,8 @@
|
||||||
use std::{collections::BTreeMap, sync::Arc};
|
use std::{collections::BTreeMap, sync::Arc};
|
||||||
|
|
||||||
use axum::{
|
use axum::{
|
||||||
body::{BoxBody, Full},
|
body::BoxBody,
|
||||||
extract::{Path, State},
|
extract::{Path, State},
|
||||||
http::header::{CONTENT_TYPE, SET_COOKIE},
|
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Response},
|
||||||
Form,
|
Form,
|
||||||
};
|
};
|
||||||
|
@ -12,11 +11,10 @@ use sqlx::{query, query_as};
|
||||||
use tracing::{error, info};
|
use tracing::{error, info};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
extractor::{ExtractUserToken, TOKEN_COOKIE_NAME},
|
extractor::ExtractUserToken,
|
||||||
model::user::{Role, User},
|
model::user::{Role, User},
|
||||||
state::AppState,
|
state::AppState,
|
||||||
templates::{Page, PageData},
|
templates::{Page, PageData},
|
||||||
token::Claims,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub async fn get_user(
|
pub async fn get_user(
|
||||||
|
@ -28,8 +26,8 @@ pub async fn get_user(
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(user) => user,
|
Ok(user) => user,
|
||||||
Err(err) => {
|
Err(why) => {
|
||||||
error!("Getting user: {}", err);
|
error!("Getting user: {}", why);
|
||||||
return Page::new(state.hbs.render(
|
return Page::new(state.hbs.render(
|
||||||
"error.hbs",
|
"error.hbs",
|
||||||
&PageData {
|
&PageData {
|
||||||
|
@ -61,24 +59,19 @@ pub async fn get_user_new(
|
||||||
if user.role == Role::Admin {
|
if user.role == Role::Admin {
|
||||||
tracing::debug!("Authenticated user is admin");
|
tracing::debug!("Authenticated user is admin");
|
||||||
|
|
||||||
return Page::new(state.hbs.render(
|
return Page::new(
|
||||||
"new_user.hbs",
|
state
|
||||||
&PageData {
|
.hbs
|
||||||
authed_user: Some(user),
|
.render::<PageData>("new_user.hbs", &Default::default()),
|
||||||
..Default::default()
|
);
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!("Authenticated user is not an admin");
|
tracing::debug!("Authenticated user is not an admin");
|
||||||
return Page::new(state.hbs.render::<PageData>(
|
return Page::new(
|
||||||
"error.hbs",
|
state
|
||||||
&PageData {
|
.hbs
|
||||||
authed_user: Some(user),
|
.render::<PageData>("error.hbs", &Default::default()),
|
||||||
message: Some("You are not an admin".into()),
|
);
|
||||||
..Default::default()
|
|
||||||
},
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tracing::debug!("There is no authenticated user");
|
tracing::debug!("There is no authenticated user");
|
||||||
|
@ -89,8 +82,8 @@ pub async fn get_user_new(
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(r) => r.count.unwrap_or(0),
|
Ok(r) => r.count.unwrap_or(0),
|
||||||
Err(err) => {
|
Err(why) => {
|
||||||
error!("Getting user count: {}", err);
|
error!("Getting user count: {}", why);
|
||||||
return Page::new(
|
return Page::new(
|
||||||
state
|
state
|
||||||
.hbs
|
.hbs
|
||||||
|
@ -125,8 +118,8 @@ pub async fn post_user_new(
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(r) => r.count.unwrap_or(0),
|
Ok(r) => r.count.unwrap_or(0),
|
||||||
Err(err) => {
|
Err(why) => {
|
||||||
error!("Getting user count: {}", err);
|
error!("Getting user count: {}", why);
|
||||||
|
|
||||||
return Page::new(state.hbs.render(
|
return Page::new(state.hbs.render(
|
||||||
"error.hbs",
|
"error.hbs",
|
||||||
|
@ -182,8 +175,8 @@ pub async fn post_user_new(
|
||||||
|
|
||||||
let hashed_password = match bcrypt::hash(form_data.password, 12) {
|
let hashed_password = match bcrypt::hash(form_data.password, 12) {
|
||||||
Ok(hash) => hash,
|
Ok(hash) => hash,
|
||||||
Err(err) => {
|
Err(why) => {
|
||||||
error!("Hashing password: {}", err);
|
error!("Hashing password: {}", why);
|
||||||
|
|
||||||
return Page::new(state.hbs.render(
|
return Page::new(state.hbs.render(
|
||||||
"error.hbs",
|
"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!(
|
let user = match query_as!(
|
||||||
User,
|
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,
|
form_data.username,
|
||||||
hashed_password,
|
hashed_password,
|
||||||
<Role as Into<i32>>::into(role),
|
|
||||||
)
|
)
|
||||||
.fetch_one(&state.pool)
|
.fetch_one(&state.pool)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(u) => u,
|
Ok(u) => u,
|
||||||
Err(err) => {
|
Err(why) => {
|
||||||
error!("Creating user: {}", err);
|
error!("Creating user: {}", why);
|
||||||
|
|
||||||
return Page::new(state.hbs.render(
|
return Page::new(state.hbs.render(
|
||||||
"error.hbs",
|
"error.hbs",
|
||||||
|
@ -239,51 +226,11 @@ pub async fn post_user_new(
|
||||||
.into_response();
|
.into_response();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Else, create cookie
|
Page::new(state.hbs.render(
|
||||||
let token = match Claims::new(&user).encode(&state.encoding_key) {
|
"new_user.hbs",
|
||||||
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",
|
|
||||||
&PageData {
|
&PageData {
|
||||||
authed_user: Some(user.clone()),
|
..Default::default()
|
||||||
user: Some(user),
|
|
||||||
message: Some("Your account has been created!".into()),
|
|
||||||
},
|
},
|
||||||
) {
|
))
|
||||||
Ok(content) => content,
|
.into_response()
|
||||||
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();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use axum::{
|
use axum::{
|
||||||
body::Full,
|
http::{header, StatusCode},
|
||||||
http::{header, Response, StatusCode},
|
|
||||||
response::IntoResponse,
|
response::IntoResponse,
|
||||||
};
|
};
|
||||||
use eyre::{Context, Result};
|
use eyre::{Context, Result};
|
||||||
|
@ -43,19 +42,10 @@ impl Page {
|
||||||
impl IntoResponse for Page {
|
impl IntoResponse for Page {
|
||||||
fn into_response(self) -> axum::response::Response {
|
fn into_response(self) -> axum::response::Response {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Ok(s) => Response::builder()
|
Ok(s) => ([(header::CONTENT_TYPE, "text/html; charset=utf-8")], s).into_response(),
|
||||||
.header(header::CONTENT_TYPE, "text/html; charset=utf-8")
|
Err(why) => {
|
||||||
.body(Full::from(s))
|
error!("Error rendering page: {}", why);
|
||||||
.unwrap()
|
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
|
||||||
.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();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -67,6 +57,4 @@ pub struct PageData {
|
||||||
|
|
||||||
// An optional flash message. Not all templates will handle this.
|
// An optional flash message. Not all templates will handle this.
|
||||||
pub message: Option<String>,
|
pub message: Option<String>,
|
||||||
|
|
||||||
pub authed_user: Option<User>,
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
use chrono::{Duration, Utc};
|
|
||||||
use jsonwebtoken::{errors::Error, DecodingKey, EncodingKey, Header, Validation};
|
use jsonwebtoken::{errors::Error, DecodingKey, EncodingKey, Header, Validation};
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
@ -13,13 +12,8 @@ pub struct Claims {
|
||||||
|
|
||||||
impl Claims {
|
impl Claims {
|
||||||
pub fn new(user: &User) -> Self {
|
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 {
|
Self {
|
||||||
exp: now.timestamp() as usize,
|
exp: 0,
|
||||||
uid: user.id,
|
uid: user.id,
|
||||||
role: user.role.into(),
|
role: user.role.into(),
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
</div>
|
</div>
|
||||||
{{/if}}
|
{{/if}}
|
||||||
<ul>
|
<ul>
|
||||||
<li>ID: {{user.id}}</li>
|
<li>ID: {{new_user.id}}</li>
|
||||||
<li>Username: {{user.username}}</li>
|
<li>Username: {{new_user.username}}</li>
|
||||||
</ul>
|
</ul>
|
||||||
{{/inline}}
|
{{/inline}}
|
||||||
{{> root.hbs}}
|
{{> root.hbs}}
|
||||||
|
|
Loading…
Reference in a new issue