use actix::{Actor, AsyncContext, Context, Handler}; use actix::Message; use crate::constants::*; use crate::data::access_token::AccessToken; use crate::data::app_config::AppConfig; use crate::data::client::ClientID; use crate::data::code_challenge::CodeChallenge; use crate::data::jwt_signer::JWTSigner; use crate::data::user::UserID; use crate::utils::err::Res; use crate::utils::string_utils::rand_str; use crate::utils::time::time; #[derive(Clone, Debug, serde::Serialize, serde::Deserialize, Eq, PartialEq)] pub struct SessionID(pub String); #[derive(Clone, Debug)] pub struct Session { pub session_id: SessionID, pub client: ClientID, pub user: UserID, pub auth_time: u64, pub redirect_uri: String, pub authorization_code: String, pub authorization_code_expire_at: u64, pub access_token: Option, pub access_token_expire_at: u64, pub refresh_token: String, pub refresh_token_expire_at: u64, pub nonce: Option, pub code_challenge: Option, } impl Session { pub fn is_expired(&self) -> bool { self.authorization_code_expire_at < time() && self.access_token_expire_at < time() && self.refresh_token_expire_at < time() } pub fn regenerate_access_and_refresh_tokens(&mut self, app_config: &AppConfig, jwt_signer: &JWTSigner) -> Res { let access_token = AccessToken { issuer: app_config.website_origin.to_string(), subject_identifier: self.user.clone(), issued_at: time(), exp_time: time() + OPEN_ID_ACCESS_TOKEN_TIMEOUT, rand_val: rand_str(OPEN_ID_ACCESS_TOKEN_LEN), nonce: self.nonce.clone(), }; self.access_token_expire_at = access_token.exp_time; self.access_token = Some(jwt_signer.sign_token(access_token.to_jwt_claims())?); self.refresh_token = rand_str(OPEN_ID_REFRESH_TOKEN_LEN); self.refresh_token_expire_at = OPEN_ID_REFRESH_TOKEN_TIMEOUT + time(); Ok(()) } } #[derive(Message)] #[rtype(result = "()")] pub struct PushNewSession(pub Session); #[derive(Message)] #[rtype(result = "Option")] pub struct FindSessionByAuthorizationCode(pub String); #[derive(Message)] #[rtype(result = "Option")] pub struct FindSessionByRefreshToken(pub String); #[derive(Message)] #[rtype(result = "Option")] pub struct FindSessionByAccessToken(pub String); #[derive(Message)] #[rtype(result = "()")] pub struct UpdateSession(pub Session); #[derive(Default)] pub struct OpenIDSessionsActor { session: Vec, } impl OpenIDSessionsActor { pub fn clean_old_sessions(&mut self) { self.session.retain(|s| !s.is_expired()); } } impl Actor for OpenIDSessionsActor { type Context = Context; fn started(&mut self, ctx: &mut Self::Context) { // Clean up at a regular interval failed attempts ctx.run_interval(OPEN_ID_SESSION_CLEANUP_INTERVAL, |act, _ctx| { log::trace!("Cleaning up old login sessions"); act.clean_old_sessions(); }); } } impl Handler for OpenIDSessionsActor { type Result = (); fn handle(&mut self, msg: PushNewSession, _ctx: &mut Self::Context) -> Self::Result { self.session.push(msg.0) } } impl Handler for OpenIDSessionsActor { type Result = Option; fn handle(&mut self, msg: FindSessionByAuthorizationCode, _ctx: &mut Self::Context) -> Self::Result { self.session .iter() .find(|f| f.authorization_code.eq(&msg.0)) .cloned() } } impl Handler for OpenIDSessionsActor { type Result = Option; fn handle(&mut self, msg: FindSessionByRefreshToken, _ctx: &mut Self::Context) -> Self::Result { self.session .iter() .find(|f| f.refresh_token.eq(&msg.0)) .cloned() } } impl Handler for OpenIDSessionsActor { type Result = Option; fn handle(&mut self, msg: FindSessionByAccessToken, _ctx: &mut Self::Context) -> Self::Result { self.session .iter() .find(|f| f.access_token.as_ref().map(|t| t.eq(&msg.0)).unwrap_or(false)) .cloned() } } impl Handler for OpenIDSessionsActor { type Result = (); fn handle(&mut self, msg: UpdateSession, _ctx: &mut Self::Context) -> Self::Result { if let Some(r) = self.session.iter().enumerate() .find(|f| f.1.session_id.eq(&msg.0.session_id)).map(|f| f.0) { self.session[r] = msg.0; } } }