From 0ee2c41f3cc2d3c213f573013911763706fe73e2 Mon Sep 17 00:00:00 2001 From: Pierre HUBERT Date: Tue, 9 Apr 2024 19:38:09 +0200 Subject: [PATCH] Can update and delete API tokens --- virtweb_backend/src/api_tokens.rs | 67 +++++++++++++------ .../src/controllers/api_tokens_controller.rs | 30 ++++++++- virtweb_backend/src/main.rs | 6 +- 3 files changed, 80 insertions(+), 23 deletions(-) diff --git a/virtweb_backend/src/api_tokens.rs b/virtweb_backend/src/api_tokens.rs index 3222c9a..9d8bab3 100644 --- a/virtweb_backend/src/api_tokens.rs +++ b/virtweb_backend/src/api_tokens.rs @@ -10,6 +10,15 @@ use std::path::Path; #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] pub struct TokenID(pub uuid::Uuid); +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct TokenRight { + verb: TokenVerb, + uri: String, +} + +#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] +pub struct TokenRights(Vec); + #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct Token { pub id: TokenID, @@ -19,7 +28,7 @@ pub struct Token { updated: u64, #[serde(skip_serializing_if = "TokenPubKey::is_invalid")] pub pub_key: TokenPubKey, - pub rights: Vec, + pub rights: TokenRights, pub last_used: Option, pub ip_restriction: Option, pub delete_after_inactivity: Option, @@ -27,8 +36,12 @@ pub struct Token { impl Token { /// Turn the token into a JSON string - pub fn to_json(&self) -> String { - serde_json::to_string(self).expect("Failed to serialize an API token!") + fn save(&self) -> anyhow::Result<()> { + let json = serde_json::to_string(self)?; + + std::fs::write(AppConfig::get().api_token_definition_path(self.id), json)?; + + Ok(()) } /// Load token information from a file @@ -46,22 +59,27 @@ pub enum TokenVerb { DELETE, } -#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] -pub struct TokenRights { - verb: TokenVerb, - uri: String, -} - /// Structure used to create a token #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] pub struct NewToken { pub name: String, pub description: String, - pub rights: Vec, + pub rights: TokenRights, pub ip_restriction: Option, pub delete_after_inactivity: Option, } +impl TokenRights { + pub fn check_error(&self) -> Option<&'static str> { + for r in &self.0 { + if !r.uri.starts_with("/api/") { + return Some("All API rights shall start with /api/"); + } + } + None + } +} + impl NewToken { /// Check for error in token pub fn check_error(&self) -> Option<&'static str> { @@ -81,10 +99,8 @@ impl NewToken { return Some("Description is too long!"); } - for r in &self.rights { - if !r.uri.starts_with("/api/") { - return Some("All API rights shall start with /api/"); - } + if let Some(err) = self.rights.check_error() { + return Some(err); } if let Some(t) = self.delete_after_inactivity { @@ -114,11 +130,6 @@ pub async fn create(t: &NewToken) -> anyhow::Result<(Token, TokenPrivKey)> { delete_after_inactivity: t.delete_after_inactivity, }; - std::fs::write( - AppConfig::get().api_token_definition_path(token.id), - token.to_json(), - )?; - Ok((token, priv_key)) } @@ -135,3 +146,21 @@ pub async fn full_list() -> anyhow::Result> { pub async fn get_single(id: TokenID) -> anyhow::Result { Token::load_from_file(&AppConfig::get().api_token_definition_path(id)) } + +/// Update API tokens rights +pub async fn update_rights(id: TokenID, rights: TokenRights) -> anyhow::Result<()> { + let mut token = get_single(id).await?; + token.rights = rights; + token.updated = time(); + token.save()?; + Ok(()) +} + +/// Delete an API token +pub async fn delete(id: TokenID) -> anyhow::Result<()> { + let path = AppConfig::get().api_token_definition_path(id); + if path.exists() { + std::fs::remove_file(path)?; + } + Ok(()) +} diff --git a/virtweb_backend/src/controllers/api_tokens_controller.rs b/virtweb_backend/src/controllers/api_tokens_controller.rs index 4f80eae..3f40161 100644 --- a/virtweb_backend/src/controllers/api_tokens_controller.rs +++ b/virtweb_backend/src/controllers/api_tokens_controller.rs @@ -1,7 +1,7 @@ //! # API tokens management use crate::api_tokens; -use crate::api_tokens::{NewToken, TokenID}; +use crate::api_tokens::{NewToken, TokenID, TokenRights}; use crate::controllers::api_tokens_controller::rest_token::RestToken; use crate::controllers::HttpResult; use crate::utils::jwt_utils::TokenPrivKey; @@ -70,3 +70,31 @@ pub async fn get_single(path: web::Path) -> HttpResult { Ok(HttpResponse::Ok().json(RestToken::new(token))) } + +#[derive(serde::Deserialize)] +pub struct UpdateTokenBody { + rights: TokenRights, +} + +/// Update a token +pub async fn update( + path: web::Path, + body: web::Json, +) -> HttpResult { + if let Some(err) = body.rights.check_error() { + log::error!("Failed to validate updated API token information! {err}"); + return Ok(HttpResponse::BadRequest() + .json(format!("Failed to validate API token information! {err}"))); + } + + api_tokens::update_rights(path.uid, body.0.rights).await?; + + Ok(HttpResponse::Accepted().finish()) +} + +/// Delete a token +pub async fn delete(path: web::Path) -> HttpResult { + api_tokens::delete(path.uid).await?; + + Ok(HttpResponse::Accepted().finish()) +} diff --git a/virtweb_backend/src/main.rs b/virtweb_backend/src/main.rs index 4176739..a968342 100644 --- a/virtweb_backend/src/main.rs +++ b/virtweb_backend/src/main.rs @@ -290,14 +290,14 @@ async fn main() -> std::io::Result<()> { "/api/tokens/{uid}", web::get().to(api_tokens_controller::get_single), ) - /* TODO .route( + .route( "/api/tokens/{uid}", - web::put().to(api_tokens_controller::update), + web::patch().to(api_tokens_controller::update), ) .route( "/api/tokens/{uid}", web::delete().to(api_tokens_controller::delete), - )*/ + ) // Static assets .route("/", web::get().to(static_controller::root_index)) .route(