From 2b47bb113e633828b4d0924a5b91e3309f52f647 Mon Sep 17 00:00:00 2001 From: Jan Schermer Date: Tue, 10 Jun 2025 19:00:06 -0700 Subject: [PATCH] Added documentation for various functions --- src/auth.rs | 8 +--- src/auth/auth_extractor.rs | 2 + src/auth/token.rs | 86 +++++++++++++++++++++++++++++++------- 3 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/auth.rs b/src/auth.rs index ea850c5..1f41886 100644 --- a/src/auth.rs +++ b/src/auth.rs @@ -5,12 +5,8 @@ use axum::Router; use crate::auth::token::*; use crate::storage::DbPool; -// route prefix: `/auth/token/` -// mod token; - -// use self::token::token_auth_router; - +/// Authentication routes pub fn auth_router(pool: DbPool) -> Router { + // The token auth router handles all token-related authentication routes Router::new().nest("/token", token_auth_router(pool.clone())).with_state(pool) - // .nest("/token", token_auth_router()) } diff --git a/src/auth/auth_extractor.rs b/src/auth/auth_extractor.rs index 73b4f43..8b5e8dd 100644 --- a/src/auth/auth_extractor.rs +++ b/src/auth/auth_extractor.rs @@ -6,12 +6,14 @@ use axum::http::request::Parts; use axum::http::{HeaderMap, Request, StatusCode, header}; use std::fmt::Debug; +/// AuthInfo is an extractor that retrieves authentication information from the request. #[derive(Debug)] pub struct AuthInfo { token: TokenDTO, roles: Vec, } +/// Extracts authentication information from the request parts. impl FromRequestParts for AuthInfo { type Rejection = StatusCode; diff --git a/src/auth/token.rs b/src/auth/token.rs index 2d73318..53819cd 100644 --- a/src/auth/token.rs +++ b/src/auth/token.rs @@ -3,24 +3,21 @@ use axum::extract::{Path, Query, State}; use axum::{Json, Router}; use axum::response::{IntoResponse, NoContent, Response}; use axum::routing::post; +use axum::http::StatusCode; use log::error; -use serde::Deserialize; +use serde::{Deserialize, Serialize}; use sqlx::Error; use rand::{distributions::Alphanumeric, Rng}; use uuid::Uuid; use crate::storage::DbPool; -enum TokenType { -} - -#[derive(Debug)] +#[derive(Debug, Serialize)] pub struct IdentityDTO { id: String, name: String } - #[derive(Debug)] pub struct TokenDTO { key: String, @@ -36,11 +33,27 @@ pub struct TokenRoleMembershipDTO { token_id: String, } +/// Represents a request body for the `/auth/token/lookup` endpoint. #[derive(Deserialize)] struct RequestBodyPostLookup { token: String, } +/// Represents the response body for the `/auth/token/lookup` endpoint. +#[derive(Serialize)] +struct TokenLookupResponse { + id: String, + type_name: String, + roles: Vec, +} + +/// Represents an error response for the API. +#[derive(Serialize)] +struct ErrorResponse { + error: String, +} + +/// Generates a random string of the specified length using alphanumeric characters. // TODO: Make string generation secure fn get_random_string(len: usize) -> String { rand::thread_rng() @@ -50,8 +63,10 @@ fn get_random_string(len: usize) -> String { .collect() } -// Returns if a token was created or not. Prints out the created token to the console. +/// Creates a root token if none exists in the database. +/// Returns true if a new root token was created, false if one already exists. pub async fn create_root_token_if_none_exist(pool: &DbPool) -> bool { + // Check if a root token already exists let exists = sqlx::query!( r#"SELECT service_token.* FROM service_token, service_token_role_membership WHERE service_token.id = service_token_role_membership.token_id AND @@ -61,36 +76,43 @@ pub async fn create_root_token_if_none_exist(pool: &DbPool) -> bool { if exists { return false; } + // If no root token exists, create one let result = create_root_token(pool).await; if result.is_err() { let error = result.err().unwrap(); + // Log the error and panic error!("create_root_token failed: {:?}", error); panic!("create_root_token failed: {:?}", error); } + // If successful, print the root token. This will only happen once. println!("\n\nYour root token is: {}", result.unwrap()); println!("It will only be displayed once!\n\n"); true } -// Return the token key if successful +/// Creates a root token in the database. async fn create_root_token(pool: &DbPool) -> Result { let id = Uuid::new_v4().to_string(); let key = "s.".to_string() + &get_random_string(24); + // Insert the root token into the database let result = sqlx::query!(r#" INSERT INTO service_token (id, key) VALUES ($1, $2); INSERT INTO service_token_role_membership (token_id, role_name) VALUES ($3, 'root'); "#, id, key, id).execute(pool).await; + // If the insert was successful, return the key if result.is_ok() { return Ok(key); } + // Else, return the error Err(result.unwrap_err()) } -// Gets the current time in seconds since unix epoch +/// Gets the current time in seconds since unix epoch fn get_time_as_int() -> i64 { std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as i64 } +/// Gets the type of token. (The first character of the key always specifies the type) fn get_token_type(token: &TokenDTO) -> Result { Ok(match token.key.clone().chars().next().unwrap_or('?') { 's' => "service", @@ -103,6 +125,9 @@ fn get_token_type(token: &TokenDTO) -> Result { }.to_string()) } +/// Retrieves a token from the database using its key. +/// If the token is found and not expired, it returns the token. +/// Else, it returns an error. pub async fn get_token_from_key(token_key: &str, pool: &DbPool) -> Result { let time = get_time_as_int(); sqlx::query_as!( @@ -111,6 +136,8 @@ pub async fn get_token_from_key(token_key: &str, pool: &DbPool) -> Result Vec { let result = sqlx::query_as!( TokenRoleMembershipDTO, @@ -119,6 +146,7 @@ pub async fn get_roles_from_token(token: &TokenDTO, pool:&DbPool) -> Vec result.unwrap_or(Vec::new()).iter().map(|r| r.role_name.to_string()).collect() } +/// Return a router, that may be used to route traffic to the corresponding handlers pub fn token_auth_router(pool: DbPool) -> Router { Router::new() .route("/lookup", post(post_lookup)) @@ -126,15 +154,43 @@ pub fn token_auth_router(pool: DbPool) -> Router { } +/// Handles the `/auth/token/lookup` endpoint. +/// Retrieves the token and its associated roles from the database using the provided token key. +/// The output format does not yet match the openBao specification and is for testing only! async fn post_lookup( State(pool): State, Json(body): Json -) -> Result { - let token = body.token; - - Ok(IntoResponse::into_response(token)) +) -> Response { + let token_str = body.token; + // Validate the token string + match get_token_from_key(&token_str, &pool).await { + // If the token is found, retrieve its type and roles + Ok(token) => { + let type_name = get_token_type(&token).unwrap_or_else(|_| String::from("Unknown")); + let roles = get_roles_from_token(&token, &pool).await; + let resp = TokenLookupResponse { + id: token.id, + type_name, + roles, + }; + // Return the token information as a JSON response + (StatusCode::OK, axum::Json(resp)).into_response() + } + // If the token is not found, return a 404 Not Found error + Err(e) => { + error!("Failed to retrieve token: {:?}", e); + let err = ErrorResponse { + error: "Failed to retrieve token".to_string(), + }; + (StatusCode::NOT_FOUND, axum::Json(err)).into_response() + } + } } +// +// The following functions are placeholders for the various token-related operations. +// + async fn get_accessors() {} async fn post_create() {} @@ -164,7 +220,9 @@ async fn post_revoke_orphan() {} async fn post_revoke_self() {} -async fn get_roles() {} +async fn get_roles() { + +} async fn get_role_by_name() {}