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
|
/// 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;
|
@ -52,7 +52,7 @@ pub async fn login_route(users: web::Data<Addr<UsersActor>>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Check if user is already authenticated
|
// Check if user is already authenticated
|
||||||
if SessionIdentity::is_authenticated(&id) {
|
if SessionIdentity(&id).is_authenticated() {
|
||||||
return redirect_user("/");
|
return redirect_user("/");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,7 +68,7 @@ pub async fn login_route(users: web::Data<Addr<UsersActor>>,
|
|||||||
|
|
||||||
match response {
|
match response {
|
||||||
LoginResult::Success(user) => {
|
LoginResult::Success(user) => {
|
||||||
id.remember(SessionIdentity::from_user(&user).serialize());
|
SessionIdentity(&id).set_user(&user);
|
||||||
|
|
||||||
return redirect_user("/");
|
return redirect_user("/");
|
||||||
}
|
}
|
||||||
|
@ -1,40 +1,71 @@
|
|||||||
use std::fmt::Display;
|
|
||||||
|
|
||||||
use actix_identity::Identity;
|
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::data::user::User;
|
||||||
|
use crate::utils::time::time;
|
||||||
|
|
||||||
pub struct SessionIdentity {
|
#[derive(Debug, Serialize, Deserialize, Eq, PartialEq)]
|
||||||
|
enum SessionStatus {
|
||||||
|
SignedIn,
|
||||||
|
NeedNewPassword,
|
||||||
|
NeedMFA,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
struct SessionIdentityData {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub is_admin: bool,
|
pub is_admin: bool,
|
||||||
|
last_access: u64,
|
||||||
|
pub status: SessionStatus,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SessionIdentity {
|
pub struct SessionIdentity<'a>(pub &'a Identity);
|
||||||
pub fn from_user(user: &User) -> Self {
|
|
||||||
Self {
|
impl<'a> SessionIdentity<'a> {
|
||||||
|
pub fn set_user(&self, user: &User) {
|
||||||
|
self.set_session_data(&SessionIdentityData {
|
||||||
id: user.uid.clone(),
|
id: user.uid.clone(),
|
||||||
is_admin: user.admin,
|
is_admin: user.admin,
|
||||||
}
|
last_access: time(),
|
||||||
|
status: SessionStatus::SignedIn,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn deserialize<D: Display>(input: D) -> Self {
|
fn get_session_data(&self) -> Option<SessionIdentityData> {
|
||||||
let input = input.to_string();
|
let mut res: Option<SessionIdentityData> = self.0.identity()
|
||||||
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()
|
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(Self::deserialize)
|
.map(String::as_str)
|
||||||
.map(|s| !s.id.is_empty())
|
.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)
|
.unwrap_or(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1 +1,2 @@
|
|||||||
pub mod err;
|
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