3 Commits

Author SHA1 Message Date
0c5a232a25 Can get a single API token
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone/pr Build is passing
2024-04-09 19:04:49 +02:00
f56e9c14b2 Can get the list of tokens 2024-04-09 18:56:12 +02:00
60a3cb3d10 Can create API tokens 2024-04-09 18:36:18 +02:00
5 changed files with 81 additions and 15 deletions

View File

@@ -1,20 +1,23 @@
//! # API tokens management
use crate::app_config::AppConfig;
use crate::constants;
use crate::utils::jwt_utils;
use crate::utils::jwt_utils::{TokenPrivKey, TokenPubKey};
use crate::utils::time_utils::time;
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 Token {
pub id: TokenID,
pub name: String,
pub description: String,
pub id: TokenID,
created: u64,
updated: u64,
#[serde(skip_serializing_if = "TokenPubKey::is_invalid")]
pub pub_key: TokenPubKey,
pub rights: Vec<TokenRights>,
pub last_used: Option<u64>,
@@ -22,6 +25,18 @@ pub struct Token {
pub delete_after_inactivity: Option<u64>,
}
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!")
}
/// Load token information from a file
pub fn load_from_file(path: &Path) -> anyhow::Result<Self> {
Ok(serde_json::from_str(&std::fs::read_to_string(path)?)?)
}
}
#[derive(serde::Serialize, serde::Deserialize, Debug, Clone, Copy, Eq, PartialEq)]
pub enum TokenVerb {
GET,
@@ -83,23 +98,40 @@ impl NewToken {
}
/// Create a new Token
pub async fn create(token: &NewToken) -> anyhow::Result<(Token, TokenPrivKey)> {
pub async fn create(t: &NewToken) -> anyhow::Result<(Token, TokenPrivKey)> {
let (pub_key, priv_key) = jwt_utils::generate_key_pair()?;
let full_token = Token {
name: token.name.to_string(),
description: token.description.to_string(),
let token = Token {
name: t.name.to_string(),
description: t.description.to_string(),
id: TokenID(uuid::Uuid::new_v4()),
created: time(),
updated: time(),
pub_key,
rights: token.rights.clone(),
rights: t.rights.clone(),
last_used: Some(time()),
ip_restriction: token.ip_restriction,
delete_after_inactivity: token.delete_after_inactivity,
ip_restriction: t.ip_restriction,
delete_after_inactivity: t.delete_after_inactivity,
};
// TODO : save
std::fs::write(
AppConfig::get().api_token_definition_path(token.id),
token.to_json(),
)?;
Ok((full_token, priv_key))
Ok((token, priv_key))
}
/// Get the entire list of api tokens
pub async fn full_list() -> anyhow::Result<Vec<Token>> {
let mut list = Vec::new();
for f in std::fs::read_dir(AppConfig::get().api_tokens_path())? {
list.push(Token::load_from_file(&f?.path())?);
}
Ok(list)
}
/// Get the information about a single token
pub async fn get_single(id: TokenID) -> anyhow::Result<Token> {
Token::load_from_file(&AppConfig::get().api_token_definition_path(id))
}

View File

@@ -1,3 +1,4 @@
use crate::api_tokens::TokenID;
use crate::constants;
use crate::libvirt_lib_structures::XMLUuid;
use crate::libvirt_rest_structures::net::NetworkName;
@@ -272,6 +273,10 @@ impl AppConfig {
pub fn api_tokens_path(&self) -> PathBuf {
self.storage_path().join(constants::STORAGE_TOKENS_DIR)
}
pub fn api_token_definition_path(&self, id: TokenID) -> PathBuf {
self.api_tokens_path().join(format!("{}.json", id.0))
}
}
#[derive(Debug, Clone, serde::Serialize)]

View File

@@ -1,7 +1,7 @@
//! # API tokens management
use crate::api_tokens;
use crate::api_tokens::NewToken;
use crate::api_tokens::{NewToken, TokenID};
use crate::controllers::api_tokens_controller::rest_token::RestToken;
use crate::controllers::HttpResult;
use crate::utils::jwt_utils::TokenPrivKey;
@@ -47,3 +47,26 @@ pub async fn create(new_token: web::Json<NewToken>) -> HttpResult {
priv_key,
}))
}
/// Get the list of API tokens
pub async fn list() -> HttpResult {
let list = api_tokens::full_list()
.await?
.into_iter()
.map(RestToken::new)
.collect::<Vec<_>>();
Ok(HttpResponse::Ok().json(list))
}
#[derive(serde::Deserialize)]
pub struct TokenIDInPath {
uid: TokenID,
}
/// Get the information about a single token
pub async fn get_single(path: web::Path<TokenIDInPath>) -> HttpResult {
let token = api_tokens::get_single(path.uid).await?;
Ok(HttpResponse::Ok().json(RestToken::new(token)))
}

View File

@@ -282,7 +282,7 @@ async fn main() -> std::io::Result<()> {
"/api/tokens/create",
web::post().to(api_tokens_controller::create),
)
/* TODO .route(
.route(
"/api/tokens/list",
web::get().to(api_tokens_controller::list),
)
@@ -290,7 +290,7 @@ async fn main() -> std::io::Result<()> {
"/api/tokens/{uid}",
web::get().to(api_tokens_controller::get_single),
)
.route(
/* TODO .route(
"/api/tokens/{uid}",
web::put().to(api_tokens_controller::update),
)

View File

@@ -6,10 +6,10 @@ use rand::rngs::OsRng;
use serde::de::DeserializeOwned;
use serde::Serialize;
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug, Eq, PartialEq)]
#[serde(tag = "alg")]
pub enum TokenPubKey {
/// This variant DOES make crash the program. It MUST NOT be serialized.
/// This variant DOES make crash the program. It MUST NOT used to validate JWT.
///
/// It is a hack to hide public key when getting the list of tokens
None,
@@ -18,6 +18,12 @@ pub enum TokenPubKey {
ES384 { r#pub: String },
}
impl TokenPubKey {
pub fn is_invalid(&self) -> bool {
self == &TokenPubKey::None
}
}
#[derive(serde::Serialize, serde::Deserialize, Clone, Debug)]
#[serde(tag = "alg")]
pub enum TokenPrivKey {