use actix_identity::Identity; use actix_web::{HttpMessage, HttpRequest, HttpResponse, Responder, Result, post, web}; use serde::{Deserialize, Serialize}; use utoipa::ToSchema; use crate::{ api::set::auth::user::UserStub, app::App, storage::sql::{ insert::Operations, user::{PasswordHash, User, UserId}, }, }; #[derive(ToSchema, Deserialize, Serialize)] struct LoginInfo { /// The id of the user. id: UserId, /// The password of the user. password: String, } /// Log in as a specific user #[utoipa::path( responses( ( status = OK, description = "User logged in", ), ( status = NOT_FOUND, description = "User id not found" ), ( status = FORBIDDEN, description = "Password did not match" ), ( status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String ) ), request_body = LoginInfo, )] #[post("/login")] async fn login( request: HttpRequest, app: web::Data, info: web::Json, ) -> Result { let info = info.into_inner(); if let Some(user) = User::from_id(&app, info.id).await? { if user.password_hash.verify(&info.password) { Identity::login(&request.extensions(), info.id.to_string())?; Ok(HttpResponse::Ok().finish()) } else { Ok(HttpResponse::Forbidden().finish()) } } else { Ok(HttpResponse::NotFound().finish()) } } /// Log the current user out #[utoipa::path( responses( ( status = OK, description = "User logged out", ), ( status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String ) ), )] #[post("/logout")] async fn logout(user: Identity) -> impl Responder { user.logout(); HttpResponse::Ok() } /// Provision this instance. /// /// This only works, if no users exist yet. #[utoipa::path( responses( ( status = OK, description = "User created and logged in", body = UserId, ), ( status = FORBIDDEN, description = "Instance already provisioned", ), ( status = INTERNAL_SERVER_ERROR, description = "Server encountered error", body = String ) ), request_body = UserStub, )] #[post("/provision")] async fn provision( request: HttpRequest, app: web::Data, new_user: web::Json, ) -> Result { if User::get_all(&app).await?.is_empty() { let user = new_user.into_inner(); let mut ops = Operations::new("register user (during provisioning)"); let password_hash = PasswordHash::from_password(&user.password); let user = User::register(user.name, password_hash, user.description, &mut ops); ops.apply(&app).await?; Identity::login(&request.extensions(), user.id.to_string())?; Ok(HttpResponse::Ok().json(user.id)) } else { Ok(HttpResponse::Forbidden().finish()) } }