diff --git a/crates/backend/src/controller.rs b/crates/backend/src/controller.rs index 7af3245..d243ed8 100644 --- a/crates/backend/src/controller.rs +++ b/crates/backend/src/controller.rs @@ -1,3 +1,5 @@ +mod auth; mod user; +pub use auth::AuthController; pub use user::UserController; diff --git a/crates/backend/src/controller/auth.rs b/crates/backend/src/controller/auth.rs new file mode 100644 index 0000000..de0e326 --- /dev/null +++ b/crates/backend/src/controller/auth.rs @@ -0,0 +1,44 @@ +use actix_web::{ + error::{ErrorBadRequest, ErrorInternalServerError}, + web, Responder, +}; +use argon2::{Argon2, PasswordHash, PasswordVerifier}; +use sea_orm::{ColumnTrait, EntityTrait, QueryFilter}; +use serde::Deserialize; + +use crate::AppState; + +use super::user::UserWithoutPassword; + +#[derive(Deserialize)] +pub struct LoginRequest { + email: String, + password: String, +} + +pub struct AuthController; + +impl AuthController { + pub async fn login( + state: web::Data, + login: web::Json, + ) -> actix_web::Result { + let db = &state.db; + let login = login.into_inner(); + + let user = entity::user::Entity::find() + .filter(entity::user::Column::Email.eq(login.email)) + .one(db) + .await + .map_err(ErrorInternalServerError)? + .ok_or(ErrorBadRequest("Login Failed"))?; + + let argon2 = Argon2::default(); + let parsed_hash = PasswordHash::new(&user.hash).map_err(ErrorInternalServerError)?; + argon2 + .verify_password(login.password.as_bytes(), &parsed_hash) + .map_err(ErrorBadRequest)?; + + Ok(web::Json(UserWithoutPassword::from(user))) + } +} diff --git a/crates/backend/src/controller/user.rs b/crates/backend/src/controller/user.rs index e38e558..814c778 100644 --- a/crates/backend/src/controller/user.rs +++ b/crates/backend/src/controller/user.rs @@ -3,7 +3,7 @@ use argon2::{ password_hash::{rand_core::OsRng, PasswordHasher, SaltString}, Argon2, }; -use sea_orm::{ActiveModelTrait, ActiveValue, DatabaseConnection, EntityTrait}; +use sea_orm::{ActiveModelTrait, ActiveValue, EntityTrait}; use serde::{Deserialize, Serialize}; use uuid::Uuid; @@ -69,7 +69,6 @@ impl UserController { name: ActiveValue::Set(user.name), email: ActiveValue::Set(user.email), hash: ActiveValue::Set(password_hash.to_string()), - salt: ActiveValue::Set(salt.to_string()), }; let result = user.insert(db).await.map_err(ErrorInternalServerError)?; diff --git a/crates/backend/src/main.rs b/crates/backend/src/main.rs index fc72f6f..b779476 100644 --- a/crates/backend/src/main.rs +++ b/crates/backend/src/main.rs @@ -1,4 +1,5 @@ -use actix_web::{web, App, HttpResponse, HttpServer, Responder}; +use actix_web::{web, App, HttpServer}; +use migration::MigratorTrait; use sea_orm::{Database, DatabaseConnection}; use std::env; @@ -24,8 +25,15 @@ async fn main() -> std::io::Result<()> { .await .expect("Connecting to Database failed"); + println!("Running Migrations"); + migration::Migrator::up(&conn, None) + .await + .expect("Running migrations failed"); + println!("Finished running migrations"); + let state = AppState { db: conn }; + println!("Listening for connections..."); HttpServer::new(move || { let cors = if cfg!(debug_assertions) { actix_cors::Cors::permissive() @@ -41,7 +49,3 @@ async fn main() -> std::io::Result<()> { .run() .await } - -async fn index() -> impl Responder { - HttpResponse::Ok().body("API Test Response") -} diff --git a/crates/backend/src/routes.rs b/crates/backend/src/routes.rs index 0a20d5a..e4b7cac 100644 --- a/crates/backend/src/routes.rs +++ b/crates/backend/src/routes.rs @@ -1,4 +1,4 @@ -use crate::controller::UserController; +use crate::controller::{AuthController, UserController}; use actix_web::web; pub fn config(cfg: &mut web::ServiceConfig) { @@ -9,6 +9,7 @@ pub fn config(cfg: &mut web::ServiceConfig) { .get(UserController::list_users) .post(UserController::create_user), ) - .service(web::resource("/users/{user_id}")), + .service(web::resource("/users/{user_id}")) + .service(web::scope("/auth").route("/login", web::post().to(AuthController::login))), ); } diff --git a/crates/entity/src/user.rs b/crates/entity/src/user.rs index 5c896f4..653e299 100644 --- a/crates/entity/src/user.rs +++ b/crates/entity/src/user.rs @@ -2,65 +2,18 @@ use sea_orm::entity::prelude::*; -#[derive(Copy, Clone, Default, Debug, DeriveEntity)] -pub struct Entity; - -impl EntityName for Entity { - fn table_name(&self) -> &str { - "user" - } -} - -#[derive(Clone, Debug, PartialEq, DeriveModel, DeriveActiveModel, Eq)] +#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq)] +#[sea_orm(table_name = "user")] pub struct Model { + #[sea_orm(primary_key, auto_increment = false)] pub id: Uuid, pub name: String, + #[sea_orm(unique)] pub email: String, pub hash: String, - pub salt: String, } -#[derive(Copy, Clone, Debug, EnumIter, DeriveColumn)] -pub enum Column { - Id, - Name, - Email, - Hash, - Salt, -} - -#[derive(Copy, Clone, Debug, EnumIter, DerivePrimaryKey)] -pub enum PrimaryKey { - Id, -} - -impl PrimaryKeyTrait for PrimaryKey { - type ValueType = Uuid; - fn auto_increment() -> bool { - false - } -} - -#[derive(Copy, Clone, Debug, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)] pub enum Relation {} -impl ColumnTrait for Column { - type EntityName = Entity; - fn def(&self) -> ColumnDef { - match self { - Self::Id => ColumnType::Uuid.def(), - Self::Name => ColumnType::String(None).def(), - Self::Email => ColumnType::String(None).def().unique(), - Self::Hash => ColumnType::String(None).def(), - Self::Salt => ColumnType::String(None).def(), - } - } -} - -impl RelationTrait for Relation { - fn def(&self) -> RelationDef { - panic!("No RelationDef") - } -} - impl ActiveModelBehavior for ActiveModel {} diff --git a/crates/migration/src/m20240705_100914_create_user.rs b/crates/migration/src/m20240705_100914_create_user.rs index a8c940f..2c8146c 100644 --- a/crates/migration/src/m20240705_100914_create_user.rs +++ b/crates/migration/src/m20240705_100914_create_user.rs @@ -21,7 +21,6 @@ impl MigrationTrait for Migration { .col(ColumnDef::new(User::Name).string().not_null()) .col(ColumnDef::new(User::Email).string().not_null().unique_key()) .col(ColumnDef::new(User::Hash).string().not_null()) - .col(ColumnDef::new(User::Salt).string().not_null()) .to_owned(), ) .await @@ -41,5 +40,4 @@ enum User { Name, Email, Hash, - Salt, }