use crate::constants; use crate::models::users::{User, UserID}; use crate::utils::rand_utils::rand_string; use actix_session::Session; use actix_web::dev::Payload; use actix_web::{Error, FromRequest, HttpRequest}; use futures_util::future::{Ready, ready}; use std::net::IpAddr; /// Money session errors #[derive(thiserror::Error, Debug)] enum MoneySessionError { #[error("Missing state!")] OIDCMissingState, #[error("Missing IP address!")] OIDCMissingIP, #[error("Invalid state!")] OIDCInvalidState, #[error("Invalid IP address!")] OIDCInvalidIP, } /// Money session /// /// Basic wrapper around actix-session extractor pub struct MoneySession(Session); impl MoneySession { /// Generate OpenID state for this session pub fn gen_oidc_state(&self, ip: IpAddr) -> anyhow::Result { let random_string = rand_string(50); self.0 .insert(constants::sessions::OIDC_STATE_KEY, random_string.clone())?; self.0.insert(constants::sessions::OIDC_REMOTE_IP, ip)?; Ok(random_string) } /// Validate OpenID state pub fn validate_state(&self, state: &str, ip: IpAddr) -> anyhow::Result<()> { let session_state: String = self .0 .get(constants::sessions::OIDC_STATE_KEY)? .ok_or(MoneySessionError::OIDCMissingState)?; let session_ip: IpAddr = self .0 .get(constants::sessions::OIDC_REMOTE_IP)? .ok_or(MoneySessionError::OIDCMissingIP)?; if session_state != state { return Err(anyhow::anyhow!(MoneySessionError::OIDCInvalidState)); } if session_ip != ip { return Err(anyhow::anyhow!(MoneySessionError::OIDCInvalidIP)); } Ok(()) } /// Set current user pub fn set_user(&self, user: &User) -> anyhow::Result<()> { self.0.insert(constants::sessions::USER_ID, user.id())?; Ok(()) } /// Get current user pub fn current_user(&self) -> anyhow::Result> { Ok(self.0.get(constants::sessions::USER_ID)?) } /// Remove defined user pub fn unset_current_user(&self) -> anyhow::Result<()> { self.0.remove(constants::sessions::USER_ID); Ok(()) } } impl FromRequest for MoneySession { type Error = Error; type Future = Ready>; #[inline] fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future { ready( Session::from_request(req, &mut Payload::None) .into_inner() .map(MoneySession), ) } }