158 lines
4.7 KiB
Rust
158 lines
4.7 KiB
Rust
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<String>,
|
|
pub access_token_expire_at: u64,
|
|
pub refresh_token: String,
|
|
pub refresh_token_expire_at: u64,
|
|
|
|
pub nonce: Option<String>,
|
|
pub code_challenge: Option<CodeChallenge>,
|
|
}
|
|
|
|
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().0,
|
|
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<Session>")]
|
|
pub struct FindSessionByAuthorizationCode(pub String);
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "Option<Session>")]
|
|
pub struct FindSessionByRefreshToken(pub String);
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "Option<Session>")]
|
|
pub struct FindSessionByAccessToken(pub String);
|
|
|
|
#[derive(Message)]
|
|
#[rtype(result = "()")]
|
|
pub struct UpdateSession(pub Session);
|
|
|
|
#[derive(Default)]
|
|
pub struct OpenIDSessionsActor {
|
|
session: Vec<Session>,
|
|
}
|
|
|
|
impl OpenIDSessionsActor {
|
|
pub fn clean_old_sessions(&mut self) {
|
|
self.session.retain(|s| !s.is_expired());
|
|
}
|
|
}
|
|
|
|
impl Actor for OpenIDSessionsActor {
|
|
type Context = Context<Self>;
|
|
|
|
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<PushNewSession> for OpenIDSessionsActor {
|
|
type Result = ();
|
|
|
|
fn handle(&mut self, msg: PushNewSession, _ctx: &mut Self::Context) -> Self::Result {
|
|
self.session.push(msg.0)
|
|
}
|
|
}
|
|
|
|
impl Handler<FindSessionByAuthorizationCode> for OpenIDSessionsActor {
|
|
type Result = Option<Session>;
|
|
|
|
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<FindSessionByRefreshToken> for OpenIDSessionsActor {
|
|
type Result = Option<Session>;
|
|
|
|
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<FindSessionByAccessToken> for OpenIDSessionsActor {
|
|
type Result = Option<Session>;
|
|
|
|
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<UpdateSession> 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;
|
|
}
|
|
}
|
|
} |