diff --git a/src/modules/file/file.controller.ts b/src/modules/file/file.controller.ts index 012dc7431..6dd1d1a82 100644 --- a/src/modules/file/file.controller.ts +++ b/src/modules/file/file.controller.ts @@ -10,6 +10,7 @@ import { Post, Put, Query, + UseGuards, } from '@nestjs/common'; import { ApiBearerAuth, @@ -31,6 +32,10 @@ import { UpdateFileMetaDto } from './dto/update-file-meta.dto'; import { ValidateUUIDPipe } from '../workspaces/pipes/validate-uuid.pipe'; import { WorkspacesInBehalfValidationFile } from '../workspaces/guards/workspaces-resources-in-behalf.decorator'; import { CreateFileDto } from './dto/create-file.dto'; +import { RequiredSharingPermissions } from '../sharing/guards/sharing-permissions.decorator'; +import { SharingActionName } from '../sharing/sharing.domain'; +import { BehalfUserDecorator } from '../sharing/decorators/behalfUser.decorator'; +import { SharingPermissionsGuard } from '../sharing/guards/sharing-permissions.guard'; const filesStatuses = ['ALL', 'EXISTS', 'TRASHED', 'DELETED'] as const; @@ -44,11 +49,14 @@ export class FileController { summary: 'Create File', }) @ApiBearerAuth() + @RequiredSharingPermissions(SharingActionName.UploadFile) + @UseGuards(SharingPermissionsGuard) async createFile( @UserDecorator() user: User, @Body() createFileDto: CreateFileDto, + @BehalfUserDecorator() behalfUser?: User, ) { - return this.fileUseCases.createFile(user, createFileDto); + return this.fileUseCases.createFile(behalfUser ?? user, createFileDto); } @Get('/count') @@ -146,6 +154,7 @@ export class FileController { required: true, description: 'file uuid', }) + @RequiredSharingPermissions(SharingActionName.RenameItems) @WorkspacesInBehalfValidationFile([ { sourceKey: 'params', fieldName: 'uuid', newFieldName: 'itemId' }, ]) diff --git a/src/modules/file/file.module.ts b/src/modules/file/file.module.ts index 0d71919b6..535c5e416 100644 --- a/src/modules/file/file.module.ts +++ b/src/modules/file/file.module.ts @@ -13,6 +13,7 @@ import { ThumbnailModule } from '../thumbnail/thumbnail.module'; import { FileModel } from './file.model'; import { SharingModule } from '../sharing/sharing.module'; import { WorkspacesModule } from '../workspaces/workspaces.module'; +import { UserModule } from '../user/user.module'; @Module({ imports: [ @@ -24,6 +25,7 @@ import { WorkspacesModule } from '../workspaces/workspaces.module'; forwardRef(() => WorkspacesModule), BridgeModule, CryptoModule, + UserModule, ], controllers: [FileController], providers: [SequelizeFileRepository, FileUseCases], diff --git a/src/modules/folder/folder.controller.ts b/src/modules/folder/folder.controller.ts index dbc56433b..12d691953 100644 --- a/src/modules/folder/folder.controller.ts +++ b/src/modules/folder/folder.controller.ts @@ -49,6 +49,9 @@ import { CreateFolderDto } from './dto/create-folder.dto'; import { CheckFoldersExistenceDto } from './dto/folder-existence-in-folder.dto'; import { InvalidParentFolderException } from './exception/invalid-parent-folder'; import { CheckFileExistenceInFolderDto } from './dto/files-existence-in-folder.dto'; +import { RequiredSharingPermissions } from '../sharing/guards/sharing-permissions.decorator'; +import { SharingActionName } from '../sharing/sharing.domain'; +import { BehalfUserDecorator } from '../sharing/decorators/behalfUser.decorator'; const foldersStatuses = ['ALL', 'EXISTS', 'TRASHED', 'DELETED'] as const; @@ -689,14 +692,16 @@ export class FolderController { @WorkspacesInBehalfValidationFolder([ { sourceKey: 'params', fieldName: 'uuid', newFieldName: 'itemId' }, ]) + @RequiredSharingPermissions(SharingActionName.RenameItems) async updateFolderMetadata( @Param('uuid', ValidateUUIDPipe) folderUuid: Folder['uuid'], @UserDecorator() user: User, @Body() updateFolderMetaDto: UpdateFolderMetaDto, + @BehalfUserDecorator() behalfUser?: User, ) { return this.folderUseCases.updateFolderMetaData( - user, + behalfUser ?? user, folderUuid, updateFolderMetaDto, ); diff --git a/src/modules/sharing/decorators/behalfUser.decorator.ts b/src/modules/sharing/decorators/behalfUser.decorator.ts index 908a68404..21d898dbd 100644 --- a/src/modules/sharing/decorators/behalfUser.decorator.ts +++ b/src/modules/sharing/decorators/behalfUser.decorator.ts @@ -4,6 +4,6 @@ export const BehalfUserDecorator = createParamDecorator( async (_, ctx: ExecutionContext) => { const req = ctx.switchToHttp().getRequest(); - return req.behalfUser; + return req?.behalfUser; }, ); diff --git a/src/modules/sharing/guards/sharing-permissions.guard.ts b/src/modules/sharing/guards/sharing-permissions.guard.ts index b2e3f9153..8f59adb5a 100644 --- a/src/modules/sharing/guards/sharing-permissions.guard.ts +++ b/src/modules/sharing/guards/sharing-permissions.guard.ts @@ -38,7 +38,6 @@ export class SharingPermissionsGuard implements CanActivate { PermissionsMetadataName, context.getHandler(), ); - const { action } = permissionsOptions; const request = context.switchToHttp().getRequest(); const requester = request?.user as User; @@ -49,10 +48,16 @@ export class SharingPermissionsGuard implements CanActivate { const resourcesToken = request.headers['internxt-resources-token']; - if (!resourcesToken || typeof resourcesToken !== 'string') { + if ( + !resourcesToken || + typeof resourcesToken !== 'string' || + !permissionsOptions + ) { return true; } + const { action } = permissionsOptions; + const decoded = verifyWithDefaultSecret(resourcesToken) as | { owner?: { @@ -100,7 +105,8 @@ export class SharingPermissionsGuard implements CanActivate { throw new NotFoundException('Resource owner not found'); } - request.behalfUser = resourceOwner; + request.user = resourceOwner; + request.isSharedItem = true; return true; } diff --git a/src/modules/trash/trash.module.ts b/src/modules/trash/trash.module.ts index 759944391..df19b2681 100644 --- a/src/modules/trash/trash.module.ts +++ b/src/modules/trash/trash.module.ts @@ -10,6 +10,7 @@ import { ShareModule } from '../share/share.module'; import { ShareModel } from '../share/share.repository'; import { FileModel } from '../file/file.model'; import { WorkspacesModule } from '../workspaces/workspaces.module'; +import { SharingModule } from '../sharing/sharing.module'; @Module({ imports: [ @@ -17,6 +18,7 @@ import { WorkspacesModule } from '../workspaces/workspaces.module'; forwardRef(() => FileModule), forwardRef(() => ShareModule), forwardRef(() => WorkspacesModule), + forwardRef(() => SharingModule), FolderModule, NotificationModule, UserModule, diff --git a/src/modules/workspaces/guards/workspaces-resources-in-behalf.decorator.ts b/src/modules/workspaces/guards/workspaces-resources-in-behalf.decorator.ts index ba20a1ba9..8f08f4e64 100644 --- a/src/modules/workspaces/guards/workspaces-resources-in-behalf.decorator.ts +++ b/src/modules/workspaces/guards/workspaces-resources-in-behalf.decorator.ts @@ -1,6 +1,7 @@ import { applyDecorators, UseGuards, SetMetadata } from '@nestjs/common'; import { WorkspacesResourcesItemsInBehalfGuard } from './workspaces-resources-in-items-in-behalf.guard'; import { WorkspaceItemType } from '../attributes/workspace-items-users.attributes'; +import { SharingPermissionsGuard } from '../../sharing/guards/sharing-permissions.guard'; export interface DataSource { sourceKey?: 'body' | 'query' | 'params' | 'headers'; @@ -39,7 +40,8 @@ const createValidationDecorator = ( return applyDecorators( SetMetadata(WORKSPACE_IN_BEHALF_SOURCES_META_KEY, dataSourcesWithOptions), SetMetadata(WORKSPACE_IN_BEHALF_ACTION_META_KEY, options?.action), - UseGuards(WorkspacesResourcesItemsInBehalfGuard), + // TODO: rename this guard or remove sharings from here + UseGuards(SharingPermissionsGuard, WorkspacesResourcesItemsInBehalfGuard), ); }; diff --git a/src/modules/workspaces/guards/workspaces-resources-in-items-in-behalf.guard.ts b/src/modules/workspaces/guards/workspaces-resources-in-items-in-behalf.guard.ts index 6361def18..b7b289787 100644 --- a/src/modules/workspaces/guards/workspaces-resources-in-items-in-behalf.guard.ts +++ b/src/modules/workspaces/guards/workspaces-resources-in-items-in-behalf.guard.ts @@ -88,7 +88,10 @@ export class WorkspacesResourcesItemsInBehalfGuard implements CanActivate { const actionHandler = this.getActionHandler(action); - const canUserPerformAction = await actionHandler(requester, extractedData); + const isSharedItem = request.isSharedItem; + const canUserPerformAction = !isSharedItem + ? await actionHandler(requester, extractedData) + : true; if (!canUserPerformAction) { throw new ForbiddenException(