From 0a30fe0cf69149d6005a2949583bbb0a7deeb64c Mon Sep 17 00:00:00 2001 From: Felicio Mununga Date: Wed, 22 Jun 2022 13:30:24 +0200 Subject: [PATCH] Check owner, member and author prior handling (#283) * check owner, member and author * fix isOwner --- packages/status-js/src/client/chat.ts | 81 ++++++++++++++++++- .../src/client/community/community.ts | 13 +++ .../client/community/handle-waku-message.ts | 77 ++++++++++++++---- .../src/client/community/map-chat-message.ts | 8 +- 4 files changed, 155 insertions(+), 24 deletions(-) diff --git a/packages/status-js/src/client/chat.ts b/packages/status-js/src/client/chat.ts index e17aa685..7eaf68dd 100644 --- a/packages/status-js/src/client/chat.ts +++ b/packages/status-js/src/client/chat.ts @@ -25,6 +25,7 @@ export type ChatMessage = ChatMessageProto & { pinned: boolean reactions: Reactions chatUuid: string + signer: string responseToMessage?: Omit } @@ -215,7 +216,11 @@ export class Chat { this.emitMessages(this.messages) } - public handleEditedMessage = (messageId: string, text: string) => { + public handleEditedMessage = ( + messageId: string, + text: string, + signerPublicKey: string + ) => { let messageIndex = this.messages.length while (--messageIndex >= 0) { const _message = this.messages[messageIndex] @@ -230,6 +235,10 @@ export class Chat { return } + if (!this.isAuthor(this.messages[messageIndex], signerPublicKey)) { + return + } + this.messages[messageIndex] = { ...this.messages[messageIndex], text, @@ -239,7 +248,10 @@ export class Chat { this.emitMessages(this.messages) } - public handleDeletedMessage = (messageId: string) => { + public handleDeletedMessage = ( + messageId: string, + signerPublicKey: string + ) => { let messageIndex = this.messages.length while (--messageIndex >= 0) { const _message = this.messages[messageIndex] @@ -253,6 +265,10 @@ export class Chat { return } + if (!this.isAuthor(this.messages[messageIndex], signerPublicKey)) { + return + } + this.messages.splice(messageIndex, 1) this.emitMessages(this.messages) @@ -380,7 +396,31 @@ export class Chat { } public editMessage = async (messageId: string, text: string) => { - // todo?: check if message exists + if (!this.client.account) { + throw new Error('Text message cannot be edited without a created account') + } + + let messageIndex = this.messages.length + while (--messageIndex >= 0) { + const _message = this.messages[messageIndex] + + if (_message.messageId === messageId) { + break + } + } + + if (messageIndex < 0) { + throw new Error('Text message was not found') + } + + if ( + !this.isAuthor( + this.messages[messageIndex], + `0x${this.client.account.publicKey}` + ) + ) { + throw new Error('Text message can only be edited by its author') + } if (text === '') { throw new Error('Text message cannot be empty') @@ -404,7 +444,33 @@ export class Chat { } public deleteMessage = async (messageId: string) => { - // todo: check if message exists + if (!this.client.account) { + throw new Error( + 'Text message cannot be deleted without a created account' + ) + } + + let messageIndex = this.messages.length + while (--messageIndex >= 0) { + const _message = this.messages[messageIndex] + + if (_message.messageId === messageId) { + break + } + } + + if (messageIndex < 0) { + throw new Error('Text message was not found') + } + + if ( + !this.isAuthor( + this.messages[messageIndex], + `0x${this.client.account.publicKey}` + ) + ) { + throw new Error('Text message can only be deleted by its author') + } const payload = DeleteMessage.encode({ clock: BigInt(Date.now()), @@ -457,4 +523,11 @@ export class Chat { this.symmetricKey ) } + + public isAuthor = ( + message: ChatMessage, + signerPublicKey: string + ): boolean => { + return message.signer === signerPublicKey + } } diff --git a/packages/status-js/src/client/community/community.ts b/packages/status-js/src/client/community/community.ts index df9e620f..a0834d44 100644 --- a/packages/status-js/src/client/community/community.ts +++ b/packages/status-js/src/client/community/community.ts @@ -5,6 +5,7 @@ import { CommunityRequestToJoin } from '~/protos/communities' import { MessageType } from '~/protos/enums' import { getDifferenceByKeys } from '~/src/helpers/get-difference-by-keys' import { getObjectsDifference } from '~/src/helpers/get-objects-difference' +import { compressPublicKey } from '~/src/utils/compress-public-key' import { idToContentTopic } from '../../contentTopic' import { createSymKeyFromPassword } from '../../encryption' @@ -20,6 +21,7 @@ import type { export class Community { private client: Client + /** Compressed. */ public publicKey: string public id: string private contentTopic!: string @@ -254,4 +256,15 @@ export class Community { this.symmetricKey ) } + + public isOwner = ( + /** Uncompressed. */ + signerPublicKey: string + ): boolean => { + return this.publicKey === `0x${compressPublicKey(signerPublicKey)}` + } + + public isMember = (signerPublicKey: string): boolean => { + return this.getMember(signerPublicKey) !== undefined + } } diff --git a/packages/status-js/src/client/community/handle-waku-message.ts b/packages/status-js/src/client/community/handle-waku-message.ts index 9178c08d..4bde1058 100644 --- a/packages/status-js/src/client/community/handle-waku-message.ts +++ b/packages/status-js/src/client/community/handle-waku-message.ts @@ -52,15 +52,16 @@ export function handleWakuMessage( } messageToDecode = decodedMetadata.payload - const publicKey = recoverPublicKey( + const signerPublicKeyBytes = recoverPublicKey( decodedMetadata.signature, decodedMetadata.payload ) const messageId = payloadToId( decodedProtocol?.publicMessage ?? wakuMessage.payload, - publicKey + signerPublicKeyBytes ) + const signerPublicKey = `0x${bytesToHex(signerPublicKeyBytes)}` // already handled if (client.wakuMessages.has(messageId)) { @@ -76,6 +77,10 @@ export function handleWakuMessage( // decode const decodedPayload = CommunityDescription.decode(messageToDecode) + if (!community.isOwner(signerPublicKey)) { + return + } + // handle (state and callback) community.handleDescription(decodedPayload) @@ -88,16 +93,26 @@ export function handleWakuMessage( switch (decodedPayload.messageType) { case MessageType.COMMUNITY_CHAT: { + if (!community.isMember(signerPublicKey)) { + return + } + const chatUuid = getChatUuid(decodedPayload.chatId) + const chat = community.chats.get(chatUuid) + + if (!chat) { + return + } // map const chatMessage = mapChatMessage(decodedPayload, { messageId, chatUuid, + signerPublicKey, }) // handle - community.chats.get(chatUuid)?.handleNewMessage(chatMessage) + chat.handleNewMessage(chatMessage) break } @@ -115,12 +130,23 @@ export function handleWakuMessage( switch (decodedPayload.messageType) { case MessageType.COMMUNITY_CHAT: { + if (!community.isMember(signerPublicKey)) { + return + } + const messageId = decodedPayload.messageId const chatUuid = getChatUuid(decodedPayload.chatId) + const chat = community.chats.get(chatUuid) - community.chats - .get(chatUuid) - ?.handleEditedMessage(messageId, decodedPayload.text) + if (!chat) { + return + } + + chat.handleEditedMessage( + messageId, + decodedPayload.text, + signerPublicKey + ) break } @@ -138,10 +164,19 @@ export function handleWakuMessage( switch (decodedPayload.messageType) { case MessageType.COMMUNITY_CHAT: { + if (!community.isMember(signerPublicKey)) { + return + } + const messageId = decodedPayload.messageId const chatUuid = getChatUuid(decodedPayload.chatId) + const chat = community.chats.get(chatUuid) - community.chats.get(chatUuid)?.handleDeletedMessage(messageId) + if (!chat) { + return + } + + chat.handleDeletedMessage(messageId, signerPublicKey) break } @@ -159,12 +194,19 @@ export function handleWakuMessage( switch (decodedPayload.messageType) { case MessageType.COMMUNITY_CHAT: { + if (!community.isMember(signerPublicKey)) { + return + } + const messageId = decodedPayload.messageId const chatUuid = getChatUuid(decodedPayload.chatId) + const chat = community.chats.get(chatUuid) - community.chats - .get(chatUuid) - ?.handlePinnedMessage(messageId, decodedPayload.pinned) + if (!chat) { + return + } + + chat.handlePinnedMessage(messageId, decodedPayload.pinned) break } @@ -182,16 +224,19 @@ export function handleWakuMessage( switch (decodedPayload.messageType) { case MessageType.COMMUNITY_CHAT: { + if (!community.isMember(signerPublicKey)) { + return + } + const messageId = decodedPayload.messageId const chatUuid = getChatUuid(decodedPayload.chatId) - const chat = community.chats.get(chatUuid) - chat?.handleEmojiReaction( - messageId, - decodedPayload, - `0x${bytesToHex(publicKey)}` - ) + if (!chat) { + return + } + + chat.handleEmojiReaction(messageId, decodedPayload, signerPublicKey) break } diff --git a/packages/status-js/src/client/community/map-chat-message.ts b/packages/status-js/src/client/community/map-chat-message.ts index 054ec9c3..9f4efe38 100644 --- a/packages/status-js/src/client/community/map-chat-message.ts +++ b/packages/status-js/src/client/community/map-chat-message.ts @@ -6,17 +6,17 @@ export function mapChatMessage( props: { messageId: string chatUuid: string - publicKey: string + signerPublicKey: string } ): ChatMessage { - const { messageId, chatUuid, publicKey } = props + const { messageId, chatUuid, signerPublicKey } = props - const message = { + const message: ChatMessage = { ...decodedMessage, messageId, chatUuid, pinned: false, - signer: publicKey, + signer: signerPublicKey, reactions: { THUMBS_UP: new Set(), THUMBS_DOWN: new Set(),