@@ -2,7 +2,7 @@ use std::collections::HashSet;
22
33use actix_web:: { HttpRequest , HttpResponse , Responder , web} ;
44use chrono:: { DateTime , Utc } ;
5- use serde:: { Deserialize , Serialize } ;
5+ use serde:: { Deserialize , Deserializer , Serialize , de } ;
66use ulid:: Ulid ;
77
88use crate :: {
@@ -15,6 +15,7 @@ use crate::{
1515 rbac:: {
1616 Users ,
1717 map:: { roles, users} ,
18+ role:: model:: { DefaultPrivilege , Role } ,
1819 user:: { User , UserType } ,
1920 } ,
2021 utils:: { get_user_and_tenant_from_request, is_admin} ,
@@ -97,6 +98,39 @@ pub struct ValidateApiKeyRequest {
9798 api_key : String ,
9899}
99100
101+ #[ derive( Debug , Deserialize ) ]
102+ pub struct ListApiKeysQuery {
103+ #[ serde( default , deserialize_with = "deserialize_api_key_privilege" ) ]
104+ priviledge : Option < DefaultPrivilege > ,
105+ }
106+
107+ fn deserialize_api_key_privilege < ' de , D > (
108+ deserializer : D ,
109+ ) -> Result < Option < DefaultPrivilege > , D :: Error >
110+ where
111+ D : Deserializer < ' de > ,
112+ {
113+ Option :: < String > :: deserialize ( deserializer) ?
114+ . map ( |privilege| match privilege. as_str ( ) {
115+ "reader" => Ok ( DefaultPrivilege :: Reader { resource : None } ) ,
116+ "ingestor" => Ok ( DefaultPrivilege :: Ingestor { resource : None } ) ,
117+ "admin" => Ok ( DefaultPrivilege :: Admin ) ,
118+ "writer" => Ok ( DefaultPrivilege :: Writer { resource : None } ) ,
119+ "editor" => Ok ( DefaultPrivilege :: Editor ) ,
120+ _ => Err ( de:: Error :: unknown_variant (
121+ & privilege,
122+ & [ "reader" , "ingestor" , "admin" , "writer" , "editor" ] ,
123+ ) ) ,
124+ } )
125+ . transpose ( )
126+ }
127+
128+ fn role_has_privilege ( role : & Role , privilege : & DefaultPrivilege ) -> bool {
129+ role. privileges ( )
130+ . iter ( )
131+ . any ( |candidate| std:: mem:: discriminant ( candidate) == std:: mem:: discriminant ( privilege) )
132+ }
133+
100134impl ApiKeyListEntry {
101135 fn from_user ( user : & User ) -> Option < Self > {
102136 let api_key = user. as_api_key ( ) ?;
@@ -285,11 +319,27 @@ pub async fn delete_api_key(
285319
286320/// GET /api/prism/v1/apikeys
287321///
288- /// List all API keys (masked). Only admins can list keys.
289- pub async fn list_api_keys ( req : HttpRequest ) -> Result < impl Responder , ApiKeyError > {
322+ /// List all API keys (masked). Only admins can list keys. Optionally filter by
323+ /// role privilege with `?priviledge=reader|ingestor|admin|writer|editor`.
324+ pub async fn list_api_keys (
325+ req : HttpRequest ,
326+ web:: Query ( query) : web:: Query < ListApiKeysQuery > ,
327+ ) -> Result < impl Responder , ApiKeyError > {
290328 let ( _, tenant_id) = verify_admin ( & req) ?;
329+ let tenant = tenant_or_default ( & tenant_id) ;
330+ let tenant_roles = roles ( ) ;
291331 let entries: Vec < ApiKeyListEntry > = collect_tenant_api_keys ( & tenant_id)
292332 . iter ( )
333+ . filter ( |user| {
334+ query. priviledge . as_ref ( ) . is_none_or ( |privilege| {
335+ user. roles . iter ( ) . any ( |role_name| {
336+ tenant_roles
337+ . get ( tenant)
338+ . and_then ( |roles| roles. get ( role_name) )
339+ . is_some_and ( |role| role_has_privilege ( role, privilege) )
340+ } )
341+ } )
342+ } )
293343 . filter_map ( ApiKeyListEntry :: from_user)
294344 . collect ( ) ;
295345 Ok ( HttpResponse :: Ok ( ) . json ( entries) )
0 commit comments