You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

120 lines
3.8 KiB

use actix_identity::Identity;
use actix_session::Session;
use actix_web::{
delete,
http::{header::ContentType, Method},
post, web, HttpMessage, HttpRequest, HttpResponse, Responder,
};
use serde::{Deserialize, Serialize};
use crate::{model::user::User, repo::user_repository::UserRepository};
pub mod todo_routes;
pub mod user_routes;
#[derive(Debug, Deserialize)]
pub struct LoginData {
login: String,
password: String,
}
#[derive(Debug, Serialize)]
pub struct ActiveUser {
id: uuid::Uuid,
login: String,
}
/// Helper function to get a valid User from session.
fn get_user_from_session(session: Session) -> Option<User> {
match session.get::<User>("user") {
Ok(Some(user)) => Some(user),
Ok(None) => {
log::warn!("Could no DESERIALIZE user from session despite someone is logged in");
None
}
Err(err) => {
log::warn!("Could no GET user from session despite someone is logged in: {err}");
None
}
}
}
/// Handles any route that is not defined explicitly.
pub async fn index(id: Option<Identity>, method: Method) -> impl Responder {
match method {
Method::GET => match id {
Some(id) => HttpResponse::Ok()
.content_type(ContentType::plaintext())
.body(format!(
"You are logged in. Welcome! ({:?})",
id.id().unwrap()
)),
None => HttpResponse::Unauthorized().body("Please log in to continue!"),
},
_ => HttpResponse::Forbidden().finish(),
}
}
/// Creates an Identity in the session if the provided credentials are valid.
#[post("/login")]
pub async fn post_login(
req: HttpRequest,
payload: web::Json<LoginData>,
repo: web::Data<UserRepository>,
session: Session,
) -> impl Responder {
log::debug!("Received {payload:?}");
match repo.read_by_login(&payload.login) {
Ok(Some(user)) => {
let hash = sha256::digest(String::from(format!("{}{}", payload.password, user.salt())));
if hash == user.hash() {
log::debug!("User successfully logged in: {payload:?} == {user:?}");
// TODO: Mayb handle more gracefully
Identity::login(&req.extensions(), format!("{}", user.id()))
.expect("Log the user in");
// Create answer for frontend
let res = ActiveUser {
id: user.id().clone(),
login: String::from(user.login()),
};
// let res = format!(
// "{{'id': '{}', 'login': '{}'}}",
// user.id(),
// user.login()
// );
// TODO: Mayb handle more gracefully
session
.insert("user", user)
.expect("Insert user into session");
HttpResponse::Ok().json(res)
} else {
log::debug!(
"Wrong password hash for user: ({}: {}) != ({}: {})",
payload.login,
hash,
user.login(),
user.hash()
);
HttpResponse::Unauthorized().finish()
}
}
Ok(None) => {
log::debug!("User not found: {payload:?}");
HttpResponse::Unauthorized().finish()
}
Err(_) => {
let msg = format!("Could not create user: {payload:?}");
log::debug!("{}", msg);
HttpResponse::InternalServerError().body(msg)
}
}
}
/// Removes the current Identity from the session -> logs the user out.
#[delete("/logout")]
pub async fn delete_logout(id: Identity) -> impl Responder {
id.logout();
HttpResponse::Ok()
}