summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormrw1593 <botahamec@outlook.com>2023-05-13 12:22:11 -0400
committermrw1593 <botahamec@outlook.com>2023-05-29 10:45:54 -0400
commit0b55587443103b20491139d54670474a35286be8 (patch)
tree7aed93ba53a71ce245208afb6593e7726e8b0026
parentefe24b616f91121512cb6b2c61cd9b850f943e2d (diff)
Add a login endpoint that does nothing
-rw-r--r--src/api/mod.rs2
-rw-r--r--src/api/ops.rs59
-rw-r--r--src/main.rs1
-rw-r--r--src/models/user.rs5
-rw-r--r--src/services/db.rs18
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,