Lee Morgan пре 3 недеља
родитељ
комит
be54663ebd
5 измењених фајлова са 72 додато и 2 уклоњено
  1. 29 0
      src/controllers/login.rs
  2. 1 0
      src/controllers/mod.rs
  3. 3 1
      src/main.rs
  4. 3 1
      src/routes.rs
  5. 36 0
      src/users.rs

+ 29 - 0
src/controllers/login.rs

@@ -0,0 +1,29 @@
+use actix_web::{HttpResponse, web, post};
+use serde::Deserialize;
+use serde_json::json;
+use std::sync::Mutex;
+use crate::users::User;
+
+#[derive(Deserialize)]
+struct Body {
+    email: String,
+    password: String
+}
+
+#[post("/login")]
+pub async fn route(
+    web::Json(body): web::Json<Body>,
+    users: web::Data<Mutex<Vec<User>>>
+) -> HttpResponse {
+    let email = body.email.to_lowercase();
+    let u = users.lock().unwrap();
+    let user =  match User::find_by_email(u.to_vec(), email) {
+        Some(u) => match u.verify_password(&body.password) {
+            Some(s) => s,
+            None => { return HttpResponse::Unauthorized().json(json!({"msg": "Unauthorized"})); }
+        }
+        None => { return HttpResponse::Unauthorized().json(json!({"msg": "Unauthorized"})); }
+    };
+    let cookie = user.create_auth_cookie();
+    HttpResponse::Ok().cookie(cookie).json(json!({"success": true}))
+}

+ 1 - 0
src/controllers/mod.rs

@@ -1,2 +1,3 @@
 pub mod home;
 pub mod first_admin;
+pub mod login;

+ 3 - 1
src/main.rs

@@ -20,6 +20,8 @@ async fn main() -> Result<(), std::io::Error> {
     let ha_token: String = env_var("HA_TOKEN");
     let app_env: Environment = env_var("APP_ENV");
 
+    let ae = web::Data::new(app_env);
+
     //Read User Data
     let users = Mutex::new(users::User::read());
     let users_data = web::Data::new(users);
@@ -34,7 +36,7 @@ async fn main() -> Result<(), std::io::Error> {
         App::new()
             .app_data(users_data.clone())
             .app_data(ha_sender.clone())
-            .app_data(app_env.clone())
+            .app_data(ae.clone())
             .wrap(middleware::Compress::default())
             .configure(routes::config)
     })

+ 3 - 1
src/routes.rs

@@ -1,11 +1,13 @@
 use actix_web::web::ServiceConfig;
 use crate::controllers::{
     home,
-    first_admin
+    first_admin,
+    login
 };
 
 pub fn config(cfg: &mut ServiceConfig) {
     cfg.service(home::route);
     
     cfg.service(first_admin::route);
+    cfg.service(login::route);
 }

+ 36 - 0
src/users.rs

@@ -1,5 +1,13 @@
+use actix_web::cookie::{Cookie, SameSite, time::Duration};
 use uuid::Uuid;
 use serde::{Serialize, Deserialize};
+use argon2::{
+    Argon2,
+    password_hash::{
+        PasswordHash,
+        PasswordVerifier
+    }
+};
 use std::{fs, io::Write, path::Path};
 
 #[derive(Debug, Serialize, Deserialize, Clone)]
@@ -76,4 +84,32 @@ impl User {
         fs::rename(&tmp_path, path)
             .expect("Failed to replace users file");
     }
+
+    pub fn find_by_email(users: Vec<User>, email: String) -> Option<Self> {
+        users
+            .iter()
+            .find(|u| u.email == email).cloned()
+    }
+
+    pub fn verify_password(self, password: &String) -> Option<Self> {
+        let parsed_hash = match PasswordHash::new(&self.pass_hash) {
+            Ok(p) => p,
+            Err(_) => { return None }
+        };
+
+        match Argon2::default().verify_password(password.as_bytes(), &parsed_hash) {
+            Ok(_) => Some(self),
+            Err(_) => None
+        }
+    }
+
+    pub fn create_auth_cookie(&self) -> Cookie<'static> {
+        Cookie::build("user", self.id.to_string())
+            .path("/")
+            .http_only(true)
+            .same_site(SameSite::Lax)
+            .secure(true)
+            .max_age(Duration::days(90))
+            .finish()
+    }
 }