From b7e5e04fc05717f525414b1c5d568203de7a1ac6 Mon Sep 17 00:00:00 2001 From: LucasDLTG Date: Fri, 8 May 2026 13:11:59 +0200 Subject: [PATCH] feat: get roles associted to api key --- ...9cb4004548fc2efc7cbcb056e11e2534eb441.json | 34 +++++++++++++++++++ ...07900ff3d6c7781ebd9c0d8b47031539d8bdf.json | 28 --------------- src/middlewares/auth/apikey.rs | 8 +++++ src/middlewares/auth/middleware.rs | 16 +++++++-- src/routes/v1/apikey.rs | 6 ++-- 5 files changed, 59 insertions(+), 33 deletions(-) create mode 100644 .sqlx/query-931f64701ca90a159a54dab717b9cb4004548fc2efc7cbcb056e11e2534eb441.json delete mode 100644 .sqlx/query-c0816078de6c25d441752500b9307900ff3d6c7781ebd9c0d8b47031539d8bdf.json diff --git a/.sqlx/query-931f64701ca90a159a54dab717b9cb4004548fc2efc7cbcb056e11e2534eb441.json b/.sqlx/query-931f64701ca90a159a54dab717b9cb4004548fc2efc7cbcb056e11e2534eb441.json new file mode 100644 index 0000000..6975824 --- /dev/null +++ b/.sqlx/query-931f64701ca90a159a54dab717b9cb4004548fc2efc7cbcb056e11e2534eb441.json @@ -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" +} diff --git a/.sqlx/query-c0816078de6c25d441752500b9307900ff3d6c7781ebd9c0d8b47031539d8bdf.json b/.sqlx/query-c0816078de6c25d441752500b9307900ff3d6c7781ebd9c0d8b47031539d8bdf.json deleted file mode 100644 index 0cee679..0000000 --- a/.sqlx/query-c0816078de6c25d441752500b9307900ff3d6c7781ebd9c0d8b47031539d8bdf.json +++ /dev/null @@ -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" -} diff --git a/src/middlewares/auth/apikey.rs b/src/middlewares/auth/apikey.rs index bb95981..6304d10 100644 --- a/src/middlewares/auth/apikey.rs +++ b/src/middlewares/auth/apikey.rs @@ -1,7 +1,15 @@ use uuid::Uuid; +#[derive(PartialEq, Eq, Clone, Debug)] +pub enum ApiKeyClaimsRoles { + Admin, + Read, + Write, +} + #[derive(Clone, Debug)] pub struct ApiKeyClaims { pub sub: Uuid, pub api_key_id: Uuid, + pub roles: Vec, } diff --git a/src/middlewares/auth/middleware.rs b/src/middlewares/auth/middleware.rs index ec4e4ad..700a9d9 100644 --- a/src/middlewares/auth/middleware.rs +++ b/src/middlewares/auth/middleware.rs @@ -8,7 +8,7 @@ use axum::{ use crate::databases::postgres::{ 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::state::app_state::AppState; use crate::utils::crypto::hash_key; @@ -127,7 +127,7 @@ async fn try_api_key( let row = sqlx::query!( 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 JOIN auth.app_user u ON u.id = ak.created_by WHERE ak.key_hash = $1 @@ -145,6 +145,17 @@ async fn try_api_key( 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( state, request, @@ -152,6 +163,7 @@ async fn try_api_key( Auth::ApiKey(ApiKeyClaims { sub: row.user_id, api_key_id: row.key_id, + roles, }), ) .await diff --git a/src/routes/v1/apikey.rs b/src/routes/v1/apikey.rs index b12c28b..4835ddd 100644 --- a/src/routes/v1/apikey.rs +++ b/src/routes/v1/apikey.rs @@ -8,6 +8,7 @@ use rand::RngCore; use rand::rngs::OsRng; use crate::dto::api::{CreateApiKeyRequest, CreateApiKeyResponse}; +use crate::middlewares::auth::apikey::ApiKeyClaimsRoles; use crate::middlewares::auth::middleware::Auth; use crate::state::app_state::AppState; use crate::utils::crypto::hash_key; @@ -23,15 +24,14 @@ pub async fn create_api_key( Extension(claims): Extension, Json(body): Json, ) -> Result, StatusCode> { - if matches!(claims, Auth::ApiKey(_)) { + if matches!(&claims, Auth::ApiKey(api_key) if !api_key.roles.contains(&ApiKeyClaimsRoles::Admin)) + { return Err(StatusCode::FORBIDDEN); } let raw_key = generate_api_key(); let key_hash = hash_key(&raw_key); - dbg!(&claims); - sqlx::query!( r#" INSERT INTO auth.api_key (key_hash, name, created_by, scopes)