|
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