175 lines
4.5 KiB
Rust
175 lines
4.5 KiB
Rust
use std::ops::Index;
|
|
use axum::extract::{Path, Query, State};
|
|
use axum::{Json, Router};
|
|
use axum::response::{IntoResponse, NoContent, Response};
|
|
use axum::routing::post;
|
|
use log::error;
|
|
use serde::Deserialize;
|
|
use sqlx::Error;
|
|
use rand::{distributions::Alphanumeric, Rng};
|
|
use uuid::Uuid;
|
|
use crate::storage::DbPool;
|
|
|
|
enum TokenType {
|
|
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct IdentityDTO {
|
|
id: String,
|
|
name: String
|
|
}
|
|
|
|
|
|
#[derive(Debug)]
|
|
pub struct TokenDTO {
|
|
key: String,
|
|
id: String,
|
|
identity_id: Option<String>,
|
|
parent_id: Option<String>,
|
|
expiry: Option<i64>,
|
|
}
|
|
|
|
#[derive(Debug)]
|
|
pub struct TokenRoleMembershipDTO {
|
|
role_name: String,
|
|
token_id: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct RequestBodyPostLookup {
|
|
token: String,
|
|
}
|
|
|
|
// TODO: Make string generation secure
|
|
fn get_random_string(len: usize) -> String {
|
|
rand::thread_rng()
|
|
.sample_iter(&Alphanumeric)
|
|
.take(len)
|
|
.map(char::from)
|
|
.collect()
|
|
}
|
|
|
|
// Returns if a token was created or not. Prints out the created token to the console.
|
|
pub async fn create_root_token_if_none_exist(pool: &DbPool) -> bool {
|
|
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
|
|
service_token_role_membership.role_name = 'root'
|
|
LIMIT 1"#).fetch_one(pool).await
|
|
.is_ok();
|
|
if exists {
|
|
return false;
|
|
}
|
|
let result = create_root_token(pool).await;
|
|
if result.is_err() {
|
|
let error = result.err().unwrap();
|
|
error!("create_root_token failed: {:?}", error);
|
|
panic!("create_root_token failed: {:?}", error);
|
|
}
|
|
println!("\n\nYour root token is: {}", result.unwrap());
|
|
println!("It will only be displayed once!\n\n");
|
|
true
|
|
}
|
|
|
|
// Return the token key if successful
|
|
async fn create_root_token(pool: &DbPool) -> Result<String, Error> {
|
|
let id = Uuid::new_v4().to_string();
|
|
let key = "s.".to_string() + &get_random_string(24);
|
|
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 result.is_ok() {
|
|
return Ok(key);
|
|
}
|
|
Err(result.unwrap_err())
|
|
}
|
|
|
|
// 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
|
|
}
|
|
|
|
fn get_token_type(token: &TokenDTO) -> Result<String, &str> {
|
|
Ok(match token.key.clone().chars().next().unwrap_or('?') {
|
|
's' => "service",
|
|
'b' => "batch",
|
|
'r' => "recovery",
|
|
_ => {
|
|
error!("Unsupported token type");
|
|
return Err("Unsupported token type");
|
|
}
|
|
}.to_string())
|
|
}
|
|
|
|
pub async fn get_token_from_key(token_key: &str, pool: &DbPool) -> Result<TokenDTO, Error> {
|
|
let time = get_time_as_int();
|
|
sqlx::query_as!(
|
|
TokenDTO,
|
|
r#"SELECT * FROM 'service_token' WHERE key = $1 AND (expiry IS NULL OR expiry > $2) LIMIT 1"#,
|
|
token_key, time).fetch_one(pool).await
|
|
}
|
|
|
|
pub async fn get_roles_from_token(token: &TokenDTO, pool:&DbPool) -> Vec<String> {
|
|
let result = sqlx::query_as!(
|
|
TokenRoleMembershipDTO,
|
|
r#"SELECT * FROM 'service_token_role_membership' WHERE token_id = $1"#,
|
|
token.id).fetch_all(pool).await;
|
|
result.unwrap_or(Vec::new()).iter().map(|r| r.role_name.to_string()).collect()
|
|
}
|
|
|
|
pub fn token_auth_router(pool: DbPool) -> Router<DbPool> {
|
|
Router::new()
|
|
.route("/lookup", post(post_lookup))
|
|
.with_state(pool)
|
|
}
|
|
|
|
|
|
async fn post_lookup(
|
|
State(pool): State<DbPool>,
|
|
Json(body): Json<RequestBodyPostLookup>
|
|
) -> Result<Response, ()> {
|
|
let token = body.token;
|
|
|
|
Ok(IntoResponse::into_response(token))
|
|
}
|
|
|
|
async fn get_accessors() {}
|
|
|
|
async fn post_create() {}
|
|
|
|
async fn post_create_orphan() {}
|
|
|
|
async fn post_create_role() {}
|
|
|
|
async fn get_lookup() {}
|
|
|
|
|
|
async fn get_lookup_self() {}
|
|
|
|
async fn post_lookup_self() {}
|
|
|
|
async fn post_renew() {}
|
|
|
|
async fn post_renew_accessor() {}
|
|
|
|
async fn post_renew_self() {}
|
|
|
|
async fn post_revoke() {}
|
|
|
|
async fn post_revoke_accessor() {}
|
|
|
|
async fn post_revoke_orphan() {}
|
|
|
|
async fn post_revoke_self() {}
|
|
|
|
async fn get_roles() {}
|
|
|
|
async fn get_role_by_name() {}
|
|
|
|
async fn post_role_by_name() {}
|
|
|
|
async fn delete_role_by_name() {}
|
|
|
|
async fn post_tidy() {}
|