Can get VM caps
This commit is contained in:
		
							
								
								
									
										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)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user