diff --git a/apps/server/src/modules/board/service/card.service.ts b/apps/server/src/modules/board/service/card.service.ts index b7fee25c94f..0ec7afa9516 100644 --- a/apps/server/src/modules/board/service/card.service.ts +++ b/apps/server/src/modules/board/service/card.service.ts @@ -1,5 +1,6 @@ import { Injectable, NotFoundException } from '@nestjs/common'; -import { Card, Column, ContentElementType, EntityId } from '@shared/domain'; +import { Card, Column, ContentElementType, EntityId, PermissionContextEntity } from '@shared/domain'; +import { PermissionContextRepo } from '@shared/repo'; import { ObjectId } from 'bson'; import { BoardDoRepo } from '../repo'; import { BoardDoService } from './board-do.service'; @@ -10,7 +11,8 @@ export class CardService { constructor( private readonly boardDoRepo: BoardDoRepo, private readonly boardDoService: BoardDoService, - private readonly contentElementService: ContentElementService + private readonly contentElementService: ContentElementService, + private readonly permissionCtxRepo: PermissionContextRepo ) {} async findById(cardId: EntityId): Promise { @@ -38,6 +40,14 @@ export class CardService { parent.addChild(card); + const parentContext = await this.permissionCtxRepo.findByContextReference(parent.id); + const permissionCtxEntity = new PermissionContextEntity({ + name: 'Card permission context', + parentContext, + contextReference: new ObjectId(card.id), + }); + await this.permissionCtxRepo.save(permissionCtxEntity); + await this.boardDoRepo.save(parent.children, parent); if (requiredEmptyElements) { diff --git a/apps/server/src/modules/board/service/column-board.service.ts b/apps/server/src/modules/board/service/column-board.service.ts index d1c98be3053..0c244998036 100644 --- a/apps/server/src/modules/board/service/column-board.service.ts +++ b/apps/server/src/modules/board/service/column-board.service.ts @@ -38,6 +38,11 @@ const DEFAULT_TEACHER_PERMISSIONS = [ Permission.BOARD_CARD_CREATE, Permission.BOARD_CARD_MOVE, + Permission.BOARD_CARD_DELETE, + Permission.BOARD_CARD_UPDATE, + + Permission.BOARD_ELEMENT_CREATE, + Permission.BOARD_ELEMENT_MOVE, ]; const DEFAULT_SUBSTITUTE_TEACHER_PERMISSIONS = DEFAULT_TEACHER_PERMISSIONS; diff --git a/apps/server/src/modules/board/service/column.service.ts b/apps/server/src/modules/board/service/column.service.ts index 6da6dfa4c95..dbd24331e07 100644 --- a/apps/server/src/modules/board/service/column.service.ts +++ b/apps/server/src/modules/board/service/column.service.ts @@ -1,12 +1,17 @@ import { Injectable } from '@nestjs/common'; -import { Column, ColumnBoard, EntityId } from '@shared/domain'; +import { Column, ColumnBoard, EntityId, PermissionContextEntity } from '@shared/domain'; import { ObjectId } from 'bson'; +import { PermissionContextRepo } from '@shared/repo'; import { BoardDoRepo } from '../repo'; import { BoardDoService } from './board-do.service'; @Injectable() export class ColumnService { - constructor(private readonly boardDoRepo: BoardDoRepo, private readonly boardDoService: BoardDoService) {} + constructor( + private readonly boardDoRepo: BoardDoRepo, + private readonly boardDoService: BoardDoService, + private readonly permissionCtxRepo: PermissionContextRepo + ) {} async findById(columnId: EntityId): Promise { const column = await this.boardDoRepo.findByClassAndId(Column, columnId); @@ -24,6 +29,14 @@ export class ColumnService { parent.addChild(column); + const parentContext = await this.permissionCtxRepo.findByContextReference(parent.id); + const permissionCtxEntity = new PermissionContextEntity({ + name: 'Column permission context', + parentContext, + contextReference: new ObjectId(column.id), + }); + await this.permissionCtxRepo.save(permissionCtxEntity); + await this.boardDoRepo.save(parent.children, parent); return column; diff --git a/apps/server/src/modules/board/service/content-element.service.ts b/apps/server/src/modules/board/service/content-element.service.ts index 20f225e665b..6c6caca31a7 100644 --- a/apps/server/src/modules/board/service/content-element.service.ts +++ b/apps/server/src/modules/board/service/content-element.service.ts @@ -7,8 +7,11 @@ import { ContentElementType, EntityId, isAnyContentElement, + PermissionContextEntity, SubmissionItem, } from '@shared/domain'; +import { PermissionContextRepo } from '@shared/repo'; +import { ObjectId } from 'bson'; import { AnyElementContentBody } from '../controller/dto'; import { BoardDoRepo } from '../repo'; import { BoardDoService } from './board-do.service'; @@ -19,7 +22,8 @@ export class ContentElementService { constructor( private readonly boardDoRepo: BoardDoRepo, private readonly boardDoService: BoardDoService, - private readonly contentElementFactory: ContentElementFactory + private readonly contentElementFactory: ContentElementFactory, + private readonly permissionCtxRepo: PermissionContextRepo ) {} async findById(elementId: EntityId): Promise { @@ -40,9 +44,29 @@ export class ContentElementService { return parent; } + async pocCreateElementPermissionCtx(element: AnyContentElementDo, parentContext: PermissionContextEntity) { + const permissionCtxEntity = new PermissionContextEntity({ + name: 'Element permission context', + parentContext, + contextReference: new ObjectId(element.id), + }); + await this.permissionCtxRepo.save(permissionCtxEntity); + } + async create(parent: Card | SubmissionItem, type: ContentElementType): Promise { const element = this.contentElementFactory.build(type); parent.addChild(element); + + const parentContext = await this.permissionCtxRepo.findByContextReference(parent.id); + + const permissionCtxEntity = new PermissionContextEntity({ + name: 'Card permission context', + parentContext, + contextReference: new ObjectId(element.id), + }); + await this.pocCreateElementPermissionCtx(element, parentContext); + await this.permissionCtxRepo.save(permissionCtxEntity); + await this.boardDoRepo.save(parent.children, parent); return element; } diff --git a/apps/server/src/modules/board/uc/card.uc.ts b/apps/server/src/modules/board/uc/card.uc.ts index 7404e946843..de66925d310 100644 --- a/apps/server/src/modules/board/uc/card.uc.ts +++ b/apps/server/src/modules/board/uc/card.uc.ts @@ -1,5 +1,5 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { AnyBoardDo, AnyContentElementDo, Card, ContentElementType, EntityId } from '@shared/domain'; +import { AnyBoardDo, AnyContentElementDo, Card, ContentElementType, EntityId, Permission } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; import { AuthorizationService, Action, PermissionContextService } from '@modules/authorization'; import { BoardDoAuthorizableService, CardService, ContentElementService } from '../service'; @@ -24,7 +24,18 @@ export class CardUc extends BaseUc { this.logger.debug({ action: 'findCards', userId, cardIds }); const cards = await this.cardService.findByIds(cardIds); - const allowedCards = await this.filterAllowed(userId, cards, Action.read); + + const cardsWithPermission = await Promise.all( + cards.map(async (card) => { + const hasPermission = await this.pocHasPermission(userId, card.id, [Permission.BOARD_READ]); + return { card, hasPermission }; + }) + ); + + const allowedCards = cardsWithPermission + .filter((cardWithPermission) => cardWithPermission.hasPermission) + .map((cardWithPermission) => cardWithPermission.card); + // const allowedCards = await this.filterAllowed(userId, cards, Action.read); return allowedCards; } @@ -32,8 +43,10 @@ export class CardUc extends BaseUc { async updateCardHeight(userId: EntityId, cardId: EntityId, height: number): Promise { this.logger.debug({ action: 'updateCardHeight', userId, cardId, height }); + await this.pocCheckPermission(userId, cardId, [Permission.BOARD_CARD_UPDATE]); + const card = await this.cardService.findById(cardId); - await this.checkPermission(userId, card, Action.write); + // await this.checkPermission(userId, card, Action.write); await this.cardService.updateHeight(card, height); } @@ -41,8 +54,9 @@ export class CardUc extends BaseUc { async updateCardTitle(userId: EntityId, cardId: EntityId, title: string): Promise { this.logger.debug({ action: 'updateCardTitle', userId, cardId, title }); + await this.pocCheckPermission(userId, cardId, [Permission.BOARD_CARD_UPDATE]); const card = await this.cardService.findById(cardId); - await this.checkPermission(userId, card, Action.write); + // await this.checkPermission(userId, card, Action.write); await this.cardService.updateTitle(card, title); } @@ -50,8 +64,9 @@ export class CardUc extends BaseUc { async deleteCard(userId: EntityId, cardId: EntityId): Promise { this.logger.debug({ action: 'deleteCard', userId, cardId }); + await this.pocCheckPermission(userId, cardId, [Permission.BOARD_CARD_DELETE]); const card = await this.cardService.findById(cardId); - await this.checkPermission(userId, card, Action.write); + // await this.checkPermission(userId, card, Action.write); await this.cardService.delete(card); } @@ -66,8 +81,10 @@ export class CardUc extends BaseUc { ): Promise { this.logger.debug({ action: 'createElement', userId, cardId, type }); + await this.pocCheckPermission(userId, cardId, [Permission.BOARD_ELEMENT_CREATE]); + const card = await this.cardService.findById(cardId); - await this.checkPermission(userId, card, Action.write); + // await this.checkPermission(userId, card, Action.write); const element = await this.elementService.create(card, type); if (toPosition !== undefined && typeof toPosition === 'number') { @@ -85,11 +102,14 @@ export class CardUc extends BaseUc { ): Promise { this.logger.debug({ action: 'moveCard', userId, elementId, targetCardId, targetPosition }); + await this.pocCheckPermission(userId, elementId, [Permission.BOARD_ELEMENT_MOVE]); + await this.pocCheckPermission(userId, targetCardId, [Permission.BOARD_ELEMENT_MOVE]); + const element = await this.elementService.findById(elementId); const targetCard = await this.cardService.findById(targetCardId); - await this.checkPermission(userId, element, Action.write); - await this.checkPermission(userId, targetCard, Action.write); + // await this.checkPermission(userId, element, Action.write); + // await this.checkPermission(userId, targetCard, Action.write); await this.elementService.move(element, targetCard, targetPosition); } diff --git a/apps/server/src/shared/domain/entity/permission-context.entity.ts b/apps/server/src/shared/domain/entity/permission-context.entity.ts index 29b245c8e96..71d03a93b24 100644 --- a/apps/server/src/shared/domain/entity/permission-context.entity.ts +++ b/apps/server/src/shared/domain/entity/permission-context.entity.ts @@ -41,7 +41,7 @@ export class PermissionContextEntity extends BaseEntityWithTimestamps { @Property() userDelta: UserDelta; - @ManyToOne(() => PermissionContextEntity, { wrappedReference: true, fieldName: 'parentContext' }) + @ManyToOne(() => PermissionContextEntity, { wrappedReference: true, fieldName: 'parentContext', nullable: true }) @Index() _parentContext: Reference | null; diff --git a/apps/server/src/shared/domain/interface/permission.enum.ts b/apps/server/src/shared/domain/interface/permission.enum.ts index 74ea12194a5..507b15f1cf3 100644 --- a/apps/server/src/shared/domain/interface/permission.enum.ts +++ b/apps/server/src/shared/domain/interface/permission.enum.ts @@ -14,8 +14,13 @@ export enum Permission { BOARD_COLUMN_UPDATE_TITLE = 'BOARD_COLUMN_UPDATE_TITLE', BOARD_CARD_CREATE = 'BOARD_CARD_CREATE', + BOARD_CARD_UPDATE = 'BOARD_CARD_UPDATE', + BOARD_CARD_DELETE = 'BOARD_CARD_DELETE', BOARD_CARD_MOVE = 'BOARD_CARD_MOVE', + BOARD_ELEMENT_CREATE = 'BOARD_ELEMENT_CREATE', + BOARD_ELEMENT_MOVE = 'BOARD_ELEMENT_MOVE', + /** POC END: BOARD PERMISSIONS */ ACCOUNT_CREATE = 'ACCOUNT_CREATE', ACCOUNT_EDIT = 'ACCOUNT_EDIT', diff --git a/apps/server/src/shared/repo/permission-context/permission-context.repo.ts b/apps/server/src/shared/repo/permission-context/permission-context.repo.ts index a13416fc38c..d0fd632214d 100644 --- a/apps/server/src/shared/repo/permission-context/permission-context.repo.ts +++ b/apps/server/src/shared/repo/permission-context/permission-context.repo.ts @@ -1,6 +1,6 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { Injectable } from '@nestjs/common'; -import { EntityId, PermissionContextEntity } from '@shared/domain'; +import { EntityId, IPermissionContextProperties, PermissionContextEntity } from '@shared/domain'; import { BaseRepo } from '../base.repo'; // TODO: add test