feat: get roles associted to api key

This commit is contained in:
2026-05-08 13:11:59 +02:00
parent eaf197f86e
commit b7e5e04fc0
5 changed files with 59 additions and 33 deletions
@@ -0,0 +1,34 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT u.id AS user_id, ak.id as key_id, ak.scopes as roles\n FROM auth.api_key ak\n JOIN auth.app_user u ON u.id = ak.created_by\n WHERE ak.key_hash = $1\n AND ak.revoked_at IS NULL\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "user_id",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "key_id",
"type_info": "Uuid"
},
{
"ordinal": 2,
"name": "roles",
"type_info": "TextArray"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false,
false
]
},
"hash": "931f64701ca90a159a54dab717b9cb4004548fc2efc7cbcb056e11e2534eb441"
}
@@ -1,28 +0,0 @@
{
"db_name": "PostgreSQL",
"query": "\n SELECT u.id AS user_id, ak.id as key_id\n FROM auth.api_key ak\n JOIN auth.app_user u ON u.id = ak.created_by\n WHERE ak.key_hash = $1\n AND ak.revoked_at IS NULL\n ",
"describe": {
"columns": [
{
"ordinal": 0,
"name": "user_id",
"type_info": "Uuid"
},
{
"ordinal": 1,
"name": "key_id",
"type_info": "Uuid"
}
],
"parameters": {
"Left": [
"Text"
]
},
"nullable": [
false,
false
]
},
"hash": "c0816078de6c25d441752500b9307900ff3d6c7781ebd9c0d8b47031539d8bdf"
}
+8
View File
@@ -1,7 +1,15 @@
use uuid::Uuid; use uuid::Uuid;
#[derive(PartialEq, Eq, Clone, Debug)]
pub enum ApiKeyClaimsRoles {
Admin,
Read,
Write,
}
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
pub struct ApiKeyClaims { pub struct ApiKeyClaims {
pub sub: Uuid, pub sub: Uuid,
pub api_key_id: Uuid, pub api_key_id: Uuid,
pub roles: Vec<ApiKeyClaimsRoles>,
} }
+14 -2
View File
@@ -8,7 +8,7 @@ use axum::{
use crate::databases::postgres::{ use crate::databases::postgres::{
api_key::update_last_access, user_repository::ensure_user_exists, api_key::update_last_access, user_repository::ensure_user_exists,
}; };
use crate::middlewares::auth::apikey::ApiKeyClaims; use crate::middlewares::auth::apikey::{ApiKeyClaims, ApiKeyClaimsRoles};
use crate::middlewares::auth::keycloak::{KeycloakClaims, get_jwks, refresh_jwks, validate_token}; use crate::middlewares::auth::keycloak::{KeycloakClaims, get_jwks, refresh_jwks, validate_token};
use crate::state::app_state::AppState; use crate::state::app_state::AppState;
use crate::utils::crypto::hash_key; use crate::utils::crypto::hash_key;
@@ -127,7 +127,7 @@ async fn try_api_key(
let row = sqlx::query!( let row = sqlx::query!(
r#" r#"
SELECT u.id AS user_id, ak.id as key_id SELECT u.id AS user_id, ak.id as key_id, ak.scopes as roles
FROM auth.api_key ak FROM auth.api_key ak
JOIN auth.app_user u ON u.id = ak.created_by JOIN auth.app_user u ON u.id = ak.created_by
WHERE ak.key_hash = $1 WHERE ak.key_hash = $1
@@ -145,6 +145,17 @@ async fn try_api_key(
tracing::debug!("API key valid, user_id={}", row.user_id); tracing::debug!("API key valid, user_id={}", row.user_id);
let roles = row
.roles
.into_iter()
.filter_map(|r| match r.as_str() {
"admin" => Some(ApiKeyClaimsRoles::Admin),
"read" => Some(ApiKeyClaimsRoles::Read),
"write" => Some(ApiKeyClaimsRoles::Write),
_ => None,
})
.collect();
handle_auth( handle_auth(
state, state,
request, request,
@@ -152,6 +163,7 @@ async fn try_api_key(
Auth::ApiKey(ApiKeyClaims { Auth::ApiKey(ApiKeyClaims {
sub: row.user_id, sub: row.user_id,
api_key_id: row.key_id, api_key_id: row.key_id,
roles,
}), }),
) )
.await .await
+3 -3
View File
@@ -8,6 +8,7 @@ use rand::RngCore;
use rand::rngs::OsRng; use rand::rngs::OsRng;
use crate::dto::api::{CreateApiKeyRequest, CreateApiKeyResponse}; use crate::dto::api::{CreateApiKeyRequest, CreateApiKeyResponse};
use crate::middlewares::auth::apikey::ApiKeyClaimsRoles;
use crate::middlewares::auth::middleware::Auth; use crate::middlewares::auth::middleware::Auth;
use crate::state::app_state::AppState; use crate::state::app_state::AppState;
use crate::utils::crypto::hash_key; use crate::utils::crypto::hash_key;
@@ -23,15 +24,14 @@ pub async fn create_api_key(
Extension(claims): Extension<Auth>, Extension(claims): Extension<Auth>,
Json(body): Json<CreateApiKeyRequest>, Json(body): Json<CreateApiKeyRequest>,
) -> Result<Json<CreateApiKeyResponse>, StatusCode> { ) -> Result<Json<CreateApiKeyResponse>, StatusCode> {
if matches!(claims, Auth::ApiKey(_)) { if matches!(&claims, Auth::ApiKey(api_key) if !api_key.roles.contains(&ApiKeyClaimsRoles::Admin))
{
return Err(StatusCode::FORBIDDEN); return Err(StatusCode::FORBIDDEN);
} }
let raw_key = generate_api_key(); let raw_key = generate_api_key();
let key_hash = hash_key(&raw_key); let key_hash = hash_key(&raw_key);
dbg!(&claims);
sqlx::query!( sqlx::query!(
r#" r#"
INSERT INTO auth.api_key (key_hash, name, created_by, scopes) INSERT INTO auth.api_key (key_hash, name, created_by, scopes)