From 0973f752f2d3d6ce9c888caeb8db836d641e316c Mon Sep 17 00:00:00 2001 From: HayenNico <141850426+HayenNico@users.noreply.github.com> Date: Wed, 3 Jun 2026 19:38:34 +0200 Subject: [PATCH 1/3] transfer attachmets casl refactor to new branch --- src/attachments/attachments.v4.controller.ts | 55 ++-- src/casl/action.enum.ts | 21 +- src/casl/casl-ability.factory.ts | 248 ++++++------------- 3 files changed, 110 insertions(+), 214 deletions(-) diff --git a/src/attachments/attachments.v4.controller.ts b/src/attachments/attachments.v4.controller.ts index e223df9ba..560093e9e 100644 --- a/src/attachments/attachments.v4.controller.ts +++ b/src/attachments/attachments.v4.controller.ts @@ -109,27 +109,18 @@ export class AttachmentsV4Controller { this.generateAttachmentInstanceForPermissions(attachment); const user: JWTUser = request.user as JWTUser; - const ability = this.caslAbilityFactory.attachmentInstanceAccess(user); + const ability = this.caslAbilityFactory.attachmentAccess(user); try { switch (group) { - case Action.AttachmentCreateEndpoint: - return ability.can( - Action.AttachmentCreateInstance, - attachmentInstance, - ); - case Action.AttachmentReadEndpoint: - return ability.can(Action.AttachmentReadInstance, attachmentInstance); - case Action.AttachmentUpdateEndpoint: - return ability.can( - Action.AttachmentUpdateInstance, - attachmentInstance, - ); - case Action.AttachmentDeleteEndpoint: - return ability.can( - Action.AttachmentDeleteInstance, - attachmentInstance, - ); + case Action.AttachmentCreate: + return ability.can(Action.AttachmentCreate, attachmentInstance); + case Action.AttachmentRead: + return ability.can(Action.AttachmentRead, attachmentInstance); + case Action.AttachmentUpdate: + return ability.can(Action.AttachmentUpdate, attachmentInstance); + case Action.AttachmentDelete: + return ability.can(Action.AttachmentDelete, attachmentInstance); default: throw new InternalServerErrorException( "Permission for the action is not specified", @@ -147,7 +138,7 @@ export class AttachmentsV4Controller { if (!filter.where) { filter.where = {}; } - const ability = this.caslAbilityFactory.attachmentInstanceAccess(user); + const ability = this.caslAbilityFactory.attachmentAccess(user); const canAccessAny = ability.can(Action.AccessAny, Attachment); if (!canAccessAny) { @@ -215,7 +206,7 @@ export class AttachmentsV4Controller { // GET /attachments @UseGuards(PoliciesGuard) @CheckPolicies("attachments", (ability: AppAbility) => - ability.can(Action.AttachmentReadEndpoint, Attachment), + ability.can(Action.AttachmentRead, Attachment), ) @ApiOperation({ summary: "It returns a list of attachments.", @@ -309,7 +300,7 @@ export class AttachmentsV4Controller { // GET /attachments/:aid @UseGuards(PoliciesGuard) @CheckPolicies("attachments", (ability: AppAbility) => - ability.can(Action.AttachmentReadEndpoint, Attachment), + ability.can(Action.AttachmentRead, Attachment), ) @ApiOperation({ summary: "It returns the attachment requested.", @@ -334,7 +325,7 @@ export class AttachmentsV4Controller { await this.checkPermissionsForAttachment( request, aid, - Action.AttachmentReadEndpoint, + Action.AttachmentRead, ); return this.attachmentsService.findOne({ aid }); } @@ -342,7 +333,7 @@ export class AttachmentsV4Controller { // PATCH /attachments/:aid @UseGuards(PoliciesGuard) @CheckPolicies("attachments", (ability: AppAbility) => - ability.can(Action.AttachmentUpdateEndpoint, Attachment), + ability.can(Action.AttachmentUpdate, Attachment), ) @ApiOperation({ summary: "It updates the attachment.", @@ -378,7 +369,7 @@ Set \`content-type\` header to \`application/merge-patch+json\` if you would lik const foundAttachment = await this.checkPermissionsForAttachment( request, aid, - Action.AttachmentUpdateEndpoint, + Action.AttachmentUpdate, ); const updateAttachmentDtoForservice = request.headers["content-type"] === "application/merge-patch+json" @@ -396,7 +387,7 @@ Set \`content-type\` header to \`application/merge-patch+json\` if you would lik // PUT /attachments/:aid @UseGuards(PoliciesGuard) @CheckPolicies("attachments", (ability: AppAbility) => - ability.can(Action.AttachmentUpdateEndpoint, Attachment), + ability.can(Action.AttachmentUpdate, Attachment), ) @ApiOperation({ summary: "It updates the attachment.", @@ -428,7 +419,7 @@ Set \`content-type\` header to \`application/merge-patch+json\` if you would lik await this.checkPermissionsForAttachment( request, aid, - Action.AttachmentUpdateEndpoint, + Action.AttachmentUpdate, ); return this.attachmentsService.findOneAndReplace( { _id: aid }, @@ -439,7 +430,7 @@ Set \`content-type\` header to \`application/merge-patch+json\` if you would lik // POST /attachments @UseGuards(PoliciesGuard) @CheckPolicies("attachments", (ability: AppAbility) => - ability.can(Action.AttachmentCreateEndpoint, Attachment), + ability.can(Action.AttachmentCreate, Attachment), ) @ApiOperation({ summary: "It creates a new attachment.", @@ -463,14 +454,14 @@ Set \`content-type\` header to \`application/merge-patch+json\` if you would lik this.checkPermissionsForAttachmentCreate( request, createAttachmentDto, - Action.AttachmentCreateEndpoint, + Action.AttachmentCreate, ); return this.attachmentsService.create(createAttachmentDto); } @UseGuards(PoliciesGuard) @CheckPolicies("attachments", (ability: AppAbility) => - ability.can(Action.AttachmentCreateEndpoint, Attachment), + ability.can(Action.AttachmentCreate, Attachment), ) @Post("/isValid") @HttpCode(HttpStatus.OK) @@ -504,7 +495,7 @@ Set \`content-type\` header to \`application/merge-patch+json\` if you would lik this.checkPermissionsForAttachmentCreate( request, CreateAttachmentDtoInstance, - Action.AttachmentCreateEndpoint, + Action.AttachmentCreate, ); const errorsAttachment = await validate( CreateAttachmentDtoInstance, @@ -519,7 +510,7 @@ Set \`content-type\` header to \`application/merge-patch+json\` if you would lik // DELETE /attachments/:aid @UseGuards(PoliciesGuard) @CheckPolicies("attachments", (ability: AppAbility) => - ability.can(Action.AttachmentDeleteEndpoint, Attachment), + ability.can(Action.AttachmentDelete, Attachment), ) @ApiOperation({ summary: "It deletes the attachment.", @@ -542,7 +533,7 @@ Set \`content-type\` header to \`application/merge-patch+json\` if you would lik await this.checkPermissionsForAttachment( request, aid, - Action.AttachmentDeleteEndpoint, + Action.AttachmentDelete, ); return this.attachmentsService.findOneAndDelete({ aid }); } diff --git a/src/casl/action.enum.ts b/src/casl/action.enum.ts index c1e6f3baa..470119241 100644 --- a/src/casl/action.enum.ts +++ b/src/casl/action.enum.ts @@ -13,6 +13,12 @@ export enum Action { // Currently used by addAccessBasedFilters for admin/special group users AccessAny = "access_any", + // Attachments + AttachmentCreate = "attachment_create", + AttachmentRead = "attachment_read", + AttachmentUpdate = "attachment_update", + AttachmentDelete = "attachment_delete", + // --------------- // Datasets // endpoint authorization actions @@ -236,21 +242,6 @@ export enum Action { InstrumentCreate = "instrument_create", InstrumentDelete = "instrument_delete", - // ------------------------------------- - // Attachment - // ------------------------------------- - // attachment endpoint authorization - AttachmentCreateEndpoint = "attachment_create_endpoint", - AttachmentReadEndpoint = "attachment_read_endpoint", - AttachmentUpdateEndpoint = "attachment_update_endpoint", - AttachmentDeleteEndpoint = "attachment_delete_endpoint", - // ------------------------------------- - // attachment data instance authorization - AttachmentCreateInstance = "attachment_create_instance", - AttachmentReadInstance = "attachment_read_instance", - AttachmentUpdateInstance = "attachment_update_instance", - AttachmentDeleteInstance = "attachment_delete_instance", - // ------------------------------------- // History // ------------------------------------- diff --git a/src/casl/casl-ability.factory.ts b/src/casl/casl-ability.factory.ts index dc048de16..3b79ee0aa 100644 --- a/src/casl/casl-ability.factory.ts +++ b/src/casl/casl-ability.factory.ts @@ -46,7 +46,7 @@ export class CaslAbilityFactory { private endpointAccessors: { [endpoint: string]: (user: JWTUser) => AppAbility; } = { - attachments: this.attachmentEndpointAccess, + attachments: this.attachmentAccess, datablocks: this.datablockEndpointAccess, datasets: this.datasetEndpointAccess, history: this.historyEndpointAccess, @@ -74,6 +74,86 @@ export class CaslAbilityFactory { return accessFunction.call(this, user); } + attachmentAccess(user: JWTUser) { + const { can, build } = new AbilityBuilder( + createMongoAbility, + ); + const ifPublished = { isPublished: true }; + + /** + * Unauthenticated user + */ + can(Action.AttachmentRead, Attachment, ifPublished); + + if (!user) { + return build({ + detectSubjectType: (item) => + item.constructor as ExtractSubjectType, + }); + } + + const ifOwner = { ownerGroup: { $in: user.currentGroups } }; + const ifAccess = { accessGroups: { $in: user.currentGroups } }; + + /** + * Authenticated user + */ + can(Action.AttachmentRead, Attachment, ifOwner); + can(Action.AttachmentRead, Attachment, ifAccess); + can(Action.AttachmentRead, Attachment, ifPublished); + + if ( + user.currentGroups.some((g) => + this.accessGroups?.attachment.includes(g), + ) || + this.accessGroups?.attachment.includes("#all") + ) { + /** + * User belonging to ATTACHMENT_GROUPS + */ + can(Action.AttachmentCreate, Attachment, ifOwner); + can(Action.AttachmentUpdate, Attachment, ifOwner); + can(Action.AttachmentDelete, Attachment, ifOwner); + } + + if ( + user.currentGroups.some((g) => + this.accessGroups?.attachmentPrivileged.includes(g), + ) + ) { + /** + * User belonging to ATTACHMENT_PRIVILEGED_GROUPS + */ + can(Action.AttachmentCreate, Attachment); + can(Action.AttachmentUpdate, Attachment, ifOwner); + can(Action.AttachmentDelete, Attachment, ifOwner); + } + + if (user.currentGroups.some((g) => this.accessGroups?.admin.includes(g))) { + /** + * User belonging to ADMIN_GROUPS + */ + can(Action.AccessAny, Attachment); + + can(Action.AttachmentCreate, Attachment); + can(Action.AttachmentRead, Attachment); + can(Action.AttachmentUpdate, Attachment); + can(Action.AttachmentDelete, Attachment); + } + + if (user.currentGroups.some((g) => this.accessGroups?.delete.includes(g))) { + /** + * User belonging to DELETE_GROUPS + */ + can(Action.AttachmentDelete, Attachment); + } + + return build({ + detectSubjectType: (item) => + item.constructor as ExtractSubjectType, + }); + } + datasetEndpointAccess(user: JWTUser) { const { can, cannot, build } = new AbilityBuilder( createMongoAbility, @@ -365,67 +445,6 @@ export class CaslAbilityFactory { }); } - attachmentEndpointAccess(user: JWTUser) { - const { can, build } = new AbilityBuilder( - createMongoAbility, - ); - /* - * default allow anyone to read attachments - */ - can(Action.AttachmentReadEndpoint, Attachment); - - if (user) { - if ( - user.currentGroups.some((g) => this.accessGroups?.delete.includes(g)) - ) { - /* - * user that belongs to any of the group listed in DELETE_GROUPS - */ - - can(Action.AttachmentDeleteEndpoint, Attachment); - } - if ( - user.currentGroups.some((g) => this.accessGroups?.admin.includes(g)) - ) { - /** - * authenticated users belonging to any of the group listed in ADMIN_GROUPS - */ - - can(Action.AttachmentCreateEndpoint, Attachment); - can(Action.AttachmentUpdateEndpoint, Attachment); - can(Action.AttachmentDeleteEndpoint, Attachment); - } else if ( - user.currentGroups.some((g) => - this.accessGroups?.attachmentPrivileged.includes(g), - ) - ) { - // ------------------------------------- - // users belonging to any of the group listed in ATTACHMENT_PRIVILEGED_GROUPS - // - can(Action.AttachmentCreateEndpoint, Attachment); - can(Action.AttachmentUpdateEndpoint, Attachment); - can(Action.AttachmentDeleteEndpoint, Attachment); - } else if ( - user.currentGroups.some((g) => - this.accessGroups?.attachment.includes(g), - ) || - this.accessGroups?.attachment.includes("#all") - ) { - // ------------------------------------- - // users belonging to any of the group listed in ATTACHMENT_GROUPS - // - can(Action.AttachmentCreateEndpoint, Attachment); - can(Action.AttachmentUpdateEndpoint, Attachment); - can(Action.AttachmentDeleteEndpoint, Attachment); - } - } - - return build({ - detectSubjectType: (item) => - item.constructor as ExtractSubjectType, - }); - } - /** * Controls user access to the history endpoints based on role-based permissions. * @@ -2252,111 +2271,6 @@ export class CaslAbilityFactory { }); } - attachmentInstanceAccess(user: JWTUser) { - const { can, build } = new AbilityBuilder( - createMongoAbility, - ); - // ------------------------------------- - // any user can read public attachments - // ------------------------------------- - can(Action.AttachmentReadInstance, Attachment, { - isPublished: true, - }); - if (user) { - if ( - user.currentGroups.some((g) => this.accessGroups?.delete.includes(g)) - ) { - // ------------------------------------- - // users that belong to any of the group listed in DELETE_GROUPS - // ------------------------------------- - - can(Action.AttachmentDeleteInstance, Attachment); - } - - if ( - user.currentGroups.some((g) => this.accessGroups?.admin.includes(g)) - ) { - // ------------------------------------- - // users belonging to any of the group listed in ADMIN_GROUPS - // ------------------------------------- - - can(Action.AttachmentReadInstance, Attachment); - can(Action.AttachmentCreateInstance, Attachment); - can(Action.AttachmentUpdateInstance, Attachment); - can(Action.AttachmentDeleteInstance, Attachment); - - can(Action.AccessAny, Attachment); - } else if ( - user.currentGroups.some((g) => - this.accessGroups?.attachmentPrivileged.includes(g), - ) - ) { - // ------------------------------------- - // users belonging to any of the group listed in ATTACHMENT_PRIVILEGED_GROUPS - // - - can(Action.AttachmentCreateInstance, Attachment); - can(Action.AttachmentReadInstance, Attachment, { - ownerGroup: { $in: user.currentGroups }, - }); - can(Action.AttachmentReadInstance, Attachment, { - accessGroups: { $in: user.currentGroups }, - }); - - can(Action.AttachmentUpdateInstance, Attachment, { - ownerGroup: { $in: user.currentGroups }, - }); - can(Action.AttachmentDeleteInstance, Attachment, { - ownerGroup: { $in: user.currentGroups }, - }); - } else if ( - user.currentGroups.some((g) => - this.accessGroups?.attachment.includes(g), - ) || - this.accessGroups?.attachment.includes("#all") - ) { - // ------------------------------------- - // users belonging to any of the group listed in ATTACHMENT_GROUPS - // - - can(Action.AttachmentCreateInstance, Attachment, { - ownerGroup: { $in: user.currentGroups }, - }); - can(Action.AttachmentReadInstance, Attachment, { - ownerGroup: { $in: user.currentGroups }, - }); - can(Action.AttachmentReadInstance, Attachment, { - accessGroups: { $in: user.currentGroups }, - }); - can(Action.AttachmentReadInstance, Attachment, { - isPublished: true, - }); - can(Action.AttachmentUpdateInstance, Attachment, { - ownerGroup: { $in: user.currentGroups }, - }); - can(Action.AttachmentDeleteInstance, Attachment, { - ownerGroup: { $in: user.currentGroups }, - }); - } else { - // ------------------------------------- - // users with no elevated permissions - // ------------------------------------- - - can(Action.AttachmentReadInstance, Attachment, { - ownerGroup: { $in: user.currentGroups }, - }); - can(Action.AttachmentReadInstance, Attachment, { - accessGroups: { $in: user.currentGroups }, - }); - } - } - - return build({ - detectSubjectType: (item) => - item.constructor as ExtractSubjectType, - }); - } - publishedDataInstanceAccess(user: JWTUser) { const { can, build } = new AbilityBuilder( createMongoAbility, From 60c116d87e2febc06741f7428e7c740637b5633e Mon Sep 17 00:00:00 2001 From: HayenNico <141850426+HayenNico@users.noreply.github.com> Date: Wed, 3 Jun 2026 20:38:25 +0200 Subject: [PATCH 2/3] add handling for unauthenticated users --- src/attachments/attachments.v4.controller.ts | 30 +++++++++++++------- 1 file changed, 19 insertions(+), 11 deletions(-) diff --git a/src/attachments/attachments.v4.controller.ts b/src/attachments/attachments.v4.controller.ts index 560093e9e..4cf7c2d02 100644 --- a/src/attachments/attachments.v4.controller.ts +++ b/src/attachments/attachments.v4.controller.ts @@ -135,19 +135,27 @@ export class AttachmentsV4Controller { user: JWTUser, filter: IAttachmentFiltersV4, ): IAttachmentFiltersV4 { - if (!filter.where) { - filter.where = {}; - } const ability = this.caslAbilityFactory.attachmentAccess(user); - const canAccessAny = ability.can(Action.AccessAny, Attachment); + const canViewAny = ability.can(Action.AccessAny, Attachment); + const canView = ability.can(Action.AttachmentRead, Attachment); - if (!canAccessAny) { + filter.where = filter.where ?? {}; + + if (!user) { + if (filter.where["$and"]) { + filter.where["$and"].push({ + isPublished: true, + }); + } else { + filter.where["$and"] = [{ isPublished: true }]; + } + } else if (!canViewAny && canView) { if (filter.where["$and"]) { filter.where["$and"].push({ $or: [ - { ownerGroup: { $in: user?.currentGroups || [] } }, - { accessGroups: { $in: user?.currentGroups || [] } }, - { sharedWith: { $in: [user?.email || ""] } }, + { ownerGroup: { $in: user.currentGroups } }, + { accessGroups: { $in: user.currentGroups } }, + { sharedWith: { $in: [user.email] } }, { isPublished: true }, ], }); @@ -155,9 +163,9 @@ export class AttachmentsV4Controller { filter.where["$and"] = [ { $or: [ - { ownerGroup: { $in: user?.currentGroups || [] } }, - { accessGroups: { $in: user?.currentGroups || [] } }, - { sharedWith: { $in: [user?.email || ""] } }, + { ownerGroup: { $in: user.currentGroups } }, + { accessGroups: { $in: user.currentGroups } }, + { sharedWith: { $in: [user.email] } }, { isPublished: true }, ], }, From fdb3e3dfd03c1c8e10faf58392412d7952b2d6c8 Mon Sep 17 00:00:00 2001 From: HayenNico <141850426+HayenNico@users.noreply.github.com> Date: Wed, 3 Jun 2026 22:29:14 +0200 Subject: [PATCH 3/3] extract attachments casl ability in separate file --- src/casl/abilities/attachments.ability.ts | 106 ++++++++++++++++++++++ src/casl/casl-ability.factory.ts | 81 +---------------- src/casl/casl.module.ts | 4 +- 3 files changed, 112 insertions(+), 79 deletions(-) create mode 100644 src/casl/abilities/attachments.ability.ts diff --git a/src/casl/abilities/attachments.ability.ts b/src/casl/abilities/attachments.ability.ts new file mode 100644 index 000000000..5fcee353e --- /dev/null +++ b/src/casl/abilities/attachments.ability.ts @@ -0,0 +1,106 @@ +import { + AbilityBuilder, + ExtractSubjectType, + MongoAbility, + createMongoAbility, +} from "@casl/ability"; +import { Injectable } from "@nestjs/common"; +import { ConfigService } from "@nestjs/config"; +import { AccessGroupsType } from "src/config/configuration"; +import { Action } from "../action.enum"; +import { + Subjects, + PossibleAbilities, + Conditions, +} from "../types/casl-subjects"; +import { JWTUser } from "src/auth/interfaces/jwt-user.interface"; +import { Attachment } from "src/attachments/schemas/attachment.schema"; + +@Injectable() +export class AttachmentAbility { + constructor(private configService: ConfigService) { + this.accessGroups = + this.configService.get("accessGroups"); + } + private accessGroups; + + buildAbility(user: JWTUser): MongoAbility { + const { can, build } = new AbilityBuilder( + createMongoAbility, + ); + const ifPublished = { isPublished: true }; + + /** + * Unauthenticated user + */ + can(Action.AttachmentRead, Attachment, ifPublished); + + if (!user) { + return build({ + detectSubjectType: (item) => + item.constructor as ExtractSubjectType, + }); + } + + const ifOwner = { ownerGroup: { $in: user.currentGroups } }; + const ifAccess = { accessGroups: { $in: user.currentGroups } }; + + /** + * Authenticated user + */ + can(Action.AttachmentRead, Attachment, ifOwner); + can(Action.AttachmentRead, Attachment, ifAccess); + can(Action.AttachmentRead, Attachment, ifPublished); + + if ( + user.currentGroups.some((g) => + this.accessGroups?.attachment.includes(g), + ) || + this.accessGroups?.attachment.includes("#all") + ) { + /** + * User belonging to ATTACHMENT_GROUPS + */ + can(Action.AttachmentCreate, Attachment, ifOwner); + can(Action.AttachmentUpdate, Attachment, ifOwner); + can(Action.AttachmentDelete, Attachment, ifOwner); + } + + if ( + user.currentGroups.some((g) => + this.accessGroups?.attachmentPrivileged.includes(g), + ) + ) { + /** + * User belonging to ATTACHMENT_PRIVILEGED_GROUPS + */ + can(Action.AttachmentCreate, Attachment); + can(Action.AttachmentUpdate, Attachment, ifOwner); + can(Action.AttachmentDelete, Attachment, ifOwner); + } + + if (user.currentGroups.some((g) => this.accessGroups?.admin.includes(g))) { + /** + * User belonging to ADMIN_GROUPS + */ + can(Action.AccessAny, Attachment); + + can(Action.AttachmentCreate, Attachment); + can(Action.AttachmentRead, Attachment); + can(Action.AttachmentUpdate, Attachment); + can(Action.AttachmentDelete, Attachment); + } + + if (user.currentGroups.some((g) => this.accessGroups?.delete.includes(g))) { + /** + * User belonging to DELETE_GROUPS + */ + can(Action.AttachmentDelete, Attachment); + } + + return build({ + detectSubjectType: (item) => + item.constructor as ExtractSubjectType, + }); + } +} diff --git a/src/casl/casl-ability.factory.ts b/src/casl/casl-ability.factory.ts index 3b79ee0aa..28e26612a 100644 --- a/src/casl/casl-ability.factory.ts +++ b/src/casl/casl-ability.factory.ts @@ -10,7 +10,6 @@ import { ConfigService } from "@nestjs/config"; import { JobConfigService } from "src/config/job-config/jobconfig.service"; import { JWTUser } from "src/auth/interfaces/jwt-user.interface"; import { AccessGroupsType } from "src/config/configuration"; -import { Attachment } from "src/attachments/schemas/attachment.schema"; import { Datablock } from "src/datablocks/schemas/datablock.schema"; import { DatasetClass } from "src/datasets/schemas/dataset.schema"; import { Instrument } from "src/instruments/schemas/instrument.schema"; @@ -29,6 +28,7 @@ import { SampleClass } from "src/samples/schemas/sample.schema"; import { User } from "src/users/schemas/user.schema"; import { Action } from "./action.enum"; import { Subjects, PossibleAbilities, Conditions } from "./types/casl-subjects"; +import { AttachmentAbility } from "./abilities/attachments.ability"; export type AppAbility = MongoAbility; @@ -37,6 +37,7 @@ export class CaslAbilityFactory { constructor( private configService: ConfigService, private jobConfigService: JobConfigService, + private attachmentAbility: AttachmentAbility, ) { this.accessGroups = this.configService.get("accessGroups"); @@ -75,83 +76,7 @@ export class CaslAbilityFactory { } attachmentAccess(user: JWTUser) { - const { can, build } = new AbilityBuilder( - createMongoAbility, - ); - const ifPublished = { isPublished: true }; - - /** - * Unauthenticated user - */ - can(Action.AttachmentRead, Attachment, ifPublished); - - if (!user) { - return build({ - detectSubjectType: (item) => - item.constructor as ExtractSubjectType, - }); - } - - const ifOwner = { ownerGroup: { $in: user.currentGroups } }; - const ifAccess = { accessGroups: { $in: user.currentGroups } }; - - /** - * Authenticated user - */ - can(Action.AttachmentRead, Attachment, ifOwner); - can(Action.AttachmentRead, Attachment, ifAccess); - can(Action.AttachmentRead, Attachment, ifPublished); - - if ( - user.currentGroups.some((g) => - this.accessGroups?.attachment.includes(g), - ) || - this.accessGroups?.attachment.includes("#all") - ) { - /** - * User belonging to ATTACHMENT_GROUPS - */ - can(Action.AttachmentCreate, Attachment, ifOwner); - can(Action.AttachmentUpdate, Attachment, ifOwner); - can(Action.AttachmentDelete, Attachment, ifOwner); - } - - if ( - user.currentGroups.some((g) => - this.accessGroups?.attachmentPrivileged.includes(g), - ) - ) { - /** - * User belonging to ATTACHMENT_PRIVILEGED_GROUPS - */ - can(Action.AttachmentCreate, Attachment); - can(Action.AttachmentUpdate, Attachment, ifOwner); - can(Action.AttachmentDelete, Attachment, ifOwner); - } - - if (user.currentGroups.some((g) => this.accessGroups?.admin.includes(g))) { - /** - * User belonging to ADMIN_GROUPS - */ - can(Action.AccessAny, Attachment); - - can(Action.AttachmentCreate, Attachment); - can(Action.AttachmentRead, Attachment); - can(Action.AttachmentUpdate, Attachment); - can(Action.AttachmentDelete, Attachment); - } - - if (user.currentGroups.some((g) => this.accessGroups?.delete.includes(g))) { - /** - * User belonging to DELETE_GROUPS - */ - can(Action.AttachmentDelete, Attachment); - } - - return build({ - detectSubjectType: (item) => - item.constructor as ExtractSubjectType, - }); + return this.attachmentAbility.buildAbility(user); } datasetEndpointAccess(user: JWTUser) { diff --git a/src/casl/casl.module.ts b/src/casl/casl.module.ts index 3c8e1f2d5..e9334b24e 100644 --- a/src/casl/casl.module.ts +++ b/src/casl/casl.module.ts @@ -2,9 +2,11 @@ import { Module } from "@nestjs/common"; import { ConfigModule } from "@nestjs/config"; import { CaslAbilityFactory } from "./casl-ability.factory"; import { JobConfigModule } from "src/config/job-config/jobconfig.module"; +import { AttachmentAbility } from "./abilities/attachments.ability"; + @Module({ imports: [JobConfigModule, ConfigModule], - providers: [CaslAbilityFactory], + providers: [CaslAbilityFactory, AttachmentAbility], exports: [CaslAbilityFactory], }) export class CaslModule {}