diff --git a/live/admin.py b/live/admin.py index d52586e..82651c4 100644 --- a/live/admin.py +++ b/live/admin.py @@ -15,8 +15,8 @@ class InstanceAdmin(admin.ModelAdmin): Custom User admin settings. """ list_display = ('user', 'instance_auth_type', 'name', 'description', - 'instance_status', 'created_at', 'last_modified', 'hash') - search_fields = ('name', 'description', 'user__username', 'hash', 'instance_auth_type') + 'instance_status', 'created_at', 'last_modified', 'hash', 'allowed_domains') + search_fields = ('name', 'description', 'user__username', 'hash', 'instance_auth_type', 'allowed_domains') list_filter = ('instance_status', 'created_at', 'last_modified', 'user') ordering = ('-created_at',) readonly_fields = ('hash', 'created_at', 'last_modified') diff --git a/live/migrations/0014_instance_alloweddomains.py b/live/migrations/0014_instance_alloweddomains.py new file mode 100644 index 0000000..4292d65 --- /dev/null +++ b/live/migrations/0014_instance_alloweddomains.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.6 on 2024-09-12 20:16 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("live", "0013_alter_socialuser_username_and_more"), + ] + + operations = [ + migrations.AddField( + model_name="instance", + name="allowedDomains", + field=models.JSONField(blank=True, null=True), + ), + ] diff --git a/live/migrations/0015_alter_socialuser_username.py b/live/migrations/0015_alter_socialuser_username.py new file mode 100644 index 0000000..0af9266 --- /dev/null +++ b/live/migrations/0015_alter_socialuser_username.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.6 on 2024-09-12 20:20 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("live", "0014_instance_alloweddomains"), + ] + + operations = [ + migrations.AlterField( + model_name="socialuser", + name="username", + field=models.CharField(default={"all"}, max_length=100), + ), + ] diff --git a/live/migrations/0016_alter_instance_alloweddomains_and_more.py b/live/migrations/0016_alter_instance_alloweddomains_and_more.py new file mode 100644 index 0000000..d22b691 --- /dev/null +++ b/live/migrations/0016_alter_instance_alloweddomains_and_more.py @@ -0,0 +1,22 @@ +# Generated by Django 5.0.6 on 2024-09-12 20:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("live", "0015_alter_socialuser_username"), + ] + + operations = [ + migrations.AlterField( + model_name="instance", + name="allowedDomains", + field=models.JSONField(blank=True, default=["*"], null=True), + ), + migrations.AlterField( + model_name="socialuser", + name="username", + field=models.CharField(default="", max_length=100), + ), + ] diff --git a/live/migrations/0017_rename_alloweddomains_instance_allowed_domains.py b/live/migrations/0017_rename_alloweddomains_instance_allowed_domains.py new file mode 100644 index 0000000..331db8e --- /dev/null +++ b/live/migrations/0017_rename_alloweddomains_instance_allowed_domains.py @@ -0,0 +1,17 @@ +# Generated by Django 5.0.6 on 2024-09-12 20:24 + +from django.db import migrations + + +class Migration(migrations.Migration): + dependencies = [ + ("live", "0016_alter_instance_alloweddomains_and_more"), + ] + + operations = [ + migrations.RenameField( + model_name="instance", + old_name="allowedDomains", + new_name="allowed_domains", + ), + ] diff --git a/live/migrations/0018_alter_instance_allowed_domains.py b/live/migrations/0018_alter_instance_allowed_domains.py new file mode 100644 index 0000000..76e1787 --- /dev/null +++ b/live/migrations/0018_alter_instance_allowed_domains.py @@ -0,0 +1,20 @@ +# Generated by Django 5.0.6 on 2024-09-12 21:09 + +import live.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("live", "0017_rename_alloweddomains_instance_allowed_domains"), + ] + + operations = [ + migrations.AlterField( + model_name="instance", + name="allowed_domains", + field=models.JSONField( + blank=True, default=live.models.default_allowed_domains, null=True + ), + ), + ] diff --git a/live/models.py b/live/models.py index 9751fef..bccfe47 100644 --- a/live/models.py +++ b/live/models.py @@ -13,6 +13,11 @@ User = get_user_model() +# TO DO: A better way to initialize the default value for allowed_domains. +def default_allowed_domains(): + return ["*"] + + TYPE_CHOICES = ( (0x1 << 0, 'Open to All'), (0x1 << 1, 'Open within Ogranization'), @@ -40,6 +45,7 @@ class Instance(models.Model): - name: Name of the instance, required. - description: Description of the instance, required. - instance_status: Status of the instance, required. + - allowed_domains: List of allowed email domains for the instance. - created_at: Timestamp of the instance creation. - last_modified: Timestamp of the last modification of the instance. - hash: Unique hash for the instance object. @@ -50,6 +56,7 @@ class Instance(models.Model): name = models.CharField(max_length=100) description = models.TextField() instance_status = models.IntegerField(choices=STATUS_CHOICES, default=0x1 << 1) + allowed_domains = models.JSONField(null=True, blank=True, default=default_allowed_domains) created_at = models.DateTimeField(auto_now_add=True) last_modified = models.DateTimeField(auto_now=True) hash = models.CharField(max_length=16, unique=True, editable=False) @@ -80,6 +87,12 @@ def getExistingInstance(hash): """ return Instance.objects.get(hash=hash) + def getAllowedDomains(hash): + """ + Get the allowed domains for the instance. + """ + return Instance.objects.get(hash=hash).allowed_domains + class SocialUser(models.Model): """ diff --git a/live/serializers.py b/live/serializers.py index 49e906c..349409e 100644 --- a/live/serializers.py +++ b/live/serializers.py @@ -21,7 +21,7 @@ class InstanceSerializer(serializers.ModelSerializer): class Meta: model = Instance fields = ['user', 'instance_auth_type', 'name', 'description', 'instance_status', - 'created_at', 'last_modified', 'hash'] + 'created_at', 'last_modified', 'hash', 'allowed_domains'] read_only_fields = ['hash', 'created_at', 'last_modified', 'user'] def create(self, validated_data): @@ -116,6 +116,9 @@ def get_or_create_social_user(self, user): Create a social user if it doesn't exist or return the existing one. """ instance = Instance.getExistingInstance(self.context.get("hash")) + allowed_domains = instance.allowed_domains + if allowed_domains != ["*"] and user.email.split("@")[1] not in allowed_domains: + raise serializers.ValidationError("User email domain is not allowed.") try: social_user = SocialUser.objects.get(username=user.email) except SocialUser.DoesNotExist: diff --git a/live/views.py b/live/views.py index 175ac23..0449a9c 100644 --- a/live/views.py +++ b/live/views.py @@ -454,7 +454,7 @@ def get(self, request, *args, **kwargs): redirect_uri = request.GET.get("redirect_uri") if redirect_uri not in settings.SOCIAL_AUTH_ALLOWED_REDIRECT_URIS: return Response( - "redirect_uri must be in SOCIAL_AUTH_ALLOWED_REDIRECT_URIS", + data={"detail": "Invalid redirect URI"}, status=status.HTTP_400_BAD_REQUEST, ) strategy = load_strategy(request)