use std::sync::Arc;
use actix_web::{HttpRequest, HttpResponse, web};
use actix_web::cookie::{Cookie, SameSite};
use actix_web::cookie::time::{Duration, OffsetDateTime};
use actix_web::http::StatusCode;
use fernet::Fernet;
use minijinja;
use serde::Serialize;
use crate::{constant, squire};
#[derive(Serialize)]
struct RedirectResponse {
redirect_url: String,
}
#[derive(Serialize)]
pub struct DetailError {
pub detail: String,
}
#[post("/login")]
pub async fn login(request: HttpRequest,
config: web::Data<Arc<squire::settings::Config>>,
fernet: web::Data<Arc<Fernet>>,
session: web::Data<Arc<constant::Session>>) -> HttpResponse {
let verified = squire::authenticator::verify_login(&request, &config, &session);
if let Err(err) = verified {
let err_message = err.to_string();
log::warn!("Error response::{}", err_message);
return HttpResponse::Unauthorized().json(DetailError {
detail: err_message
});
}
let mapped = verified.unwrap();
let payload = serde_json::to_string(&mapped).unwrap();
let encrypted_payload = fernet.encrypt(payload.as_bytes());
let cookie_duration = Duration::seconds(config.session_duration);
let expiration = OffsetDateTime::now_utc() + cookie_duration;
let cookie = Cookie::build("session_token", encrypted_payload)
.http_only(true)
.same_site(SameSite::Strict)
.max_age(cookie_duration)
.expires(expiration)
.finish();
log::info!("Session for '{}' will be valid until {}", mapped.get("username").unwrap(), expiration);
let mut response = HttpResponse::Ok().json(RedirectResponse {
redirect_url: "/monitor".to_string(),
});
response.add_cookie(&cookie).unwrap();
response
}
#[get("/logout")]
pub async fn logout(request: HttpRequest,
fernet: web::Data<Arc<Fernet>>,
session: web::Data<Arc<constant::Session>>,
metadata: web::Data<Arc<constant::MetaData>>,
config: web::Data<Arc<squire::settings::Config>>,
template: web::Data<Arc<minijinja::Environment<'static>>>) -> HttpResponse {
let host = request.connection_info().host().to_owned();
let logout_template = template.get_template("logout").unwrap();
let mut response = HttpResponse::build(StatusCode::OK);
response.content_type("text/html; charset=utf-8");
let rendered;
let auth_response = squire::authenticator::verify_token(&request, &config, &fernet, &session);
log::debug!("Session Validation Response: {}", auth_response.detail);
if auth_response.username != "NA" {
log::info!("{} from {} attempted to log out", auth_response.username, host)
}
if auth_response.ok {
rendered = logout_template.render(minijinja::context!(
version => metadata.pkg_version,
detail => "You have been logged out successfully."
)).unwrap();
let mut cookie = Cookie::new("session_token", "");
cookie.set_same_site(SameSite::Strict);
cookie.make_removal();
response.cookie(cookie);
} else {
log::debug!("No stored session found for {}", host);
rendered = logout_template.render(minijinja::context!(
version => metadata.pkg_version,
detail => "You are not logged in. Please click the button below to proceed.",
show_login => true
)).unwrap();
}
response.body(rendered)
}
#[get("/error")]
pub async fn error(request: HttpRequest,
metadata: web::Data<Arc<constant::MetaData>>,
template: web::Data<Arc<minijinja::Environment<'static>>>) -> HttpResponse {
if let Some(detail) = request.cookie("detail") {
log::info!("Error response for /error: {}", detail.value());
let session = template.get_template("session").unwrap();
return HttpResponse::build(StatusCode::UNAUTHORIZED)
.content_type("text/html; charset=utf-8")
.body(session.render(minijinja::context!(
version => metadata.pkg_version,
reason => detail.value()
)).unwrap());
}
log::info!("Sending unauthorized response for /error");
let error = template.get_template("error").unwrap();
HttpResponse::build(StatusCode::UNAUTHORIZED)
.content_type("text/html; charset=utf-8")
.body(error.render(minijinja::context!(
version => metadata.pkg_version,
title => "LOGIN FAILED",
description => "USER ERROR - REPLACE USER",
help => r"Forgot Password?\n\nRelax and try to remember your password.",
button_text => "LOGIN", button_link => "/",
block_navigation => true
)).unwrap())
}
#[allow(dead_code)]
pub fn failed_auth(auth_response: squire::authenticator::AuthToken) -> HttpResponse {
let mut response = HttpResponse::build(StatusCode::FOUND);
let detail = auth_response.detail;
let age = Duration::new(3, 0);
let cookie = Cookie::build("detail", detail)
.path("/error")
.http_only(true)
.same_site(SameSite::Strict)
.max_age(age)
.finish();
response.cookie(cookie);
response.append_header(("Location", "/error"));
response.finish()
}