VirtWeb/virtweb_backend/src/actors/vnc_tokens_actor.rs

107 lines
2.8 KiB
Rust

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<VNCToken>);
impl Actor for VNCTokensActor {
type Context = Context<Self>;
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<String>")]
pub struct IssueTokenReq(XMLUuid);
impl Handler<IssueTokenReq> for VNCTokensActor {
type Result = anyhow::Result<String>;
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<XMLUuid>")]
pub struct UseTokenReq(String);
impl Handler<UseTokenReq> for VNCTokensActor {
type Result = anyhow::Result<XMLUuid>;
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<VNCTokensActor>);
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<String> {
self.0.send(IssueTokenReq(id)).await?
}
/// Use a VNC access token
pub async fn use_token(&self, token: String) -> anyhow::Result<XMLUuid> {
self.0.send(UseTokenReq(token)).await?
}
}