@@ -32,16 +32,22 @@ use serde::Deserialize;
3232/// are derived from these roles (same flow as native/OAuth users).
3333/// `apiKey` lets a caller register their own key; omitting it preserves
3434/// the existing server-generated key behavior.
35+ #[ derive( Debug , Deserialize , PartialEq , Eq ) ]
36+ #[ serde( rename_all = "lowercase" ) ]
37+ pub enum ApiKeyType {
38+ Ingestor ,
39+ Query ,
40+ }
41+
3542#[ derive( Debug , Deserialize ) ]
3643#[ serde( rename_all = "camelCase" , deny_unknown_fields) ]
3744pub struct CreateApiKeyRequest {
3845 pub key_name : String ,
3946 #[ serde( default ) ]
4047 pub roles : HashSet < String > ,
4148 pub api_key : Option < String > ,
42- /// Helm uses this mode to request the server-managed global ingest role.
43- #[ serde( default ) ]
44- pub ingestor_only : bool ,
49+ /// Omit for the existing UI/API flow that assigns explicit roles.
50+ pub api_key_type : Option < ApiKeyType > ,
4551}
4652
4753/// Require a hyphenated UUID v4, allowing either letter case.
@@ -67,8 +73,8 @@ pub enum ApiKeyError {
6773 #[ error( "apiKey must be a UUID v4 in 8-4-4-4-12 format" ) ]
6874 InvalidApiKey ,
6975
70- #[ error( "ingestorOnly cannot be combined with roles" ) ]
71- IngestorOnlyWithRoles ,
76+ #[ error( "apiKeyType cannot be combined with roles" ) ]
77+ ApiKeyTypeWithRoles ,
7278
7379 #[ error( "Unauthorized: {0}" ) ]
7480 Unauthorized ( String ) ,
@@ -93,7 +99,7 @@ impl actix_web::ResponseError for ApiKeyError {
9399 ApiKeyError :: DuplicateKeyName ( _) | ApiKeyError :: DuplicateApiKey => {
94100 actix_web:: http:: StatusCode :: CONFLICT
95101 }
96- ApiKeyError :: InvalidApiKey | ApiKeyError :: IngestorOnlyWithRoles => {
102+ ApiKeyError :: InvalidApiKey | ApiKeyError :: ApiKeyTypeWithRoles => {
97103 actix_web:: http:: StatusCode :: BAD_REQUEST
98104 }
99105 ApiKeyError :: Unauthorized ( _) => actix_web:: http:: StatusCode :: FORBIDDEN ,
@@ -107,3 +113,31 @@ impl actix_web::ResponseError for ApiKeyError {
107113 }
108114 }
109115}
116+
117+ #[ cfg( test) ]
118+ mod request_tests {
119+ use super :: { ApiKeyType , CreateApiKeyRequest } ;
120+
121+ #[ test]
122+ fn typed_keys_and_existing_role_requests_deserialize ( ) {
123+ let ingestor: CreateApiKeyRequest =
124+ serde_json:: from_str ( r#"{"keyName":"ingest-key","apiKeyType":"ingestor"}"# ) . unwrap ( ) ;
125+ assert_eq ! ( ingestor. api_key_type, Some ( ApiKeyType :: Ingestor ) ) ;
126+
127+ let query: CreateApiKeyRequest =
128+ serde_json:: from_str ( r#"{"keyName":"query-key","apiKeyType":"query"}"# ) . unwrap ( ) ;
129+ assert_eq ! ( query. api_key_type, Some ( ApiKeyType :: Query ) ) ;
130+
131+ let ui: CreateApiKeyRequest =
132+ serde_json:: from_str ( r#"{"keyName":"ui-key","roles":["reader-role"]}"# ) . unwrap ( ) ;
133+ assert_eq ! ( ui. api_key_type, None ) ;
134+ assert ! ( ui. roles. contains( "reader-role" ) ) ;
135+
136+ assert ! (
137+ serde_json:: from_str:: <CreateApiKeyRequest >(
138+ r#"{"keyName":"old-key","ingestorOnly":true}"#
139+ )
140+ . is_err( )
141+ ) ;
142+ }
143+ }
0 commit comments