fix: don't drop groupUpdateMessages from a blocked user

because we still need to process some things even if he is blocked
(getting promoted, etc).
If the message needs to be dropped, we should not have that group at
all, so that message will be dropped nevertheless
pull/3052/head
Audric Ackermann 11 months ago
parent 1aa9091026
commit 8ce5f6f429

@ -33,7 +33,6 @@ const Container = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
justify-content: center; justify-content: center;
padding: var(--margins-lg);
background-color: var(--background-secondary-color); background-color: var(--background-secondary-color);
`; `;

@ -1,5 +1,5 @@
import _, { difference } from 'lodash'; import _, { difference } from 'lodash';
import React, { useMemo } from 'react'; import React, { useMemo, useState } from 'react';
import { useDispatch } from 'react-redux'; import { useDispatch } from 'react-redux';
import useKey from 'react-use/lib/useKey'; import useKey from 'react-use/lib/useKey';
import styled from 'styled-components'; import styled from 'styled-components';
@ -31,6 +31,8 @@ import { groupInfoActions } from '../../state/ducks/metaGroups';
import { useMemberGroupChangePending } from '../../state/selectors/groups'; import { useMemberGroupChangePending } from '../../state/selectors/groups';
import { useSelectedIsGroupV2 } from '../../state/selectors/selectedConversation'; import { useSelectedIsGroupV2 } from '../../state/selectors/selectedConversation';
import { SessionSpinner } from '../basic/SessionSpinner'; import { SessionSpinner } from '../basic/SessionSpinner';
import { SessionToggle } from '../basic/SessionToggle';
import { isDevProd, isTestIntegration } from '../../shared/env_vars';
type Props = { type Props = {
conversationId: string; conversationId: string;
@ -193,6 +195,7 @@ export const UpdateGroupMembersDialog = (props: Props) => {
const displayName = useConversationUsername(conversationId); const displayName = useConversationUsername(conversationId);
const groupAdmins = useGroupAdmins(conversationId); const groupAdmins = useGroupAdmins(conversationId);
const isProcessingUIChange = useMemberGroupChangePending(); const isProcessingUIChange = useMemberGroupChangePending();
const [alsoRemoveMessages, setAlsoRemoveMessages] = useState(false);
const { const {
addTo, addTo,
@ -217,7 +220,7 @@ export const UpdateGroupMembersDialog = (props: Props) => {
addMembersWithHistory: [], addMembersWithHistory: [],
addMembersWithoutHistory: [], addMembersWithoutHistory: [],
removeMembers: difference(existingMembers, membersToKeepWithUpdate) as Array<PubkeyType>, removeMembers: difference(existingMembers, membersToKeepWithUpdate) as Array<PubkeyType>,
alsoRemoveMessages: false, // FIXME audric debugger we need this to be a toggle for QA alsoRemoveMessages,
}); });
dispatch(groupv2Action as any); dispatch(groupv2Action as any);
@ -272,6 +275,17 @@ export const UpdateGroupMembersDialog = (props: Props) => {
return ( return (
<SessionWrapperModal title={titleText} onClose={closeDialog}> <SessionWrapperModal title={titleText} onClose={closeDialog}>
{isDevProd() || isTestIntegration() ? (
<>
Also remove messages:
<SessionToggle
active={alsoRemoveMessages}
onClick={() => {
setAlsoRemoveMessages(!alsoRemoveMessages);
}}
/>
</>
) : null}
<StyledClassicMemberList className="group-member-list__selection"> <StyledClassicMemberList className="group-member-list__selection">
<ClassicMemberList <ClassicMemberList
convoId={conversationId} convoId={conversationId}

@ -2705,6 +2705,7 @@ async function commitConversationAndRefreshWrapper(id: string) {
const savedDetails = await Data.saveConversation(convo.attributes); const savedDetails = await Data.saveConversation(convo.attributes);
await convo.refreshInMemoryDetails(savedDetails); await convo.refreshInMemoryDetails(savedDetails);
// Performance impact on this is probably to be pretty bad. We might want to push for that DB refactor to be done sooner so we do not need to fetch info from the DB anymore // Performance impact on this is probably to be pretty bad. We might want to push for that DB refactor to be done sooner so we do not need to fetch info from the DB anymore
for (let index = 0; index < LibSessionUtil.requiredUserVariants.length; index++) { for (let index = 0; index < LibSessionUtil.requiredUserVariants.length; index++) {
const variant = LibSessionUtil.requiredUserVariants[index]; const variant = LibSessionUtil.requiredUserVariants[index];

@ -360,47 +360,53 @@ async function shouldDropIncomingPrivateMessage(
function shouldDropBlockedUserMessage( function shouldDropBlockedUserMessage(
content: SignalService.Content, content: SignalService.Content,
groupPubkey: string fromSwarmOf: string
): boolean { ): boolean {
// Even if the user is blocked, we should allow the message if: // Even if the user is blocked, we should allow a group control message message if:
// - it is a group message AND // - it is a group message AND
// - the group exists already on the db (to not join a closed group created by a blocked user) AND // - the group exists already on the db (to not join a closed group created by a blocked user) AND
// - the group is not blocked AND // - the group is not blocked AND
// - the message is only control (no body/attachments/quote/groupInvitation/contact/preview) // - the message is a LegacyControlMessage or GroupUpdateMessage
// In addition to the above, we also want to allow a groupUpdatePromote message (sent as a 1o1 message)
if (!groupPubkey) { if (!fromSwarmOf) {
return true; return true;
} }
const groupConvo = ConvoHub.use().get(groupPubkey); const convo = ConvoHub.use().get(fromSwarmOf);
if (!groupConvo || !groupConvo.isClosedGroup()) { if (!convo || !content.dataMessage || isEmpty(content.dataMessage)) {
// returning true means that we drop that message
return true; return true;
} }
if (groupConvo.isBlocked()) { if (convo.isClosedGroup() && convo.isBlocked()) {
// when we especially blocked a group, we don't want to process anything from it
return true; return true;
} }
// first check that dataMessage is the only field set in the Content const data = content.dataMessage as SignalService.DataMessage; // forcing it as we do know this field is set based on last line
let msgWithoutDataMessage = pickBy(
content,
(_value, key) => key !== 'dataMessage' && key !== 'toJSON'
);
msgWithoutDataMessage = pickBy(msgWithoutDataMessage, identity);
const isMessageDataMessageOnly = isEmpty(msgWithoutDataMessage); if (convo.isPrivate()) {
if (!isMessageDataMessageOnly) { const isGroupV2PromoteMessage = !isEmpty(
content.dataMessage?.groupUpdateMessage?.promoteMessage
);
if (isGroupV2PromoteMessage) {
// we want to allow a group v2 promote message sent by a blocked user (because that user is an admin of a group)
return false;
}
}
if (!convo.isClosedGroup()) {
// 1o1 messages are handled above.
// if we get here and it's not part a closed group, we should drop that message.
// it might be a message sent to a community from a user we've blocked
return true; return true;
} }
const data = content.dataMessage as SignalService.DataMessage; // forcing it as we do know this field is set based on last line const isLegacyGroupUpdateMessage = !isEmpty(data.closedGroupControlMessage);
const isControlDataMessageOnly =
!data.body && const isGroupV2UpdateMessage = !isEmpty(data.groupUpdateMessage);
!data.preview?.length &&
!data.attachments?.length && return !isLegacyGroupUpdateMessage && !isGroupV2UpdateMessage;
!data.openGroupInvitation &&
!data.quote;
return !isControlDataMessageOnly;
} }
async function dropIncomingGroupMessage(envelope: EnvelopePlus, sentAtTimestamp: number) { async function dropIncomingGroupMessage(envelope: EnvelopePlus, sentAtTimestamp: number) {
@ -466,10 +472,14 @@ export async function innerHandleSwarmContentMessage({
const envelopeSource = envelope.source; const envelopeSource = envelope.source;
// We want to allow a blocked user message if that's a control message for a known group and the group is not blocked // We want to allow a blocked user message if that's a control message for a known group and the group is not blocked
if (shouldDropBlockedUserMessage(content, envelopeSource)) { if (shouldDropBlockedUserMessage(content, envelopeSource)) {
window?.log?.info('Dropping blocked user message'); window?.log?.info(
`Dropping blocked user message ${ed25519Str(envelope.senderIdentity || envelope.source)}`
);
return; return;
} }
window?.log?.info('Allowing group-control message only from blocked user'); window?.log?.info(
`Allowing control/update message only from blocked user ${ed25519Str(envelope.senderIdentity)} in group: ${ed25519Str(envelope.source)}`
);
} }
if (await dropIncomingGroupMessage(envelope, sentAtTimestamp)) { if (await dropIncomingGroupMessage(envelope, sentAtTimestamp)) {
@ -513,12 +523,11 @@ export async function innerHandleSwarmContentMessage({
* For a private conversation message, this is just the conversation with that user * For a private conversation message, this is just the conversation with that user
*/ */
if (!isPrivateConversationMessage) { if (!isPrivateConversationMessage) {
console.info('conversationModelForUIUpdate might need to be checked for groupv2 case'); // debugger // this is a group message,
// this is a closed group message, we have a second conversation to make sure exists // we have a second conversation to make sure exists: the group conversation
conversationModelForUIUpdate = await ConvoHub.use().getOrCreateAndWait( conversationModelForUIUpdate = PubKey.is03Pubkey(envelope.source)
envelope.source, ? await ConvoHub.use().getOrCreateAndWait(envelope.source, ConversationTypeEnum.GROUPV2)
ConversationTypeEnum.GROUP : await ConvoHub.use().getOrCreateAndWait(envelope.source, ConversationTypeEnum.GROUP);
);
} }
const expireUpdate = await DisappearingMessages.checkForExpireUpdateInContentMessage( const expireUpdate = await DisappearingMessages.checkForExpireUpdateInContentMessage(

@ -356,7 +356,6 @@ async function handleGroupMemberLeftMessage({
}) })
); );
// TODO We should process this message type even if the sender is blocked
} }
async function handleGroupUpdateMemberLeftNotificationMessage({ async function handleGroupUpdateMemberLeftNotificationMessage({
@ -469,7 +468,6 @@ async function handleGroupDeleteMemberContentMessage({
) )
); );
convo.updateLastMessage(); convo.updateLastMessage();
// TODO we should process this message type even if the sender is blocked
} }
async function handleGroupUpdateInviteResponseMessage({ async function handleGroupUpdateInviteResponseMessage({
@ -494,7 +492,6 @@ async function handleGroupUpdateInviteResponseMessage({
window.inboxStore.dispatch(groupInfoActions.inviteResponseReceived({ groupPk, member: author })); window.inboxStore.dispatch(groupInfoActions.inviteResponseReceived({ groupPk, member: author }));
// TODO We should process this message type even if the sender is blocked
} }
async function handleGroupUpdatePromoteMessage({ async function handleGroupUpdatePromoteMessage({
@ -535,7 +532,6 @@ async function handleGroupUpdatePromoteMessage({
}) })
); );
// TODO we should process this even if the sender is blocked
} }
async function handle1o1GroupUpdateMessage( async function handle1o1GroupUpdateMessage(

@ -69,7 +69,6 @@ export class ClosedGroupVisibleMessage extends ClosedGroupMessage {
type WithDestinationGroupPk = { destination: GroupPubkeyType }; type WithDestinationGroupPk = { destination: GroupPubkeyType };
// TODO audric debugger This will need to extend ExpirableMessage after Disappearing Messages V2 is merged and checkd still working
export class ClosedGroupV2VisibleMessage extends DataMessage { export class ClosedGroupV2VisibleMessage extends DataMessage {
private readonly chatMessage: VisibleMessage; private readonly chatMessage: VisibleMessage;
public readonly destination: GroupPubkeyType; public readonly destination: GroupPubkeyType;

Loading…
Cancel
Save