rustream/squire/
authenticator.rs1use std::collections::HashMap;
2use std::path::Path;
3use std::sync::Arc;
4
5use actix_web::{HttpRequest, web};
6use actix_web::http::header::HeaderValue;
7use chrono::Utc;
8use fernet::Fernet;
9
10use crate::constant;
11use crate::squire;
12
13struct Credentials {
17 username: String,
18 signature: String,
19 timestamp: String,
20}
21
22pub struct AuthToken {
26 pub ok: bool,
27 pub detail: String,
28 pub username: String,
29 pub time_left: i64
30}
31
32
33fn extract_credentials(authorization: &HeaderValue) -> Result<Credentials, &'static str> {
48 let header = authorization.to_str().unwrap().to_string();
49 let b64_decode_response = squire::secure::base64_decode(&header);
51 return match b64_decode_response {
52 Ok(decoded_auth) => {
53 if decoded_auth.is_empty() {
54 log::warn!("Authorization header was received without a value");
55 return Err("No credentials received");
56 }
57 let vector: Vec<&str> = decoded_auth.split(',').collect();
58 Ok(Credentials {
59 username: squire::secure::hex_decode(vector.first().unwrap()),
61 signature: vector.get(1).unwrap().to_string(),
62 timestamp: vector.get(2).unwrap().to_string(),
63 })
64 }
65 Err(err) => {
66 Err(err)
67 }
68 };
69}
70
71pub fn verify_login(
84 request: &HttpRequest,
85 config: &web::Data<Arc<squire::settings::Config>>,
86 session: &web::Data<Arc<constant::Session>>,
87) -> Result<HashMap<&'static str, String>, String> {
88 let err_response;
89 if let Some(authorization) = request.headers().get("authorization") {
90 let extracted_credentials = extract_credentials(authorization);
91 match extracted_credentials {
92 Ok(credentials) => {
93 if let Some(password) = config.authorization.get(&credentials.username) {
95 let message = format!("{}{}{}",
96 squire::secure::hex_encode(&credentials.username),
97 squire::secure::hex_encode(password),
98 credentials.timestamp);
99 let expected_signature = squire::secure::calculate_hash(message);
101 if expected_signature == credentials.signature {
102 let key = squire::secure::keygen();
103 session.mapping.lock().unwrap().insert(credentials.username.to_string(), key.to_string());
104 let mut mapped = HashMap::new();
105 mapped.insert("username", credentials.username.to_string());
106 mapped.insert("key", key.to_string());
107 mapped.insert("timestamp", credentials.timestamp.to_string());
108 return Ok(mapped);
109 } else {
110 log::warn!("{} entered bad credentials", credentials.username);
111 err_response = "Incorrect username or password";
112 }
113 } else {
114 log::warn!("{} is not allowed", credentials.username);
115 err_response = "Incorrect username or password";
116 }
117 }
118 Err(err) => {
119 err_response = err;
120 }
121 }
122 } else {
123 log::warn!("Authorization header was missing");
124 err_response = "No credentials received";
125 }
126 Err(err_response.to_string())
127}
128
129pub fn verify_token(
142 request: &HttpRequest,
143 config: &squire::settings::Config,
144 fernet: &Fernet,
145 session: &constant::Session,
146) -> AuthToken {
147 if session.mapping.lock().unwrap().is_empty() {
148 log::warn!("No stored sessions, no point in validating further");
149 return AuthToken {
150 ok: false,
151 detail: "Server doesn't recognize your session".to_string(),
152 username: "NA".to_string(),
153 time_left: 0
154 };
155 }
156 if let Some(cookie) = request.cookie("session_token") {
157 if let Ok(decrypted) = fernet.decrypt(cookie.value()) {
158 let payload: HashMap<String, String> = serde_json::from_str(&String::from_utf8_lossy(&decrypted)).unwrap();
159 let username = payload.get("username").unwrap().to_string();
160 let cookie_key = payload.get("key").unwrap().to_string();
161 let timestamp = payload.get("timestamp").unwrap().parse::<i64>().unwrap();
162 let stored_key = session.mapping.lock().unwrap().get(&username).unwrap().to_string();
163 let current_time = Utc::now().timestamp();
164 if stored_key != *cookie_key {
166 return AuthToken {
167 ok: false,
168 detail: "Invalid session token".to_string(),
169 username,
170 time_left: 0
171 };
172 }
173 if current_time - timestamp > config.session_duration {
174 return AuthToken {
175 ok: false,
176 detail: "Session Expired".to_string(),
177 username,
178 time_left: 0
179 };
180 }
181 let time_left = timestamp + config.session_duration - current_time;
182 AuthToken {
183 ok: true,
184 detail: format!("Session valid for {}s", time_left),
185 username,
186 time_left
187 }
188 } else {
189 AuthToken {
190 ok: false,
191 detail: "Invalid session token".to_string(),
192 username: "NA".to_string(),
193 time_left: 0
194 }
195 }
196 } else {
197 AuthToken {
198 ok: false,
199 detail: "Session information not found".to_string(),
200 username: "NA".to_string(),
201 time_left: 0
202 }
203 }
204}
205
206pub fn verify_secure_index(path: &Path, username: &String) -> bool {
228 for dir in path.iter() {
229 let child = dir.to_string_lossy().to_string();
230 if child.ends_with(constant::SECURE_INDEX) && child != format!("{}_{}", username, constant::SECURE_INDEX) {
231 let user_dir = child
232 .strip_suffix(constant::SECURE_INDEX).unwrap()
233 .strip_suffix('_').unwrap();
234 log::warn!("'{}' tried to access {:?} that belongs to '{}'", username, path, user_dir);
235 return false;
236 }
237 }
238 true
239}