Issue tokens to initialize VNC connections

This commit is contained in:
2023-10-18 12:27:50 +02:00
parent 3042bbdac6
commit 4f75cd918d
10 changed files with 173 additions and 2 deletions

View File

@ -1 +1,2 @@
pub mod libvirt_actor;
pub mod vnc_tokens_actor;

View File

@ -0,0 +1,108 @@
use crate::libvirt_lib_structures::DomainXMLUuid;
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;
const VNC_TOKEN_LIFETIME: u64 = 120;
#[derive(thiserror::Error, Debug)]
enum VNCTokenError {
#[error("Could not consume token, because it does not exist!")]
ConsumeErrorTokenNotFound,
#[error("Could not consume token, because it has expired!")]
ConsumeErrorTokenExpired,
}
#[derive(Debug, Clone)]
struct VNCToken {
token: String,
vm: DomainXMLUuid,
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(DomainXMLUuid);
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<DomainXMLUuid>")]
pub struct ConsumeTokenReq(String);
impl Handler<ConsumeTokenReq> for VNCTokensActor {
type Result = anyhow::Result<DomainXMLUuid>;
fn handle(&mut self, msg: ConsumeTokenReq, _ctx: &mut Self::Context) -> Self::Result {
log::debug!("Attempt to consume a token {:?}", msg.0);
let token_index = self
.0
.iter()
.position(|i| i.token == msg.0)
.ok_or(VNCTokenError::ConsumeErrorTokenNotFound)?;
let token = self.0.remove(token_index);
if token.is_expired() {
return Err(VNCTokenError::ConsumeErrorTokenExpired.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: DomainXMLUuid) -> anyhow::Result<String> {
self.0.send(IssueTokenReq(id)).await?
}
/// Consume a VNC access token
pub async fn consume_token(&self, token: String) -> anyhow::Result<DomainXMLUuid> {
self.0.send(ConsumeTokenReq(token)).await?
}
}