This is an automated email from the ASF dual-hosted git repository.
yasithdev pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airavata-portals.git
The following commit(s) were added to refs/heads/main by this push:
new 41ec59ea3 refactor(portal): remove the last Thrift model-type
references; the portal is Thrift-free (Track D) (#207)
41ec59ea3 is described below
commit 41ec59ea364cabf183867f5cbd395f8d21fde4c0
Author: Yasith Jayawardana <[email protected]>
AuthorDate: Tue Jun 9 05:34:48 2026 -0400
refactor(portal): remove the last Thrift model-type references; the portal
is Thrift-free (Track D) (#207)
The portal no longer imports any Thrift model type
(airavata.model.*.ttypes) or
the thrift library anywhere:
- AuthzToken: replace the Thrift security value type with a plain carrier in
apps/auth/utils.py (same accessToken / claimsMap attributes, so the gRPC
client
factory + IAM REST helpers are unchanged); token_authentication.py
imports it.
- exceptions.py: drop the dead Thrift exception handlers
(AuthorizationException /
ExperimentNotFoundException / TTransportException / TException) and their
imports
— no Thrift call can raise them now; the gRPC RpcError handler covers the
views.
- Delete thrift_utils.py (the auto-serializer factory) — every serializer is
proto-native, so nothing imports it.
- Test fixtures (apps/auth/tests/test_signals.py, apps/auth + apps/api
tests/test_views.py) build proto UserProfile / GroupModel / GatewayGroups
with
proto field names instead of the Thrift types. test_signals now passes;
the
IAMUser/Group view tests remain pre-existing failures (they mock the
long-removed
profile_service/airavata_client Thrift surfaces — a separate gRPC-facade
test-rewrite, unchanged in count vs origin/main).
manage.py check green; the api test failure set is unchanged vs origin/main
(same
6 pre-existing). The legacy airavata-python-sdk==2.2.7 + thrift deps can be
dropped
once airavata_django_portal_sdk (experiment_util /
queue_settings_calculators) is
folded in — the final increment.
---
.../django_airavata/apps/api/exceptions.py | 36 +--
.../django_airavata/apps/api/tests/test_views.py | 116 ++++-----
.../django_airavata/apps/api/thrift_utils.py | 258 ---------------------
.../apps/auth/tests/test_signals.py | 22 +-
.../django_airavata/apps/auth/tests/test_views.py | 20 +-
.../apps/auth/token_authentication.py | 3 +-
.../django_airavata/apps/auth/utils.py | 14 +-
7 files changed, 102 insertions(+), 367 deletions(-)
diff --git a/airavata-django-portal/django_airavata/apps/api/exceptions.py
b/airavata-django-portal/django_airavata/apps/api/exceptions.py
index b14bce2c7..3b703f469 100644
--- a/airavata-django-portal/django_airavata/apps/api/exceptions.py
+++ b/airavata-django-portal/django_airavata/apps/api/exceptions.py
@@ -2,23 +2,17 @@ import logging
import sys
import grpc
-from airavata.api.error.ttypes import (
- AuthorizationException,
- ExperimentNotFoundException
-)
from django.core.exceptions import ObjectDoesNotExist
from django.http import JsonResponse
from rest_framework import status
from rest_framework.exceptions import NotAuthenticated
from rest_framework.response import Response
from rest_framework.views import exception_handler
-from thrift.Thrift import TException
-from thrift.transport import TTransport
log = logging.getLogger(__name__)
-# Track D: map new-stack gRPC status codes to HTTP responses, mirroring the
-# Thrift exception handling below so migrated views behave identically.
+# Map gRPC status codes to HTTP responses (the portal talks only to the gRPC
+# facade now; the legacy Thrift exception handlers are gone).
GRPC_STATUS_TO_HTTP = {
grpc.StatusCode.NOT_FOUND: status.HTTP_404_NOT_FOUND,
grpc.StatusCode.PERMISSION_DENIED: status.HTTP_403_FORBIDDEN,
@@ -52,32 +46,6 @@ def custom_exception_handler(exc, context):
log.warning("gRPC error %s", code, exc_info=exc)
return Response({'detail': detail}, status=http_status)
- if isinstance(exc, AuthorizationException):
- log.warning("AuthorizationException", exc_info=exc)
- return Response(
- {'detail': str(exc)},
- status=status.HTTP_403_FORBIDDEN)
-
- if isinstance(exc, ExperimentNotFoundException):
- log.warning("ExperimentNotFoundException", exc_info=exc)
- return Response(
- {'detail': str(exc)},
- status=status.HTTP_404_NOT_FOUND)
-
- if isinstance(exc, TTransport.TTransportException):
- log.warning("TTransportException", exc_info=exc)
- return Response(
- {'detail': str(exc), 'apiServerDown': True},
- status=status.HTTP_500_INTERNAL_SERVER_ERROR)
-
- # Default TException handler, should come after more specific subclasses of
- # TException
- if isinstance(exc, TException):
- log.error("TException", exc_info=exc, extra={'request':
context['request']})
- return Response(
- {'detail': str(exc)},
- status=status.HTTP_500_INTERNAL_SERVER_ERROR)
-
if isinstance(exc, ObjectDoesNotExist):
log.warning("ObjectDoesNotExist", exc_info=exc)
return Response(
diff --git
a/airavata-django-portal/django_airavata/apps/api/tests/test_views.py
b/airavata-django-portal/django_airavata/apps/api/tests/test_views.py
index 201538dc4..067339201 100644
--- a/airavata-django-portal/django_airavata/apps/api/tests/test_views.py
+++ b/airavata-django-portal/django_airavata/apps/api/tests/test_views.py
@@ -1,8 +1,14 @@
from unittest.mock import MagicMock, call, patch
-from airavata.model.appcatalog.gatewaygroups.ttypes import GatewayGroups
-from airavata.model.group.ttypes import GroupModel
-from airavata.model.user.ttypes import UserProfile
+from
airavata_sdk.generated.org.apache.airavata.model.appcatalog.gatewaygroups.gateway_groups_pb2
import ( # noqa: E501
+ GatewayGroups,
+)
+from airavata_sdk.generated.org.apache.airavata.model.group.group_manager_pb2
import ( # noqa: E501
+ GroupModel,
+)
+from airavata_sdk.generated.org.apache.airavata.model.user.user_profile_pb2
import (
+ UserProfile,
+)
from django.contrib.auth.models import User
from django.test import TestCase, override_settings
from django.urls import reverse
@@ -49,19 +55,19 @@ class GroupViewSetTests(TestCase):
}
request.airavata_client = MagicMock(name="airavata_client")
request.airavata_client.getGatewayGroups.return_value = GatewayGroups(
- gatewayId=GATEWAY_ID,
- adminsGroupId="adminsGroupId",
- readOnlyAdminsGroupId="readOnlyAdminsGroupId",
- defaultGatewayUsersGroupId="defaultGatewayUsersGroupId"
+ gateway_id=GATEWAY_ID,
+ admins_group_id="adminsGroupId",
+ read_only_admins_group_id="readOnlyAdminsGroupId",
+ default_gateway_users_group_id="defaultGatewayUsersGroupId"
)
request.authz_token = "dummy"
request.session = {}
group_manager_mock.createGroup.return_value = "abc123"
user_profile = UserProfile(
- airavataInternalUserId=f"testuser1@{GATEWAY_ID}",
- userId="testuser1",
- firstName="Test",
- lastName="User1",
+ airavata_internal_user_id=f"testuser1@{GATEWAY_ID}",
+ user_id="testuser1",
+ first_name="Test",
+ last_name="User1",
emails=["[email protected]"]
)
user_profile_mock.getUserProfileById.return_value = user_profile
@@ -105,10 +111,10 @@ class GroupViewSetTests(TestCase):
}
request.airavata_client = MagicMock(name="airavata_client")
request.airavata_client.getGatewayGroups.return_value = GatewayGroups(
- gatewayId=GATEWAY_ID,
- adminsGroupId="adminsGroupId",
- readOnlyAdminsGroupId="readOnlyAdminsGroupId",
- defaultGatewayUsersGroupId="defaultGatewayUsersGroupId"
+ gateway_id=GATEWAY_ID,
+ admins_group_id="adminsGroupId",
+ read_only_admins_group_id="readOnlyAdminsGroupId",
+ default_gateway_users_group_id="defaultGatewayUsersGroupId"
)
request.authz_token = "dummy"
request.session = {}
@@ -127,10 +133,10 @@ class GroupViewSetTests(TestCase):
# Only user added is testuser3, so getUserProfileById will be called
# for that user
user_profile = UserProfile(
- airavataInternalUserId=f"testuser3@{GATEWAY_ID}",
- userId="testuser3",
- firstName="Test",
- lastName="User3",
+ airavata_internal_user_id=f"testuser3@{GATEWAY_ID}",
+ user_id="testuser3",
+ first_name="Test",
+ last_name="User3",
emails=["[email protected]"]
)
user_profile_mock.getUserProfileById.return_value = user_profile
@@ -203,10 +209,10 @@ class IAMUserViewSetTests(TestCase):
# Mock api clients
iam_user_profile = UserProfile(
- airavataInternalUserId=f"testuser1@{GATEWAY_ID}",
- userId="testuser1",
- firstName="Test",
- lastName="User1",
+ airavata_internal_user_id=f"testuser1@{GATEWAY_ID}",
+ user_id="testuser1",
+ first_name="Test",
+ last_name="User1",
emails=["[email protected]"]
)
iam_admin_client.get_user.return_value = iam_user_profile
@@ -219,10 +225,10 @@ class IAMUserViewSetTests(TestCase):
request.authz_token = "dummy"
user_profile_mock.doesUserExist.return_value = True
user_profile = UserProfile(
- airavataInternalUserId=f"testuser1@{GATEWAY_ID}",
- userId="testuser1",
- firstName="Test",
- lastName="User1",
+ airavata_internal_user_id=f"testuser1@{GATEWAY_ID}",
+ user_id="testuser1",
+ first_name="Test",
+ last_name="User1",
emails=["[email protected]"]
)
user_profile_mock.getUserProfileById.return_value = user_profile
@@ -234,10 +240,10 @@ class IAMUserViewSetTests(TestCase):
group_manager_mock.getGroup.return_value = group
request.airavata_client = MagicMock(name="airavata_client")
request.airavata_client.getGatewayGroups.return_value = GatewayGroups(
- gatewayId=GATEWAY_ID,
- adminsGroupId="adminsGroupId",
- readOnlyAdminsGroupId="readOnlyAdminsGroupId",
- defaultGatewayUsersGroupId="defaultGatewayUsersGroupId"
+ gateway_id=GATEWAY_ID,
+ admins_group_id="adminsGroupId",
+ read_only_admins_group_id="readOnlyAdminsGroupId",
+ default_gateway_users_group_id="defaultGatewayUsersGroupId"
)
request.session = {}
@@ -305,10 +311,10 @@ class IAMUserViewSetTests(TestCase):
# Mock api clients
iam_user_profile = UserProfile(
- airavataInternalUserId=f"testuser1@{GATEWAY_ID}",
- userId="testuser1",
- firstName="Test",
- lastName="User1",
+ airavata_internal_user_id=f"testuser1@{GATEWAY_ID}",
+ user_id="testuser1",
+ first_name="Test",
+ last_name="User1",
emails=["[email protected]"]
)
iam_admin_client.get_user.return_value = iam_user_profile
@@ -321,10 +327,10 @@ class IAMUserViewSetTests(TestCase):
request.authz_token = "dummy"
user_profile_mock.doesUserExist.return_value = True
user_profile = UserProfile(
- airavataInternalUserId=f"testuser1@{GATEWAY_ID}",
- userId="testuser1",
- firstName="Test",
- lastName="User1",
+ airavata_internal_user_id=f"testuser1@{GATEWAY_ID}",
+ user_id="testuser1",
+ first_name="Test",
+ last_name="User1",
emails=["[email protected]"]
)
user_profile_mock.getUserProfileById.return_value = user_profile
@@ -342,10 +348,10 @@ class IAMUserViewSetTests(TestCase):
group_manager_mock.getGroup.side_effect = side_effect
request.airavata_client = MagicMock(name="airavata_client")
request.airavata_client.getGatewayGroups.return_value = GatewayGroups(
- gatewayId=GATEWAY_ID,
- adminsGroupId="adminsGroupId",
- readOnlyAdminsGroupId="readOnlyAdminsGroupId",
- defaultGatewayUsersGroupId="defaultGatewayUsersGroupId"
+ gateway_id=GATEWAY_ID,
+ admins_group_id="adminsGroupId",
+ read_only_admins_group_id="readOnlyAdminsGroupId",
+ default_gateway_users_group_id="defaultGatewayUsersGroupId"
)
request.session = {}
@@ -414,10 +420,10 @@ class IAMUserViewSetTests(TestCase):
# Mock api clients
iam_user_profile = UserProfile(
- airavataInternalUserId=f"testuser1@{GATEWAY_ID}",
- userId="testuser1",
- firstName="Test",
- lastName="User1",
+ airavata_internal_user_id=f"testuser1@{GATEWAY_ID}",
+ user_id="testuser1",
+ first_name="Test",
+ last_name="User1",
emails=["[email protected]"]
)
iam_admin_client.get_user.return_value = iam_user_profile
@@ -430,10 +436,10 @@ class IAMUserViewSetTests(TestCase):
request.authz_token = "dummy"
user_profile_mock.doesUserExist.return_value = True
user_profile = UserProfile(
- airavataInternalUserId=f"testuser1@{GATEWAY_ID}",
- userId="testuser1",
- firstName="Test",
- lastName="User1",
+ airavata_internal_user_id=f"testuser1@{GATEWAY_ID}",
+ user_id="testuser1",
+ first_name="Test",
+ last_name="User1",
emails=["[email protected]"]
)
user_profile_mock.getUserProfileById.return_value = user_profile
@@ -442,10 +448,10 @@ class IAMUserViewSetTests(TestCase):
request.airavata_client = MagicMock(name="airavata_client")
request.airavata_client.getGatewayGroups.return_value = GatewayGroups(
- gatewayId=GATEWAY_ID,
- adminsGroupId="adminsGroupId",
- readOnlyAdminsGroupId="readOnlyAdminsGroupId",
- defaultGatewayUsersGroupId="defaultGatewayUsersGroupId"
+ gateway_id=GATEWAY_ID,
+ admins_group_id="adminsGroupId",
+ read_only_admins_group_id="readOnlyAdminsGroupId",
+ default_gateway_users_group_id="defaultGatewayUsersGroupId"
)
request.session = {}
diff --git a/airavata-django-portal/django_airavata/apps/api/thrift_utils.py
b/airavata-django-portal/django_airavata/apps/api/thrift_utils.py
deleted file mode 100644
index d4e6884f4..000000000
--- a/airavata-django-portal/django_airavata/apps/api/thrift_utils.py
+++ /dev/null
@@ -1,258 +0,0 @@
-"""
-Used to create Django Rest Framework serializers for Apache Thrift Data Types
-"""
-import copy
-import datetime
-import logging
-import enum
-
-from rest_framework.serializers import (
- BooleanField,
- CharField,
- DateTimeField,
- DecimalField,
- DictField,
- Field,
- IntegerField,
- ListField,
- ListSerializer,
- Serializer,
- SerializerMetaclass,
- ValidationError
-)
-from thrift.Thrift import TType
-from airavata.model.experiment.ttypes import ExperimentType
-from airavata.model.status.ttypes import ExperimentState
-from airavata.model.application.io.ttypes import DataType
-from airavata.model.appcatalog.parallelism.ttypes import
ApplicationParallelismType
-
-logger = logging.getLogger(__name__)
-
-# used to map apache thrift data types to django serializer fields
-mapping = {
- TType.STRING: CharField,
- TType.I08: IntegerField,
- TType.I16: IntegerField,
- TType.I32: IntegerField,
- TType.I64: IntegerField,
- TType.DOUBLE: DecimalField,
- TType.BOOL: BooleanField,
- TType.MAP: DictField
-}
-
-
-class UTCPosixTimestampDateTimeField(DateTimeField):
-
- def __init__(self, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.default = self.current_time_ms
- self.initial = self.initial_value
- self.required = False
-
- def to_representation(self, obj):
- # Create datetime instance from milliseconds that is aware of timezon
- dt = datetime.datetime.fromtimestamp(obj / 1000, datetime.timezone.utc)
- return super().to_representation(dt)
-
- def to_internal_value(self, data):
- dt = super().to_internal_value(data)
- return int(dt.timestamp() * 1000)
-
- def initial_value(self):
- return self.to_representation(self.current_time_ms())
-
- def current_time_ms(self):
- return int(datetime.datetime.utcnow().timestamp() * 1000)
-
-
-class ThriftEnumField(Field):
-
- def __init__(self, enumClass, *args, **kwargs):
- super().__init__(*args, **kwargs)
- self.enumClass = enumClass
-
- def to_representation(self, obj):
- # For IntEnum, obj is the enum member, its `.name` is the string
- if obj is None:
- return None
- return obj.name
-
- def to_internal_value(self, data):
- # Convert string name back into an IntEnum member
- if self.allow_null and data is None:
- return None
- try:
- return self.enumClass[data]
- except KeyError:
- raise ValidationError(f"'{data}' is not a valid name for enum
{self.enumClass.__name__}")
-
-
-def create_serializer(thrift_data_type, enable_date_time_conversion=False,
**kwargs):
- """
- Create django rest framework serializer based on the thrift data type
- :param thrift_data_type: Thrift data type
- :param kwargs: Other Django Framework Serializer initialization parameters
- :param enable_date_time_conversion: enable conversion of field with name
ending with time to
- UTCPosixTimestampDateTimeField instead of IntegerField
- :return: instance of custom serializer for the given thrift data type
- """
- return create_serializer_class(thrift_data_type,
enable_date_time_conversion)(**kwargs)
-
-
-def create_serializer_class(thrift_data_type,
enable_date_time_conversion=False):
- class CustomSerializerMeta(SerializerMetaclass):
-
- def __new__(cls, name, bases, attrs):
- meta = attrs.get('Meta', None)
- thrift_spec = thrift_data_type.thrift_spec
- for field in thrift_spec:
- # Don't replace existing attrs to allow subclasses to override
- if field and field[2] not in attrs:
- required = (field[2] in meta.required
- if meta and hasattr(meta, 'required')
- else False)
- read_only = (field[2] in meta.read_only
- if meta and hasattr(meta, 'read_only')
- else False)
- allow_null = not required
- field_serializer = process_field(
- field, enable_date_time_conversion, required=required,
read_only=read_only,
- allow_null=allow_null)
- attrs[field[2]] = field_serializer
- return super().__new__(cls, name, bases, attrs)
-
- class CustomSerializer(Serializer, metaclass=CustomSerializerMeta):
- """
- Custom Serializer which handle the list fields which holds custom
class objects
- """
-
- def process_nested_fields(self, validated_data):
- fields = self.fields
- params = copy.deepcopy(validated_data)
- for field_name, serializer in fields.items():
- if (isinstance(serializer, ListField) or
- isinstance(serializer, ListSerializer)):
- if (params.get(field_name, None) is not None or
- not serializer.allow_null):
- if isinstance(serializer.child, Serializer):
- # If this is a list of experiment inputs, need to
manually
- # convert the 'type' field from an integer to a
DataType enum
- if field_name == 'experimentInputs' and 'type' in
serializer.child.fields:
- for item in params[field_name]:
- if 'type' in item and
isinstance(item['type'], int):
- item['type'] = DataType(item['type'])
- elif field_name == 'experimentOutputs' and 'type'
in serializer.child.fields:
- for item in params[field_name]:
- if 'type' in item and
isinstance(item['type'], int):
- item['type'] = DataType(item['type'])
- elif field_name == 'experimentStatus' and 'state'
in serializer.child.fields:
- for item in params[field_name]:
- if 'state' in item and
isinstance(item['state'], int):
- item['state'] =
ExperimentState(item['state'])
- params[field_name] = [serializer.child.create(
- item) for item in params[field_name]]
- else:
- params[field_name] = serializer.to_representation(
- params[field_name])
- elif isinstance(serializer, Serializer):
- if field_name in params and params[field_name] is not None:
- params[field_name] = serializer.create(
- params[field_name])
- return params
-
- def create(self, validated_data):
- params = self.process_nested_fields(validated_data)
-
- # The Thrift models expect a mandatory ID but provide a default
value for creation.
- # The latest library upgrade is tight and fails if an ID is
explicitly passed as `None`.
- # This logic removes such fields allowing the default to be used.
- thrift_spec = thrift_data_type.thrift_spec
- for field_spec in thrift_spec:
- if field_spec:
- field_name = field_spec[2]
- default_value = field_spec[4]
- if default_value is not None:
- if field_name in params and params[field_name] is None:
- del params[field_name]
-
- if (thrift_data_type.__name__ == 'ExperimentModel' and
- 'experimentType' in params and
isinstance(params['experimentType'], int)):
- params['experimentType'] =
ExperimentType(params['experimentType'])
-
- if (thrift_data_type.__name__ ==
'ApplicationDeploymentDescription' and
- 'parallelism' in params and isinstance(params['parallelism'],
int)):
- params['parallelism'] =
ApplicationParallelismType(params['parallelism'])
-
- return thrift_data_type(**params)
-
- def update(self, instance, validated_data):
- return self.create(validated_data)
-
- return CustomSerializer
-
-
-def process_field(field, enable_date_time_conversion, required=False,
read_only=False, allow_null=False):
- """
- Used to process a thrift data type field
- :param field:
- :param required:
- :param read_only:
- :param allow_null:
- :return:
- """
- if field[1] in mapping:
- # handling scenarios when the thrift field type is present in the
- # mapping
- field_class = mapping[field[1]]
- kwargs = dict(required=required, read_only=read_only)
- # allow_null isn't allowed for BooleanField
- if field_class not in (BooleanField,):
- kwargs['allow_null'] = allow_null
- # allow_null CharField are also allowed to be blank
- if field_class == CharField:
- kwargs['allow_blank'] = allow_null
- thrift_model_class = mapping[field[1]]
-
- if thrift_model_class == IntegerField and field[3] is not None and
isinstance(field[3], type) and issubclass(field[3], enum.IntEnum):
- return ThriftEnumField(field[3], required=required,
read_only=read_only, allow_null=allow_null)
-
- if enable_date_time_conversion and thrift_model_class == IntegerField
and field[2].lower().endswith("time"):
- thrift_model_class = UTCPosixTimestampDateTimeField
- return thrift_model_class(**kwargs)
- elif field[1] == TType.LIST:
- # handling scenario when the thrift field type is list
- list_field_serializer = process_list_field(field)
- return ListField(child=list_field_serializer,
- required=required,
- read_only=read_only,
- allow_null=allow_null)
- elif field[1] == TType.STRUCT:
- # handling scenario when the thrift field type is struct
- return create_serializer(field[3][0],
- required=required,
- read_only=read_only,
- allow_null=allow_null)
-
-
-def process_list_field(field):
- """
- Used to process thrift list type field
- :param field:
- :return:
- """
- list_details = field[3]
- item_ttype = list_details[0]
- # For enums, extra type info is the enum Class (e.g. ExperimentState)
- # For structs, it's a tuple of (StructClass, StructClass.thrift_spec)
- item_type_info = list_details[1]
-
- if (item_ttype == TType.I32 and
- item_type_info is not None and
- isinstance(item_type_info, type) and
- issubclass(item_type_info, enum.IntEnum)):
- return ThriftEnumField(item_type_info)
-
- if item_ttype in mapping:
- return mapping[item_ttype]()
- elif item_ttype == TType.STRUCT:
- return create_serializer(item_type_info[0])
diff --git
a/airavata-django-portal/django_airavata/apps/auth/tests/test_signals.py
b/airavata-django-portal/django_airavata/apps/auth/tests/test_signals.py
index 7ba1111fe..91cc569c4 100644
--- a/airavata-django-portal/django_airavata/apps/auth/tests/test_signals.py
+++ b/airavata-django-portal/django_airavata/apps/auth/tests/test_signals.py
@@ -1,5 +1,9 @@
-from airavata.model.group.ttypes import GroupModel
-from airavata.model.user.ttypes import UserProfile
+from airavata_sdk.generated.org.apache.airavata.model.group.group_manager_pb2
import ( # noqa: E501
+ GroupModel,
+)
+from airavata_sdk.generated.org.apache.airavata.model.user.user_profile_pb2
import (
+ UserProfile,
+)
from django.core import mail
from django.shortcuts import reverse
from django.test import RequestFactory, TestCase, override_settings
@@ -25,12 +29,12 @@ class EmailUserAddedToGroupSignalReceiverTests(TestCase):
factory = RequestFactory()
self.request = factory.get("/")
self.user = UserProfile(
- airavataInternalUserId=f"testuser@{GATEWAY_ID}",
- userId="testuser",
- gatewayId=GATEWAY_ID,
+ airavata_internal_user_id=f"testuser@{GATEWAY_ID}",
+ user_id="testuser",
+ gateway_id=GATEWAY_ID,
emails=["[email protected]"],
- firstName="Test",
- lastName="User")
+ first_name="Test",
+ last_name="User")
def test(self):
group = GroupModel(id="abc123", name="Test Group")
@@ -68,7 +72,7 @@ class EmailUserAddedToGroupSignalReceiverTests(TestCase):
msg.reply_to,
[f"\"{PORTAL_ADMINS[0][0]}\" <{PORTAL_ADMINS[0][1]}>"])
self.assertSequenceEqual(
- msg.to, [f"\"{self.user.firstName} {self.user.lastName}\" "
+ msg.to, [f"\"{self.user.first_name} {self.user.last_name}\" "
f"<{self.user.emails[0]}>"])
self.assertIn(
self.request.build_absolute_uri(
@@ -78,4 +82,4 @@ class EmailUserAddedToGroupSignalReceiverTests(TestCase):
self.request.build_absolute_uri(
reverse("django_airavata_workspace:experiments")),
msg.body)
- self.assertIn(self.user.userId, msg.body)
+ self.assertIn(self.user.user_id, msg.body)
diff --git
a/airavata-django-portal/django_airavata/apps/auth/tests/test_views.py
b/airavata-django-portal/django_airavata/apps/auth/tests/test_views.py
index 384dfcfde..2ad03cf0b 100644
--- a/airavata-django-portal/django_airavata/apps/auth/tests/test_views.py
+++ b/airavata-django-portal/django_airavata/apps/auth/tests/test_views.py
@@ -1,7 +1,9 @@
from unittest.mock import patch
from urllib.parse import urlencode
-from airavata.model.user.ttypes import UserProfile
+from airavata_sdk.generated.org.apache.airavata.model.user.user_profile_pb2
import (
+ UserProfile,
+)
from django.contrib import messages
from django.contrib.auth.models import AnonymousUser
from django.contrib.messages.middleware import MessageMiddleware
@@ -223,10 +225,10 @@ class VerifyEmailViewTestCase(TestCase):
views_iam_admin_client.is_user_enabled.return_value = False
views_iam_admin_client.enable_user.return_value = True
user_profile = UserProfile(
- airavataInternalUserId=f"testuser@{GATEWAY_ID}",
- userId="testuser",
- firstName="Test",
- lastName="User1",
+ airavata_internal_user_id=f"testuser@{GATEWAY_ID}",
+ user_id="testuser",
+ first_name="Test",
+ last_name="User1",
emails=["[email protected]"]
)
views_iam_admin_client.get_user.return_value = user_profile
@@ -270,10 +272,10 @@ class ResendEmailLinkTestCase(TestCase):
views_iam_admin_client.is_user_exist.return_value = True
user_profile = UserProfile(
- airavataInternalUserId=f"testuser@{GATEWAY_ID}",
- userId="testuser",
- firstName="Test",
- lastName="User1",
+ airavata_internal_user_id=f"testuser@{GATEWAY_ID}",
+ user_id="testuser",
+ first_name="Test",
+ last_name="User1",
emails=["[email protected]"]
)
views_iam_admin_client.get_user.return_value = user_profile
diff --git
a/airavata-django-portal/django_airavata/apps/auth/token_authentication.py
b/airavata-django-portal/django_airavata/apps/auth/token_authentication.py
index e41ddd60e..8de476760 100644
--- a/airavata-django-portal/django_airavata/apps/auth/token_authentication.py
+++ b/airavata-django-portal/django_airavata/apps/auth/token_authentication.py
@@ -11,10 +11,11 @@ import logging
import ssl
import jwt
-from airavata.model.security.ttypes import AuthzToken
from django.conf import settings
from rest_framework import authentication, exceptions
+from .utils import AuthzToken
+
logger = logging.getLogger(__name__)
_jwks_client = None
diff --git a/airavata-django-portal/django_airavata/apps/auth/utils.py
b/airavata-django-portal/django_airavata/apps/auth/utils.py
index cfa53c042..5e39acb1f 100644
--- a/airavata-django-portal/django_airavata/apps/auth/utils.py
+++ b/airavata-django-portal/django_airavata/apps/auth/utils.py
@@ -2,7 +2,6 @@
import time
-from airavata.model.security.ttypes import AuthzToken
from django.conf import settings
from django.contrib.auth import authenticate
from django.core.mail import EmailMessage
@@ -14,6 +13,19 @@ from requests_oauthlib import OAuth2Session
from . import models
+class AuthzToken:
+ """Plain carrier for the Keycloak access token + gateway/user claims.
+
+ Replaces the legacy Thrift ``AuthzToken`` value type; keeps the same
+ ``accessToken`` / ``claimsMap`` attribute names so existing consumers (the
+ gRPC client factory, IAM admin REST helpers, ...) are unchanged.
+ """
+
+ def __init__(self, accessToken, claimsMap=None):
+ self.accessToken = accessToken
+ self.claimsMap = claimsMap or {}
+
+
def get_authz_token(request, user=None, access_token=None):
"""Construct AuthzToken instance from session; refresh token if needed."""
if access_token is not None: