Skip to content

Commit

Permalink
add user permissions for chat and chat messages
Browse files Browse the repository at this point in the history
  • Loading branch information
shivaam committed Mar 6, 2024
1 parent 8167e34 commit 19c9831
Show file tree
Hide file tree
Showing 13 changed files with 332 additions and 40 deletions.
21 changes: 0 additions & 21 deletions babblebox/babblebox/api/clients/pulsar_client.py

This file was deleted.

14 changes: 8 additions & 6 deletions babblebox/babblebox/api/clients/pulsar_client_avro.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,14 @@
topic = settings.NEW_MESSAGE_TOPIC
client = None
producer = None
try:
client = pulsar.Client(settings.PULSAR_URL)
print("No client created")
producer = client.create_producer(topic, schema=BytesSchema())
except Exception as e:
print("Exception raised" + str(e))
if settings.DISABLE_PULSAR_CLIENT is True:
print("Pulsar is enabled")
try:
client = pulsar.Client(settings.PULSAR_URL)
print("No client created")
producer = client.create_producer(topic, schema=BytesSchema())
except Exception as e:
print("Exception raised" + str(e))

class PulsarClient:
@classmethod
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Generated by Django 4.2.9 on 2024-03-06 01:53

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("api", "0001_initial"),
]

operations = [
migrations.CreateModel(
name="Participant",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("is_owner", models.BooleanField(default=False)),
("has_read_access", models.BooleanField(default=True)),
("has_write_access", models.BooleanField(default=True)),
("last_updated", models.DateTimeField(auto_now=True)),
("chat", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.chat")),
("user", models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.AddField(
model_name="chat",
name="participants",
field=models.ManyToManyField(through="api.Participant", to=settings.AUTH_USER_MODEL),
),
migrations.AddIndex(
model_name="participant",
index=models.Index(fields=["chat"], name="chat_participant_idx"),
),
migrations.AddIndex(
model_name="participant",
index=models.Index(fields=["user"], name="user_participant_idx"),
),
migrations.AddIndex(
model_name="participant",
index=models.Index(fields=["chat", "user"], name="chat_user_participant_idx"),
),
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
# Generated by Django 4.2.9 on 2024-03-06 16:57

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("api", "0002_participant_chat_participants_and_more"),
]

operations = [
migrations.CreateModel(
name="ChatParticipant",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("has_read_access", models.BooleanField(default=True)),
("has_write_access", models.BooleanField(default=True)),
("last_updated", models.DateTimeField(auto_now=True)),
],
),
migrations.RemoveField(
model_name="chat",
name="participants",
),
migrations.AddField(
model_name="chat",
name="owner",
field=models.ForeignKey(
default=3,
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="chat_owner",
to=settings.AUTH_USER_MODEL,
),
preserve_default=False,
),
migrations.AddField(
model_name="chatmessage",
name="owner",
field=models.ForeignKey(
default=3,
editable=False,
on_delete=django.db.models.deletion.CASCADE,
related_name="chat_message_owner",
to=settings.AUTH_USER_MODEL,
),
preserve_default=False,
),
migrations.DeleteModel(
name="Participant",
),
migrations.AddField(
model_name="chatparticipant",
name="chat",
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to="api.chat"),
),
migrations.AddField(
model_name="chatparticipant",
name="user",
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AddIndex(
model_name="chatparticipant",
index=models.Index(fields=["chat"], name="chat_participant_idx"),
),
migrations.AddIndex(
model_name="chatparticipant",
index=models.Index(fields=["user"], name="user_participant_idx"),
),
migrations.AddIndex(
model_name="chatparticipant",
index=models.Index(fields=["chat", "user"], name="chat_user_participant_idx"),
),
]
19 changes: 19 additions & 0 deletions babblebox/babblebox/api/migrations/0004_chat_participants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Generated by Django 4.2.9 on 2024-03-06 16:58

from django.conf import settings
from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
("api", "0003_chatparticipant_remove_chat_participants_chat_owner_and_more"),
]

operations = [
migrations.AddField(
model_name="chat",
name="participants",
field=models.ManyToManyField(through="api.ChatParticipant", to=settings.AUTH_USER_MODEL),
),
]
17 changes: 17 additions & 0 deletions babblebox/babblebox/api/migrations/0005_chat_is_public.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# Generated by Django 4.2.9 on 2024-03-06 17:35

from django.db import migrations, models


class Migration(migrations.Migration):
dependencies = [
("api", "0004_chat_participants"),
]

operations = [
migrations.AddField(
model_name="chat",
name="is_public",
field=models.BooleanField(default=False),
),
]
49 changes: 44 additions & 5 deletions babblebox/babblebox/api/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,20 +51,60 @@ def save(self, *args, **kwargs):
class Chat(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
topic = models.CharField(max_length=255, editable=True)
is_public = models.BooleanField(default=False, editable=True)
# Add a particpants field to store the participants of the chat that is a mant-to-many relationship with users
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='chat_owner',
editable=False,
null=False,
blank=False
)
participants = models.ManyToManyField(settings.AUTH_USER_MODEL, through='api.ChatParticipant')

def save(self, *args, **kwargs):
if not self.id:
# Generate a unique ID
self.id = str(uuid.uuid4())
super(Chat, self).save(*args, **kwargs)

def is_owner(self, user):
return self.owner == user


class ChatParticipant(models.Model):
chat = models.ForeignKey(Chat, on_delete=models.CASCADE)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
has_read_access = models.BooleanField(default=True)
has_write_access = models.BooleanField(default=True)
last_updated = models.DateTimeField(auto_now=True)

class Meta:
indexes = [
models.Index(fields=['chat'], name='chat_participant_idx'),
models.Index(fields=['user'], name='user_participant_idx'),
models.Index(fields=['chat', 'user'], name='chat_user_participant_idx'),
]

@classmethod
def get_participants_for_chat(cls, chat_id):
return cls.objects.filter(chat=chat_id)

class ChatMessage(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
chat_id = models.ForeignKey(Chat, on_delete=models.CASCADE)
audio_message_id = models.ForeignKey(AudioFile, on_delete=models.CASCADE)
timestamp = models.DateTimeField(auto_now_add=True)
image_id = models.ForeignKey(ImageFile, on_delete=models.CASCADE, null=True, blank=True)
owner = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='chat_message_owner',
editable=False,
null=False,
blank=False
)

def is_owner(self, user):
return self.owner == user

class Meta:
indexes = [
Expand All @@ -90,6 +130,5 @@ def get_avro_schema(cls):


logger = logging.getLogger(__name__)

logger.info(f'Django DEBUG mode is {"on" if settings.DEBUG else "off"}')
print(ChatMessage.get_avro_schema())

26 changes: 26 additions & 0 deletions babblebox/babblebox/api/permissions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
from rest_framework import permissions

class IsOwnerOrReadOnly(permissions.BasePermission):
"""
Custom permission to only allow owners of an object to edit it.
"""

def has_object_permission(self, request, view, obj):
# Read permissions are allowed to any request,
# so we'll always allow GET, HEAD or OPTIONS requests.
if request.method in permissions.SAFE_METHODS:
return True

# Write permissions are only allowed to the owner of the chat.
return obj.owner == request.user


class IsParticipantOrOwner(permissions.BasePermission):
"""
Custom permission to only allow participants or the owner of the chat to view it.
"""

def has_object_permission(self, request, view, obj):
if request.user == obj.owner:
return True
return obj.participants.filter(id=request.user.id).exists()
12 changes: 9 additions & 3 deletions babblebox/babblebox/api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from rest_framework import serializers
from .models import AudioFile, ChatMessage, Chat, ImageFile
from .models import AudioFile, ChatMessage, Chat, ImageFile, ChatParticipant


class AudioFileSerializer(serializers.ModelSerializer):
Expand All @@ -14,11 +14,17 @@ class Meta:
fields = ['id', 'image', 'file_location']


class ParticipantSerializer(serializers.ModelSerializer):
class Meta:
model = ChatParticipant
fields = ['chat', 'user', 'has_read_access', 'has_write_access', 'last_updated']


class ChatSerializer(serializers.ModelSerializer):
class Meta:
model = Chat
fields = ['id', 'topic']

fields = ['id', 'topic', 'participants', 'owner', 'is_public']
read_only_fields = ['owner']

class ChatMessageSerializer(serializers.ModelSerializer):
audio_file = AudioFileSerializer(write_only=True)
Expand Down
5 changes: 3 additions & 2 deletions babblebox/babblebox/api/urls.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
from django.urls import path, include
from rest_framework.routers import DefaultRouter

from .views import AudioFileViewSet, ChatViewSet, ChatMessageViewSet, ImageFileViewSet
from .views import AudioFileViewSet, ChatViewSet, ChatMessageViewSet, ImageFileViewSet, ParticipantViewSet

router = DefaultRouter()
router.register(r'AudioFile', AudioFileViewSet)
router.register(r'ImageFile', ImageFileViewSet)
router.register(r'Chat', ChatViewSet)
router.register(r'ChatMessage', ChatMessageViewSet)
router.register(r'Participant', ParticipantViewSet)

urlpatterns = [
path('', include(router.urls)),
]
]
Loading

0 comments on commit 19c9831

Please sign in to comment.