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",
 | 
			
		||||
    // 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 {
 | 
			
		||||
            ..Default::default()
 | 
			
		||||
            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…
	
	Add table
		Add a link
		
	
		Reference in a new issue