@@ -85,6 +85,14 @@ async def clear(self) -> None:
8585 async with self ._lock :
8686 self .cache .clear ()
8787
88+ async def delete_by_prefix (self , prefix : str ) -> int :
89+ """Delete cache entries whose keys start with the given prefix."""
90+ async with self ._lock :
91+ keys = [key for key in self .cache if key .startswith (prefix )]
92+ for key in keys :
93+ del self .cache [key ]
94+ return len (keys )
95+
8896 async def remove_expired (self ) -> None :
8997 """Remove all expired entries from cache."""
9098 async with self ._lock :
@@ -258,17 +266,20 @@ def _contains_api_key(principal_context: Any) -> bool:
258266 # Authorization Check Cache Methods (Async)
259267
260268 @staticmethod
261- def _create_authorization_cache_key (
269+ def _create_authorization_resource_cache_prefix (
262270 resource_type : str ,
263271 resource_selector : str ,
264- operation : str ,
265- principal_context : Any ,
266272 ) -> str :
267- """
268- Create a cache key for authorization checks.
273+ resource_key = AuthenticationCache ._hash_dict (
274+ {
275+ "resource_type" : resource_type ,
276+ "resource_selector" : resource_selector ,
277+ }
278+ )
279+ return f"authz:{ resource_key } :"
269280
270- Combines resource info, operation, and principal context into a unique key.
271- """
281+ @ staticmethod
282+ def _authorization_principal_key_data ( principal_context : Any ) -> dict [ str , Any ]:
272283 # Extract relevant fields from principal context for cache key
273284 principal_key_data = {}
274285 if principal_context :
@@ -301,15 +312,48 @@ def _create_authorization_cache_key(
301312 "context_hash" : AuthenticationCache ._hash_dict (context_dict )
302313 }
303314
304- # Create the cache key components
305- cache_data = {
306- "resource_type" : resource_type ,
307- "resource_selector" : resource_selector ,
308- "operation" : operation ,
309- "principal" : principal_key_data ,
310- }
315+ return principal_key_data
316+
317+ @ staticmethod
318+ def _create_authorization_principal_cache_key ( principal_context : Any ) -> str :
319+ return AuthenticationCache . _hash_dict (
320+ AuthenticationCache . _authorization_principal_key_data ( principal_context )
321+ )
311322
312- return f"authz:{ AuthenticationCache ._hash_dict (cache_data )} "
323+ @staticmethod
324+ def _create_authorization_resource_principal_cache_prefix (
325+ resource_type : str ,
326+ resource_selector : str ,
327+ principal_context : Any ,
328+ ) -> str :
329+ return (
330+ AuthenticationCache ._create_authorization_resource_cache_prefix (
331+ resource_type , resource_selector
332+ )
333+ + AuthenticationCache ._create_authorization_principal_cache_key (
334+ principal_context
335+ )
336+ + ":"
337+ )
338+
339+ @staticmethod
340+ def _create_authorization_cache_key (
341+ resource_type : str ,
342+ resource_selector : str ,
343+ operation : str ,
344+ principal_context : Any ,
345+ ) -> str :
346+ """
347+ Create a cache key for authorization checks.
348+
349+ Combines resource info, operation, and principal context into a unique key.
350+ """
351+ return (
352+ AuthenticationCache ._create_authorization_resource_principal_cache_prefix (
353+ resource_type , resource_selector , principal_context
354+ )
355+ + AuthenticationCache ._hash_dict ({"operation" : operation })
356+ )
313357
314358 async def get_authorization_check (
315359 self ,
@@ -319,6 +363,12 @@ async def get_authorization_check(
319363 principal_context : Any ,
320364 ) -> bool | None :
321365 """Get cached authorization check result."""
366+ if self ._contains_api_key (principal_context ):
367+ logger .debug (
368+ "Skipping authorization check cache lookup for API key principal"
369+ )
370+ return None
371+
322372 cache_key = self ._create_authorization_cache_key (
323373 resource_type , resource_selector , operation , principal_context
324374 )
@@ -339,6 +389,12 @@ async def set_authorization_check(
339389 allowed : bool ,
340390 ) -> None :
341391 """Cache authorization check result."""
392+ if self ._contains_api_key (principal_context ):
393+ logger .debug (
394+ "Skipping authorization check cache write for API key principal"
395+ )
396+ return
397+
342398 cache_key = self ._create_authorization_cache_key (
343399 resource_type , resource_selector , operation , principal_context
344400 )
@@ -358,6 +414,24 @@ async def clear_all(self) -> None:
358414 await self .authorization_check_cache .clear ()
359415 logger .info ("All authentication and authorization caches cleared" )
360416
417+ async def clear_authorization_checks_for_resource_principal (
418+ self ,
419+ resource_type : str ,
420+ resource_selector : str ,
421+ principal_context : Any ,
422+ ) -> None :
423+ """Clear cached authorization check results for one resource/principal."""
424+ prefix = self ._create_authorization_resource_principal_cache_prefix (
425+ resource_type , resource_selector , principal_context
426+ )
427+ deleted = await self .authorization_check_cache .delete_by_prefix (prefix )
428+ logger .info (
429+ "Authorization check cache cleared for %s:%s matched_entries=%d" ,
430+ resource_type ,
431+ resource_selector ,
432+ deleted ,
433+ )
434+
361435 async def cleanup_expired (self ) -> None :
362436 """Remove expired entries from all caches."""
363437 await self .agent_identity_cache .remove_expired ()
0 commit comments