Can update and delete API tokens
This commit is contained in:
		@@ -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(())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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(
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user