Refactor sessions management

This commit is contained in:
Pierre HUBERT 2022-04-01 22:51:33 +02:00
parent 372dfa3f31
commit 41ee80a077
5 changed files with 74 additions and 30 deletions

View File

@ -6,4 +6,10 @@ pub const DEFAULT_ADMIN_USERNAME: &str = "admin";
pub const DEFAULT_ADMIN_PASSWORD: &str = "admin";
/// App name
pub const APP_NAME: &str = "Basic OIDC";
pub const APP_NAME: &str = "Basic OIDC";
/// Maximum session duration after inactivity, in seconds
pub const MAX_SESSION_DURATION: u64 = 60 * 30;
/// Minimum interval between each last activity record in session
pub const MIN_ACTIVITY_RECORD_TIME: u64 = 10;

View File

@ -52,7 +52,7 @@ pub async fn login_route(users: web::Data<Addr<UsersActor>>,
}
// Check if user is already authenticated
if SessionIdentity::is_authenticated(&id) {
if SessionIdentity(&id).is_authenticated() {
return redirect_user("/");
}
@ -68,7 +68,7 @@ pub async fn login_route(users: web::Data<Addr<UsersActor>>,
match response {
LoginResult::Success(user) => {
id.remember(SessionIdentity::from_user(&user).serialize());
SessionIdentity(&id).set_user(&user);
return redirect_user("/");
}

View File

@ -1,40 +1,71 @@
use std::fmt::Display;
use actix_identity::Identity;
use serde::{Deserialize, Serialize};
use crate::constants::{MAX_SESSION_DURATION, MIN_ACTIVITY_RECORD_TIME};
use crate::data::user::User;
use crate::utils::time::time;
pub struct SessionIdentity {
pub id: String,
pub is_admin: bool,
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
enum SessionStatus {
SignedIn,
NeedNewPassword,
NeedMFA,
}
impl SessionIdentity {
pub fn from_user(user: &User) -> Self {
Self {
#[derive(Debug, Serialize, Deserialize)]
struct SessionIdentityData {
pub id: String,
pub is_admin: bool,
last_access: u64,
pub status: SessionStatus,
}
pub struct SessionIdentity<'a>(pub &'a Identity);
impl<'a> SessionIdentity<'a> {
pub fn set_user(&self, user: &User) {
self.set_session_data(&SessionIdentityData {
id: user.uid.clone(),
is_admin: user.admin,
}
last_access: time(),
status: SessionStatus::SignedIn,
});
}
pub fn deserialize<D: Display>(input: D) -> Self {
let input = input.to_string();
let mut iter = input.split('-');
Self {
id: iter.next().unwrap_or_default().to_string(),
is_admin: iter.next().unwrap_or_default() == "true",
}
}
pub fn serialize(&self) -> String {
format!("{}-{}", self.id, self.is_admin)
}
pub fn is_authenticated(i: &Identity) -> bool {
i.identity()
fn get_session_data(&self) -> Option<SessionIdentityData> {
let mut res: Option<SessionIdentityData> = self.0.identity()
.as_ref()
.map(Self::deserialize)
.map(|s| !s.id.is_empty())
.map(String::as_str)
.map(serde_json::from_str)
.map(|f| f.expect("Failed to deserialize session data!"));
if let Some(session) = res.as_mut() {
if session.last_access + MAX_SESSION_DURATION < time() {
log::info!("Session is expired for {}", session.id);
self.0.forget();
return None;
}
if session.last_access + MIN_ACTIVITY_RECORD_TIME < time() {
log::debug!("Refresh last access for session");
session.last_access = time();
self.set_session_data(session);
}
}
res
}
fn set_session_data(&self, data: &SessionIdentityData) {
let s = serde_json::to_string(data)
.expect("Failed to serialize session data!");
self.0.remember(s);
}
pub fn is_authenticated(&self) -> bool {
self.get_session_data()
.map(|s| s.status == SessionStatus::SignedIn)
.unwrap_or(false)
}
}

View File

@ -1 +1,2 @@
pub mod err;
pub mod err;
pub mod time;

6
src/utils/time.rs Normal file
View File

@ -0,0 +1,6 @@
use std::time::{SystemTime, UNIX_EPOCH};
/// Get the current time since epoch
pub fn time() -> u64 {
SystemTime::now().duration_since(UNIX_EPOCH).unwrap().as_secs()
}