From 71fb3a00e9afa313b00ff89a1fda672a649d9cf6 Mon Sep 17 00:00:00 2001 From: Omar Ezzat Date: Wed, 22 Nov 2023 14:15:13 +0100 Subject: [PATCH] move permission function into BaseService --- .../src/modules/board/service/base.service.ts | 83 +++++++++++++++++++ .../src/modules/board/service/card.service.ts | 25 +++--- .../modules/board/service/column.service.ts | 24 +++--- .../board/service/content-element.service.ts | 56 ++----------- .../board/service/submission-item.service.ts | 63 ++------------ 5 files changed, 122 insertions(+), 129 deletions(-) diff --git a/apps/server/src/modules/board/service/base.service.ts b/apps/server/src/modules/board/service/base.service.ts index 2692fe0cf7b..ce9978b6f52 100644 --- a/apps/server/src/modules/board/service/base.service.ts +++ b/apps/server/src/modules/board/service/base.service.ts @@ -1,5 +1,6 @@ import { AnyBoardDo, + AnyContentElementDo, Card, Column, ColumnBoard, @@ -34,6 +35,88 @@ export abstract class BaseService { protected readonly courseRepo: CourseRepo ) {} + protected async createBoardPermissionCtx( + referenceId: EntityId, + parentReferenceId: EntityId | null, + name?: string + ): Promise { + const parentContext = parentReferenceId + ? await this.permissionCtxRepo.findByContextReference(parentReferenceId) + : null; + + const permissionCtxEntity = new PermissionContextEntity({ + name, + parentContext, + contextReference: new ObjectId(referenceId), + }); + await this.permissionCtxRepo.save(permissionCtxEntity); + + return permissionCtxEntity; + } + + protected async pocCreateSubmissionItemPermissionCtx( + userId: EntityId, + submissionContainer: SubmissionContainerElement, + submissionItemId: EntityId + ) { + // NOTE: this will be simplified once we have user groups + const parentContext = await this.permissionCtxRepo.findByContextReference(submissionContainer.id); + + const rootId = (await this.boardDoRepo.getAncestorIds(submissionContainer))[0]; + const columnBoard = await this.boardDoRepo.findByClassAndId(ColumnBoard, rootId); + const course = await this.courseRepo.findById(columnBoard.context.id); + const revokeStudentsPermissions = course.students + .getItems() + .filter((student) => student.id !== userId) + .map((student) => { + return { + userId: student.id, + includedPermissions: [], + excludedPermissions: [PermissionCrud.UPDATE, PermissionCrud.DELETE], + }; + }); + + const permissionCtxEntity = new PermissionContextEntity({ + name: 'Element permission context', + parentContext, + contextReference: new ObjectId(submissionItemId), + userDelta: new UserDelta(revokeStudentsPermissions), + }); + await this.permissionCtxRepo.save(permissionCtxEntity); + } + + protected async pocCreateElementPermissionCtx(element: AnyContentElementDo, parent: Card | SubmissionItem) { + const parentContext = await this.permissionCtxRepo.findByContextReference(parent.id); + + if (element instanceof SubmissionContainerElement) { + // NOTE: this will be simplified once we have user groups + const rootId = (await this.boardDoRepo.getAncestorIds(parent))[0]; + const columnBoard = await this.boardDoRepo.findByClassAndId(ColumnBoard, rootId); + const course = await this.courseRepo.findById(columnBoard.context.id); + const updatedStudentsPermissions = course.students.getItems().map((student) => { + return { + userId: student.id, + includedPermissions: [PermissionCrud.CREATE], + excludedPermissions: [], + }; + }); + const permissionCtxEntity = new PermissionContextEntity({ + name: 'SubmissionContainerElement permission context', + parentContext, + contextReference: new ObjectId(element.id), + userDelta: new UserDelta(updatedStudentsPermissions), + }); + await this.permissionCtxRepo.save(permissionCtxEntity); + } else { + const permissionCtxEntity = new PermissionContextEntity({ + name: 'Element permission context', + parentContext, + contextReference: new ObjectId(element.id), + }); + await this.permissionCtxRepo.save(permissionCtxEntity); + } + } + // NOTE: this is a idempotent in-place migration for POC purposes protected async pocMigrateBoardToPermissionContext(id: EntityId) { const hasPermissionContext = await this.permissionCtxRepo diff --git a/apps/server/src/modules/board/service/card.service.ts b/apps/server/src/modules/board/service/card.service.ts index 0ec7afa9516..ba76ecc1e25 100644 --- a/apps/server/src/modules/board/service/card.service.ts +++ b/apps/server/src/modules/board/service/card.service.ts @@ -1,19 +1,23 @@ import { Injectable, NotFoundException } from '@nestjs/common'; -import { Card, Column, ContentElementType, EntityId, PermissionContextEntity } from '@shared/domain'; -import { PermissionContextRepo } from '@shared/repo'; +import { Card, Column, ContentElementType, EntityId } from '@shared/domain'; +import { CourseRepo, PermissionContextRepo } from '@shared/repo'; import { ObjectId } from 'bson'; import { BoardDoRepo } from '../repo'; import { BoardDoService } from './board-do.service'; import { ContentElementService } from './content-element.service'; +import { BaseService } from './base.service'; @Injectable() -export class CardService { +export class CardService extends BaseService { constructor( - private readonly boardDoRepo: BoardDoRepo, + protected readonly boardDoRepo: BoardDoRepo, private readonly boardDoService: BoardDoService, private readonly contentElementService: ContentElementService, - private readonly permissionCtxRepo: PermissionContextRepo - ) {} + protected readonly permissionCtxRepo: PermissionContextRepo, + protected readonly courseRepo: CourseRepo + ) { + super(permissionCtxRepo, boardDoRepo, courseRepo); + } async findById(cardId: EntityId): Promise { return this.boardDoRepo.findByClassAndId(Card, cardId); @@ -40,14 +44,7 @@ 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.createBoardPermissionCtx(card.id, parent.id, 'Card permission context'); await this.boardDoRepo.save(parent.children, parent); if (requiredEmptyElements) { diff --git a/apps/server/src/modules/board/service/column.service.ts b/apps/server/src/modules/board/service/column.service.ts index dbd24331e07..f0015007dc8 100644 --- a/apps/server/src/modules/board/service/column.service.ts +++ b/apps/server/src/modules/board/service/column.service.ts @@ -1,17 +1,21 @@ import { Injectable } from '@nestjs/common'; -import { Column, ColumnBoard, EntityId, PermissionContextEntity } from '@shared/domain'; +import { Column, ColumnBoard, EntityId } from '@shared/domain'; import { ObjectId } from 'bson'; -import { PermissionContextRepo } from '@shared/repo'; +import { CourseRepo, PermissionContextRepo } from '@shared/repo'; import { BoardDoRepo } from '../repo'; import { BoardDoService } from './board-do.service'; +import { BaseService } from './base.service'; @Injectable() -export class ColumnService { +export class ColumnService extends BaseService { constructor( - private readonly boardDoRepo: BoardDoRepo, + protected readonly boardDoRepo: BoardDoRepo, private readonly boardDoService: BoardDoService, - private readonly permissionCtxRepo: PermissionContextRepo - ) {} + protected readonly permissionCtxRepo: PermissionContextRepo, + protected readonly courseRepo: CourseRepo + ) { + super(permissionCtxRepo, boardDoRepo, courseRepo); + } async findById(columnId: EntityId): Promise { const column = await this.boardDoRepo.findByClassAndId(Column, columnId); @@ -29,13 +33,7 @@ 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.createBoardPermissionCtx(column.id, parent.id, 'Column permission context'); await this.boardDoRepo.save(parent.children, parent); 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 e6e01fb7a03..1143d78f743 100644 --- a/apps/server/src/modules/board/service/content-element.service.ts +++ b/apps/server/src/modules/board/service/content-element.service.ts @@ -3,33 +3,30 @@ import { AnyBoardDo, AnyContentElementDo, Card, - ColumnBoard, ContentElementFactory, ContentElementType, EntityId, isAnyContentElement, - PermissionContextEntity, - PermissionCrud, - SubmissionContainerElement, SubmissionItem, - UserDelta, } from '@shared/domain'; import { CourseRepo, PermissionContextRepo } from '@shared/repo'; -import { ObjectId } from 'bson'; import { AnyElementContentBody } from '../controller/dto'; import { BoardDoRepo } from '../repo'; import { BoardDoService } from './board-do.service'; import { ContentElementUpdateVisitor } from './content-element-update.visitor'; +import { BaseService } from './base.service'; @Injectable() -export class ContentElementService { +export class ContentElementService extends BaseService { constructor( - private readonly boardDoRepo: BoardDoRepo, + protected readonly boardDoRepo: BoardDoRepo, private readonly boardDoService: BoardDoService, private readonly contentElementFactory: ContentElementFactory, - private readonly permissionCtxRepo: PermissionContextRepo, - private readonly courseRepo: CourseRepo - ) {} + protected readonly permissionCtxRepo: PermissionContextRepo, + protected readonly courseRepo: CourseRepo + ) { + super(permissionCtxRepo, boardDoRepo, courseRepo); + } async findById(elementId: EntityId): Promise { const element = await this.boardDoRepo.findById(elementId); @@ -49,46 +46,11 @@ export class ContentElementService { return parent; } - async pocCreateElementPermissionCtx( - element: AnyContentElementDo, - parent: Card | SubmissionItem, - parentContext: PermissionContextEntity - ) { - if (element instanceof SubmissionContainerElement) { - // NOTE: this will be simplified once we have user groups - const rootId = (await this.boardDoRepo.getAncestorIds(parent))[0]; - const columnBoard = await this.boardDoRepo.findByClassAndId(ColumnBoard, rootId); - const course = await this.courseRepo.findById(columnBoard.context.id); - const updatedStudentsPermissions = course.students.getItems().map((student) => { - return { - userId: student.id, - includedPermissions: [PermissionCrud.CREATE], - excludedPermissions: [], - }; - }); - const permissionCtxEntity = new PermissionContextEntity({ - name: 'SubmissionContainerElement permission context', - parentContext, - contextReference: new ObjectId(element.id), - userDelta: new UserDelta(updatedStudentsPermissions), - }); - await this.permissionCtxRepo.save(permissionCtxEntity); - } else { - 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); - await this.pocCreateElementPermissionCtx(element, parent, parentContext); + await this.pocCreateElementPermissionCtx(element, parent); await this.boardDoRepo.save(parent.children, parent); return element; diff --git a/apps/server/src/modules/board/service/submission-item.service.ts b/apps/server/src/modules/board/service/submission-item.service.ts index 18640d742a3..ae06f7d1280 100644 --- a/apps/server/src/modules/board/service/submission-item.service.ts +++ b/apps/server/src/modules/board/service/submission-item.service.ts @@ -1,70 +1,23 @@ import { ObjectId } from 'bson'; -import { Injectable, NotFoundException, UnprocessableEntityException } from '@nestjs/common'; +import { Injectable, UnprocessableEntityException } from '@nestjs/common'; -import { - ColumnBoard, - EntityId, - isSubmissionContainerElement, - PermissionContextEntity, - PermissionCrud, - SubmissionContainerElement, - SubmissionItem, - UserDelta, -} from '@shared/domain'; +import { EntityId, isSubmissionContainerElement, SubmissionContainerElement, SubmissionItem } from '@shared/domain'; import { ValidationError } from '@shared/common'; import { CourseRepo, PermissionContextRepo } from '@shared/repo'; import { BoardDoRepo } from '../repo'; import { BoardDoService } from './board-do.service'; +import { BaseService } from './base.service'; @Injectable() -export class SubmissionItemService { +export class SubmissionItemService extends BaseService { constructor( - private readonly boardDoRepo: BoardDoRepo, + protected readonly boardDoRepo: BoardDoRepo, private readonly boardDoService: BoardDoService, - private readonly permissionCtxRepo: PermissionContextRepo, - private readonly courseRepo: CourseRepo - ) {} - - private async pocCreateSubmissionItemPermissionCtx( - userId: EntityId, - submissionContainer: SubmissionContainerElement, - submissionItemId: EntityId + protected readonly permissionCtxRepo: PermissionContextRepo, + protected readonly courseRepo: CourseRepo ) { - // NOTE: this will be simplified once we have user groups - const parentContext = await this.permissionCtxRepo.findByContextReference(submissionContainer.id); - - const rootId = (await this.boardDoRepo.getAncestorIds(submissionContainer))[0]; - const columnBoard = await this.boardDoRepo.findByClassAndId(ColumnBoard, rootId); - const course = await this.courseRepo.findById(columnBoard.context.id); - const revokeStudentsPermissions = course.students - .getItems() - .filter((student) => student.id !== userId) - .map((student) => { - return { - userId: student.id, - includedPermissions: [], - excludedPermissions: [PermissionCrud.UPDATE, PermissionCrud.DELETE], - }; - }); - - const permissionCtxEntity = new PermissionContextEntity({ - name: 'Element permission context', - parentContext, - contextReference: new ObjectId(submissionItemId), - userDelta: new UserDelta(revokeStudentsPermissions), - }); - await this.permissionCtxRepo.save(permissionCtxEntity); - } - - async findById(id: EntityId): Promise { - const element = await this.boardDoRepo.findById(id); - - if (!(element instanceof SubmissionItem)) { - throw new NotFoundException(`There is no '${element.constructor.name}' with this id`); - } - - return element; + super(permissionCtxRepo, boardDoRepo, courseRepo); } async create(