Can get VM caps
This commit is contained in:
parent
b6e15d2cbb
commit
50600e4e56
25
remote_backend/Cargo.lock
generated
25
remote_backend/Cargo.lock
generated
@ -1250,6 +1250,29 @@ version = "0.3.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
checksum = "d4345964bb142484797b161f473a503a434de77149dd8c7427788c6e13379388"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5d12be4595afdf58bd19e4a9f4e24187da2a66700786ff660a418e9059937a4c"
|
||||||
|
dependencies = [
|
||||||
|
"lazy-regex-proc_macros",
|
||||||
|
"once_cell",
|
||||||
|
"regex",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lazy-regex-proc_macros"
|
||||||
|
version = "3.1.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "44bcd58e6c97a7fcbaffcdc95728b393b8d98933bfadad49ed4097845b57ef0b"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex",
|
||||||
|
"syn 2.0.60",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "lazy_static"
|
name = "lazy_static"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@ -1716,6 +1739,7 @@ dependencies = [
|
|||||||
"clap",
|
"clap",
|
||||||
"env_logger",
|
"env_logger",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
"lazy-regex",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"light-openid",
|
"light-openid",
|
||||||
"log",
|
"log",
|
||||||
@ -2344,6 +2368,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
|||||||
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"getrandom",
|
"getrandom",
|
||||||
|
"serde",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -21,5 +21,6 @@ lazy_static = "1.4.0"
|
|||||||
anyhow = "1.0.82"
|
anyhow = "1.0.82"
|
||||||
reqwest = { version = "0.12.4", features = ["json"] }
|
reqwest = { version = "0.12.4", features = ["json"] }
|
||||||
thiserror = "1.0.59"
|
thiserror = "1.0.59"
|
||||||
uuid = { version = "1.8.0", features = ["v4"] }
|
uuid = { version = "1.8.0", features = ["v4", "serde"] }
|
||||||
futures-util = "0.3.30"
|
futures-util = "0.3.30"
|
||||||
|
lazy-regex = "3.1.0"
|
@ -7,6 +7,7 @@ use std::io::ErrorKind;
|
|||||||
|
|
||||||
pub mod auth_controller;
|
pub mod auth_controller;
|
||||||
pub mod server_controller;
|
pub mod server_controller;
|
||||||
|
pub mod vm_controller;
|
||||||
|
|
||||||
/// Custom error to ease controller writing
|
/// Custom error to ease controller writing
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
48
remote_backend/src/controllers/vm_controller.rs
Normal file
48
remote_backend/src/controllers/vm_controller.rs
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
//! # Virtual machines routes controller
|
||||||
|
|
||||||
|
use crate::controllers::HttpResult;
|
||||||
|
use crate::virtweb_client;
|
||||||
|
use crate::virtweb_client::VMUuid;
|
||||||
|
use actix_web::HttpResponse;
|
||||||
|
|
||||||
|
#[derive(Debug, serde::Serialize)]
|
||||||
|
pub struct VMInfoAndCaps {
|
||||||
|
uiid: VMUuid,
|
||||||
|
name: String,
|
||||||
|
description: Option<String>,
|
||||||
|
can_get_state: bool,
|
||||||
|
can_start: bool,
|
||||||
|
can_shutdown: bool,
|
||||||
|
can_kill: bool,
|
||||||
|
can_reset: bool,
|
||||||
|
can_suspend: bool,
|
||||||
|
can_resume: bool,
|
||||||
|
can_screenshot: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the list of VMs that can be controlled by VirtWeb remote
|
||||||
|
pub async fn list() -> HttpResult {
|
||||||
|
let rights = virtweb_client::get_token_info().await?;
|
||||||
|
|
||||||
|
let mut res = vec![];
|
||||||
|
|
||||||
|
for v in rights.list_vm() {
|
||||||
|
let vm_info = virtweb_client::get_vm_info(v).await?;
|
||||||
|
|
||||||
|
res.push(VMInfoAndCaps {
|
||||||
|
uiid: vm_info.uuid,
|
||||||
|
name: vm_info.name,
|
||||||
|
description: vm_info.description.clone(),
|
||||||
|
can_get_state: rights.is_route_allowed("GET", &v.route_state()),
|
||||||
|
can_start: rights.is_route_allowed("GET", &v.route_start()),
|
||||||
|
can_shutdown: rights.is_route_allowed("GET", &v.route_shutdown()),
|
||||||
|
can_kill: rights.is_route_allowed("GET", &v.route_kill()),
|
||||||
|
can_reset: rights.is_route_allowed("GET", &v.route_reset()),
|
||||||
|
can_suspend: rights.is_route_allowed("GET", &v.route_suspend()),
|
||||||
|
can_resume: rights.is_route_allowed("GET", &v.route_resume()),
|
||||||
|
can_screenshot: rights.is_route_allowed("GET", &v.route_screenshot()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(HttpResponse::Ok().json(res))
|
||||||
|
}
|
@ -11,7 +11,7 @@ use actix_web::{web, App, HttpServer};
|
|||||||
use light_openid::basic_state_manager::BasicStateManager;
|
use light_openid::basic_state_manager::BasicStateManager;
|
||||||
use remote_backend::app_config::AppConfig;
|
use remote_backend::app_config::AppConfig;
|
||||||
use remote_backend::constants;
|
use remote_backend::constants;
|
||||||
use remote_backend::controllers::{auth_controller, server_controller};
|
use remote_backend::controllers::{auth_controller, server_controller, vm_controller};
|
||||||
use remote_backend::middlewares::auth_middleware::AuthChecker;
|
use remote_backend::middlewares::auth_middleware::AuthChecker;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
|
|
||||||
@ -79,6 +79,7 @@ async fn main() -> std::io::Result<()> {
|
|||||||
"/api/auth/sign_out",
|
"/api/auth/sign_out",
|
||||||
web::get().to(auth_controller::sign_out),
|
web::get().to(auth_controller::sign_out),
|
||||||
)
|
)
|
||||||
|
.route("/api/vm/list", web::get().to(vm_controller::list))
|
||||||
})
|
})
|
||||||
.bind(&AppConfig::get().listen_address)?
|
.bind(&AppConfig::get().listen_address)?
|
||||||
.run()
|
.run()
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
use crate::app_config::AppConfig;
|
use crate::app_config::AppConfig;
|
||||||
use crate::utils::time;
|
use crate::utils::time;
|
||||||
|
use lazy_regex::regex;
|
||||||
use std::fmt::Display;
|
use std::fmt::Display;
|
||||||
|
use std::str::FromStr;
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
use uuid::Uuid;
|
use uuid::{Error, Uuid};
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum VirtWebClientError {
|
pub enum VirtWebClientError {
|
||||||
@ -10,6 +12,53 @@ pub enum VirtWebClientError {
|
|||||||
InvalidStatusCode(u16),
|
InvalidStatusCode(u16),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq, Debug, Copy, Clone, serde::Serialize, serde::Deserialize)]
|
||||||
|
pub struct VMUuid(Uuid);
|
||||||
|
|
||||||
|
impl VMUuid {
|
||||||
|
pub fn route_info(&self) -> String {
|
||||||
|
format!("/api/vm/{}", self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn route_state(&self) -> String {
|
||||||
|
format!("/api/vm/{}/state", self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn route_start(&self) -> String {
|
||||||
|
format!("/api/vm/{}/start", self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn route_shutdown(&self) -> String {
|
||||||
|
format!("/api/vm/{}/shutdown", self.0)
|
||||||
|
}
|
||||||
|
pub fn route_kill(&self) -> String {
|
||||||
|
format!("/api/vm/{}/kill", self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn route_reset(&self) -> String {
|
||||||
|
format!("/api/vm/{}/reset", self.0)
|
||||||
|
}
|
||||||
|
pub fn route_suspend(&self) -> String {
|
||||||
|
format!("/api/vm/{}/suspend", self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn route_resume(&self) -> String {
|
||||||
|
format!("/api/vm/{}/resume", self.0)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn route_screenshot(&self) -> String {
|
||||||
|
format!("/api/vm/{}/screenshot", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for VMUuid {
|
||||||
|
type Err = Error;
|
||||||
|
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
Ok(VMUuid(Uuid::from_str(s)?))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(serde::Serialize, Debug)]
|
#[derive(serde::Serialize, Debug)]
|
||||||
pub struct TokenClaims {
|
pub struct TokenClaims {
|
||||||
pub sub: String,
|
pub sub: String,
|
||||||
@ -20,6 +69,13 @@ pub struct TokenClaims {
|
|||||||
pub nonce: String,
|
pub nonce: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, Debug)]
|
||||||
|
pub struct VMInfo {
|
||||||
|
pub uuid: VMUuid,
|
||||||
|
pub name: String,
|
||||||
|
pub description: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, Debug)]
|
#[derive(serde::Deserialize, Debug)]
|
||||||
pub struct TokenRight {
|
pub struct TokenRight {
|
||||||
verb: String,
|
verb: String,
|
||||||
@ -29,10 +85,49 @@ pub struct TokenRight {
|
|||||||
pub type TokenRights = Vec<TokenRight>;
|
pub type TokenRights = Vec<TokenRight>;
|
||||||
|
|
||||||
#[derive(serde::Deserialize)]
|
#[derive(serde::Deserialize)]
|
||||||
struct TokenInfo {
|
pub struct TokenInfo {
|
||||||
rights: TokenRights,
|
rights: TokenRights,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl TokenInfo {
|
||||||
|
/// Check whether a route is allowed or not
|
||||||
|
pub fn is_route_allowed(&self, verb: &str, route: &str) -> bool {
|
||||||
|
let search_route_split = route.split('/').collect::<Vec<_>>();
|
||||||
|
|
||||||
|
for r in &self.rights {
|
||||||
|
if r.verb != verb {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let curr_route_split = r.path.split('/').collect::<Vec<_>>();
|
||||||
|
|
||||||
|
if search_route_split.len() != curr_route_split.len() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if curr_route_split
|
||||||
|
.iter()
|
||||||
|
.zip(search_route_split.iter())
|
||||||
|
.all(|(curr, search)| curr == &"*" || curr == search)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// List the virtual machines with access
|
||||||
|
pub fn list_vm(&self) -> Vec<VMUuid> {
|
||||||
|
self.rights
|
||||||
|
.iter()
|
||||||
|
.filter(|r| r.verb == "GET")
|
||||||
|
.filter(|r| regex!("^/api/vm/[^/]+$").is_match(&r.path))
|
||||||
|
.map(|r| VMUuid::from_str(r.path.rsplit_once('/').unwrap().1).unwrap())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Perform a request on the API
|
/// Perform a request on the API
|
||||||
async fn request<D: Display, E: serde::de::DeserializeOwned>(uri: D) -> anyhow::Result<E> {
|
async fn request<D: Display, E: serde::de::DeserializeOwned>(uri: D) -> anyhow::Result<E> {
|
||||||
let url = format!("{}{}", AppConfig::get().virtweb_base_url, uri);
|
let url = format!("{}{}", AppConfig::get().virtweb_base_url, uri);
|
||||||
@ -62,10 +157,17 @@ async fn request<D: Display, E: serde::de::DeserializeOwned>(uri: D) -> anyhow::
|
|||||||
Ok(res.json().await?)
|
Ok(res.json().await?)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get current token rights
|
/// Get current token information
|
||||||
pub async fn get_token_rights() -> anyhow::Result<TokenRights> {
|
pub async fn get_token_info() -> anyhow::Result<TokenInfo> {
|
||||||
let res: TokenInfo =
|
let res: TokenInfo =
|
||||||
request(format!("/api/token/{}", AppConfig::get().virtweb_token_id)).await?;
|
request(format!("/api/token/{}", AppConfig::get().virtweb_token_id)).await?;
|
||||||
|
|
||||||
Ok(res.rights)
|
Ok(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get a vm information
|
||||||
|
pub async fn get_vm_info(id: VMUuid) -> anyhow::Result<VMInfo> {
|
||||||
|
let res: VMInfo = request(format!("/api/vm/{}", id.0)).await?;
|
||||||
|
|
||||||
|
Ok(res)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user