Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 33 additions & 18 deletions apps/chatter/consumers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from channels.generic.websocket import AsyncWebsocketConsumer
from rest_framework.authtoken.models import Token
from django.contrib.auth.models import AnonymousUser
from django.contrib.auth.hashers import check_password
from channels.db import database_sync_to_async

from .models import Entry
Expand All @@ -28,6 +29,16 @@ def create_entry(author, text, room_id):
# def get_latest_messages(room_id):
# return sync_to_async(Entry.last_50_messages, thread_sensitive=True)(room_id=room_id)

@database_sync_to_async
def check_room_password(room_id, password):
hashed_password = Room.objects.get(id=room_id).password
print('GOT HASHED PASSWORD ', hashed_password)
print('CHECKING PASSWORD ', password)
if not hashed_password:
return True
else:
return check_password(password, hashed_password)

# WEBSOCKETS - The consumer is like the view for websockets.
# It is registered in the app routing.py
class ChatConsumer(AsyncWebsocketConsumer):
Expand All @@ -36,6 +47,7 @@ class ChatConsumer(AsyncWebsocketConsumer):
NEW_ENTRY = 'NEW_ENTRY'
ENTRIES = 'ENTRIES'
ERROR = 'ERROR'
INIT_RESPONSE = 'INIT_RESPONSE'

async def connect(self):
print('CONNECTION RECEIVED')
Expand All @@ -55,12 +67,13 @@ async def disconnect(self, close_code):
self.channel_name
)

async def init_chat(self, data):
user = await get_user(data['token'])
async def init_chat(self, isAuthorized):
# user = await get_user(data['token'])
message = {
'command': self.INIT_CHAT,
'success': '{0} has joined the room'.format(str(user))
'command': self.INIT_RESPONSE,
'authorized': isAuthorized
}
print('INIT_CHAT RECEIVED, RESPONDING WITH ', message)
await self.send_message(message)

async def fetch_entries(self, data):
Expand Down Expand Up @@ -94,12 +107,6 @@ async def error_reponse(self, error):
}
await self.send_message(content)

commands = {
INIT_CHAT: init_chat,
FETCH_ENTRIES: fetch_entries,
NEW_ENTRY: new_entry
}

async def receive(self, text_data):
print('IN RECEIVE WITH MESSAGE STRING ' + text_data)
message = json.loads(text_data)
Expand All @@ -108,22 +115,30 @@ async def receive(self, text_data):
if message_serializer.is_valid():
print('VALID SERIALIZER: ', message_serializer.validated_data)
validMessage = message_serializer.validated_data
if 'password' in validMessage:
password = validMessage['password']
else:
password = ''
isAuthorized = await check_room_password(self.room_id, password)
if validMessage['command'] == ChatConsumer.INIT_CHAT:
await self.init_chat(validMessage)
elif validMessage['command'] == ChatConsumer.FETCH_ENTRIES:
await self.fetch_entries(validMessage)
elif validMessage['command'] == ChatConsumer.NEW_ENTRY:
await self.new_entry(validMessage)
await self.init_chat(isAuthorized)
elif isAuthorized:
if validMessage['command'] == ChatConsumer.FETCH_ENTRIES:
await self.fetch_entries(validMessage)
elif validMessage['command'] == ChatConsumer.NEW_ENTRY:
await self.new_entry(validMessage)
else:
await self.error_reponse('No procedure is available for command \"{0}\"'
.format(validMessage['command']))
else:
await self.error_reponse('No procedure is available for command \"{0}\"'
.format(validMessage['command']))
await self.error_reponse('The password was incorrect')
else:
await self.error_reponse('The data sent could not be recognized')
print('INVALID SERIALIZER: ', message_serializer.errors)


async def send_message(self, message):
print('Sending message: ' + json.dumps(message))
# print('Sending message: ' + json.dumps(message))
await self.send(text_data=json.dumps(message))

async def send_chat_message(self, message):
Expand Down
18 changes: 18 additions & 0 deletions apps/chatter/migrations/0005_room_password_hash.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.0.6 on 2020-05-20 17:23

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('chatter', '0004_auto_20200519_1651'),
]

operations = [
migrations.AddField(
model_name='room',
name='password_hash',
field=models.CharField(blank=True, default='', max_length=255),
),
]
18 changes: 18 additions & 0 deletions apps/chatter/migrations/0006_auto_20200520_1724.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 3.0.6 on 2020-05-20 17:24

from django.db import migrations


class Migration(migrations.Migration):

dependencies = [
('chatter', '0005_room_password_hash'),
]

operations = [
migrations.RenameField(
model_name='room',
old_name='password_hash',
new_name='password',
),
]
26 changes: 26 additions & 0 deletions apps/chatter/migrations/0007_auto_20200520_2148.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Generated by Django 3.0.6 on 2020-05-20 21:48

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),
('chatter', '0006_auto_20200520_1724'),
]

operations = [
migrations.AlterField(
model_name='entry',
name='author',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL),
),
migrations.AlterField(
model_name='entry',
name='room',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='chatter.Room'),
),
]
8 changes: 4 additions & 4 deletions apps/chatter/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,21 @@

class Room(models.Model):
name = models.CharField(max_length=80)
password_hash = models.CharField(max_length=255, required=False)
password = models.CharField(max_length=255, blank=True, default='')

def __str__(self):
return self.name

class Entry(models.Model):
text = models.CharField(max_length=255)
timestamp = models.DateTimeField(auto_now_add=True, blank=True)
timestamp = models.DateTimeField(auto_now_add=True)
author = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.DO_NOTHING
on_delete=models.CASCADE
)
room = models.ForeignKey(
Room,
on_delete=models.DO_NOTHING
on_delete=models.CASCADE
)

def was_published_recently(self):
Expand Down
1 change: 1 addition & 0 deletions apps/chatter/routing.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# chatter/routing.py
from django.urls import re_path

from . import consumers

# WEBSOCKETS - The routing is kind of like the urls.py but for a different protocol
Expand Down
30 changes: 26 additions & 4 deletions apps/chatter/serializers.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,45 @@
from rest_framework import serializers
from rest_framework.renderers import JSONRenderer
from django.contrib.auth import hashers

from .models import Room, Entry
# from .constants import INIT_CHAT, FETCH_ENTRIES, NEW_ENTRY, ENTRIES, ERROR, INIT_RESPONSE

class RoomSerializer(serializers.HyperlinkedModelSerializer):
has_password = serializers.SerializerMethodField()
class Meta:
model = Room
fields = ['id', 'name']
fields = ['id', 'name', 'password', 'has_password']
extra_kwargs = {
'password': {'write_only': True},
'id': {'read_only': True}
}

def create(self, validated_data):
if 'password' in validated_data:
password_hash = hashers.make_password(validated_data['password'])
validated_data['password'] = password_hash
else:
validated_data['password'] = ''
Comment on lines +22 to +23
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you had null=True on the password field, then you could skip having to set this to an empty string. Fore future reference.

return Room.objects.create(**validated_data)

def get_has_password(self, obj):
return obj.password is not ''

class EntrySerializer(serializers.ModelSerializer):
class Meta:
model = Entry
fields = ('id', 'timestamp', 'author', 'text')
extra_kwargs = {'author': {'read_only': True}}
fields = ('timestamp', 'author', 'text')
extra_kwargs = {
'author': {'read_only': True},
'timestamp': {'read_only': True}
}

class MessageSerializer(serializers.Serializer):
command = serializers.ChoiceField(choices=['INIT_CHAT', 'FETCH_ENTRIES', 'NEW_ENTRY', 'ENTRIES'])
command = serializers.CharField(max_length=32)
token = serializers.CharField(required=False, min_length=40, max_length=40)
text = serializers.CharField(required=False, max_length=255)
password = serializers.CharField(required=False, max_length=255, allow_blank=True)
# Not necessary at this end of the API?
# entries = EntrySerializer(required=False, many=True)
success = serializers.CharField(required=False, max_length=100)
Expand Down
1 change: 1 addition & 0 deletions apps/chatter/urls.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# chatter/urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter

from . import views

router = DefaultRouter()
Expand Down