Refactor sessions management
This commit is contained in:
parent
372dfa3f31
commit
41ee80a077
@ -7,3 +7,9 @@ pub const DEFAULT_ADMIN_PASSWORD: &str = "admin";
|
||||
|
||||
/// App name
|
||||
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;
|
@ -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("/");
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
@ -1 +1,2 @@
|
||||
pub mod err;
|
||||
pub mod time;
|
6
src/utils/time.rs
Normal file
6
src/utils/time.rs
Normal 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()
|
||||
}
|
Loading…
Reference in New Issue
Block a user