diff --git a/api/src/attachment/controllers/attachment.controller.spec.ts b/api/src/attachment/controllers/attachment.controller.spec.ts index 2aaf2a0f..e22622d3 100644 --- a/api/src/attachment/controllers/attachment.controller.spec.ts +++ b/api/src/attachment/controllers/attachment.controller.spec.ts @@ -137,6 +137,7 @@ describe('AttachmentController', () => { name: attachmentFile.originalname, location: expect.stringMatching(new RegExp(`^/${name}`)), context: 'block_attachment', + access: 'public', createdByRef: 'User', createdBy: '9'.repeat(24), }); diff --git a/api/src/attachment/controllers/attachment.controller.ts b/api/src/attachment/controllers/attachment.controller.ts index 11a88868..8286f31f 100644 --- a/api/src/attachment/controllers/attachment.controller.ts +++ b/api/src/attachment/controllers/attachment.controller.ts @@ -130,7 +130,7 @@ export class AttachmentController extends BaseController { async uploadFile( @UploadedFiles() files: { file: Express.Multer.File[] }, @Req() req: Request, - @Query() { context }: AttachmentContextParamDto, + @Query() { context, access = 'public' }: AttachmentContextParamDto, ): Promise { if (!files || !Array.isArray(files?.file) || files.file.length === 0) { throw new BadRequestException('No file was selected'); @@ -150,6 +150,7 @@ export class AttachmentController extends BaseController { size: file.size, type: file.mimetype, context, + access, createdBy: userId, createdByRef: 'User', }); diff --git a/api/src/attachment/dto/attachment.dto.ts b/api/src/attachment/dto/attachment.dto.ts index 14bdca30..cb91bbe5 100644 --- a/api/src/attachment/dto/attachment.dto.ts +++ b/api/src/attachment/dto/attachment.dto.ts @@ -24,8 +24,10 @@ import { ObjectIdDto } from '@/utils/dto/object-id.dto'; import { IsObjectId } from '@/utils/validation-rules/is-object-id'; import { + AttachmentAccess, AttachmentContext, AttachmentCreatedByRef, + TAttachmentAccess, TAttachmentContext, TAttachmentCreatedByRef, } from '../types'; @@ -67,7 +69,7 @@ export class AttachmentMetadataDto { /** * Attachment context */ - @ApiPropertyOptional({ + @ApiProperty({ description: 'Attachment Context', enum: Object.values(AttachmentContext), }) @@ -79,7 +81,7 @@ export class AttachmentMetadataDto { /** * Attachment Owner Type */ - @ApiPropertyOptional({ + @ApiProperty({ description: 'Attachment Owner Type', enum: Object.values(AttachmentCreatedByRef), }) @@ -88,12 +90,24 @@ export class AttachmentMetadataDto { @IsIn(Object.values(AttachmentCreatedByRef)) createdByRef: TAttachmentCreatedByRef; + /** + * Attachment Access + */ + @ApiProperty({ + description: 'Attachment Access', + enum: Object.values(AttachmentAccess), + }) + @IsString() + @IsNotEmpty() + @IsIn(Object.values(AttachmentAccess)) + access: TAttachmentAccess; + /** * Attachment Owner : Subscriber or User ID */ - @ApiPropertyOptional({ + @ApiProperty({ description: 'Attachment Owner : Subscriber / User ID', - enum: Object.values(AttachmentContext), + type: String, }) @IsString() @IsNotEmpty() @@ -126,7 +140,7 @@ export class AttachmentDownloadDto extends ObjectIdDto { } export class AttachmentContextParamDto { - @ApiPropertyOptional({ + @ApiProperty({ description: 'Attachment Context', enum: Object.values(AttachmentContext), }) @@ -134,4 +148,13 @@ export class AttachmentContextParamDto { @IsIn(Object.values(AttachmentContext)) @IsNotEmpty() context: TAttachmentContext; + + @ApiPropertyOptional({ + description: 'Attachment Access', + enum: Object.values(AttachmentAccess), + }) + @IsString() + @IsIn(Object.values(AttachmentAccess)) + @IsOptional() + access?: TAttachmentAccess; } diff --git a/api/src/attachment/mocks/attachment.mock.ts b/api/src/attachment/mocks/attachment.mock.ts index 112201a8..dde89f84 100644 --- a/api/src/attachment/mocks/attachment.mock.ts +++ b/api/src/attachment/mocks/attachment.mock.ts @@ -17,6 +17,7 @@ export const attachment: Attachment = { location: '/Screenshot from 2022-03-11 08-41-27-2a9799a8b6109c88fd9a7a690c1101934c.png', context: 'block_attachment', + access: 'public', id: '65940d115178607da65c82b6', createdAt: new Date(), updatedAt: new Date(), @@ -47,6 +48,7 @@ export const attachments: Attachment[] = [ '/app/src/attachment/uploads/Screenshot from 2022-03-11 08-41-27-2a9799a8b6109c88fd9a7a690c1101934c.png', channel: { ['some-channel']: {} }, context: 'block_attachment', + access: 'public', id: '65940d115178607da65c82b7', createdAt: new Date(), updatedAt: new Date(), @@ -61,6 +63,7 @@ export const attachments: Attachment[] = [ '/app/src/attachment/uploads/Screenshot from 2022-03-18 08-58-15-af61e7f71281f9fd3f1ad7ad10107741c.png', channel: { ['some-channel']: {} }, context: 'block_attachment', + access: 'public', id: '65940d115178607da65c82b8', createdAt: new Date(), updatedAt: new Date(), diff --git a/api/src/attachment/schemas/attachment.schema.ts b/api/src/attachment/schemas/attachment.schema.ts index 10d341c4..1a23f370 100644 --- a/api/src/attachment/schemas/attachment.schema.ts +++ b/api/src/attachment/schemas/attachment.schema.ts @@ -24,8 +24,10 @@ import { } from '@/utils/types/filter.types'; import { + AttachmentAccess, AttachmentContext, AttachmentCreatedByRef, + TAttachmentAccess, TAttachmentContext, TAttachmentCreatedByRef, } from '../types'; @@ -100,6 +102,12 @@ export class AttachmentStub extends BaseSchema { @Prop({ type: String, enum: Object.values(AttachmentContext) }) context: TAttachmentContext; + /** + * Context of the attachment + */ + @Prop({ type: String, enum: Object.values(AttachmentAccess) }) + access: TAttachmentAccess; + /** * Optional property representing the URL of the attachment. * diff --git a/api/src/attachment/types/index.ts b/api/src/attachment/types/index.ts index b2a136b3..cf3f0ce3 100644 --- a/api/src/attachment/types/index.ts +++ b/api/src/attachment/types/index.ts @@ -34,6 +34,13 @@ export enum AttachmentContext { export type TAttachmentContext = `${AttachmentContext}`; +export enum AttachmentAccess { + Public = 'public', + Private = 'private', +} + +export type TAttachmentAccess = `${AttachmentAccess}`; + export class AttachmentFile { /** * File original file name diff --git a/api/src/channel/lib/Handler.ts b/api/src/channel/lib/Handler.ts index 49604f43..53ccdefc 100644 --- a/api/src/channel/lib/Handler.ts +++ b/api/src/channel/lib/Handler.ts @@ -259,6 +259,7 @@ export default abstract class ChannelHandler< type, size, context: 'message_attachment', + access: 'private', createdByRef: 'Subscriber', createdBy: subscriber.id, }); diff --git a/api/src/channel/lib/__test__/common.mock.ts b/api/src/channel/lib/__test__/common.mock.ts index 34826cbe..e825cac4 100644 --- a/api/src/channel/lib/__test__/common.mock.ts +++ b/api/src/channel/lib/__test__/common.mock.ts @@ -89,6 +89,7 @@ const attachment: Attachment = { }, }, context: 'block_attachment', + access: 'public', createdByRef: 'User', createdBy: null, createdAt: new Date(), diff --git a/api/src/chat/services/chat.service.ts b/api/src/chat/services/chat.service.ts index 67c27774..72b63a1a 100644 --- a/api/src/chat/services/chat.service.ts +++ b/api/src/chat/services/chat.service.ts @@ -281,6 +281,7 @@ export class ChatService { size, type, context: 'subscriber_avatar', + access: 'private', createdByRef: 'Subscriber', createdBy: subscriber.id, }); diff --git a/api/src/extensions/channels/web/base-web-channel.ts b/api/src/extensions/channels/web/base-web-channel.ts index 851ce843..5cc56eb0 100644 --- a/api/src/extensions/channels/web/base-web-channel.ts +++ b/api/src/extensions/channels/web/base-web-channel.ts @@ -626,6 +626,7 @@ export default abstract class BaseWebChannelHandler< size: Buffer.byteLength(data.file), type: data.type, context: 'message_attachment', + access: 'private', createdByRef: 'Subscriber', createdBy: req.session?.web?.profile?.id, }); @@ -692,6 +693,7 @@ export default abstract class BaseWebChannelHandler< size: file.size, type: file.mimetype, context: 'message_attachment', + access: 'private', createdByRef: 'Subscriber', createdBy: req.session.web.profile?.id, }); diff --git a/api/src/migration/migrations/1735836154221-v-2-2-0.migration.ts b/api/src/migration/migrations/1735836154221-v-2-2-0.migration.ts index 4c14bb40..dfcebfa0 100644 --- a/api/src/migration/migrations/1735836154221-v-2-2-0.migration.ts +++ b/api/src/migration/migrations/1735836154221-v-2-2-0.migration.ts @@ -80,6 +80,7 @@ const populateBlockAttachments = async ({ logger }: MigrationServices) => { { $set: { context: AttachmentContext.BlockAttachment, + access: 'public', createdByRef: AttachmentCreatedByRef.User, createdBy: user._id, }, @@ -130,6 +131,7 @@ const populateSettingAttachments = async ({ logger }: MigrationServices) => { { $set: { context: AttachmentContext.SettingAttachment, + access: 'public', createdByRef: AttachmentCreatedByRef.User, createdBy: user._id, }, @@ -168,6 +170,7 @@ const populateUserAvatars = async ({ logger }: MigrationServices) => { { $set: { context: AttachmentContext.UserAvatar, + access: 'private', createdByRef: AttachmentCreatedByRef.User, createdBy: user._id, }, @@ -229,6 +232,7 @@ const populateSubscriberAvatars = async ({ logger }: MigrationServices) => { { $set: { context: AttachmentContext.SubscriberAvatar, + access: 'private', createdByRef: AttachmentCreatedByRef.Subscriber, createdBy: subscriber._id, }, @@ -361,6 +365,7 @@ const undoPopulateAttachments = async ({ logger }: MigrationServices) => { { $unset: { context: '', + access: '', createdByRef: '', createdBy: '', }, @@ -640,9 +645,10 @@ const migrateAndPopulateAttachmentMessages = async ({ await attachmentService.updateOne( msg.message.attachment.payload.attachment_id as string, { + context: 'message_attachment', + access: 'private', createdByRef: msg.sender ? 'Subscriber' : 'User', createdBy: msg.sender ? msg.sender : adminUser.id, - context: 'message_attachment', }, ); // Rename `attachment_id` to `id` @@ -672,9 +678,10 @@ const migrateAndPopulateAttachmentMessages = async ({ size: fileBuffer.length, type: response.headers['content-type'], channel: {}, + context: 'message_attachment', + access: msg.sender ? 'private' : 'public', createdBy: msg.sender ? msg.sender : adminUser.id, createdByRef: msg.sender ? 'Subscriber' : 'User', - context: 'message_attachment', }); if (attachment) { diff --git a/api/src/user/controllers/user.controller.ts b/api/src/user/controllers/user.controller.ts index 5c528450..a322ceff 100644 --- a/api/src/user/controllers/user.controller.ts +++ b/api/src/user/controllers/user.controller.ts @@ -295,6 +295,7 @@ export class ReadWriteUserController extends ReadOnlyUserController { size: avatarFile.size, type: avatarFile.mimetype, context: 'user_avatar', + access: 'private', createdByRef: 'User', createdBy: req.user.id, }) diff --git a/api/src/utils/test/fixtures/attachment.ts b/api/src/utils/test/fixtures/attachment.ts index 2bb0469e..cfbe046f 100644 --- a/api/src/utils/test/fixtures/attachment.ts +++ b/api/src/utils/test/fixtures/attachment.ts @@ -23,6 +23,7 @@ export const attachmentFixtures: AttachmentCreateDto[] = [ }, }, context: 'content_attachment', + access: 'public', createdByRef: 'User', createdBy: '9'.repeat(24), }, @@ -37,6 +38,7 @@ export const attachmentFixtures: AttachmentCreateDto[] = [ }, }, context: 'content_attachment', + access: 'public', createdByRef: 'User', createdBy: '9'.repeat(24), },