1919from ansible_base .lib .utils .auth import get_user_by_ansible_id
2020from ansible_base .lib .utils .translations import translatableConditionally as _
2121from ansible_base .resource_registry .models import Resource , ResourceType
22+ from ansible_base .resource_registry .rest_client import get_resource_server_client
2223from ansible_base .resource_registry .signals .handlers import no_reverse_sync
2324
2425logger = logging .getLogger ("ansible_base.jwt_consumer.common.auth" )
@@ -52,6 +53,7 @@ def __init__(self, user_fields=default_mapped_user_fields) -> None:
5253 self .cache = JWTCache ()
5354 self .user = None
5455 self .token = None
56+ self .gateway_claims = None # Store claims from gateway
5557
5658 @log_excess_runtime (logger , debug_cutoff = 0.01 )
5759 def parse_jwt_token (self , request ):
@@ -142,10 +144,47 @@ def parse_jwt_token(self, request):
142144 resource .service_id = self .token ['service_id' ]
143145 resource .save (update_fields = ['ansible_id' , 'service_id' ])
144146
147+ # Fetch JWT claims from gateway service-index (required for RBAC processing)
148+ user_ansible_id = self .token ['sub' ]
149+ logger .debug (f"Fetching claims from gateway for user { user_ansible_id } " )
150+
151+ jwt_claims = self ._fetch_jwt_claims_from_gateway (user_ansible_id )
152+
153+ if jwt_claims :
154+ self .gateway_claims = jwt_claims
155+ logger .debug (f"Successfully loaded gateway claims for user { user_ansible_id } " )
156+ else :
157+ logger .error (f"Failed to fetch claims from gateway for user { user_ansible_id } . RBAC processing will not be available." )
158+ # Note: We don't raise an exception here to allow basic authentication to succeed
159+ # RBAC processing will fail gracefully with appropriate error messages
145160 setattr (self .user , "resource_api_actions" , self .token .get ("resource_api_actions" , None ))
146161
147162 logger .info (f"User { self .user .username } authenticated from JWT auth" )
148163
164+
165+ def _fetch_jwt_claims_from_gateway (self , user_ansible_id ):
166+ """
167+ Fetch JWT claims for a user from the gateway service-index API.
168+ Returns None if claims cannot be retrieved.
169+ """
170+ try :
171+ client = get_resource_server_client ("service-index" )
172+ response = client .get_jwt_claims (user_ansible_id )
173+
174+ if response .status_code == 200 :
175+ claims = response .json ()
176+ logger .debug (f"Retrieved JWT claims from gateway for user { user_ansible_id } " )
177+ return claims
178+ else :
179+ logger .warning (
180+ f"Failed to retrieve JWT claims from gateway for user { user_ansible_id } : "
181+ f"{ response .status_code } "
182+ )
183+ return None
184+ except Exception as e :
185+ logger .error (f"Error fetching JWT claims from gateway for user { user_ansible_id } : { e } " )
186+ return None
187+
149188 def log_and_raise (self , conditional_translate_object , expand_values = {}, error_code = None ):
150189 logger .error (conditional_translate_object .not_translated () % expand_values )
151190 translated_error_message = conditional_translate_object .translated () % expand_values
@@ -226,7 +265,9 @@ def validate_token(self, unencrypted_token, decryption_key, request_id=None):
226265 return validated_body
227266
228267 def decode_jwt_token (self , unencrypted_token , decryption_key , additional_options = {}):
229- local_required_field = ["sub" , "user_data" , "exp" , "objects" , "object_roles" , "global_roles" , "version" ]
268+ # Core required fields - objects, object_roles, global_roles are now deprecated
269+ # and will be loaded from the gateway jwt_claims endpoint instead
270+ local_required_field = ["sub" , "user_data" , "exp" , "version" ]
230271 options = {"require" : local_required_field }
231272 options .update (additional_options )
232273 return jwt .decode (
@@ -259,16 +300,23 @@ def get_role_definition(self, name: str) -> Optional[Model]:
259300 def process_rbac_permissions (self ):
260301 """
261302 This is a default process_permissions which should be usable if you are using RBAC from DAB
303+ Uses gateway claims data exclusively - no fallback to JWT token fields
262304 """
263- if self .token is None or self .user is None :
264- logger .error ("Unable to process rbac permissions because user or token is not defined, please call authenticate first" )
305+ if self .user is None :
306+ logger .error ("Unable to process rbac permissions because user is not defined, please call authenticate first" )
307+ return
308+
309+ if self .gateway_claims is None :
310+ logger .error ("Unable to process rbac permissions because gateway claims are not available. Ensure gateway jwt_claims endpoint is accessible." )
265311 return
266312
267313 from ansible_base .rbac .models import RoleUserAssignment
268314
269315 role_diff = RoleUserAssignment .objects .filter (user = self .user , role_definition__name__in = settings .ANSIBLE_BASE_JWT_MANAGED_ROLES )
270316
271- for system_role_name in self .token .get ("global_roles" , []):
317+ # Process global roles from gateway claims
318+ global_roles = self .gateway_claims .get ("global_roles" , [])
319+ for system_role_name in global_roles :
272320 logger .debug (f"Processing system role { system_role_name } for { self .user .username } " )
273321 rd = self .get_role_definition (system_role_name )
274322 if rd :
@@ -282,7 +330,11 @@ def process_rbac_permissions(self):
282330 logger .error (f"Unable to grant { self .user .username } system level role { system_role_name } because it does not exist" )
283331 continue
284332
285- for object_role_name in self .token .get ('object_roles' , {}).keys ():
333+ # Process object roles from gateway claims
334+ object_roles = self .gateway_claims .get ('object_roles' , {})
335+ objects = self .gateway_claims .get ('objects' , {})
336+
337+ for object_role_name in object_roles .keys ():
286338 rd = self .get_role_definition (object_role_name )
287339 if rd is None :
288340 logger .error (f"Unable to grant { self .user .username } object role { object_role_name } because it does not exist" )
@@ -291,11 +343,11 @@ def process_rbac_permissions(self):
291343 logger .error (f"Unable to grant { self .user .username } object role { object_role_name } because it is not a JWT managed role" )
292344 continue
293345
294- object_type = self . token [ ' object_roles' ] [object_role_name ]['content_type' ]
295- object_indexes = self . token [ ' object_roles' ] [object_role_name ]['objects' ]
346+ object_type = object_roles [object_role_name ]['content_type' ]
347+ object_indexes = object_roles [object_role_name ]['objects' ]
296348
297349 for index in object_indexes :
298- object_data = self . token [ ' objects' ] [object_type ][index ]
350+ object_data = objects [object_type ][index ]
299351 try :
300352 resource , obj = self .get_or_create_resource (object_type , object_data )
301353 except IntegrityError as e :
@@ -310,7 +362,7 @@ def process_rbac_permissions(self):
310362 role_diff = role_diff .exclude (pk = assignment .pk )
311363 logger .info (f"Granted user { self .user .username } role { object_role_name } to object { obj .name } with ansible_id { object_data ['ansible_id' ]} " )
312364
313- # Remove all permissions not authorized by the JWT
365+ # Remove all permissions not authorized by the gateway claims
314366 for role_assignment in role_diff :
315367 rd = role_assignment .role_definition
316368 content_object = role_assignment .content_object
@@ -322,9 +374,17 @@ def process_rbac_permissions(self):
322374 def get_or_create_resource (self , content_type : str , data : dict ) -> Tuple [Optional [Resource ], Optional [Model ]]:
323375 """
324376 Gets or creates a resource from a content type and its default data
377+ Uses gateway claims exclusively - no fallback to JWT token fields
325378
326379 This can only build or get organizations or teams
380+ Args:
381+ content_type: Type of content ('team', 'organization')
382+ data: Resource data dictionary
327383 """
384+ if self .gateway_claims is None :
385+ logger .error ("Unable to create resource because gateway claims are not available" )
386+ return None , None
387+
328388 object_ansible_id = data ['ansible_id' ]
329389 try :
330390 resource = Resource .objects .get (ansible_id = object_ansible_id )
@@ -337,7 +397,7 @@ def get_or_create_resource(self, content_type: str, data: dict) -> Tuple[Optiona
337397 if content_type == 'team' :
338398 # For a team we first have to make sure the org is there
339399 org_id = data ['org' ]
340- organization_data = self .token ['objects' ]["organization" ][org_id ]
400+ organization_data = self .gateway_claims ['objects' ]["organization" ][org_id ]
341401
342402 # Now that we have the org we can build a team
343403 org_resource , _ = self .get_or_create_resource ("organization" , organization_data )
@@ -359,7 +419,7 @@ def get_or_create_resource(self, content_type: str, data: dict) -> Tuple[Optiona
359419
360420 return resource , resource .content_object
361421 else :
362- logger .error (f"build_resource_stub does not know how to build an object of type { type } " )
422+ logger .error (f"build_resource_stub does not know how to build an object of type { content_type } " )
363423 return None , None
364424
365425
0 commit comments