use crate::libvirt_lib_structures::XMLUuid; use crate::utils::rand_utils::rand_str; use crate::utils::time_utils::time; use actix::{Actor, Addr, AsyncContext, Context, Handler, Message}; use std::time::Duration; const TOKENS_CLEAN_INTERVAL: Duration = Duration::from_secs(60); const VNC_TOKEN_LEN: usize = 15; pub const VNC_TOKEN_LIFETIME: u64 = 30; #[derive(thiserror::Error, Debug)] enum VNCTokenError { #[error("Could not use token, because it does not exist!")] UseErrorTokenNotFound, #[error("Could not use token, because it has expired!")] UseErrorTokenExpired, } #[derive(Debug, Clone)] struct VNCToken { token: String, vm: XMLUuid, expire: u64, } impl VNCToken { fn is_expired(&self) -> bool { self.expire < time() } } struct VNCTokensActor(Vec); impl Actor for VNCTokensActor { type Context = Context; fn started(&mut self, ctx: &mut Self::Context) { ctx.run_interval(TOKENS_CLEAN_INTERVAL, |act, _ctx| { // Regularly remove outdated entries log::debug!("Remove outdated VNC token entries"); act.0.retain(|e| !e.is_expired()) }); } } #[derive(Message)] #[rtype(result = "anyhow::Result")] pub struct IssueTokenReq(XMLUuid); impl Handler for VNCTokensActor { type Result = anyhow::Result; fn handle(&mut self, msg: IssueTokenReq, _ctx: &mut Self::Context) -> Self::Result { log::debug!("Issue a new VNC token for domain {:?}", msg.0); let token = VNCToken { token: rand_str(VNC_TOKEN_LEN), vm: msg.0, expire: time() + VNC_TOKEN_LIFETIME, }; self.0.push(token.clone()); Ok(token.token) } } #[derive(Message)] #[rtype(result = "anyhow::Result")] pub struct UseTokenReq(String); impl Handler for VNCTokensActor { type Result = anyhow::Result; fn handle(&mut self, msg: UseTokenReq, _ctx: &mut Self::Context) -> Self::Result { log::debug!("Attempt to use a token {:?}", msg.0); let token = self .0 .iter() .find(|i| i.token == msg.0) .ok_or(VNCTokenError::UseErrorTokenNotFound)?; if token.is_expired() { return Err(VNCTokenError::UseErrorTokenExpired.into()); } Ok(token.vm) } } #[derive(Clone)] pub struct VNCTokensManager(Addr); impl VNCTokensManager { pub fn start() -> Self { Self(VNCTokensActor(vec![]).start()) } /// Issue a new VNC access token for a domain pub async fn issue_token(&self, id: XMLUuid) -> anyhow::Result { self.0.send(IssueTokenReq(id)).await? } /// Use a VNC access token pub async fn use_token(&self, token: String) -> anyhow::Result { self.0.send(UseTokenReq(token)).await? } }