Issue tokens to initialize VNC connections
This commit is contained in:
@ -1 +1,2 @@
|
||||
pub mod libvirt_actor;
|
||||
pub mod vnc_tokens_actor;
|
||||
|
108
virtweb_backend/src/actors/vnc_tokens_actor.rs
Normal file
108
virtweb_backend/src/actors/vnc_tokens_actor.rs
Normal 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?
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user