diff --git a/user/admin.py b/user/admin.py index ff848e53..9b4e0c1f 100644 --- a/user/admin.py +++ b/user/admin.py @@ -15,6 +15,7 @@ class UserNotificationAdmin(admin.ModelAdmin): class UserAdmin(admin.ModelAdmin): list_display = [ "id", + "sso_identifier", "username", "first_name", "last_name", diff --git a/user/migrations/0010_user_sso_identifier.py b/user/migrations/0010_user_sso_identifier.py new file mode 100644 index 00000000..dce49c14 --- /dev/null +++ b/user/migrations/0010_user_sso_identifier.py @@ -0,0 +1,18 @@ +# Generated by Django 5.1.6 on 2025-09-12 06:10 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('user', '0009_user_oauth_token'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='sso_identifier', + field=models.CharField(blank=True, db_comment='Identifies the account based on an unique identifier from the SSO system', max_length=255, null=True), + ), + ] diff --git a/user/models/user.py b/user/models/user.py index 33390885..4acffb0b 100644 --- a/user/models/user.py +++ b/user/models/user.py @@ -6,6 +6,7 @@ Created on: 15.11.21 """ from django.contrib.auth.models import AbstractUser +from django.core.exceptions import ObjectDoesNotExist from django.db import models @@ -32,6 +33,12 @@ class User(AbstractUser): db_comment="OAuth token for the user", related_name="+" ) + sso_identifier = models.CharField( + blank=True, + null=True, + db_comment="Identifies the account based on an unique identifier from the SSO system", + max_length=255, + ) def is_notification_setting_set(self, notification_enum: UserNotificationEnum): return self.notifications.filter( @@ -264,4 +271,48 @@ class User(AbstractUser): self.oauth_token.delete() self.oauth_token = token self.save() - return self \ No newline at end of file + return self + + @staticmethod + def resolve_user_using_propagation_data(data: dict): + """ Fetches user from db by the given data from propagation process + + Args: + data (dict): json containing user information from the sso system + + Returns: + user (User): The resolved user + """ + username = data.get("username", None) + sso_identifier = data.get("sso_identifier", None) + if not username and not sso_identifier: + raise AssertionError("No username or sso identifier provided") + + try: + user = User.objects.get(username=username) + except ObjectDoesNotExist: + try: + user = User.objects.get(sso_identifier=sso_identifier) + except ObjectDoesNotExist: + raise ObjectDoesNotExist("No user with this username or sso identifier was found") + + return user + + def update_user_using_propagation_data(self, data: dict): + """ Update user data based on propagation data from sso system + + Args: + data (dict): json containing user information from the sso system + + Returns: + user (User): The updated user + """ + skipable_attrs = { + "is_staff", + "is_superuser", + } + for _attr, _val in data.items(): + if _attr in skipable_attrs: + continue + setattr(self, _attr, _val) + return self diff --git a/user/views/propagate.py b/user/views/propagate.py index 3afb6fcf..bb076502 100644 --- a/user/views/propagate.py +++ b/user/views/propagate.py @@ -44,17 +44,8 @@ class PropagateUserView(View): try: status = "updated" - user = User.objects.get(username=body.get('username')) - # Update user data, excluding some changes - skipable_attrs = { - "username", - "is_staff", - "is_superuser", - } - for _attr, _val in body.items(): - if _attr in skipable_attrs: - continue - setattr(user, _attr, _val) + user = User.resolve_user_using_propagation_data(body) + user = user.update_user_using_propagation_data(body) except ObjectDoesNotExist: user = User(**body) status = "created"