1use std::sync::Arc;
2
3use actix_web::cookie::time::{Duration, OffsetDateTime};
4use actix_web::cookie::{Cookie, SameSite};
5use actix_web::http::StatusCode;
6use actix_web::{web, HttpRequest, HttpResponse};
7use fernet::Fernet;
8use minijinja;
9use serde::Serialize;
10
11use crate::{constant, squire};
12
13#[derive(Serialize)]
15struct RedirectResponse {
16 redirect_url: String,
17}
18
19#[derive(Serialize)]
21pub struct DetailError {
22 pub detail: String,
23}
24
25#[post("/login")]
39pub async fn login(request: HttpRequest,
40 config: web::Data<Arc<squire::settings::Config>>,
41 fernet: web::Data<Arc<Fernet>>,
42 session: web::Data<Arc<constant::Session>>) -> HttpResponse {
43 let verified = squire::authenticator::verify_login(&request, &config, &session);
44 if let Err(err) = verified {
45 let err_message = err.to_string();
46 log::warn!("Error response::{}", err_message);
47 return HttpResponse::Unauthorized().json(DetailError {
48 detail: err_message
49 });
50 }
51
52 let mapped = verified.unwrap();
53 let payload = serde_json::to_string(&mapped).unwrap();
54 let encrypted_payload = fernet.encrypt(payload.as_bytes());
55
56 let cookie_duration = Duration::seconds(config.session_duration);
57 let expiration = OffsetDateTime::now_utc() + cookie_duration;
58 let cookie = Cookie::build("session_token", encrypted_payload)
59 .http_only(true)
60 .same_site(SameSite::Strict)
61 .max_age(cookie_duration)
62 .expires(expiration)
63 .finish();
64 log::info!("Session for '{}' will be valid until {}", mapped.get("username").unwrap(), expiration);
65
66 let mut response = HttpResponse::Ok().json(RedirectResponse {
67 redirect_url: "/monitor".to_string(),
68 });
69 response.add_cookie(&cookie).unwrap();
70 response
71}
72
73#[get("/logout")]
88pub async fn logout(request: HttpRequest,
89 fernet: web::Data<Arc<Fernet>>,
90 session: web::Data<Arc<constant::Session>>,
91 metadata: web::Data<Arc<constant::MetaData>>,
92 config: web::Data<Arc<squire::settings::Config>>,
93 template: web::Data<Arc<minijinja::Environment<'static>>>) -> HttpResponse {
94 let host = request.connection_info().host().to_owned();
95 let logout_template = template.get_template("logout").unwrap();
96 let mut response = HttpResponse::build(StatusCode::OK);
97 response.content_type("text/html; charset=utf-8");
98
99 let rendered;
100 let auth_response = squire::authenticator::verify_token(&request, &config, &fernet, &session);
101 log::debug!("Session Validation Response: {}", auth_response.detail);
102
103 if auth_response.username != "NA" {
104 log::info!("{} from {} attempted to log out", auth_response.username, host)
105 }
106
107 if auth_response.ok {
108 rendered = logout_template.render(minijinja::context!(
109 version => metadata.pkg_version,
110 detail => "You have been logged out successfully."
111 )).unwrap();
112
113 let mut cookie = Cookie::new("session_token", "");
114 cookie.set_same_site(SameSite::Strict);
115 cookie.make_removal();
116 response.cookie(cookie);
117 } else {
118 log::debug!("{} - {}", auth_response.detail, host);
119 rendered = logout_template.render(minijinja::context!(
120 version => metadata.pkg_version,
121 detail => "You are not logged in. Please click the button below to proceed.",
122 show_login => true
123 )).unwrap();
124 }
125 response.body(rendered)
126}
127
128#[get("/error")]
140pub async fn error(request: HttpRequest,
141 metadata: web::Data<Arc<constant::MetaData>>,
142 template: web::Data<Arc<minijinja::Environment<'static>>>) -> HttpResponse {
143 if let Some(detail) = request.cookie("detail") {
144 log::info!("Error response for /error: {}", detail.value());
145 let session = template.get_template("session").unwrap();
146 return HttpResponse::build(StatusCode::UNAUTHORIZED)
147 .content_type("text/html; charset=utf-8")
148 .body(session.render(minijinja::context!(
149 version => metadata.pkg_version,
150 reason => detail.value()
151 )).unwrap());
152 }
153
154 log::info!("Sending unauthorized response for /error");
155 let error = template.get_template("error").unwrap();
156 HttpResponse::build(StatusCode::UNAUTHORIZED)
157 .content_type("text/html; charset=utf-8")
158 .body(error.render(minijinja::context!(
159 version => metadata.pkg_version,
160 title => "LOGIN FAILED",
161 description => "USER ERROR - REPLACE USER",
162 help => r"Forgot Password?\n\nRelax and try to remember your password.",
163 button_text => "LOGIN", button_link => "/",
164 block_navigation => true
165 )).unwrap())
166}
167
168pub fn failed_auth(auth_response: squire::authenticator::AuthToken) -> HttpResponse {
179 let mut response = HttpResponse::build(StatusCode::FOUND);
180 let detail = auth_response.detail;
181 let age = Duration::new(3, 0);
182 let cookie = Cookie::build("detail", detail)
183 .path("/error")
184 .http_only(true)
185 .same_site(SameSite::Strict)
186 .max_age(age)
187 .finish();
188 response.cookie(cookie);
189 response.append_header(("Location", "/error"));
190 response.finish()
191}