Added documentation for various functions
This commit is contained in:
parent
47f8e01210
commit
2b47bb113e
3 changed files with 76 additions and 20 deletions
|
|
@ -5,12 +5,8 @@ use axum::Router;
|
||||||
use crate::auth::token::*;
|
use crate::auth::token::*;
|
||||||
use crate::storage::DbPool;
|
use crate::storage::DbPool;
|
||||||
|
|
||||||
// route prefix: `/auth/token/`
|
/// Authentication routes
|
||||||
// mod token;
|
|
||||||
|
|
||||||
// use self::token::token_auth_router;
|
|
||||||
|
|
||||||
pub fn auth_router(pool: DbPool) -> Router<DbPool> {
|
pub fn auth_router(pool: DbPool) -> Router<DbPool> {
|
||||||
|
// The token auth router handles all token-related authentication routes
|
||||||
Router::new().nest("/token", token_auth_router(pool.clone())).with_state(pool)
|
Router::new().nest("/token", token_auth_router(pool.clone())).with_state(pool)
|
||||||
// .nest("/token", token_auth_router())
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -6,12 +6,14 @@ use axum::http::request::Parts;
|
||||||
use axum::http::{HeaderMap, Request, StatusCode, header};
|
use axum::http::{HeaderMap, Request, StatusCode, header};
|
||||||
use std::fmt::Debug;
|
use std::fmt::Debug;
|
||||||
|
|
||||||
|
/// AuthInfo is an extractor that retrieves authentication information from the request.
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct AuthInfo {
|
pub struct AuthInfo {
|
||||||
token: TokenDTO,
|
token: TokenDTO,
|
||||||
roles: Vec<String>,
|
roles: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Extracts authentication information from the request parts.
|
||||||
impl FromRequestParts<DbPool> for AuthInfo {
|
impl FromRequestParts<DbPool> for AuthInfo {
|
||||||
type Rejection = StatusCode;
|
type Rejection = StatusCode;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,24 +3,21 @@ use axum::extract::{Path, Query, State};
|
||||||
use axum::{Json, Router};
|
use axum::{Json, Router};
|
||||||
use axum::response::{IntoResponse, NoContent, Response};
|
use axum::response::{IntoResponse, NoContent, Response};
|
||||||
use axum::routing::post;
|
use axum::routing::post;
|
||||||
|
use axum::http::StatusCode;
|
||||||
use log::error;
|
use log::error;
|
||||||
use serde::Deserialize;
|
use serde::{Deserialize, Serialize};
|
||||||
use sqlx::Error;
|
use sqlx::Error;
|
||||||
use rand::{distributions::Alphanumeric, Rng};
|
use rand::{distributions::Alphanumeric, Rng};
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
use crate::storage::DbPool;
|
use crate::storage::DbPool;
|
||||||
|
|
||||||
enum TokenType {
|
|
||||||
|
|
||||||
}
|
#[derive(Debug, Serialize)]
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct IdentityDTO {
|
pub struct IdentityDTO {
|
||||||
id: String,
|
id: String,
|
||||||
name: String
|
name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct TokenDTO {
|
pub struct TokenDTO {
|
||||||
key: String,
|
key: String,
|
||||||
|
|
@ -36,11 +33,27 @@ pub struct TokenRoleMembershipDTO {
|
||||||
token_id: String,
|
token_id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents a request body for the `/auth/token/lookup` endpoint.
|
||||||
#[derive(Deserialize)]
|
#[derive(Deserialize)]
|
||||||
struct RequestBodyPostLookup {
|
struct RequestBodyPostLookup {
|
||||||
token: String,
|
token: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Represents the response body for the `/auth/token/lookup` endpoint.
|
||||||
|
#[derive(Serialize)]
|
||||||
|
struct TokenLookupResponse {
|
||||||
|
id: String,
|
||||||
|
type_name: String,
|
||||||
|
roles: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// 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
|
// TODO: Make string generation secure
|
||||||
fn get_random_string(len: usize) -> String {
|
fn get_random_string(len: usize) -> String {
|
||||||
rand::thread_rng()
|
rand::thread_rng()
|
||||||
|
|
@ -50,8 +63,10 @@ fn get_random_string(len: usize) -> String {
|
||||||
.collect()
|
.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 {
|
pub async fn create_root_token_if_none_exist(pool: &DbPool) -> bool {
|
||||||
|
// Check if a root token already exists
|
||||||
let exists = sqlx::query!(
|
let exists = sqlx::query!(
|
||||||
r#"SELECT service_token.* FROM service_token, service_token_role_membership
|
r#"SELECT service_token.* FROM service_token, service_token_role_membership
|
||||||
WHERE service_token.id = service_token_role_membership.token_id AND
|
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 {
|
if exists {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// If no root token exists, create one
|
||||||
let result = create_root_token(pool).await;
|
let result = create_root_token(pool).await;
|
||||||
if result.is_err() {
|
if result.is_err() {
|
||||||
let error = result.err().unwrap();
|
let error = result.err().unwrap();
|
||||||
|
// Log the error and panic
|
||||||
error!("create_root_token failed: {:?}", error);
|
error!("create_root_token failed: {:?}", error);
|
||||||
panic!("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!("\n\nYour root token is: {}", result.unwrap());
|
||||||
println!("It will only be displayed once!\n\n");
|
println!("It will only be displayed once!\n\n");
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return the token key if successful
|
/// Creates a root token in the database.
|
||||||
async fn create_root_token(pool: &DbPool) -> Result<String, Error> {
|
async fn create_root_token(pool: &DbPool) -> Result<String, Error> {
|
||||||
let id = Uuid::new_v4().to_string();
|
let id = Uuid::new_v4().to_string();
|
||||||
let key = "s.".to_string() + &get_random_string(24);
|
let key = "s.".to_string() + &get_random_string(24);
|
||||||
|
// Insert the root token into the database
|
||||||
let result = sqlx::query!(r#"
|
let result = sqlx::query!(r#"
|
||||||
INSERT INTO service_token (id, key) VALUES ($1, $2);
|
INSERT INTO service_token (id, key) VALUES ($1, $2);
|
||||||
INSERT INTO service_token_role_membership (token_id, role_name) VALUES ($3, 'root');
|
INSERT INTO service_token_role_membership (token_id, role_name) VALUES ($3, 'root');
|
||||||
"#, id, key, id).execute(pool).await;
|
"#, id, key, id).execute(pool).await;
|
||||||
|
// If the insert was successful, return the key
|
||||||
if result.is_ok() {
|
if result.is_ok() {
|
||||||
return Ok(key);
|
return Ok(key);
|
||||||
}
|
}
|
||||||
|
// Else, return the error
|
||||||
Err(result.unwrap_err())
|
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 {
|
fn get_time_as_int() -> i64 {
|
||||||
std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_secs() as 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<String, &str> {
|
fn get_token_type(token: &TokenDTO) -> Result<String, &str> {
|
||||||
Ok(match token.key.clone().chars().next().unwrap_or('?') {
|
Ok(match token.key.clone().chars().next().unwrap_or('?') {
|
||||||
's' => "service",
|
's' => "service",
|
||||||
|
|
@ -103,6 +125,9 @@ fn get_token_type(token: &TokenDTO) -> Result<String, &str> {
|
||||||
}.to_string())
|
}.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<TokenDTO, Error> {
|
pub async fn get_token_from_key(token_key: &str, pool: &DbPool) -> Result<TokenDTO, Error> {
|
||||||
let time = get_time_as_int();
|
let time = get_time_as_int();
|
||||||
sqlx::query_as!(
|
sqlx::query_as!(
|
||||||
|
|
@ -111,6 +136,8 @@ pub async fn get_token_from_key(token_key: &str, pool: &DbPool) -> Result<TokenD
|
||||||
token_key, time).fetch_one(pool).await
|
token_key, time).fetch_one(pool).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Retrieves the roles associated with a given token from the database.
|
||||||
|
/// If the token does not exist, it returns an empty vector.
|
||||||
pub async fn get_roles_from_token(token: &TokenDTO, pool:&DbPool) -> Vec<String> {
|
pub async fn get_roles_from_token(token: &TokenDTO, pool:&DbPool) -> Vec<String> {
|
||||||
let result = sqlx::query_as!(
|
let result = sqlx::query_as!(
|
||||||
TokenRoleMembershipDTO,
|
TokenRoleMembershipDTO,
|
||||||
|
|
@ -119,6 +146,7 @@ pub async fn get_roles_from_token(token: &TokenDTO, pool:&DbPool) -> Vec<String>
|
||||||
result.unwrap_or(Vec::new()).iter().map(|r| r.role_name.to_string()).collect()
|
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<DbPool> {
|
pub fn token_auth_router(pool: DbPool) -> Router<DbPool> {
|
||||||
Router::new()
|
Router::new()
|
||||||
.route("/lookup", post(post_lookup))
|
.route("/lookup", post(post_lookup))
|
||||||
|
|
@ -126,15 +154,43 @@ pub fn token_auth_router(pool: DbPool) -> Router<DbPool> {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// 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(
|
async fn post_lookup(
|
||||||
State(pool): State<DbPool>,
|
State(pool): State<DbPool>,
|
||||||
Json(body): Json<RequestBodyPostLookup>
|
Json(body): Json<RequestBodyPostLookup>
|
||||||
) -> Result<Response, ()> {
|
) -> Response {
|
||||||
let token = body.token;
|
let token_str = body.token;
|
||||||
|
// Validate the token string
|
||||||
Ok(IntoResponse::into_response(token))
|
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 get_accessors() {}
|
||||||
|
|
||||||
async fn post_create() {}
|
async fn post_create() {}
|
||||||
|
|
@ -164,7 +220,9 @@ async fn post_revoke_orphan() {}
|
||||||
|
|
||||||
async fn post_revoke_self() {}
|
async fn post_revoke_self() {}
|
||||||
|
|
||||||
async fn get_roles() {}
|
async fn get_roles() {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
async fn get_role_by_name() {}
|
async fn get_role_by_name() {}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue