Compare commits

...
9 changed files with 81 additions and 23 deletions
@@ -333,13 +333,10 @@ export function MessageController(app: HonoApp) {
statusCode: 204,
security: ['botToken', 'bearerToken', 'sessionToken'],
tags: ['Channels', 'Messages'],
description:
'Clears all read state and acknowledgement records for a channel, marking all messages as unread. Returns 204 No Content on success.',
deprecated: true,
description: 'Deprecated. Has no effect on the read state. Returns 204 No Content.',
}),
async (ctx) => {
const userId = ctx.get('user').id;
const channelId = createChannelID(ctx.req.valid('param').channel_id);
await ctx.get('readStateService').deleteReadState({userId, channelId});
return ctx.body(null, 204);
},
);
@@ -62,6 +62,8 @@ describe('DM Privacy Bidirectional Enforcement', () => {
const target = await createTestAccount(harness);
await ensureSessionStarted(harness, sender.token);
await ensureSessionStarted(harness, target.token);
await updateUserSettings(harness, sender.token, {default_guilds_restricted: false});
await updateUserSettings(harness, target.token, {default_guilds_restricted: false});
const guild = await createGuild(harness, sender.token, 'Mutual Community');
const systemChannel = await getChannel(harness, sender.token, guild.system_channel_id!);
const invite = await createChannelInvite(harness, sender.token, systemChannel.id);
@@ -80,6 +82,8 @@ describe('DM Privacy Bidirectional Enforcement', () => {
await ensureSessionStarted(harness, sender.token);
await ensureSessionStarted(harness, target.token);
await createFriendship(harness, sender, target);
await updateUserSettings(harness, sender.token, {default_guilds_restricted: false});
await updateUserSettings(harness, target.token, {default_guilds_restricted: false});
const guild = await createGuild(harness, sender.token, 'Verified Community');
await createBuilder(harness, '')
.post(`/test/guilds/${guild.id}/features`)
@@ -97,6 +101,25 @@ describe('DM Privacy Bidirectional Enforcement', () => {
.expect(HTTP_STATUS.OK)
.execute();
});
it('blocks message from a non-friend guild member to a new account by default', async () => {
const sender = await createTestAccount(harness);
const target = await createTestAccount(harness);
await ensureSessionStarted(harness, sender.token);
await ensureSessionStarted(harness, target.token);
await updateUserSettings(harness, sender.token, {default_guilds_restricted: false});
await createFriendship(harness, sender, target);
const guild = await createGuild(harness, sender.token, 'Default Community');
const systemChannel = await getChannel(harness, sender.token, guild.system_channel_id!);
const invite = await createChannelInvite(harness, sender.token, systemChannel.id);
await acceptInvite(harness, target.token, invite.code);
const channel = await createDmChannel(harness, sender.token, target.userId);
await removeRelationship(harness, sender.token, target.userId);
await createBuilder(harness, sender.token)
.post(`/channels/${channel.id}/messages`)
.body({content: 'default restricted target'})
.expect(HTTP_STATUS.BAD_REQUEST, 'CANNOT_SEND_MESSAGES_TO_USER')
.execute();
});
it('blocks message when sender restricts the only mutual guild', async () => {
const sender = await createTestAccount(harness);
const target = await createTestAccount(harness);
@@ -175,6 +198,8 @@ describe('DM Privacy Bidirectional Enforcement', () => {
const user2 = await createTestAccount(harness);
await ensureSessionStarted(harness, user1.token);
await ensureSessionStarted(harness, user2.token);
await updateUserSettings(harness, user1.token, {default_guilds_restricted: false});
await updateUserSettings(harness, user2.token, {default_guilds_restricted: false});
const guild = await createGuild(harness, user1.token, 'Shared Community');
const systemChannel = await getChannel(harness, user1.token, guild.system_channel_id!);
const invite = await createChannelInvite(harness, user1.token, systemChannel.id);
+1 -1
View File
@@ -216,7 +216,7 @@ export class UserSettings {
friend_source_flags: friendSourceFlags,
incoming_call_flags: IncomingCallFlags.FRIENDS_ONLY,
group_dm_add_permission_flags: GroupDmAddPermissionFlags.FRIENDS_ONLY,
default_guilds_restricted: false,
default_guilds_restricted: true,
bot_default_guilds_restricted: false,
restricted_guilds: new Set(),
bot_restricted_guilds: new Set(),
+2 -1
View File
@@ -3030,7 +3030,8 @@
"content": {"application/json": {"schema": {"$ref": "#/components/schemas/Error"}}}
}
},
"description": "Clears all read state and acknowledgement records for a channel, marking all messages as unread. Returns 204 No Content on success.",
"description": "Deprecated. Has no effect on the read state. Returns 204 No Content.",
"deprecated": true,
"security": [{"botToken": []}, {"sessionToken": []}],
"parameters": [
{
@@ -0,0 +1,48 @@
// SPDX-License-Identifier: AGPL-3.0-or-later
import {createTestAccount} from '@app/api/auth/tests/AuthTestUtils';
import {createChannel, createGuild} from '@app/api/guild/tests/GuildTestUtils';
import {sendMessage} from '@app/api/message/tests/MessageTestUtils';
import {type ApiTestHarness, createApiTestHarness} from '@app/api/test/ApiTestHarness';
import {HTTP_STATUS} from '@app/api/test/TestConstants';
import {createBuilder} from '@app/api/test/TestRequestBuilder';
import {afterEach, beforeEach, describe, expect, test} from 'vitest';
interface AckResponse {
read_states: Array<{
id: string;
last_message_id: string | null;
}>;
}
describe('DELETE /channels/:channel_id/messages/ack', () => {
let harness: ApiTestHarness;
beforeEach(async () => {
harness = await createApiTestHarness();
});
afterEach(async () => {
await harness?.shutdown();
});
test('leaves the read state untouched', async () => {
const account = await createTestAccount(harness);
const guild = await createGuild(harness, account.token, 'Read State Guild');
const channel = await createChannel(harness, account.token, guild.id, 'read-state-channel');
const older = await sendMessage(harness, account.token, channel.id, 'older');
const newer = await sendMessage(harness, account.token, channel.id, 'newer');
await createBuilder<AckResponse>(harness, account.token)
.post('/read-states/ack')
.body({read_states: [{channel_id: channel.id, message_id: newer.id}]})
.expect(HTTP_STATUS.OK)
.execute();
await createBuilder(harness, account.token)
.delete(`/channels/${channel.id}/messages/ack`)
.expect(HTTP_STATUS.NO_CONTENT)
.execute();
const response = await createBuilder<AckResponse>(harness, account.token)
.post('/read-states/ack')
.body({read_states: [{channel_id: channel.id, message_id: older.id}]})
.expect(HTTP_STATUS.OK)
.execute();
expect(response.read_states[0]?.last_message_id).toBe(newer.id);
});
});
@@ -35,8 +35,6 @@ export abstract class IReadStateRepository {
}>
>;
abstract deleteReadState(userId: UserID, channelId: ChannelID): Promise<void>;
abstract bulkAckMessages(
userId: UserID,
readStates: Array<{
@@ -4,7 +4,6 @@ import type {ChannelID, MessageID, UserID} from '@app/api/BrandedTypes';
import {channelIdToMessageId} from '@app/api/BrandedTypes';
import {
BatchBuilder,
deleteOneOrMany,
fetchMany,
fetchManyInChunks,
fetchOne,
@@ -199,15 +198,6 @@ export class ReadStateRepository implements IReadStateRepository {
return appliedUpdates;
}
async deleteReadState(userId: UserID, channelId: ChannelID): Promise<void> {
await deleteOneOrMany(
ReadStates.deleteByPk({
user_id: userId,
channel_id: channelId,
}),
);
}
async bulkAckMessages(
userId: UserID,
readStates: Array<{
@@ -141,10 +141,6 @@ export class ReadStateService {
}
}
async deleteReadState({userId, channelId}: {userId: UserID; channelId: ChannelID}): Promise<void> {
await this.repository.deleteReadState(userId, channelId);
}
async incrementMentionCount({
userId,
channelId,
@@ -9,6 +9,7 @@ import {
createGroupDmChannel,
createGuild,
getChannel,
updateUserSettings,
} from '@app/api/channel/tests/ChannelTestUtils';
import {ensureSessionStarted} from '@app/api/message/tests/MessageTestUtils';
import {type ApiTestHarness, createApiTestHarness} from '@app/api/test/ApiTestHarness';
@@ -39,6 +40,8 @@ describe('Voice Call Ringing', () => {
const user2 = await createTestAccount(harness);
await ensureSessionStarted(harness, user1.token);
await ensureSessionStarted(harness, user2.token);
await updateUserSettings(harness, user1.token, {default_guilds_restricted: false});
await updateUserSettings(harness, user2.token, {default_guilds_restricted: false});
const guild = await createGuild(harness, user1.token, 'Mutual Guild');
const invite = await createChannelInvite(harness, user1.token, guild.system_channel_id!);
await acceptInvite(harness, user2.token, invite.code);