working user signup!
This commit is contained in:
parent
c5c884f069
commit
dd17a68cf2
5 changed files with 107 additions and 25 deletions
|
@ -49,6 +49,17 @@ 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 {
|
||||
type Error = eyre::Error;
|
||||
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
use std::{collections::BTreeMap, sync::Arc};
|
||||
|
||||
use axum::{
|
||||
body::BoxBody,
|
||||
body::{BoxBody, Full},
|
||||
extract::{Path, State},
|
||||
http::header::{CONTENT_TYPE, SET_COOKIE},
|
||||
response::{IntoResponse, Response},
|
||||
Form,
|
||||
};
|
||||
|
@ -11,10 +12,11 @@ use sqlx::{query, query_as};
|
|||
use tracing::{error, info};
|
||||
|
||||
use crate::{
|
||||
extractor::ExtractUserToken,
|
||||
extractor::{ExtractUserToken, TOKEN_COOKIE_NAME},
|
||||
model::user::{Role, User},
|
||||
state::AppState,
|
||||
templates::{Page, PageData},
|
||||
token::Claims,
|
||||
};
|
||||
|
||||
pub async fn get_user(
|
||||
|
@ -59,19 +61,24 @@ pub async fn get_user_new(
|
|||
if user.role == Role::Admin {
|
||||
tracing::debug!("Authenticated user is admin");
|
||||
|
||||
return Page::new(
|
||||
state
|
||||
.hbs
|
||||
.render::<PageData>("new_user.hbs", &Default::default()),
|
||||
);
|
||||
return Page::new(state.hbs.render(
|
||||
"new_user.hbs",
|
||||
&PageData {
|
||||
authed_user: Some(user),
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
tracing::debug!("Authenticated user is not an admin");
|
||||
return Page::new(
|
||||
state
|
||||
.hbs
|
||||
.render::<PageData>("error.hbs", &Default::default()),
|
||||
);
|
||||
return Page::new(state.hbs.render::<PageData>(
|
||||
"error.hbs",
|
||||
&PageData {
|
||||
authed_user: Some(user),
|
||||
message: Some("You are not an admin".into()),
|
||||
..Default::default()
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
tracing::debug!("There is no authenticated user");
|
||||
|
@ -189,11 +196,17 @@ 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) VALUES ($1, $2) RETURNING *"#,
|
||||
r#"INSERT INTO users (username, password, role) VALUES ($1, $2, $3) RETURNING *"#,
|
||||
form_data.username,
|
||||
hashed_password,
|
||||
<Role as Into<i32>>::into(role),
|
||||
)
|
||||
.fetch_one(&state.pool)
|
||||
.await
|
||||
|
@ -226,11 +239,51 @@ pub async fn post_user_new(
|
|||
.into_response();
|
||||
}
|
||||
|
||||
Page::new(state.hbs.render(
|
||||
"new_user.hbs",
|
||||
&PageData {
|
||||
// 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",
|
||||
&PageData {
|
||||
authed_user: Some(user.clone()),
|
||||
user: Some(user),
|
||||
message: Some("Your account has been created!".into()),
|
||||
},
|
||||
))
|
||||
.into_response()
|
||||
) {
|
||||
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();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use axum::{
|
||||
http::{header, StatusCode},
|
||||
body::Full,
|
||||
http::{header, Response, StatusCode},
|
||||
response::IntoResponse,
|
||||
};
|
||||
use eyre::{Context, Result};
|
||||
|
@ -42,10 +43,19 @@ impl Page {
|
|||
impl IntoResponse for Page {
|
||||
fn into_response(self) -> axum::response::Response {
|
||||
match self.0 {
|
||||
Ok(s) => ([(header::CONTENT_TYPE, "text/html; charset=utf-8")], s).into_response(),
|
||||
Ok(s) => Response::builder()
|
||||
.header(header::CONTENT_TYPE, "text/html; charset=utf-8")
|
||||
.body(Full::from(s))
|
||||
.unwrap()
|
||||
.into_response(),
|
||||
Err(err) => {
|
||||
error!("Error rendering page: {}", err);
|
||||
(StatusCode::INTERNAL_SERVER_ERROR, "Internal server error").into_response()
|
||||
error!("Rendering page: {}", err);
|
||||
|
||||
return Response::builder()
|
||||
.status(StatusCode::INTERNAL_SERVER_ERROR)
|
||||
.body(Full::from("Internal server error"))
|
||||
.unwrap()
|
||||
.into_response();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -57,4 +67,6 @@ pub struct PageData {
|
|||
|
||||
// An optional flash message. Not all templates will handle this.
|
||||
pub message: Option<String>,
|
||||
|
||||
pub authed_user: Option<User>,
|
||||
}
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
use chrono::{Duration, Utc};
|
||||
use jsonwebtoken::{errors::Error, DecodingKey, EncodingKey, Header, Validation};
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
|
@ -12,8 +13,13 @@ 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: 0,
|
||||
exp: now.timestamp() as usize,
|
||||
uid: user.id,
|
||||
role: user.role.into(),
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
</div>
|
||||
{{/if}}
|
||||
<ul>
|
||||
<li>ID: {{new_user.id}}</li>
|
||||
<li>Username: {{new_user.username}}</li>
|
||||
<li>ID: {{user.id}}</li>
|
||||
<li>Username: {{user.username}}</li>
|
||||
</ul>
|
||||
{{/inline}}
|
||||
{{> root.hbs}}
|
||||
|
|
Loading…
Reference in a new issue