diff --git a/src/backend/.env.dist b/src/backend/.env.dist index cf392f8b45ceaa7baed330c9aac91c22f62a1a60..add1bcada5513346988388c81602be2424439b2b 100644 --- a/src/backend/.env.dist +++ b/src/backend/.env.dist @@ -18,4 +18,7 @@ IDP_ENABLED= SP_HOST= ANONYMIZATION_HASH_PREFIX=anon -ANONYMIZATION_DEFAULT_MINIMUM_COUNT=10 \ No newline at end of file +ANONYMIZATION_DEFAULT_MINIMUM_COUNT=10 + +CACHE_BACKEND='redis' +CACHE_URI='redis://127.0.0.1:6379' \ No newline at end of file diff --git a/src/backend/.env.test b/src/backend/.env.test index 74be67365bf7ef9a65b8f0df607dee29c5961188..803a7a2d55e7d72604b45d92166ad0b6e2b10c5b 100644 --- a/src/backend/.env.test +++ b/src/backend/.env.test @@ -17,4 +17,7 @@ IDP_SERVER=https://aai-test-v3.ruhr-uni-bochum.de SP_HOST= ANONYMIZATION_HASH_PREFIX=anon -ANONYMIZATION_DEFAULT_MINIMUM_COUNT=10 \ No newline at end of file +ANONYMIZATION_DEFAULT_MINIMUM_COUNT=10 + +CACHE_BACKEND='redis' +CACHE_URI='redis://127.0.0.1:6379' \ No newline at end of file diff --git a/src/backend/settings.py b/src/backend/settings.py index 258d1fccb833da1aebc6c376275a909edce17bff..e3bd093e044f0835095839898372a3c6a3ce8476 100644 --- a/src/backend/settings.py +++ b/src/backend/settings.py @@ -127,6 +127,15 @@ else: } } +# Cache(s) +# https://docs.djangoproject.com/en/4.1/topics/cache/ +CACHES = { + 'default': { + 'BACKEND': 'django.core.cache.backends.redis.RedisCache' if env("CACHE_BACKEND", default="file") == "redis" + else 'django.core.cache.backends.filebased.FileBasedCache', + 'LOCATION': '/tmp/django_cache' if env("CACHE_BACKEND", default="file") == 'file' else env("CACHE_URI", default='redis://127.0.0.1:6379') , + } +} # Password validation # https://docs.djangoproject.com/en/4.1/ref/settings/#auth-password-validators diff --git a/src/consents/views.py b/src/consents/views.py index 22ebda28fa15d924993da19689ed85b35c5bebb8..680047acf938f322b85c261c0abca97b6b2b333b 100644 --- a/src/consents/views.py +++ b/src/consents/views.py @@ -3,7 +3,9 @@ import string import random import os import secrets +import time +from django.core.cache import cache from django.conf import settings from django.core.exceptions import ObjectDoesNotExist from django.http.response import JsonResponse @@ -391,18 +393,38 @@ class CreateUserConsentViaConnectServiceView(APIView): shib_id = shib_connector_resolver_to_pairwaise_id(email=email, provider=provider) user = CustomUser.objects.filter(shibboleth_connector_identifier=shib_id).first() - + + message = "user exists" if not user: - user = CustomUser.objects.create( - shibboleth_connector_identifier=shib_id, # this is the pairwaise id - email=''.join(random.choices(string.ascii_uppercase + string.digits, k=8)) + "@manual-created.polaris", - first_name=''.join(random.choices(string.ascii_uppercase + string.digits, k=8)), - last_name=''.join(random.choices(string.ascii_uppercase + string.digits, k=8)) - ) + lock_key = f"user_creation_lock_{shib_id}" # "mutex" to prevent race conditions using a cache entry + if cache.add(lock_key, "locked", 2): # 2 is a timeout value, after wich the cache entry is deleted + try: + user = CustomUser.objects.create( + shibboleth_connector_identifier=shib_id, # this is the pairwise id + email=''.join(random.choices(string.ascii_uppercase + string.digits, k=8)) + "@manual-created.polaris", + first_name=''.join(random.choices(string.ascii_uppercase + string.digits, k=8)), + last_name=''.join(random.choices(string.ascii_uppercase + string.digits, k=8)) + ) + except Exception as e: + return JsonResponse({"message": "user could not be created"}, safe=False, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + finally: + message = "user has been created" + cache.delete(lock_key) + else: + for _ in range(4): # Wait up to 2x lock_timeout + time.sleep(0.1) + if not cache.get(lock_key): + break + # Fetch the user created by another process + user = CustomUser.objects.filter(shibboleth_connector_identifier=shib_id).first() + if not user: + return JsonResponse({"message": "user could not be created"}, safe=False, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) return JsonResponse( { - "message": "user is created", + "message": message, }, safe=False, status=status.HTTP_200_OK, diff --git a/src/xapi/views.py b/src/xapi/views.py index ed18d1db373f9e2eb5d28fc527f5da8a6c791b58..00262b44284ca4baeaa64ccc165bcad60879eb66 100644 --- a/src/xapi/views.py +++ b/src/xapi/views.py @@ -85,7 +85,7 @@ def shib_connector_resolver_to_pairwaise_id(email, provider): additionalData = None shib_id = client.service.GetOrGenerateIdAndConnect(app_secret, user_id, lrs_type, linkType, processId, additionalData) if len(shib_id) != 1: - print("Multiple pairwaise ids found, only use the first one!") + print("Multiple pairwise ids found, only use the first one!") shib_id = shib_id[0] if settings.DEBUG: print("Result shib_id: {0}".format(shib_id))