Can update and delete API tokens
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing

This commit is contained in:
Pierre HUBERT 2024-04-09 19:38:09 +02:00
parent e938b5a423
commit 0ee2c41f3c
3 changed files with 80 additions and 23 deletions

View File

@ -10,6 +10,15 @@ use std::path::Path;
#[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)] #[derive(serde::Serialize, serde::Deserialize, Clone, Copy, Debug)]
pub struct TokenID(pub uuid::Uuid); 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<TokenRight>);
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct Token { pub struct Token {
pub id: TokenID, pub id: TokenID,
@ -19,7 +28,7 @@ pub struct Token {
updated: u64, updated: u64,
#[serde(skip_serializing_if = "TokenPubKey::is_invalid")] #[serde(skip_serializing_if = "TokenPubKey::is_invalid")]
pub pub_key: TokenPubKey, pub pub_key: TokenPubKey,
pub rights: Vec<TokenRights>, pub rights: TokenRights,
pub last_used: Option<u64>, pub last_used: Option<u64>,
pub ip_restriction: Option<ipnetwork::IpNetwork>, pub ip_restriction: Option<ipnetwork::IpNetwork>,
pub delete_after_inactivity: Option<u64>, pub delete_after_inactivity: Option<u64>,
@ -27,8 +36,12 @@ pub struct Token {
impl Token { impl Token {
/// Turn the token into a JSON string /// Turn the token into a JSON string
pub fn to_json(&self) -> String { fn save(&self) -> anyhow::Result<()> {
serde_json::to_string(self).expect("Failed to serialize an API token!") 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 /// Load token information from a file
@ -46,22 +59,27 @@ pub enum TokenVerb {
DELETE, DELETE,
} }
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct TokenRights {
verb: TokenVerb,
uri: String,
}
/// Structure used to create a token /// Structure used to create a token
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone)] #[derive(serde::Serialize, serde::Deserialize, Debug, Clone)]
pub struct NewToken { pub struct NewToken {
pub name: String, pub name: String,
pub description: String, pub description: String,
pub rights: Vec<TokenRights>, pub rights: TokenRights,
pub ip_restriction: Option<ipnetwork::IpNetwork>, pub ip_restriction: Option<ipnetwork::IpNetwork>,
pub delete_after_inactivity: Option<u64>, pub delete_after_inactivity: Option<u64>,
} }
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 { impl NewToken {
/// Check for error in token /// Check for error in token
pub fn check_error(&self) -> Option<&'static str> { pub fn check_error(&self) -> Option<&'static str> {
@ -81,10 +99,8 @@ impl NewToken {
return Some("Description is too long!"); return Some("Description is too long!");
} }
for r in &self.rights { if let Some(err) = self.rights.check_error() {
if !r.uri.starts_with("/api/") { return Some(err);
return Some("All API rights shall start with /api/");
}
} }
if let Some(t) = self.delete_after_inactivity { 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, 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)) Ok((token, priv_key))
} }
@ -135,3 +146,21 @@ pub async fn full_list() -> anyhow::Result<Vec<Token>> {
pub async fn get_single(id: TokenID) -> anyhow::Result<Token> { pub async fn get_single(id: TokenID) -> anyhow::Result<Token> {
Token::load_from_file(&AppConfig::get().api_token_definition_path(id)) 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(())
}

View File

@ -1,7 +1,7 @@
//! # API tokens management //! # API tokens management
use crate::api_tokens; 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::api_tokens_controller::rest_token::RestToken;
use crate::controllers::HttpResult; use crate::controllers::HttpResult;
use crate::utils::jwt_utils::TokenPrivKey; use crate::utils::jwt_utils::TokenPrivKey;
@ -70,3 +70,31 @@ pub async fn get_single(path: web::Path<TokenIDInPath>) -> HttpResult {
Ok(HttpResponse::Ok().json(RestToken::new(token))) 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<TokenIDInPath>,
body: web::Json<UpdateTokenBody>,
) -> 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<TokenIDInPath>) -> HttpResult {
api_tokens::delete(path.uid).await?;
Ok(HttpResponse::Accepted().finish())
}

View File

@ -290,14 +290,14 @@ async fn main() -> std::io::Result<()> {
"/api/tokens/{uid}", "/api/tokens/{uid}",
web::get().to(api_tokens_controller::get_single), web::get().to(api_tokens_controller::get_single),
) )
/* TODO .route( .route(
"/api/tokens/{uid}", "/api/tokens/{uid}",
web::put().to(api_tokens_controller::update), web::patch().to(api_tokens_controller::update),
) )
.route( .route(
"/api/tokens/{uid}", "/api/tokens/{uid}",
web::delete().to(api_tokens_controller::delete), web::delete().to(api_tokens_controller::delete),
)*/ )
// Static assets // Static assets
.route("/", web::get().to(static_controller::root_index)) .route("/", web::get().to(static_controller::root_index))
.route( .route(