diff options
| author | mrw1593 <botahamec@outlook.com> | 2023-05-13 12:22:11 -0400 |
|---|---|---|
| committer | mrw1593 <botahamec@outlook.com> | 2023-05-29 10:45:54 -0400 |
| commit | 0b55587443103b20491139d54670474a35286be8 (patch) | |
| tree | 7aed93ba53a71ce245208afb6593e7726e8b0026 | |
| parent | efe24b616f91121512cb6b2c61cd9b850f943e2d (diff) | |
Add a login endpoint that does nothing
| -rw-r--r-- | src/api/mod.rs | 2 | ||||
| -rw-r--r-- | src/api/ops.rs | 59 | ||||
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/models/user.rs | 5 | ||||
| -rw-r--r-- | src/services/db.rs | 18 |
5 files changed, 85 insertions, 0 deletions
diff --git a/src/api/mod.rs b/src/api/mod.rs index 7becfbd..7627a60 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -1,5 +1,7 @@ mod liveops; +mod ops; mod users; pub use liveops::service as liveops; +pub use ops::service as ops; pub use users::service as users; diff --git a/src/api/ops.rs b/src/api/ops.rs new file mode 100644 index 0000000..9bebc2d --- /dev/null +++ b/src/api/ops.rs @@ -0,0 +1,59 @@ +use actix_web::{http::StatusCode, post, web, HttpResponse, ResponseError, Scope}; +use raise::yeet; +use serde::Deserialize; +use sqlx::MySqlPool; +use thiserror::Error; + +use crate::services::db; + +#[derive(Debug, Clone, Deserialize)] +struct LoginRequest { + username: Box<str>, + password: Box<str>, +} + +#[derive(Debug, Clone, Error)] +enum LoginFailure { + #[error("No user found with the given username")] + UserNotFound { username: Box<str> }, + #[error("The given password is incorrect")] + IncorrectPassword { username: Box<str> }, +} + +impl ResponseError for LoginFailure { + fn status_code(&self) -> actix_web::http::StatusCode { + match self { + Self::UserNotFound { .. } => StatusCode::NOT_FOUND, + Self::IncorrectPassword { .. } => StatusCode::UNAUTHORIZED, + } + } +} + +#[post("/login")] +async fn login( + body: web::Json<LoginRequest>, + conn: web::Data<MySqlPool>, +) -> Result<HttpResponse, LoginFailure> { + let conn = conn.get_ref(); + + let user = db::get_user_by_username(conn, &body.username) + .await + .unwrap(); + let Some(user) = user else { + yeet!(LoginFailure::UserNotFound{ username: body.username.clone() }); + }; + + let good_password = user.check_password(&body.password).unwrap(); + let response = if good_password { + HttpResponse::Ok().finish() + } else { + yeet!(LoginFailure::IncorrectPassword { + username: body.username.clone() + }); + }; + Ok(response) +} + +pub fn service() -> Scope { + web::scope("").service(login) +} diff --git a/src/main.rs b/src/main.rs index 7cc694d..dff3f3f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -16,6 +16,7 @@ async fn main() -> Result<(), RawUnexpected> { .app_data(Data::new(sql_pool.clone())) .service(api::liveops()) .service(api::users()) + .service(api::ops()) }) .shutdown_timeout(1) .bind(("127.0.0.1", 8080))? diff --git a/src/models/user.rs b/src/models/user.rs index f5fd20e..4649890 100644 --- a/src/models/user.rs +++ b/src/models/user.rs @@ -1,5 +1,6 @@ use std::hash::Hash; +use exun::RawUnexpected; use uuid::Uuid; use crate::services::crypto::PasswordHash; @@ -41,4 +42,8 @@ impl User { pub fn password_version(&self) -> u8 { self.password.version() } + + pub fn check_password(&self, password: &str) -> Result<bool, RawUnexpected> { + self.password.check_password(password) + } } diff --git a/src/services/db.rs b/src/services/db.rs index 3eee9ba..80335c4 100644 --- a/src/services/db.rs +++ b/src/services/db.rs @@ -84,6 +84,24 @@ pub async fn get_user<'c>( Ok(Some(record.try_into()?)) } +pub async fn get_user_by_username<'c>( + conn: impl Executor<'c, Database = MySql>, + username: &str, +) -> Result<Option<User>, RawUnexpected> { + let record = query_as!( + UserRow, + r"SELECT user_id, username, password_hash, password_salt, password_version + FROM users WHERE username = ?", + username + ) + .fetch_optional(conn) + .await?; + + let Some(record) = record else { return Ok(None) }; + + Ok(Some(record.try_into()?)) +} + pub async fn get_username<'c>( conn: impl Executor<'c, Database = MySql>, user_id: Uuid, |
