use std::collections::HashMap;
use std::path::Path;
use std::sync::Arc;
use actix_web::{HttpRequest, web};
use actix_web::http::header::HeaderValue;
use chrono::Utc;
use fernet::Fernet;
use crate::constant;
use crate::squire;
struct Credentials {
username: String,
signature: String,
timestamp: String,
}
pub struct AuthToken {
pub ok: bool,
pub detail: String,
pub username: String,
pub time_left: i64
}
fn extract_credentials(authorization: &HeaderValue) -> Result<Credentials, &'static str> {
let header = authorization.to_str().unwrap().to_string();
let b64_decode_response = squire::secure::base64_decode(&header);
return match b64_decode_response {
Ok(decoded_auth) => {
if decoded_auth.is_empty() {
log::warn!("Authorization header was received without a value");
return Err("No credentials received");
}
let vector: Vec<&str> = decoded_auth.split(',').collect();
Ok(Credentials {
username: squire::secure::hex_decode(vector.first().unwrap()),
signature: vector.get(1).unwrap().to_string(),
timestamp: vector.get(2).unwrap().to_string(),
})
}
Err(err) => {
Err(err)
}
};
}
pub fn verify_login(
request: &HttpRequest,
config: &web::Data<Arc<squire::settings::Config>>,
session: &web::Data<Arc<constant::Session>>,
) -> Result<HashMap<&'static str, String>, String> {
let err_response;
if let Some(authorization) = request.headers().get("authorization") {
let extracted_credentials = extract_credentials(authorization);
match extracted_credentials {
Ok(credentials) => {
if let Some(password) = config.authorization.get(&credentials.username) {
let message = format!("{}{}{}",
squire::secure::hex_encode(&credentials.username),
squire::secure::hex_encode(password),
credentials.timestamp);
let expected_signature = squire::secure::calculate_hash(message);
if expected_signature == credentials.signature {
let key = squire::secure::keygen();
session.mapping.lock().unwrap().insert(credentials.username.to_string(), key.to_string());
let mut mapped = HashMap::new();
mapped.insert("username", credentials.username.to_string());
mapped.insert("key", key.to_string());
mapped.insert("timestamp", credentials.timestamp.to_string());
return Ok(mapped);
} else {
log::warn!("{} entered bad credentials", credentials.username);
err_response = "Incorrect username or password";
}
} else {
log::warn!("{} is not allowed", credentials.username);
err_response = "Incorrect username or password";
}
}
Err(err) => {
err_response = err;
}
}
} else {
log::warn!("Authorization header was missing");
err_response = "No credentials received";
}
Err(err_response.to_string())
}
pub fn verify_token(
request: &HttpRequest,
config: &squire::settings::Config,
fernet: &Fernet,
session: &constant::Session,
) -> AuthToken {
if session.mapping.lock().unwrap().is_empty() {
log::warn!("No stored sessions, no point in validating further");
return AuthToken {
ok: false,
detail: "Server doesn't recognize your session".to_string(),
username: "NA".to_string(),
time_left: 0
};
}
if let Some(cookie) = request.cookie("session_token") {
if let Ok(decrypted) = fernet.decrypt(cookie.value()) {
let payload: HashMap<String, String> = serde_json::from_str(&String::from_utf8_lossy(&decrypted)).unwrap();
let username = payload.get("username").unwrap().to_string();
let cookie_key = payload.get("key").unwrap().to_string();
let timestamp = payload.get("timestamp").unwrap().parse::<i64>().unwrap();
let stored_key = session.mapping.lock().unwrap().get(&username).unwrap().to_string();
let current_time = Utc::now().timestamp();
if stored_key != *cookie_key {
return AuthToken {
ok: false,
detail: "Invalid session token".to_string(),
username,
time_left: 0
};
}
if current_time - timestamp > config.session_duration {
return AuthToken {
ok: false,
detail: "Session Expired".to_string(),
username,
time_left: 0
};
}
let time_left = timestamp + config.session_duration - current_time;
AuthToken {
ok: true,
detail: format!("Session valid for {}s", time_left),
username,
time_left
}
} else {
AuthToken {
ok: false,
detail: "Invalid session token".to_string(),
username: "NA".to_string(),
time_left: 0
}
}
} else {
AuthToken {
ok: false,
detail: "Session information not found".to_string(),
username: "NA".to_string(),
time_left: 0
}
}
}
pub fn verify_secure_index(path: &Path, username: &String) -> bool {
for dir in path.iter() {
let child = dir.to_string_lossy().to_string();
if child.ends_with(constant::SECURE_INDEX) && child != format!("{}_{}", username, constant::SECURE_INDEX) {
let user_dir = child
.strip_suffix(constant::SECURE_INDEX).unwrap()
.strip_suffix('_').unwrap();
log::warn!("'{}' tried to access {:?} that belongs to '{}'", username, path, user_dir);
return false;
}
}
true
}