107 lines
2.8 KiB
Rust
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?
|
|
}
|
|
}
|