Add basic register and login
All checks were successful
Build Crate / build (push) Successful in 1m45s
All checks were successful
Build Crate / build (push) Successful in 1m45s
This commit is contained in:
@@ -1,24 +1,60 @@
|
||||
use crate::entity::prelude::*;
|
||||
use crate::models::auth::{Claims, LoginInfo, LoginResponse};
|
||||
use crate::{
|
||||
models::auth::{Claims, LoginInfo, LoginResponse},
|
||||
entity::user::{self, Entity},
|
||||
models::auth::RegisterRequest,
|
||||
server::server_state::ServerState,
|
||||
};
|
||||
use argon2::password_hash::rand_core::OsRng;
|
||||
use argon2::password_hash::SaltString;
|
||||
use argon2::Argon2;
|
||||
use argon2::PasswordHash;
|
||||
use argon2::PasswordHasher;
|
||||
use argon2::PasswordVerifier;
|
||||
use axum::{
|
||||
extract::{Json, State},
|
||||
http::{HeaderMap, StatusCode},
|
||||
};
|
||||
use futures::sink::Fanout;
|
||||
use sea_orm::ColumnTrait;
|
||||
use sea_orm::EntityTrait;
|
||||
use sea_orm::InsertResult;
|
||||
use sea_orm::QueryFilter;
|
||||
use sea_orm::Set;
|
||||
|
||||
use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation};
|
||||
|
||||
pub fn is_valid_user(username: &str, password: &str) -> bool {
|
||||
return true;
|
||||
pub async fn credentials_are_correct(username: &str, password: &str, state: &ServerState) -> bool {
|
||||
// Get user
|
||||
let existing_user: Option<user::Model> = User::find()
|
||||
.filter(user::Column::Username.eq(username))
|
||||
.one(&state.db_conn)
|
||||
.await
|
||||
.unwrap();
|
||||
let hash_to_check: String = match existing_user {
|
||||
Some(user) => user.password_hash_and_salt,
|
||||
None => {
|
||||
// @todo make dummy password hash
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
return Argon2::default()
|
||||
.verify_password(
|
||||
password.as_bytes(),
|
||||
&PasswordHash::new(hash_to_check.as_str()).unwrap(),
|
||||
)
|
||||
.is_ok();
|
||||
}
|
||||
|
||||
pub async fn post_login_user(
|
||||
State(state): State<ServerState>,
|
||||
Json(login_info): Json<LoginInfo>,
|
||||
) -> Result<Json<LoginResponse>, StatusCode> {
|
||||
let user_authenticated = is_valid_user(&login_info.username, &login_info.password);
|
||||
let user_authenticated =
|
||||
credentials_are_correct(&login_info.username, &login_info.password, &state);
|
||||
|
||||
match user_authenticated {
|
||||
match user_authenticated.await {
|
||||
false => Err(StatusCode::UNAUTHORIZED),
|
||||
true => {
|
||||
let expires = match login_info.remember_me {
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
pub mod forgot_password;
|
||||
pub mod login;
|
||||
pub mod register;
|
||||
|
||||
89
src/server/auth/register.rs
Normal file
89
src/server/auth/register.rs
Normal file
@@ -0,0 +1,89 @@
|
||||
use crate::entity::prelude::*;
|
||||
use crate::models::generic::GenericResponse;
|
||||
use argon2::password_hash::rand_core::OsRng;
|
||||
use argon2::password_hash::SaltString;
|
||||
use argon2::Argon2;
|
||||
use argon2::PasswordHash;
|
||||
use argon2::PasswordHasher;
|
||||
use axum::{extract::State, http::StatusCode, Json};
|
||||
use chrono::Utc;
|
||||
use sea_orm::ColumnTrait;
|
||||
use sea_orm::EntityTrait;
|
||||
use sea_orm::InsertResult;
|
||||
use sea_orm::QueryFilter;
|
||||
use sea_orm::Set;
|
||||
|
||||
use crate::{
|
||||
entity::user::{self, Entity},
|
||||
models::auth::RegisterRequest,
|
||||
server::server_state::ServerState,
|
||||
};
|
||||
|
||||
pub async fn post_register_user(
|
||||
State(state): State<ServerState>,
|
||||
Json(register_info): Json<RegisterRequest>,
|
||||
) -> (StatusCode, Json<GenericResponse>) {
|
||||
// TODO -> update to use env, maybe prevent brute force too
|
||||
if register_info.registration_code != "ferris" {
|
||||
return (
|
||||
StatusCode::UNAUTHORIZED,
|
||||
Json(GenericResponse::err("Incorrect registration code")),
|
||||
);
|
||||
}
|
||||
|
||||
// See if username already exists
|
||||
let username = register_info.username;
|
||||
let existing_user: Option<user::Model> = User::find()
|
||||
.filter(user::Column::Username.eq(username.clone()))
|
||||
.one(&state.db_conn)
|
||||
.await
|
||||
.unwrap();
|
||||
if existing_user.is_some() {
|
||||
return (
|
||||
StatusCode::BAD_REQUEST,
|
||||
Json(GenericResponse::err("Username already exists")),
|
||||
);
|
||||
}
|
||||
|
||||
// Generate password
|
||||
let salt = SaltString::generate(&mut OsRng);
|
||||
let argon2 = Argon2::default();
|
||||
let password_hash = argon2
|
||||
.hash_password(register_info.password.as_bytes(), &salt)
|
||||
.unwrap()
|
||||
.to_string();
|
||||
let phc_string = PasswordHash::new(&password_hash).unwrap().to_string();
|
||||
|
||||
// If the username doen't exist, create the user
|
||||
let new_user = user::ActiveModel {
|
||||
username: Set(username),
|
||||
password_hash_and_salt: Set(phc_string),
|
||||
nickname: Set({
|
||||
if register_info.nickname == "" {
|
||||
None
|
||||
} else {
|
||||
Some(register_info.nickname)
|
||||
}
|
||||
}),
|
||||
creation_time: Set(Utc::now().naive_utc()),
|
||||
last_active_time: Set(Utc::now().naive_utc()),
|
||||
is_admin: Set(false),
|
||||
email: Set({
|
||||
if register_info.email == "" {
|
||||
None
|
||||
} else {
|
||||
Some(register_info.email)
|
||||
}
|
||||
}),
|
||||
avatar: Set(None),
|
||||
forgot_password_request: Set(None),
|
||||
..Default::default()
|
||||
};
|
||||
// TODO -> error handling
|
||||
let db_resp = user::Entity::insert(new_user)
|
||||
.exec(&state.db_conn)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
return (StatusCode::OK, Json(GenericResponse::ok()));
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
// (Server only) Routes
|
||||
use crate::endpoints::{FORGOT_PASSWORD, LOGIN, LOGIN_TEST};
|
||||
use crate::endpoints::{FORGOT_PASSWORD, LOGIN, LOGIN_TEST, REGISTER};
|
||||
use axum::routing::{post, Router};
|
||||
use futures::executor::block_on;
|
||||
use sea_orm::Database;
|
||||
@@ -8,12 +8,14 @@ use super::{
|
||||
auth::{
|
||||
forgot_password::post_forgot_password,
|
||||
login::{post_login_user, post_test_login},
|
||||
register::post_register_user,
|
||||
},
|
||||
server_state::ServerState,
|
||||
};
|
||||
|
||||
pub fn get_api_router(state: ServerState) -> Router {
|
||||
Router::new()
|
||||
.route(REGISTER, post(post_register_user))
|
||||
.route(LOGIN, post(post_login_user))
|
||||
.route(LOGIN_TEST, post(post_test_login))
|
||||
.route(FORGOT_PASSWORD, post(post_forgot_password))
|
||||
|
||||
Reference in New Issue
Block a user