1use std::sync::Arc;
2
3use actix_web::{HttpRequest, HttpResponse, web};
4use actix_web::cookie::{Cookie, SameSite};
5use actix_web::cookie::time::{Duration, OffsetDateTime};
6use actix_web::http::StatusCode;
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 (_host, _last_accessed) = squire::custom::log_connection(&request, &session);
54
55 let payload = serde_json::to_string(&mapped).unwrap();
56 let encrypted_payload = fernet.encrypt(payload.as_bytes());
57
58 let cookie_duration = Duration::seconds(config.session_duration);
59 let expiration = OffsetDateTime::now_utc() + cookie_duration;
60 let base_cookie = Cookie::build("session_token", encrypted_payload)
61 .http_only(true)
62 .same_site(SameSite::Strict)
63 .max_age(cookie_duration)
64 .expires(expiration);
65
66 let cookie = if config.secure_session {
67 log::info!("Marking 'session_token' cookie as secure!!");
68 base_cookie.secure(true).finish()
69 } else {
70 base_cookie.finish()
71 };
72 log::info!("Session for '{}' will be valid until {}", mapped.get("username").unwrap(), expiration);
73
74 let mut response = HttpResponse::Ok().json(RedirectResponse {
75 redirect_url: "/home".to_string(),
76 });
77 response.add_cookie(&cookie).unwrap();
78 response
79}
80
81#[get("/logout")]
96pub async fn logout(request: HttpRequest,
97 fernet: web::Data<Arc<Fernet>>,
98 session: web::Data<Arc<constant::Session>>,
99 metadata: web::Data<Arc<constant::MetaData>>,
100 config: web::Data<Arc<squire::settings::Config>>,
101 template: web::Data<Arc<minijinja::Environment<'static>>>) -> HttpResponse {
102 let host = request.connection_info().host().to_owned();
103 let logout_template = template.get_template("logout").unwrap();
104 let mut response = HttpResponse::build(StatusCode::OK);
105 response.content_type("text/html; charset=utf-8");
106
107 let rendered;
108 let auth_response = squire::authenticator::verify_token(&request, &config, &fernet, &session);
109 log::debug!("Session Validation Response: {}", auth_response.detail);
110
111 if auth_response.username != "NA" {
112 log::info!("{} from {} attempted to log out", auth_response.username, host)
113 }
114
115 if auth_response.ok {
116 let mut tracker = session.tracker.lock().unwrap();
117 if tracker.get(&host).is_some() {
118 tracker.remove(&host);
119 } else {
120 log::warn!("Session information for {} was not stored or no file was rendered", host);
121 }
122 rendered = logout_template.render(minijinja::context!(
123 version => metadata.pkg_version,
124 detail => "You have been logged out successfully."
125 )).unwrap();
126
127 let mut cookie = Cookie::new("session_token", "");
128 cookie.set_same_site(SameSite::Strict);
129 cookie.make_removal();
130 response.cookie(cookie);
131 } else {
132 log::debug!("No stored session found for {}", host);
133 rendered = logout_template.render(minijinja::context!(
134 version => metadata.pkg_version,
135 detail => "You are not logged in. Please click the button below to proceed.",
136 show_login => true
137 )).unwrap();
138 }
139 response.body(rendered)
141}
142
143#[get("/home")]
159pub async fn home(request: HttpRequest,
160 fernet: web::Data<Arc<Fernet>>,
161 session: web::Data<Arc<constant::Session>>,
162 metadata: web::Data<Arc<constant::MetaData>>,
163 config: web::Data<Arc<squire::settings::Config>>,
164 template: web::Data<Arc<minijinja::Environment<'static>>>) -> HttpResponse {
165 let auth_response = squire::authenticator::verify_token(&request, &config, &fernet, &session);
166 if !auth_response.ok {
167 return failed_auth(auth_response, &config);
168 }
169 let (_host, _last_accessed) = squire::custom::log_connection(&request, &session);
170 log::debug!("{}", auth_response.detail);
171
172 let listing_page = squire::content::get_all_stream_content(&config, &auth_response);
173 let listing = template.get_template("listing").unwrap();
174
175 HttpResponse::build(StatusCode::OK)
176 .content_type("text/html; charset=utf-8")
177 .body(
178 listing.render(minijinja::context!(
179 version => metadata.pkg_version,
180 files => listing_page.files,
181 user => auth_response.username,
182 secure_index => constant::SECURE_INDEX,
183 directories => listing_page.directories,
184 secured_directories => listing_page.secured_directories
185 )).unwrap()
186 )
187}
188
189#[get("/error")]
201pub async fn error(request: HttpRequest,
202 metadata: web::Data<Arc<constant::MetaData>>,
203 template: web::Data<Arc<minijinja::Environment<'static>>>) -> HttpResponse {
204 if let Some(detail) = request.cookie("detail") {
205 log::info!("Error response for /error: {}", detail.value());
206 let session = template.get_template("session").unwrap();
207 return HttpResponse::build(StatusCode::UNAUTHORIZED)
208 .content_type("text/html; charset=utf-8")
209 .body(session.render(minijinja::context!(
210 version => metadata.pkg_version,
211 reason => detail.value()
212 )).unwrap());
213 }
214
215 log::info!("Sending unauthorized response for /error");
216 let error = template.get_template("error").unwrap();
217 HttpResponse::build(StatusCode::UNAUTHORIZED)
218 .content_type("text/html; charset=utf-8")
219 .body(error.render(minijinja::context!(
220 version => metadata.pkg_version,
221 title => "LOGIN FAILED",
222 description => "USER ERROR - REPLACE USER",
223 help => r"Forgot Password?\n\nRelax and try to remember your password.",
224 button_text => "LOGIN", button_link => "/",
225 block_navigation => true
226 )).unwrap())
227}
228
229pub fn failed_auth(auth_response: squire::authenticator::AuthToken,
240 config: &squire::settings::Config) -> HttpResponse {
241 let mut response = HttpResponse::build(StatusCode::FOUND);
242 let detail = auth_response.detail;
243 let age = Duration::new(3, 0);
244 let base_cookie = Cookie::build("detail", detail)
245 .path("/error")
246 .http_only(true)
247 .same_site(SameSite::Strict)
248 .max_age(age);
249 let cookie = if config.secure_session {
250 log::debug!("Marking 'detail' cookie as secure!!");
251 base_cookie.secure(true).finish()
252 } else {
253 base_cookie.finish()
254 };
255 response.cookie(cookie);
256 response.append_header(("Location", "/error"));
257 response.finish()
258}