|
1 | 1 | import logging
|
2 | 2 | import uuid
|
3 | 3 | from datetime import datetime
|
4 |
| -from typing import Optional, Tuple |
| 4 | +from typing import Optional |
5 | 5 |
|
6 | 6 | import jwt
|
7 |
| -from django.apps import apps |
8 |
| -from django.conf import settings |
9 | 7 | from django.contrib.auth import get_user_model
|
10 | 8 | from django.core.exceptions import ObjectDoesNotExist
|
11 |
| -from django.db.models import Model |
12 | 9 | from django.db.utils import IntegrityError
|
13 | 10 | from rest_framework.authentication import BaseAuthentication
|
14 | 11 | from rest_framework.exceptions import AuthenticationFailed
|
|
19 | 16 | from ansible_base.lib.logging.runtime import log_excess_runtime
|
20 | 17 | from ansible_base.lib.utils.auth import get_user_by_ansible_id
|
21 | 18 | from ansible_base.lib.utils.translations import translatableConditionally as _
|
22 |
| -from ansible_base.rbac.claims import get_claims_hash, get_user_claims, get_user_claims_hashable_form |
| 19 | +from ansible_base.rbac.claims import get_claims_hash, get_user_claims, get_user_claims_hashable_form, save_user_claims |
23 | 20 | from ansible_base.resource_registry.models import Resource, ResourceType
|
24 | 21 | from ansible_base.resource_registry.rest_client import get_resource_server_client
|
25 | 22 | from ansible_base.resource_registry.signals.handlers import no_reverse_sync
|
|
36 | 33 | "is_superuser",
|
37 | 34 | ]
|
38 | 35 |
|
39 |
| -_permission_registry = None |
40 |
| - |
41 |
| - |
42 |
| -def permission_registry(): |
43 |
| - global _permission_registry |
44 |
| - |
45 |
| - if not _permission_registry: |
46 |
| - from ansible_base.rbac.permission_registry import permission_registry as permission_registry_singleton |
47 |
| - |
48 |
| - _permission_registry = permission_registry_singleton |
49 |
| - return _permission_registry |
50 |
| - |
51 | 36 |
|
52 | 37 | class JWTCommonAuth:
|
53 | 38 | def __init__(self, user_fields=default_mapped_user_fields) -> None:
|
@@ -241,24 +226,6 @@ def decode_jwt_token(self, unencrypted_token, decryption_key, additional_options
|
241 | 226 | algorithms=["RS256"],
|
242 | 227 | )
|
243 | 228 |
|
244 |
| - def get_role_definition(self, name: str) -> Optional[Model]: |
245 |
| - """Simply get the RoleDefinition from the database if it exists and handler corner cases |
246 |
| -
|
247 |
| - If this is the name of a managed role for which we have a corresponding definition in code, |
248 |
| - and that role can not be found in the database, it may be created here |
249 |
| - """ |
250 |
| - from ansible_base.rbac.models import RoleDefinition |
251 |
| - |
252 |
| - try: |
253 |
| - return RoleDefinition.objects.get(name=name) |
254 |
| - except RoleDefinition.DoesNotExist: |
255 |
| - |
256 |
| - constructor = permission_registry().get_managed_role_constructor_by_name(name) |
257 |
| - if constructor: |
258 |
| - rd, _ = constructor.get_or_create(apps) |
259 |
| - return rd |
260 |
| - return None |
261 |
| - |
262 | 229 | def process_rbac_permissions(self):
|
263 | 230 | """
|
264 | 231 | Process RBAC permissions using claims hash logic
|
@@ -313,7 +280,7 @@ def process_rbac_permissions(self):
|
313 | 280 | global_roles = gateway_claims.get('global_roles', [])
|
314 | 281 |
|
315 | 282 | # Process the RBAC permissions with the gateway claims
|
316 |
| - self._apply_rbac_permissions(objects, object_roles, global_roles) |
| 283 | + save_user_claims(self.user, objects, object_roles, global_roles) |
317 | 284 |
|
318 | 285 | # Update cache with the new hash
|
319 | 286 | self.cache.cache_claims_hash(user_ansible_id, jwt_claims_hash)
|
@@ -344,108 +311,6 @@ def _fetch_jwt_claims_from_gateway(self, user_ansible_id: str) -> Optional[dict]
|
344 | 311 | logger.error(f"Error fetching claims from gateway: {e}")
|
345 | 312 | return None
|
346 | 313 |
|
347 |
| - def _apply_rbac_permissions(self, objects, object_roles, global_roles): |
348 |
| - """ |
349 |
| - Apply RBAC permissions from claims data |
350 |
| - """ |
351 |
| - from ansible_base.rbac.models import RoleUserAssignment |
352 |
| - |
353 |
| - role_diff = RoleUserAssignment.objects.filter(user=self.user, role_definition__name__in=settings.ANSIBLE_BASE_JWT_MANAGED_ROLES) |
354 |
| - |
355 |
| - for system_role_name in global_roles: |
356 |
| - logger.debug(f"Processing system role {system_role_name} for {self.user.username}") |
357 |
| - rd = self.get_role_definition(system_role_name) |
358 |
| - if rd: |
359 |
| - if rd.name in settings.ANSIBLE_BASE_JWT_MANAGED_ROLES: |
360 |
| - assignment = rd.give_global_permission(self.user) |
361 |
| - role_diff = role_diff.exclude(pk=assignment.pk) |
362 |
| - logger.info(f"Granted user {self.user.username} global role {system_role_name}") |
363 |
| - else: |
364 |
| - logger.error(f"Unable to grant {self.user.username} system level role {system_role_name} because it is not a JWT managed role") |
365 |
| - else: |
366 |
| - logger.error(f"Unable to grant {self.user.username} system level role {system_role_name} because it does not exist") |
367 |
| - continue |
368 |
| - |
369 |
| - for object_role_name in object_roles.keys(): |
370 |
| - rd = self.get_role_definition(object_role_name) |
371 |
| - if rd is None: |
372 |
| - logger.error(f"Unable to grant {self.user.username} object role {object_role_name} because it does not exist") |
373 |
| - continue |
374 |
| - elif rd.name not in settings.ANSIBLE_BASE_JWT_MANAGED_ROLES: |
375 |
| - logger.error(f"Unable to grant {self.user.username} object role {object_role_name} because it is not a JWT managed role") |
376 |
| - continue |
377 |
| - |
378 |
| - object_type = object_roles[object_role_name]['content_type'] |
379 |
| - object_indexes = object_roles[object_role_name]['objects'] |
380 |
| - |
381 |
| - for index in object_indexes: |
382 |
| - object_data = objects[object_type][index] |
383 |
| - try: |
384 |
| - resource, obj = self.get_or_create_resource(object_type, object_data) |
385 |
| - except IntegrityError as e: |
386 |
| - logger.warning( |
387 |
| - f"Got integrity error ({e}) on {object_data}. Skipping {object_type} assignment. " |
388 |
| - "Please make sure the sync task is running to prevent this warning in the future." |
389 |
| - ) |
390 |
| - continue |
391 |
| - |
392 |
| - if resource is not None: |
393 |
| - assignment = rd.give_permission(self.user, obj) |
394 |
| - role_diff = role_diff.exclude(pk=assignment.pk) |
395 |
| - logger.info(f"Granted user {self.user.username} role {object_role_name} to object {obj.name} with ansible_id {object_data['ansible_id']}") |
396 |
| - |
397 |
| - # Remove all permissions not authorized by the JWT |
398 |
| - for role_assignment in role_diff: |
399 |
| - rd = role_assignment.role_definition |
400 |
| - content_object = role_assignment.content_object |
401 |
| - if content_object: |
402 |
| - rd.remove_permission(self.user, content_object) |
403 |
| - else: |
404 |
| - rd.remove_global_permission(self.user) |
405 |
| - |
406 |
| - def get_or_create_resource(self, content_type: str, data: dict) -> Tuple[Optional[Resource], Optional[Model]]: |
407 |
| - """ |
408 |
| - Gets or creates a resource from a content type and its default data |
409 |
| -
|
410 |
| - This can only build or get organizations or teams |
411 |
| - """ |
412 |
| - object_ansible_id = data['ansible_id'] |
413 |
| - try: |
414 |
| - resource = Resource.objects.get(ansible_id=object_ansible_id) |
415 |
| - logger.debug(f"Resource {object_ansible_id} already exists") |
416 |
| - return resource, resource.content_object |
417 |
| - except Resource.DoesNotExist: |
418 |
| - pass |
419 |
| - |
420 |
| - # The resource was missing so we need to create its stub |
421 |
| - if content_type == 'team': |
422 |
| - # For a team we first have to make sure the org is there |
423 |
| - org_id = data['org'] |
424 |
| - organization_data = self.token['objects']["organization"][org_id] |
425 |
| - |
426 |
| - # Now that we have the org we can build a team |
427 |
| - org_resource, _ = self.get_or_create_resource("organization", organization_data) |
428 |
| - |
429 |
| - resource = Resource.create_resource( |
430 |
| - ResourceType.objects.get(name="shared.team"), |
431 |
| - {"name": data["name"], "organization": org_resource.ansible_id}, |
432 |
| - ansible_id=data["ansible_id"], |
433 |
| - ) |
434 |
| - |
435 |
| - return resource, resource.content_object |
436 |
| - |
437 |
| - elif content_type == 'organization': |
438 |
| - resource = Resource.create_resource( |
439 |
| - ResourceType.objects.get(name="shared.organization"), |
440 |
| - {"name": data["name"]}, |
441 |
| - ansible_id=data["ansible_id"], |
442 |
| - ) |
443 |
| - |
444 |
| - return resource, resource.content_object |
445 |
| - else: |
446 |
| - logger.error(f"build_resource_stub does not know how to build an object of type {type}") |
447 |
| - return None, None |
448 |
| - |
449 | 314 |
|
450 | 315 | class JWTAuthentication(BaseAuthentication):
|
451 | 316 | map_fields = default_mapped_user_fields
|
|
0 commit comments