Check owner, member and author prior handling (#283)

* check owner, member and author

* fix isOwner
This commit is contained in:
Felicio Mununga 2022-06-22 13:30:24 +02:00 committed by GitHub
parent cb88ee7a62
commit 0a30fe0cf6
No known key found for this signature in database
GPG Key ID: 0EB8D75C775AB6F1
4 changed files with 155 additions and 24 deletions

View File

@ -25,6 +25,7 @@ export type ChatMessage = ChatMessageProto & {
pinned: boolean pinned: boolean
reactions: Reactions reactions: Reactions
chatUuid: string chatUuid: string
signer: string
responseToMessage?: Omit<ChatMessage, 'responseToMessage'> responseToMessage?: Omit<ChatMessage, 'responseToMessage'>
} }
@ -215,7 +216,11 @@ export class Chat {
this.emitMessages(this.messages) this.emitMessages(this.messages)
} }
public handleEditedMessage = (messageId: string, text: string) => { public handleEditedMessage = (
messageId: string,
text: string,
signerPublicKey: string
) => {
let messageIndex = this.messages.length let messageIndex = this.messages.length
while (--messageIndex >= 0) { while (--messageIndex >= 0) {
const _message = this.messages[messageIndex] const _message = this.messages[messageIndex]
@ -230,6 +235,10 @@ export class Chat {
return return
} }
if (!this.isAuthor(this.messages[messageIndex], signerPublicKey)) {
return
}
this.messages[messageIndex] = { this.messages[messageIndex] = {
...this.messages[messageIndex], ...this.messages[messageIndex],
text, text,
@ -239,7 +248,10 @@ export class Chat {
this.emitMessages(this.messages) this.emitMessages(this.messages)
} }
public handleDeletedMessage = (messageId: string) => { public handleDeletedMessage = (
messageId: string,
signerPublicKey: string
) => {
let messageIndex = this.messages.length let messageIndex = this.messages.length
while (--messageIndex >= 0) { while (--messageIndex >= 0) {
const _message = this.messages[messageIndex] const _message = this.messages[messageIndex]
@ -253,6 +265,10 @@ export class Chat {
return return
} }
if (!this.isAuthor(this.messages[messageIndex], signerPublicKey)) {
return
}
this.messages.splice(messageIndex, 1) this.messages.splice(messageIndex, 1)
this.emitMessages(this.messages) this.emitMessages(this.messages)
@ -380,7 +396,31 @@ export class Chat {
} }
public editMessage = async (messageId: string, text: string) => { 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 === '') { if (text === '') {
throw new Error('Text message cannot be empty') throw new Error('Text message cannot be empty')
@ -404,7 +444,33 @@ export class Chat {
} }
public deleteMessage = async (messageId: string) => { 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({ const payload = DeleteMessage.encode({
clock: BigInt(Date.now()), clock: BigInt(Date.now()),
@ -457,4 +523,11 @@ export class Chat {
this.symmetricKey this.symmetricKey
) )
} }
public isAuthor = (
message: ChatMessage,
signerPublicKey: string
): boolean => {
return message.signer === signerPublicKey
}
} }

View File

@ -5,6 +5,7 @@ import { CommunityRequestToJoin } from '~/protos/communities'
import { MessageType } from '~/protos/enums' import { MessageType } from '~/protos/enums'
import { getDifferenceByKeys } from '~/src/helpers/get-difference-by-keys' import { getDifferenceByKeys } from '~/src/helpers/get-difference-by-keys'
import { getObjectsDifference } from '~/src/helpers/get-objects-difference' import { getObjectsDifference } from '~/src/helpers/get-objects-difference'
import { compressPublicKey } from '~/src/utils/compress-public-key'
import { idToContentTopic } from '../../contentTopic' import { idToContentTopic } from '../../contentTopic'
import { createSymKeyFromPassword } from '../../encryption' import { createSymKeyFromPassword } from '../../encryption'
@ -20,6 +21,7 @@ import type {
export class Community { export class Community {
private client: Client private client: Client
/** Compressed. */
public publicKey: string public publicKey: string
public id: string public id: string
private contentTopic!: string private contentTopic!: string
@ -254,4 +256,15 @@ export class Community {
this.symmetricKey this.symmetricKey
) )
} }
public isOwner = (
/** Uncompressed. */
signerPublicKey: string
): boolean => {
return this.publicKey === `0x${compressPublicKey(signerPublicKey)}`
}
public isMember = (signerPublicKey: string): boolean => {
return this.getMember(signerPublicKey) !== undefined
}
} }

View File

@ -52,15 +52,16 @@ export function handleWakuMessage(
} }
messageToDecode = decodedMetadata.payload messageToDecode = decodedMetadata.payload
const publicKey = recoverPublicKey( const signerPublicKeyBytes = recoverPublicKey(
decodedMetadata.signature, decodedMetadata.signature,
decodedMetadata.payload decodedMetadata.payload
) )
const messageId = payloadToId( const messageId = payloadToId(
decodedProtocol?.publicMessage ?? wakuMessage.payload, decodedProtocol?.publicMessage ?? wakuMessage.payload,
publicKey signerPublicKeyBytes
) )
const signerPublicKey = `0x${bytesToHex(signerPublicKeyBytes)}`
// already handled // already handled
if (client.wakuMessages.has(messageId)) { if (client.wakuMessages.has(messageId)) {
@ -76,6 +77,10 @@ export function handleWakuMessage(
// decode // decode
const decodedPayload = CommunityDescription.decode(messageToDecode) const decodedPayload = CommunityDescription.decode(messageToDecode)
if (!community.isOwner(signerPublicKey)) {
return
}
// handle (state and callback) // handle (state and callback)
community.handleDescription(decodedPayload) community.handleDescription(decodedPayload)
@ -88,16 +93,26 @@ export function handleWakuMessage(
switch (decodedPayload.messageType) { switch (decodedPayload.messageType) {
case MessageType.COMMUNITY_CHAT: { case MessageType.COMMUNITY_CHAT: {
if (!community.isMember(signerPublicKey)) {
return
}
const chatUuid = getChatUuid(decodedPayload.chatId) const chatUuid = getChatUuid(decodedPayload.chatId)
const chat = community.chats.get(chatUuid)
if (!chat) {
return
}
// map // map
const chatMessage = mapChatMessage(decodedPayload, { const chatMessage = mapChatMessage(decodedPayload, {
messageId, messageId,
chatUuid, chatUuid,
signerPublicKey,
}) })
// handle // handle
community.chats.get(chatUuid)?.handleNewMessage(chatMessage) chat.handleNewMessage(chatMessage)
break break
} }
@ -115,12 +130,23 @@ export function handleWakuMessage(
switch (decodedPayload.messageType) { switch (decodedPayload.messageType) {
case MessageType.COMMUNITY_CHAT: { case MessageType.COMMUNITY_CHAT: {
if (!community.isMember(signerPublicKey)) {
return
}
const messageId = decodedPayload.messageId const messageId = decodedPayload.messageId
const chatUuid = getChatUuid(decodedPayload.chatId) const chatUuid = getChatUuid(decodedPayload.chatId)
const chat = community.chats.get(chatUuid)
community.chats if (!chat) {
.get(chatUuid) return
?.handleEditedMessage(messageId, decodedPayload.text) }
chat.handleEditedMessage(
messageId,
decodedPayload.text,
signerPublicKey
)
break break
} }
@ -138,10 +164,19 @@ export function handleWakuMessage(
switch (decodedPayload.messageType) { switch (decodedPayload.messageType) {
case MessageType.COMMUNITY_CHAT: { case MessageType.COMMUNITY_CHAT: {
if (!community.isMember(signerPublicKey)) {
return
}
const messageId = decodedPayload.messageId const messageId = decodedPayload.messageId
const chatUuid = getChatUuid(decodedPayload.chatId) 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 break
} }
@ -159,12 +194,19 @@ export function handleWakuMessage(
switch (decodedPayload.messageType) { switch (decodedPayload.messageType) {
case MessageType.COMMUNITY_CHAT: { case MessageType.COMMUNITY_CHAT: {
if (!community.isMember(signerPublicKey)) {
return
}
const messageId = decodedPayload.messageId const messageId = decodedPayload.messageId
const chatUuid = getChatUuid(decodedPayload.chatId) const chatUuid = getChatUuid(decodedPayload.chatId)
const chat = community.chats.get(chatUuid)
community.chats if (!chat) {
.get(chatUuid) return
?.handlePinnedMessage(messageId, decodedPayload.pinned) }
chat.handlePinnedMessage(messageId, decodedPayload.pinned)
break break
} }
@ -182,16 +224,19 @@ export function handleWakuMessage(
switch (decodedPayload.messageType) { switch (decodedPayload.messageType) {
case MessageType.COMMUNITY_CHAT: { case MessageType.COMMUNITY_CHAT: {
if (!community.isMember(signerPublicKey)) {
return
}
const messageId = decodedPayload.messageId const messageId = decodedPayload.messageId
const chatUuid = getChatUuid(decodedPayload.chatId) const chatUuid = getChatUuid(decodedPayload.chatId)
const chat = community.chats.get(chatUuid) const chat = community.chats.get(chatUuid)
chat?.handleEmojiReaction( if (!chat) {
messageId, return
decodedPayload, }
`0x${bytesToHex(publicKey)}`
) chat.handleEmojiReaction(messageId, decodedPayload, signerPublicKey)
break break
} }

View File

@ -6,17 +6,17 @@ export function mapChatMessage(
props: { props: {
messageId: string messageId: string
chatUuid: string chatUuid: string
publicKey: string signerPublicKey: string
} }
): ChatMessage { ): ChatMessage {
const { messageId, chatUuid, publicKey } = props const { messageId, chatUuid, signerPublicKey } = props
const message = { const message: ChatMessage = {
...decodedMessage, ...decodedMessage,
messageId, messageId,
chatUuid, chatUuid,
pinned: false, pinned: false,
signer: publicKey, signer: signerPublicKey,
reactions: { reactions: {
THUMBS_UP: new Set<string>(), THUMBS_UP: new Set<string>(),
THUMBS_DOWN: new Set<string>(), THUMBS_DOWN: new Set<string>(),