Add tokens routes

This commit is contained in:
2025-03-19 18:57:38 +01:00
parent 3081757536
commit 544513d118
16 changed files with 376 additions and 18 deletions

@ -2,7 +2,7 @@ use crate::app_config::AppConfig;
use crate::controllers::{HttpFailure, HttpResult};
use crate::extractors::auth_extractor::{AuthExtractor, AuthenticatedMethod};
use crate::extractors::money_session::MoneySession;
use crate::services::users_service;
use crate::services::{tokens_service, users_service};
use actix_remote_ip::RemoteIP;
use actix_web::{HttpResponse, web};
use light_openid::primitives::OpenIDConfig;
@ -118,6 +118,10 @@ pub async fn sign_out(auth: AuthExtractor, session: MoneySession) -> HttpResult
session.unset_current_user()?;
}
AuthenticatedMethod::Token(token) => {
tokens_service::delete(token.user_id(), token.id()).await?;
}
AuthenticatedMethod::Dev => {
// Nothing to be done, user is always authenticated
}

@ -4,6 +4,7 @@ use std::error::Error;
pub mod auth_controller;
pub mod server_controller;
pub mod tokens_controller;
#[derive(thiserror::Error, Debug)]
pub enum HttpFailure {

@ -8,15 +8,45 @@ pub async fn robots_txt() -> HttpResponse {
.body("User-agent: *\nDisallow: /\n")
}
#[derive(serde::Serialize)]
pub struct LenConstraints {
min: usize,
max: usize,
}
impl LenConstraints {
pub fn new(min: usize, max: usize) -> Self {
Self { min, max }
}
pub fn not_empty(max: usize) -> Self {
Self { min: 1, max }
}
pub fn max_only(max: usize) -> Self {
Self { min: 0, max }
}
pub fn check_str(&self, s: &str) -> bool {
s.len() >= self.min && s.len() <= self.max
}
pub fn check_u32(&self, v: u32) -> bool {
v >= self.min as u32 && v <= self.max as u32
}
}
#[derive(serde::Serialize)]
pub struct ServerConstraints {
// TODO
pub token_name: LenConstraints,
pub token_ip_net: LenConstraints,
pub token_max_inactivity: LenConstraints,
}
impl Default for ServerConstraints {
fn default() -> Self {
Self {
// TODO
token_name: LenConstraints::new(5, 255),
token_ip_net: LenConstraints::max_only(44),
token_max_inactivity: LenConstraints::new(3600, 3600 * 24 * 365),
}
}
}

@ -0,0 +1,87 @@
use crate::controllers::HttpResult;
use crate::controllers::server_controller::ServerConstraints;
use crate::extractors::auth_extractor::{AuthExtractor, AuthenticatedMethod};
use crate::models::tokens::{Token, TokenID};
use crate::services::tokens_service;
use crate::services::tokens_service::NewTokenInfo;
use actix_web::{HttpResponse, web};
#[derive(serde::Deserialize)]
pub struct CreateTokenBody {
name: String,
ip_net: Option<ipnet::IpNet>,
max_inactivity: u32,
read_only: bool,
right_account: bool,
right_movement: bool,
right_inbox: bool,
right_attachment: bool,
right_auth: bool,
}
#[derive(serde::Serialize)]
pub struct CreateTokenResult {
#[serde(flatten)]
info: Token,
token: String,
}
/// Create a new token
pub async fn create(auth: AuthExtractor, req: web::Json<CreateTokenBody>) -> HttpResult {
if matches!(auth.method, AuthenticatedMethod::Token(_)) {
return Ok(HttpResponse::Forbidden()
.json("It is not allowed to create a token using another token!"));
}
let constraints = ServerConstraints::default();
if !lazy_regex::regex!("^[a-zA-Z0-9 :-]+$").is_match(&req.name) {
return Ok(HttpResponse::BadRequest().json("Token name contains invalid characters!"));
}
if !constraints.token_name.check_str(&req.name) {
return Ok(HttpResponse::BadRequest().json("Invalid token name length!"));
}
if !constraints
.token_max_inactivity
.check_u32(req.max_inactivity)
{
return Ok(HttpResponse::BadRequest().json("Invalid token max inactivity!"));
}
let token = tokens_service::create(NewTokenInfo {
user_id: auth.user_id(),
max_inactivity: req.max_inactivity,
ip_net: req.ip_net,
name: req.name.clone(),
read_only: req.read_only,
right_account: req.right_account,
right_movement: req.right_movement,
right_inbox: req.right_inbox,
right_attachment: req.right_attachment,
right_auth: req.right_auth,
})
.await?;
Ok(HttpResponse::Created().json(CreateTokenResult {
token: token.token_value.to_string(),
info: token,
}))
}
/// Get the list of tokens of the user
pub async fn get_list(auth: AuthExtractor) -> HttpResult {
Ok(HttpResponse::Ok().json(tokens_service::get_list_user(auth.user_id()).await?))
}
#[derive(serde::Deserialize)]
pub struct TokenIDInPath {
id: TokenID,
}
/// Delete an API access token
pub async fn delete(auth: AuthExtractor, path: web::Path<TokenIDInPath>) -> HttpResult {
tokens_service::delete(auth.user_id(), path.id).await?;
Ok(HttpResponse::Accepted().finish())
}