sysmonk/squire/
authenticator.rs1use std::collections::HashMap;
2use std::sync::Arc;
3
4use actix_web::http::header::HeaderValue;
5use actix_web::{web, HttpRequest};
6use chrono::Utc;
7use fernet::Fernet;
8
9use crate::constant;
10use crate::squire;
11
12struct Credentials {
16 username: String,
17 signature: String,
18 timestamp: String,
19}
20
21pub struct AuthToken {
25 pub ok: bool,
26 pub detail: String,
27 pub username: String,
28}
29
30
31fn extract_credentials(authorization: &HeaderValue) -> Result<Credentials, &'static str> {
46 let header = authorization.to_str().unwrap().to_string();
47 let b64_decode_response = squire::secure::base64_decode(&header);
49 match b64_decode_response {
50 Ok(decoded_auth) => {
51 if decoded_auth.is_empty() {
52 log::warn!("Authorization header was received without a value");
53 return Err("No credentials received");
54 }
55 let vector: Vec<&str> = decoded_auth.split(',').collect();
56 Ok(Credentials {
57 username: squire::secure::hex_decode(vector.first().unwrap()),
59 signature: vector.get(1).unwrap().to_string(),
60 timestamp: vector.get(2).unwrap().to_string(),
61 })
62 }
63 Err(err) => {
64 Err(err)
65 }
66 }
67}
68
69pub fn verify_login(
82 request: &HttpRequest,
83 config: &web::Data<Arc<squire::settings::Config>>,
84 session: &web::Data<Arc<constant::Session>>,
85) -> Result<HashMap<&'static str, String>, String> {
86 let err_response;
87 if let Some(authorization) = request.headers().get("authorization") {
88 let extracted_credentials = extract_credentials(authorization);
89 match extracted_credentials {
90 Ok(credentials) => {
91 let message = format!("{}{}{}",
93 squire::secure::hex_encode(&credentials.username),
94 squire::secure::hex_encode(&config.password),
95 credentials.timestamp);
96 let expected_signature = squire::secure::calculate_hash(message);
98 if expected_signature == credentials.signature {
99 let key = squire::secure::keygen();
100 session.mapping.lock().unwrap().insert(credentials.username.to_string(), key.to_string());
101 let mut mapped = HashMap::new();
102 mapped.insert("username", credentials.username.to_string());
103 mapped.insert("key", key.to_string());
104 mapped.insert("timestamp", credentials.timestamp.to_string());
105 return Ok(mapped);
106 } else {
107 log::warn!("{} entered bad credentials", credentials.username);
108 err_response = "Incorrect username or password";
109 }
110 }
111 Err(err) => {
112 err_response = err;
113 }
114 }
115 } else {
116 log::warn!("Authorization header was missing");
117 err_response = "No credentials received";
118 }
119 Err(err_response.to_string())
120}
121
122pub fn verify_token(
135 request: &HttpRequest,
136 config: &squire::settings::Config,
137 fernet: &Fernet,
138 session: &constant::Session,
139) -> AuthToken {
140 if session.mapping.lock().unwrap().is_empty() {
141 log::warn!("No stored sessions, no point in validating further");
142 return AuthToken {
143 ok: false,
144 detail: "Server doesn't recognize your session".to_string(),
145 username: "NA".to_string(),
146 };
147 }
148 if let Some(cookie) = request.cookie("session_token") {
149 if let Ok(decrypted) = fernet.decrypt(cookie.value()) {
150 let payload: HashMap<String, String> = serde_json::from_str(&String::from_utf8_lossy(&decrypted)).unwrap();
151 let username = payload.get("username").unwrap().to_string();
152 let cookie_key = payload.get("key").unwrap().to_string();
153 let timestamp = payload.get("timestamp").unwrap().parse::<i64>().unwrap();
154 let stored_key = session.mapping.lock().unwrap().get(&username).unwrap().to_string();
155 let current_time = Utc::now().timestamp();
156 if stored_key != *cookie_key {
158 return AuthToken {
159 ok: false,
160 detail: "Invalid session token".to_string(),
161 username,
162 };
163 }
164 if current_time - timestamp > config.session_duration {
165 return AuthToken {
166 ok: false,
167 detail: "Session Expired".to_string(),
168 username,
169 };
170 }
171 let time_left = timestamp + config.session_duration - current_time;
172 AuthToken {
173 ok: true,
174 detail: format!("Session valid for {}s", time_left),
175 username,
176 }
177 } else {
178 AuthToken {
179 ok: false,
180 detail: "Invalid session token".to_string(),
181 username: "NA".to_string(),
182 }
183 }
184 } else {
185 AuthToken {
186 ok: false,
187 detail: "Session information not found".to_string(),
188 username: "NA".to_string(),
189 }
190 }
191}