diff --git a/chat/admin.py b/chat/admin.py index d4af548d..ec78db0f 100644 --- a/chat/admin.py +++ b/chat/admin.py @@ -25,12 +25,13 @@ class MessageAdmin(AdminChangeLinksMixin, admin.ModelAdmin): list_display = ( "id", "chatroom_link", + "index", "order_link", "sender_link", "receiver_link", "created_at", ) change_links = ["chatroom","order","sender","receiver"] - search_fields = ["id","order"] - ordering = ("-index", ) + search_fields = ["id","index"] + ordering = ["-chatroom_id","-index"] list_filter = ("chatroom",) \ No newline at end of file diff --git a/chat/consumers.py b/chat/consumers.py index eca9e8ec..80b5bad4 100644 --- a/chat/consumers.py +++ b/chat/consumers.py @@ -47,28 +47,33 @@ class ChatRoomConsumer(AsyncWebsocketConsumer): return None @database_sync_to_async - def save_new_message(self): + def save_new_PGP_message(self, PGP_message): '''Creates a Message object''' order = Order.objects.get(id=self.order_id) chatroom = ChatRoom.objects.get(order=order) - index = 0 - last_message = Message.objects.filter(order=order).latest() - if last_message: + try: + last_message = Message.objects.filter(order=order).latest() index = last_message.index + 1 + except: + index = 1 sender = self.scope["user"] + if order.taker == sender: + receiver = order.maker + elif order.maker == sender: + receiver = order.taker - Message.objects.create( + msg_obj = Message.objects.create( order=order, chatroom=chatroom, index=index, - sender = sender, - PGP_message=self.PGP_message, + sender=sender, + receiver=receiver, + PGP_message=PGP_message, ) - - return None + return msg_obj @database_sync_to_async def save_disconnect_user(self): @@ -103,6 +108,36 @@ class ChatRoomConsumer(AsyncWebsocketConsumer): if chatroom.taker == self.user: return chatroom.maker_connected + @database_sync_to_async + def get_peer_PGP_public_key(self): + '''Returns peer PGP public key''' + + order = Order.objects.get(id=self.order_id) + + if order.maker == self.user: + return order.taker.profile.public_key + + if order.taker == self.user: + return order.maker.profile.public_key + + @database_sync_to_async + def get_all_PGP_messages(self): + '''Returns all PGP messages''' + + order = Order.objects.get(id=self.order_id) + messages = Message.objects.filter(order=order) + + msgs = [] + for message in messages: + msgs.append({ + "index": message.index, + "time": str(message.created_at), + "message": message.PGP_message, + "nick": str(message.sender), + }) + + return msgs + async def connect(self): self.order_id = self.scope["url_route"]["kwargs"]["order_id"] self.room_group_name = f"chat_order_{self.order_id}" @@ -118,6 +153,33 @@ class ChatRoomConsumer(AsyncWebsocketConsumer): await self.accept() + # Send peer PGP public keys + peer_public_key = await self.get_peer_PGP_public_key() + await self.channel_layer.group_send( + self.room_group_name, + { + "type": "chatroom_message", + "message": peer_public_key, + "nick": self.scope["user"].username, + "peer_connected": None, + }, + ) + + # If there is any stored message, serve them. + msgs = await self.get_all_PGP_messages() + for msg in msgs: + await self.channel_layer.group_send( + self.room_group_name, + { + "type": "PGP_message", + "index": msg['index'], + "time": msg['time'], + "message": msg['message'], + "nick": msg['nick'], + "peer_connected": None, + }, + ) + async def disconnect(self, close_code): await self.save_disconnect_user() await self.channel_layer.group_discard(self.room_group_name, @@ -135,22 +197,39 @@ class ChatRoomConsumer(AsyncWebsocketConsumer): async def receive(self, text_data): text_data_json = json.loads(text_data) message = text_data_json["message"] + peer_connected = await self.is_peer_connected() # Encrypted messages are stored. They are served later when a user reconnects. - if message[0,27] == '-----BEGIN PGP MESSAGE-----': - self.PGP_message = message - await self.save_new_message() - - peer_connected = await self.is_peer_connected() - await self.channel_layer.group_send( - self.room_group_name, - { - "type": "chatroom_message", - "message": message, - "nick": self.scope["user"].username, - "peer_connected": peer_connected, - }, - ) + if message[0:27] == '-----BEGIN PGP MESSAGE-----': + # save to database + msg_obj = await self.save_new_PGP_message(message) + + index = msg_obj.index + message = msg_obj.PGP_message + time = str(msg_obj.created_at) + + await self.channel_layer.group_send( + self.room_group_name, + { + "type": "PGP_message", + "index": index, + "message": message, + "time": time, + "nick": self.scope["user"].username, + "peer_connected": peer_connected, + }, + ) + + else: + await self.channel_layer.group_send( + self.room_group_name, + { + "type": "chatroom_message", + "message": message, + "nick": self.scope["user"].username, + "peer_connected": peer_connected, + }, + ) async def chatroom_message(self, event): message = event["message"] @@ -161,7 +240,19 @@ class ChatRoomConsumer(AsyncWebsocketConsumer): "message": message, "user_nick": nick, "peer_connected": peer_connected, - "time":str(timezone.now()), })) - pass + async def PGP_message(self, event): + message = event["message"] + nick = event["nick"] + index = event["index"] + peer_connected = event["peer_connected"] + time = event["time"] + + await self.send(text_data=json.dumps({ + "index": index, + "message": message, + "user_nick": nick, + "peer_connected": peer_connected, + "time":time, + })) diff --git a/chat/models.py b/chat/models.py index 81482276..a1f07f09 100644 --- a/chat/models.py +++ b/chat/models.py @@ -47,7 +47,10 @@ class ChatRoom(models.Model): return f"Chat:{str(self.order.id)}" class Message(models.Model): - id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) + class Meta: + get_latest_by = 'index' + + # id = models.PositiveBigIntegerField(primary_key=True, default=uuid.uuid4, editable=False) order = models.ForeignKey( Order, related_name="message", @@ -67,6 +70,12 @@ class Message(models.Model): on_delete=models.SET_NULL, null=True, default=None) + receiver = models.ForeignKey( + User, + related_name="message_receiver", + on_delete=models.SET_NULL, + null=True, + default=None) PGP_message = models.TextField(max_length=5000, null=True, @@ -76,4 +85,4 @@ class Message(models.Model): created_at = models.DateTimeField(default=timezone.now) def __str__(self): - return f"Chat:{str(self.order.id)}-Index:{self.order.index}" + return f"Chat:{str(self.order.id)} - Idx:{self.index}" diff --git a/frontend/src/components/EncryptedChat.js b/frontend/src/components/EncryptedChat.js index ae1e0f6c..97dc0b7f 100644 --- a/frontend/src/components/EncryptedChat.js +++ b/frontend/src/components/EncryptedChat.js @@ -34,6 +34,7 @@ class Chat extends Component { showPGP: new Array, waitingEcho: false, lastSent: '---BLANK---', + latestIndex: 0, }; rws = new ReconnectingWebSocket('ws://' + window.location.host + '/ws/chat/' + this.props.orderId + '/'); @@ -42,13 +43,13 @@ class Chat extends Component { this.rws.addEventListener('open', () => { console.log('Connected!'); this.setState({connected: true}); - if ( this.state.peer_pub_key == null){ - this.rws.send(JSON.stringify({ - type: "message", - message: "----PLEASE SEND YOUR PUBKEY----", - nick: this.props.ur_nick, - })); - } + // if ( this.state.peer_pub_key == null){ + // this.rws.send(JSON.stringify({ + // type: "message", + // message: "----PLEASE SEND YOUR PUBKEY----", + // nick: this.props.ur_nick, + // })); + // } this.rws.send(JSON.stringify({ type: "message", message: this.state.own_pub_key, @@ -60,24 +61,24 @@ class Chat extends Component { const dataFromServer = JSON.parse(message.data); console.log('Got reply!', dataFromServer.type); - + console.log('PGP message index', dataFromServer.index, ' latestIndex ',this.state.latestIndex); if (dataFromServer){ console.log(dataFromServer) // If we receive our own key on a message - if (dataFromServer.message == this.state.own_pub_key){console.log("ECHO OF OWN PUB KEY RECEIVED!!")} + if (dataFromServer.message == this.state.own_pub_key){console.log("OWN PUB KEY RECEIVED!!")} // If we receive a request to send our public key - if (dataFromServer.message == `----PLEASE SEND YOUR PUBKEY----`) { - this.rws.send(JSON.stringify({ - type: "message", - message: this.state.own_pub_key, - nick: this.props.ur_nick, - })); - } else + // if (dataFromServer.message == `----PLEASE SEND YOUR PUBKEY----`) { + // this.rws.send(JSON.stringify({ + // type: "message", + // message: this.state.own_pub_key, + // nick: this.props.ur_nick, + // })); + // } else // If we receive a public key other than ours (our peer key!) - if (dataFromServer.message.substring(0,36) == `-----BEGIN PGP PUBLIC KEY BLOCK-----` & dataFromServer.message != this.state.own_pub_key) { + if (dataFromServer.message.substring(0,36) == `-----BEGIN PGP PUBLIC KEY BLOCK-----` && dataFromServer.message != this.state.own_pub_key) { if (dataFromServer.message == this.state.peer_pub_key){ console.log("PEER HAS RECONNECTED USING HIS PREVIOUSLY KNOWN PUBKEY") } else if (dataFromServer.message != this.state.peer_pub_key & this.state.peer_pub_key != null){ @@ -88,7 +89,8 @@ class Chat extends Component { } else // If we receive an encrypted message - if (dataFromServer.message.substring(0,27) == `-----BEGIN PGP MESSAGE-----`){ + if (dataFromServer.message.substring(0,27) == `-----BEGIN PGP MESSAGE-----` && dataFromServer.index > this.state.latestIndex){ + decryptMessage( dataFromServer.message.split('\\').join('\n'), dataFromServer.user_nick == this.props.ur_nick ? this.state.own_pub_key : this.state.peer_pub_key, @@ -99,8 +101,10 @@ class Chat extends Component { ({ waitingEcho: this.state.waitingEcho == true ? (decryptedData.decryptedMessage == this.state.lastSent ? false: true ) : false, lastSent: decryptedData.decryptedMessage == this.state.lastSent ? '----BLANK----': this.state.lastSent, + latestIndex: dataFromServer.index > this.state.latestIndex ? dataFromServer.index : this.state.latestIndex, messages: [...state.messages, - { + { + index: dataFromServer.index, encryptedMessage: dataFromServer.message.split('\\').join('\n'), plainTextMessage: decryptedData.decryptedMessage, validSignature: decryptedData.validSignature,