Skip to content

Commit

Permalink
Merge pull request #889 from open-dynaMIX/fix_race_condition
Browse files Browse the repository at this point in the history
fix(api): fix race condition when creating new Identities
  • Loading branch information
open-dynaMIX authored Jun 19, 2023
2 parents 5facbea + 73ecd39 commit a3af5eb
Showing 1 changed file with 26 additions and 10 deletions.
36 changes: 26 additions & 10 deletions api/mysagw/oidc_auth/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import logging

from django.conf import settings
from django.db import IntegrityError, transaction
from django.db.models import Q

from mysagw.identity.models import Identity
Expand Down Expand Up @@ -52,9 +53,15 @@ def __init__(self, token: str, claims: dict):
self.group = self.groups[0] if self.groups else None
self.token = token
self.is_authenticated = True
self.identity = self._get_or_create_identity()
self.identity = self._update_or_create_identity()

def _get_or_create_identity(self):
def _update_or_create_identity(self):
"""
Update or create Identity.
Analogous to QuerySet.get_or_create(), in order to handle race conditions as
gracefully as possible.
"""
if self.claims.get(settings.OIDC_CLIENT_GRANT_USERNAME_CLAIM) in [
settings.OIDC_RP_CLIENT_USERNAME,
settings.OIDC_MONITORING_CLIENT_USERNAME,
Expand All @@ -71,21 +78,30 @@ def _get_or_create_identity(self):
identity.email = self.email
identity.modified_by_user = self.id
identity.save()
return identity
except Identity.MultipleObjectsReturned:
# TODO: trigger notification for staff members or admins
logger.warning(
"Found one Identity with same idp_id and one with same email. Matching"
" on idp_id."
)
identity = Identity.objects.get(idp_id=self.id)
return Identity.objects.get(idp_id=self.id)
except Identity.DoesNotExist:
identity = Identity.objects.create(
idp_id=self.id,
email=self.email,
modified_by_user=self.id,
created_by_user=self.id,
)
return identity
try:
with transaction.atomic(using=Identity.objects.db):
return Identity.objects.create(
idp_id=self.id,
email=self.email,
modified_by_user=self.id,
created_by_user=self.id,
)
except IntegrityError: # pragma: no cover
# race condition happened
try:
return Identity.objects.get(idp_id=self.id)
except Identity.DoesNotExist:
pass
raise

def __str__(self):
return f"{self.email} - {self.id}"

0 comments on commit a3af5eb

Please sign in to comment.