feat: add api key generation endpint + role authorization

This commit is contained in:
2026-05-07 12:32:29 +02:00
parent d7ddc087a6
commit 752373c7b7
10 changed files with 315 additions and 114 deletions
+59
View File
@@ -0,0 +1,59 @@
use axum::{
Json,
extract::{Extension, State},
http::StatusCode,
};
use base64::{Engine as _, engine::general_purpose};
use rand::RngCore;
use rand::rngs::OsRng;
use sha2::{Digest, Sha256};
use uuid::Uuid;
use crate::dto::api::{CreateApiKeyRequest, CreateApiKeyResponse};
use crate::middlewares::auth::keycloak::Claims;
use crate::state::app_state::AppState;
fn generate_api_key() -> String {
let mut bytes = [0u8; 32];
OsRng.fill_bytes(&mut bytes);
general_purpose::URL_SAFE_NO_PAD.encode(bytes)
}
fn hash_key(key: &str) -> String {
let mut hasher = Sha256::new();
hasher.update(key.as_bytes());
hasher
.finalize()
.iter()
.map(|b| format!("{:02x}", b))
.collect()
}
pub async fn create_api_key(
State(state): State<AppState>,
Extension(claims): Extension<Claims>,
Json(body): Json<CreateApiKeyRequest>,
) -> Result<Json<CreateApiKeyResponse>, StatusCode> {
let raw_key = generate_api_key();
let key_hash = hash_key(&raw_key);
dbg!(&claims);
let user_id = Uuid::parse_str(&claims.sub).map_err(|_| StatusCode::UNAUTHORIZED)?;
sqlx::query!(
r#"
INSERT INTO auth.api_key (key_hash, name, created_by, scopes)
VALUES ($1, $2, $3, $4)
"#,
key_hash,
body.name,
user_id,
&body.scopes
)
.execute(&state.postgres)
.await
.map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?;
Ok(Json(CreateApiKeyResponse { api_key: raw_key }))
}
+9 -1
View File
@@ -1,9 +1,11 @@
pub mod apikey;
pub mod chat;
pub mod models;
use crate::docs::ApiDoc;
use crate::middlewares::auth::auth_middleware;
use crate::middlewares::auth::{auth_middleware, middleware::require_roles};
use crate::state::app_state::AppState;
use axum::{Json, Router, middleware, routing::get, routing::post};
use utoipa::OpenApi;
@@ -22,6 +24,12 @@ pub fn protected_router() -> Router<AppState> {
.route("/chat/completions", post(chat::chat_completions))
.route("/models/{model}/load", post(models::load_model))
.route("/models/{model}/unload", post(models::unload_model))
.route(
"/keys/generate",
post(apikey::create_api_key).route_layer(middleware::from_fn(|req, next| {
require_roles(req, next, None, None)
})),
)
}
pub fn router(state: AppState) -> Router<AppState> {