From 1f90ed182448ab4a96d195eb3a025908136c9c66 Mon Sep 17 00:00:00 2001 From: mkreuzkam-cap <144103168+mkreuzkam-cap@users.noreply.github.com> Date: Wed, 18 Oct 2023 09:54:05 +0200 Subject: [PATCH 01/14] EW-553: Export description of etherpad content as title of weblink. (#4477) --- .../service/common-cartridge-export.service.ts | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts index 25077d84338..b41b6314fa8 100644 --- a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts +++ b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts @@ -115,8 +115,18 @@ export class CommonCartridgeExportService { if (content.component === ComponentType.ETHERPAD) { return version === CommonCartridgeVersion.V_1_3_0 - ? { ...commonProps, type: CommonCartridgeResourceType.WEB_LINK_V3, url: content.content.url } - : { ...commonProps, type: CommonCartridgeResourceType.WEB_LINK_V1, url: content.content.url }; + ? { + ...commonProps, + type: CommonCartridgeResourceType.WEB_LINK_V3, + url: content.content.url, + title: content.content.description, + } + : { + ...commonProps, + type: CommonCartridgeResourceType.WEB_LINK_V1, + url: content.content.url, + title: content.content.description, + }; } return undefined; From f2f0b368e636b997aaea4aa63157bd745c56fc7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20=C3=96hlerking?= <103562092+MarvinOehlerkingCap@users.noreply.github.com> Date: Thu, 19 Oct 2023 10:13:10 +0200 Subject: [PATCH 02/14] N21-1248 Configure External Tools in Boards (#4471) - move tool reference to context external tool module - new endpoint for tool reference from context external tool id - add new tool reference controller - add board-element as context for context external tools --- .../element/external-tool-element.response.ts | 6 +- .../external-tool-element-response.mapper.ts | 2 +- .../service/feathers-roster.service.spec.ts | 16 +- .../service/feathers-roster.service.ts | 6 +- .../common/enum/tool-context-type.enum.ts | 1 + .../tool/common/mapper/context-type.mapper.ts | 1 + .../mapper/tool-status-response.mapper.ts | 14 + .../context-external-tool.module.ts | 27 +- .../api-test/tool-reference.api.spec.ts | 287 ++++++++++++++++++ .../context-external-tool-context.params.ts | 8 +- .../controller/dto/index.ts | 3 + .../tool-configuration-status.response.ts | 0 .../dto}/tool-reference-list.response.ts | 0 .../dto}/tool-reference.response.ts | 1 + .../controller/tool-reference.controller.ts | 68 +++++ .../context-external-tool/domain/index.ts | 1 + .../domain/tool-reference.ts | 0 .../entity/context-external-tool-type.enum.ts | 1 + .../context-external-tool-response.mapper.ts | 25 +- .../context-external-tool/mapper/index.ts | 1 + .../mapper/tool-reference.mapper.ts | 4 +- ...t-external-tool-validation.service.spec.ts | 8 +- ...ontext-external-tool-validation.service.ts | 6 +- .../context-external-tool.service.spec.ts | 6 +- .../service/context-external-tool.service.ts | 4 +- .../context-external-tool/service/index.ts | 1 + .../service/tool-reference.service.spec.ts | 132 ++++++++ .../service/tool-reference.service.ts | 50 +++ .../uc/context-external-tool.uc.spec.ts | 10 +- .../uc/context-external-tool.uc.ts | 14 +- .../tool/context-external-tool/uc/index.ts | 1 + .../uc/tool-reference.uc.spec.ts | 215 +++++++++++++ .../uc/tool-reference.uc.ts | 82 +++++ .../controller/api-test/tool.api.spec.ts | 142 +-------- .../controller/dto/response/index.ts | 3 - .../controller/tool.controller.ts | 38 +-- .../tool/external-tool/domain/index.ts | 1 - .../mapper/external-tool-response.mapper.ts | 24 +- .../tool/external-tool/mapper/index.ts | 1 - .../external-tool-logo-service.spec.ts | 10 +- .../service/external-tool-logo.service.ts | 14 +- .../external-tool-validation.service.spec.ts | 12 +- .../external-tool-validation.service.ts | 4 +- .../service/external-tool.service.spec.ts | 8 +- .../service/external-tool.service.ts | 2 +- .../uc/external-tool-configuration.uc.spec.ts | 28 +- .../uc/external-tool-configuration.uc.ts | 16 +- .../external-tool/uc/external-tool.uc.spec.ts | 4 +- .../tool/external-tool/uc/external-tool.uc.ts | 4 +- .../modules/tool/external-tool/uc/index.ts | 1 - .../uc/tool-reference.uc.spec.ts | 234 -------------- .../external-tool/uc/tool-reference.uc.ts | 102 ------- .../api-test/tool-school.api.spec.ts | 6 +- .../dto/school-external-tool.response.ts | 2 +- ...hool-external-tool-response.mapper.spec.ts | 2 +- .../school-external-tool-response.mapper.ts | 12 +- ...l-external-tool-validation.service.spec.ts | 4 +- ...school-external-tool-validation.service.ts | 4 +- .../school-external-tool.service.spec.ts | 20 +- .../service/school-external-tool.service.ts | 12 +- .../uc/school-external-tool.uc.spec.ts | 12 +- .../uc/school-external-tool.uc.ts | 14 +- .../src/modules/tool/tool-api.module.ts | 10 +- .../strategy/abstract-launch.strategy.spec.ts | 151 +++++++-- .../strategy/abstract-launch.strategy.ts | 19 +- .../lti11-tool-launch.strategy.spec.ts | 4 +- .../strategy/lti11-tool-launch.strategy.ts | 2 +- .../service/tool-launch.service.spec.ts | 32 +- .../service/tool-launch.service.ts | 20 +- .../tool-launch/uc/tool-launch.uc.spec.ts | 10 +- .../tool/tool-launch/uc/tool-launch.uc.ts | 8 +- ...ext-external-tool.repo.integration.spec.ts | 34 ++- .../context-external-tool.repo.ts | 4 + 73 files changed, 1246 insertions(+), 785 deletions(-) create mode 100644 apps/server/src/modules/tool/common/mapper/tool-status-response.mapper.ts create mode 100644 apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-reference.api.spec.ts rename apps/server/src/modules/tool/{external-tool/controller/dto/response => context-external-tool/controller/dto}/tool-configuration-status.response.ts (100%) rename apps/server/src/modules/tool/{external-tool/controller/dto/response => context-external-tool/controller/dto}/tool-reference-list.response.ts (100%) rename apps/server/src/modules/tool/{external-tool/controller/dto/response => context-external-tool/controller/dto}/tool-reference.response.ts (96%) create mode 100644 apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts rename apps/server/src/modules/tool/{external-tool => context-external-tool}/domain/tool-reference.ts (100%) rename apps/server/src/modules/tool/{external-tool => context-external-tool}/mapper/tool-reference.mapper.ts (80%) create mode 100644 apps/server/src/modules/tool/context-external-tool/service/tool-reference.service.spec.ts create mode 100644 apps/server/src/modules/tool/context-external-tool/service/tool-reference.service.ts create mode 100644 apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.spec.ts create mode 100644 apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.ts delete mode 100644 apps/server/src/modules/tool/external-tool/uc/tool-reference.uc.spec.ts delete mode 100644 apps/server/src/modules/tool/external-tool/uc/tool-reference.uc.ts diff --git a/apps/server/src/modules/board/controller/dto/element/external-tool-element.response.ts b/apps/server/src/modules/board/controller/dto/element/external-tool-element.response.ts index 5f51a1a26ec..fc67b7631b6 100644 --- a/apps/server/src/modules/board/controller/dto/element/external-tool-element.response.ts +++ b/apps/server/src/modules/board/controller/dto/element/external-tool-element.response.ts @@ -1,4 +1,4 @@ -import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { ApiProperty } from '@nestjs/swagger'; import { ContentElementType } from '@shared/domain'; import { TimestampsResponse } from '../timestamps.response'; @@ -7,8 +7,8 @@ export class ExternalToolElementContent { this.contextExternalToolId = props.contextExternalToolId; } - @ApiPropertyOptional() - contextExternalToolId?: string; + @ApiProperty({ type: String, required: true, nullable: true }) + contextExternalToolId: string | null; } export class ExternalToolElementResponse { diff --git a/apps/server/src/modules/board/controller/mapper/external-tool-element-response.mapper.ts b/apps/server/src/modules/board/controller/mapper/external-tool-element-response.mapper.ts index a907f4eb157..a27cab41d63 100644 --- a/apps/server/src/modules/board/controller/mapper/external-tool-element-response.mapper.ts +++ b/apps/server/src/modules/board/controller/mapper/external-tool-element-response.mapper.ts @@ -18,7 +18,7 @@ export class ExternalToolElementResponseMapper implements BaseResponseMapper { id: element.id, timestamps: new TimestampsResponse({ lastUpdatedAt: element.updatedAt, createdAt: element.createdAt }), type: ContentElementType.EXTERNAL_TOOL, - content: new ExternalToolElementContent({ contextExternalToolId: element.contextExternalToolId }), + content: new ExternalToolElementContent({ contextExternalToolId: element.contextExternalToolId ?? null }), }); return result; diff --git a/apps/server/src/modules/pseudonym/service/feathers-roster.service.spec.ts b/apps/server/src/modules/pseudonym/service/feathers-roster.service.spec.ts index ce4d5144a38..e2a8484d630 100644 --- a/apps/server/src/modules/pseudonym/service/feathers-roster.service.spec.ts +++ b/apps/server/src/modules/pseudonym/service/feathers-roster.service.spec.ts @@ -2,13 +2,13 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { DatabaseObjectNotFoundException } from '@mikro-orm/core'; import { Test, TestingModule } from '@nestjs/testing'; import { NotFoundLoggableException } from '@shared/common/loggable-exception'; -import { Course, Pseudonym, RoleName, LegacySchoolDo, UserDO, SchoolEntity } from '@shared/domain'; +import { Course, LegacySchoolDo, Pseudonym, RoleName, SchoolEntity, UserDO } from '@shared/domain'; import { contextExternalToolFactory, courseFactory, externalToolFactory, - pseudonymFactory, legacySchoolDoFactory, + pseudonymFactory, schoolExternalToolFactory, schoolFactory, setupEntities, @@ -249,10 +249,10 @@ describe('FeathersRosterService', () => { ]); contextExternalToolService.findAllByContext.mockResolvedValueOnce([otherContextExternalTool]); contextExternalToolService.findAllByContext.mockResolvedValueOnce([]); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValueOnce(schoolExternalTool); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValueOnce(otherSchoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValueOnce(externalTool); - externalToolService.findExternalToolById.mockResolvedValueOnce(otherExternalTool); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + schoolExternalToolService.findById.mockResolvedValueOnce(otherSchoolExternalTool); + externalToolService.findById.mockResolvedValueOnce(externalTool); + externalToolService.findById.mockResolvedValueOnce(otherExternalTool); return { pseudonym, @@ -299,7 +299,7 @@ describe('FeathersRosterService', () => { await service.getUserGroups(pseudonym.pseudonym, clientId); - expect(schoolExternalToolService.getSchoolExternalToolById.mock.calls).toEqual([ + expect(schoolExternalToolService.findById.mock.calls).toEqual([ [schoolExternalTool.id], [otherSchoolExternalTool.id], ]); @@ -310,7 +310,7 @@ describe('FeathersRosterService', () => { await service.getUserGroups(pseudonym.pseudonym, clientId); - expect(externalToolService.findExternalToolById.mock.calls).toEqual([[externalToolId], [otherExternalTool.id]]); + expect(externalToolService.findById.mock.calls).toEqual([[externalToolId], [otherExternalTool.id]]); }); it('should return a group for each course where the tool of the users pseudonym is used', async () => { diff --git a/apps/server/src/modules/pseudonym/service/feathers-roster.service.ts b/apps/server/src/modules/pseudonym/service/feathers-roster.service.ts index a5fd359b6c1..e808a2fc59f 100644 --- a/apps/server/src/modules/pseudonym/service/feathers-roster.service.ts +++ b/apps/server/src/modules/pseudonym/service/feathers-roster.service.ts @@ -179,12 +179,10 @@ export class FeathersRosterService { ); for await (const contextExternalTool of contextExternalTools) { - const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.getSchoolExternalToolById( + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( contextExternalTool.schoolToolRef.schoolToolId ); - const externalTool: ExternalTool = await this.externalToolService.findExternalToolById( - schoolExternalTool.toolId - ); + const externalTool: ExternalTool = await this.externalToolService.findById(schoolExternalTool.toolId); const isRequiredTool: boolean = externalTool.id === externalToolId; if (isRequiredTool) { diff --git a/apps/server/src/modules/tool/common/enum/tool-context-type.enum.ts b/apps/server/src/modules/tool/common/enum/tool-context-type.enum.ts index 20e57d7bd60..4c930b57397 100644 --- a/apps/server/src/modules/tool/common/enum/tool-context-type.enum.ts +++ b/apps/server/src/modules/tool/common/enum/tool-context-type.enum.ts @@ -1,3 +1,4 @@ export enum ToolContextType { COURSE = 'course', + BOARD_ELEMENT = 'board-element', } diff --git a/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts b/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts index 00da0a8b36d..bf8fb537924 100644 --- a/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts +++ b/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts @@ -3,6 +3,7 @@ import { ToolContextType } from '../enum'; const typeMapping: Record = { [ToolContextType.COURSE]: AuthorizableReferenceType.Course, + [ToolContextType.BOARD_ELEMENT]: AuthorizableReferenceType.BoardNode, }; export class ContextTypeMapper { diff --git a/apps/server/src/modules/tool/common/mapper/tool-status-response.mapper.ts b/apps/server/src/modules/tool/common/mapper/tool-status-response.mapper.ts new file mode 100644 index 00000000000..c199fc6f307 --- /dev/null +++ b/apps/server/src/modules/tool/common/mapper/tool-status-response.mapper.ts @@ -0,0 +1,14 @@ +import { ToolConfigurationStatusResponse } from '../../context-external-tool/controller/dto/tool-configuration-status.response'; +import { ToolConfigurationStatus } from '../enum'; + +export const statusMapping: Record = { + [ToolConfigurationStatus.LATEST]: ToolConfigurationStatusResponse.LATEST, + [ToolConfigurationStatus.OUTDATED]: ToolConfigurationStatusResponse.OUTDATED, + [ToolConfigurationStatus.UNKNOWN]: ToolConfigurationStatusResponse.UNKNOWN, +}; + +export class ToolStatusResponseMapper { + static mapToResponse(status: ToolConfigurationStatus): ToolConfigurationStatusResponse { + return statusMapping[status]; + } +} diff --git a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts index e3319512fc5..1afd639f1e7 100644 --- a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts +++ b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts @@ -1,25 +1,28 @@ -import { forwardRef, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { CommonToolModule } from '../common'; import { ExternalToolModule } from '../external-tool'; import { SchoolExternalToolModule } from '../school-external-tool'; import { ContextExternalToolAuthorizableService, ContextExternalToolService, ContextExternalToolValidationService, + ToolReferenceService, } from './service'; -import { CommonToolModule } from '../common'; @Module({ - // TODO: remove authorization module here N21-1055 - imports: [ - CommonToolModule, - ExternalToolModule, - SchoolExternalToolModule, - LoggerModule, - forwardRef(() => AuthorizationModule), + imports: [CommonToolModule, ExternalToolModule, SchoolExternalToolModule, LoggerModule], + providers: [ + ContextExternalToolService, + ContextExternalToolValidationService, + ContextExternalToolAuthorizableService, + ToolReferenceService, + ], + exports: [ + ContextExternalToolService, + ContextExternalToolValidationService, + ContextExternalToolAuthorizableService, + ToolReferenceService, ], - providers: [ContextExternalToolService, ContextExternalToolValidationService, ContextExternalToolAuthorizableService], - exports: [ContextExternalToolService, ContextExternalToolValidationService, ContextExternalToolAuthorizableService], }) export class ContextExternalToolModule {} diff --git a/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-reference.api.spec.ts b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-reference.api.spec.ts new file mode 100644 index 00000000000..f3072a95131 --- /dev/null +++ b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-reference.api.spec.ts @@ -0,0 +1,287 @@ +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; +import { HttpStatus, INestApplication } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { Course, Permission, SchoolEntity } from '@shared/domain'; +import { + cleanupCollections, + contextExternalToolEntityFactory, + courseFactory, + externalToolEntityFactory, + schoolExternalToolEntityFactory, + schoolFactory, + TestApiClient, + UserAndAccountTestFactory, +} from '@shared/testing'; +import { ServerTestModule } from '@src/modules/server'; +import { Response } from 'supertest'; +import { ToolContextType } from '../../../common/enum'; +import { ExternalToolEntity } from '../../../external-tool/entity'; +import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; +import { ContextExternalToolEntity, ContextExternalToolType } from '../../entity'; +import { ContextExternalToolContextParams, ToolReferenceListResponse, ToolReferenceResponse } from '../dto'; +import { ToolConfigurationStatusResponse } from '../dto/tool-configuration-status.response'; + +describe('ToolReferenceController (API)', () => { + let app: INestApplication; + let em: EntityManager; + + let testApiClient: TestApiClient; + + beforeAll(async () => { + const moduleRef: TestingModule = await Test.createTestingModule({ + imports: [ServerTestModule], + }).compile(); + + app = moduleRef.createNestApplication(); + + await app.init(); + + em = app.get(EntityManager); + testApiClient = new TestApiClient(app, 'tools/tool-references'); + }); + + afterAll(async () => { + await app.close(); + }); + + afterEach(async () => { + await cleanupCollections(em); + }); + + describe('[GET] tools/tool-references/:contextType/:contextId', () => { + describe('when user is not authenticated', () => { + it('should return unauthorized', async () => { + const response: Response = await testApiClient.get(`contextType/${new ObjectId().toHexString()}`); + + expect(response.statusCode).toEqual(HttpStatus.UNAUTHORIZED); + }); + }); + + describe('when user has no access to a tool', () => { + const setup = async () => { + const schoolWithoutTool: SchoolEntity = schoolFactory.buildWithId(); + const school: SchoolEntity = schoolFactory.buildWithId(); + const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({ school: schoolWithoutTool }); + const course: Course = courseFactory.buildWithId({ school, teachers: [adminUser] }); + const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId(); + const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + school, + tool: externalToolEntity, + }); + const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ + schoolTool: schoolExternalToolEntity, + contextId: course.id, + contextType: ContextExternalToolType.COURSE, + }); + + await em.persistAndFlush([ + school, + adminAccount, + adminUser, + course, + externalToolEntity, + schoolExternalToolEntity, + contextExternalToolEntity, + ]); + em.clear(); + + const params: ContextExternalToolContextParams = { + contextId: course.id, + contextType: ToolContextType.COURSE, + }; + + const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); + + return { loggedInClient, params }; + }; + + it('should filter out the tool', async () => { + const { loggedInClient, params } = await setup(); + + const response: Response = await loggedInClient.get(`${params.contextType}/${params.contextId}`); + + expect(response.statusCode).toEqual(HttpStatus.OK); + expect(response.body).toEqual({ data: [] }); + }); + }); + + describe('when user has access for a tool', () => { + const setup = async () => { + const school: SchoolEntity = schoolFactory.buildWithId(); + const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({ school }, [ + Permission.CONTEXT_TOOL_USER, + ]); + const course: Course = courseFactory.buildWithId({ school, teachers: [adminUser] }); + const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ + logoBase64: 'logoBase64', + }); + const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + school, + tool: externalToolEntity, + toolVersion: externalToolEntity.version, + }); + const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ + schoolTool: schoolExternalToolEntity, + contextId: course.id, + contextType: ContextExternalToolType.COURSE, + displayName: 'This is a test tool', + toolVersion: schoolExternalToolEntity.toolVersion, + }); + + await em.persistAndFlush([ + school, + adminAccount, + adminUser, + course, + externalToolEntity, + schoolExternalToolEntity, + contextExternalToolEntity, + ]); + em.clear(); + + const params: ContextExternalToolContextParams = { + contextId: course.id, + contextType: ToolContextType.COURSE, + }; + + const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); + + return { loggedInClient, params, contextExternalToolEntity, externalToolEntity }; + }; + + it('should return an ToolReferenceListResponse with data', async () => { + const { loggedInClient, params, contextExternalToolEntity, externalToolEntity } = await setup(); + + const response: Response = await loggedInClient.get(`${params.contextType}/${params.contextId}`); + + expect(response.statusCode).toEqual(HttpStatus.OK); + expect(response.body).toEqual({ + data: [ + { + contextToolId: contextExternalToolEntity.id, + displayName: contextExternalToolEntity.displayName as string, + status: ToolConfigurationStatusResponse.LATEST, + logoUrl: `http://localhost:3030/api/v3/tools/external-tools/${externalToolEntity.id}/logo`, + openInNewTab: externalToolEntity.openNewTab, + }, + ], + }); + }); + }); + }); + + describe('[GET] tools/tool-references/context-external-tools/:contextExternalToolId', () => { + describe('when user is not authenticated', () => { + it('should return unauthorized', async () => { + const response: Response = await testApiClient.get(`context-external-tools/${new ObjectId().toHexString()}`); + + expect(response.statusCode).toEqual(HttpStatus.UNAUTHORIZED); + }); + }); + + describe('when user has no access to a tool', () => { + const setup = async () => { + const schoolWithoutTool: SchoolEntity = schoolFactory.buildWithId(); + const school: SchoolEntity = schoolFactory.buildWithId(); + const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({ school: schoolWithoutTool }); + const course: Course = courseFactory.buildWithId({ school, teachers: [adminUser] }); + const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId(); + const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + school, + tool: externalToolEntity, + }); + const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ + schoolTool: schoolExternalToolEntity, + contextId: course.id, + contextType: ContextExternalToolType.COURSE, + }); + + await em.persistAndFlush([ + school, + adminAccount, + adminUser, + course, + externalToolEntity, + schoolExternalToolEntity, + contextExternalToolEntity, + ]); + em.clear(); + + const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); + + return { + loggedInClient, + contextExternalToolId: contextExternalToolEntity.id, + }; + }; + + it('should filter out the tool', async () => { + const { loggedInClient, contextExternalToolId } = await setup(); + + const response: Response = await loggedInClient.get(`context-external-tools/${contextExternalToolId}`); + + expect(response.statusCode).toEqual(HttpStatus.FORBIDDEN); + }); + }); + + describe('when user has access for a tool', () => { + const setup = async () => { + const school: SchoolEntity = schoolFactory.buildWithId(); + const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({ school }, [ + Permission.CONTEXT_TOOL_USER, + ]); + const course: Course = courseFactory.buildWithId({ school, teachers: [adminUser] }); + const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ + logoBase64: 'logoBase64', + }); + const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + school, + tool: externalToolEntity, + toolVersion: externalToolEntity.version, + }); + const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ + schoolTool: schoolExternalToolEntity, + contextId: course.id, + contextType: ContextExternalToolType.COURSE, + displayName: 'This is a test tool', + toolVersion: schoolExternalToolEntity.toolVersion, + }); + + await em.persistAndFlush([ + school, + adminAccount, + adminUser, + course, + externalToolEntity, + schoolExternalToolEntity, + contextExternalToolEntity, + ]); + em.clear(); + + const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); + + return { + loggedInClient, + contextExternalToolId: contextExternalToolEntity.id, + contextExternalToolEntity, + externalToolEntity, + }; + }; + + it('should return an ToolReferenceListResponse with data', async () => { + const { loggedInClient, contextExternalToolId, contextExternalToolEntity, externalToolEntity } = await setup(); + + const response: Response = await loggedInClient.get(`context-external-tools/${contextExternalToolId}`); + + expect(response.statusCode).toEqual(HttpStatus.OK); + expect(response.body).toEqual({ + contextToolId: contextExternalToolEntity.id, + displayName: contextExternalToolEntity.displayName as string, + status: ToolConfigurationStatusResponse.LATEST, + logoUrl: `http://localhost:3030/api/v3/tools/external-tools/${externalToolEntity.id}/logo`, + openInNewTab: externalToolEntity.openNewTab, + }); + }); + }); + }); +}); diff --git a/apps/server/src/modules/tool/context-external-tool/controller/dto/context-external-tool-context.params.ts b/apps/server/src/modules/tool/context-external-tool/controller/dto/context-external-tool-context.params.ts index 7d20deef026..63850daa22b 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/dto/context-external-tool-context.params.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/dto/context-external-tool-context.params.ts @@ -8,6 +8,12 @@ export class ContextExternalToolContextParams { contextId!: string; @IsEnum(ToolContextType) - @ApiProperty({ nullable: false, required: true, example: ToolContextType.COURSE }) + @ApiProperty({ + enum: ToolContextType, + enumName: 'ToolContextType', + nullable: false, + required: true, + example: ToolContextType.COURSE, + }) contextType!: ToolContextType; } diff --git a/apps/server/src/modules/tool/context-external-tool/controller/dto/index.ts b/apps/server/src/modules/tool/context-external-tool/controller/dto/index.ts index dfe16d84244..e6da4bb909f 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/dto/index.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/dto/index.ts @@ -3,3 +3,6 @@ export * from './context-external-tool-id.params'; export * from './context-external-tool-search-list.response'; export * from './context-external-tool-context.params'; export * from './context-external-tool.response'; +export * from './tool-reference-list.response'; +export * from './tool-reference.response'; +export * from './tool-configuration-status.response'; diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-configuration-status.response.ts b/apps/server/src/modules/tool/context-external-tool/controller/dto/tool-configuration-status.response.ts similarity index 100% rename from apps/server/src/modules/tool/external-tool/controller/dto/response/tool-configuration-status.response.ts rename to apps/server/src/modules/tool/context-external-tool/controller/dto/tool-configuration-status.response.ts diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-reference-list.response.ts b/apps/server/src/modules/tool/context-external-tool/controller/dto/tool-reference-list.response.ts similarity index 100% rename from apps/server/src/modules/tool/external-tool/controller/dto/response/tool-reference-list.response.ts rename to apps/server/src/modules/tool/context-external-tool/controller/dto/tool-reference-list.response.ts diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-reference.response.ts b/apps/server/src/modules/tool/context-external-tool/controller/dto/tool-reference.response.ts similarity index 96% rename from apps/server/src/modules/tool/external-tool/controller/dto/response/tool-reference.response.ts rename to apps/server/src/modules/tool/context-external-tool/controller/dto/tool-reference.response.ts index 24844d8bd2a..0ccefffa6ae 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-reference.response.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/dto/tool-reference.response.ts @@ -20,6 +20,7 @@ export class ToolReferenceResponse { @ApiProperty({ enum: ToolConfigurationStatusResponse, + enumName: 'ToolConfigurationStatusResponse', nullable: false, required: true, description: 'The status of the tool', diff --git a/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts b/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts new file mode 100644 index 00000000000..c414f4423de --- /dev/null +++ b/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts @@ -0,0 +1,68 @@ +import { Controller, Get, Param } from '@nestjs/common'; +import { ApiForbiddenResponse, ApiOkResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; +import { ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ToolReference } from '../domain'; +import { ContextExternalToolResponseMapper } from '../mapper'; +import { ToolReferenceUc } from '../uc'; +import { + ContextExternalToolContextParams, + ContextExternalToolIdParams, + ToolReferenceListResponse, + ToolReferenceResponse, +} from './dto'; + +@ApiTags('Tool') +@Authenticate('jwt') +@Controller('tools/tool-references') +export class ToolReferenceController { + constructor(private readonly toolReferenceUc: ToolReferenceUc) {} + + @Get('context-external-tools/:contextExternalToolId') + @ApiOperation({ summary: 'Get ExternalTool Reference for a given context external tool' }) + @ApiOkResponse({ + description: 'The Tool Reference has been successfully fetched.', + type: ToolReferenceResponse, + }) + @ApiForbiddenResponse({ description: 'User is not allowed to access this resource.' }) + @ApiUnauthorizedResponse({ description: 'User is not logged in.' }) + async getToolReference( + @CurrentUser() currentUser: ICurrentUser, + @Param() params: ContextExternalToolIdParams + ): Promise { + const toolReference: ToolReference = await this.toolReferenceUc.getToolReference( + currentUser.userId, + params.contextExternalToolId + ); + + const toolReferenceResponse: ToolReferenceResponse = + ContextExternalToolResponseMapper.mapToToolReferenceResponse(toolReference); + + return toolReferenceResponse; + } + + @Get('/:contextType/:contextId') + @ApiOperation({ summary: 'Get ExternalTool References for a given context' }) + @ApiOkResponse({ + description: 'The Tool References has been successfully fetched.', + type: ToolReferenceListResponse, + }) + @ApiForbiddenResponse({ description: 'User is not allowed to access this resource.' }) + @ApiUnauthorizedResponse({ description: 'User is not logged in.' }) + async getToolReferencesForContext( + @CurrentUser() currentUser: ICurrentUser, + @Param() params: ContextExternalToolContextParams + ): Promise { + const toolReferences: ToolReference[] = await this.toolReferenceUc.getToolReferencesForContext( + currentUser.userId, + params.contextType, + params.contextId + ); + + const toolReferenceResponses: ToolReferenceResponse[] = + ContextExternalToolResponseMapper.mapToToolReferenceResponses(toolReferences); + const toolReferenceListResponse = new ToolReferenceListResponse(toolReferenceResponses); + + return toolReferenceListResponse; + } +} diff --git a/apps/server/src/modules/tool/context-external-tool/domain/index.ts b/apps/server/src/modules/tool/context-external-tool/domain/index.ts index a012e1d4002..557bc04788c 100644 --- a/apps/server/src/modules/tool/context-external-tool/domain/index.ts +++ b/apps/server/src/modules/tool/context-external-tool/domain/index.ts @@ -1,2 +1,3 @@ export * from './context-external-tool.do'; export * from './context-ref'; +export * from './tool-reference'; diff --git a/apps/server/src/modules/tool/external-tool/domain/tool-reference.ts b/apps/server/src/modules/tool/context-external-tool/domain/tool-reference.ts similarity index 100% rename from apps/server/src/modules/tool/external-tool/domain/tool-reference.ts rename to apps/server/src/modules/tool/context-external-tool/domain/tool-reference.ts diff --git a/apps/server/src/modules/tool/context-external-tool/entity/context-external-tool-type.enum.ts b/apps/server/src/modules/tool/context-external-tool/entity/context-external-tool-type.enum.ts index 4d40ca6e84c..56753c354dc 100644 --- a/apps/server/src/modules/tool/context-external-tool/entity/context-external-tool-type.enum.ts +++ b/apps/server/src/modules/tool/context-external-tool/entity/context-external-tool-type.enum.ts @@ -1,3 +1,4 @@ export enum ContextExternalToolType { COURSE = 'course', + BOARD_ELEMENT = 'boardElement', } diff --git a/apps/server/src/modules/tool/context-external-tool/mapper/context-external-tool-response.mapper.ts b/apps/server/src/modules/tool/context-external-tool/mapper/context-external-tool-response.mapper.ts index 601c960299d..07610a7a508 100644 --- a/apps/server/src/modules/tool/context-external-tool/mapper/context-external-tool-response.mapper.ts +++ b/apps/server/src/modules/tool/context-external-tool/mapper/context-external-tool-response.mapper.ts @@ -1,6 +1,7 @@ +import { ToolStatusResponseMapper } from '../../common/mapper/tool-status-response.mapper'; import { CustomParameterEntryParam, CustomParameterEntryResponse } from '../../school-external-tool/controller/dto'; -import { ContextExternalToolResponse } from '../controller/dto'; -import { ContextExternalTool } from '../domain'; +import { ContextExternalToolResponse, ToolReferenceResponse } from '../controller/dto'; +import { ContextExternalTool, ToolReference } from '../domain'; export class ContextExternalToolResponseMapper { static mapContextExternalToolResponse(contextExternalTool: ContextExternalTool): ContextExternalToolResponse { @@ -33,4 +34,24 @@ export class ContextExternalToolResponseMapper { return mapped; } + + static mapToToolReferenceResponses(toolReferences: ToolReference[]): ToolReferenceResponse[] { + const toolReferenceResponses: ToolReferenceResponse[] = toolReferences.map((toolReference: ToolReference) => + this.mapToToolReferenceResponse(toolReference) + ); + + return toolReferenceResponses; + } + + static mapToToolReferenceResponse(toolReference: ToolReference): ToolReferenceResponse { + const response = new ToolReferenceResponse({ + contextToolId: toolReference.contextToolId, + displayName: toolReference.displayName, + logoUrl: toolReference.logoUrl, + openInNewTab: toolReference.openInNewTab, + status: ToolStatusResponseMapper.mapToResponse(toolReference.status), + }); + + return response; + } } diff --git a/apps/server/src/modules/tool/context-external-tool/mapper/index.ts b/apps/server/src/modules/tool/context-external-tool/mapper/index.ts index 1987491c4e0..427f02a713a 100644 --- a/apps/server/src/modules/tool/context-external-tool/mapper/index.ts +++ b/apps/server/src/modules/tool/context-external-tool/mapper/index.ts @@ -1,2 +1,3 @@ export * from './context-external-tool-request.mapper'; export * from './context-external-tool-response.mapper'; +export * from './tool-reference.mapper'; diff --git a/apps/server/src/modules/tool/external-tool/mapper/tool-reference.mapper.ts b/apps/server/src/modules/tool/context-external-tool/mapper/tool-reference.mapper.ts similarity index 80% rename from apps/server/src/modules/tool/external-tool/mapper/tool-reference.mapper.ts rename to apps/server/src/modules/tool/context-external-tool/mapper/tool-reference.mapper.ts index ec982467578..be6e6b8ab12 100644 --- a/apps/server/src/modules/tool/external-tool/mapper/tool-reference.mapper.ts +++ b/apps/server/src/modules/tool/context-external-tool/mapper/tool-reference.mapper.ts @@ -1,6 +1,6 @@ -import { ExternalTool, ToolReference } from '../domain'; -import { ContextExternalTool } from '../../context-external-tool/domain'; import { ToolConfigurationStatus } from '../../common/enum'; +import { ExternalTool } from '../../external-tool/domain'; +import { ContextExternalTool, ToolReference } from '../domain'; export class ToolReferenceMapper { static mapToToolReference( diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.spec.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.spec.ts index c419154c020..41e849b3d79 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.spec.ts @@ -1,8 +1,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { ValidationError } from '@mikro-orm/core'; import { UnprocessableEntityException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { contextExternalToolFactory, externalToolFactory } from '@shared/testing'; -import { ValidationError } from '@mikro-orm/core'; import { CommonToolValidationService } from '../../common/service'; import { ExternalTool } from '../../external-tool/domain'; import { ExternalToolService } from '../../external-tool/service'; @@ -62,7 +62,7 @@ describe('ContextExternalToolValidationService', () => { describe('when no tool with the name exists in the context', () => { const setup = () => { const externalTool: ExternalTool = externalToolFactory.buildWithId(); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + externalToolService.findById.mockResolvedValue(externalTool); const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ displayName: 'Tool 1', @@ -93,9 +93,7 @@ describe('ContextExternalToolValidationService', () => { await service.validate(contextExternalTool); - expect(schoolExternalToolService.getSchoolExternalToolById).toBeCalledWith( - contextExternalTool.schoolToolRef.schoolToolId - ); + expect(schoolExternalToolService.findById).toBeCalledWith(contextExternalTool.schoolToolRef.schoolToolId); }); it('should call commonToolValidationService.checkCustomParameterEntries', async () => { diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.ts index af6d36840f7..3777273d18e 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.ts @@ -23,13 +23,11 @@ export class ContextExternalToolValidationService { await this.checkDuplicateInContext(contextExternalTool); - const loadedSchoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.getSchoolExternalToolById( + const loadedSchoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( contextExternalTool.schoolToolRef.schoolToolId ); - const loadedExternalTool: ExternalTool = await this.externalToolService.findExternalToolById( - loadedSchoolExternalTool.toolId - ); + const loadedExternalTool: ExternalTool = await this.externalToolService.findById(loadedSchoolExternalTool.toolId); this.commonToolValidationService.checkCustomParameterEntries(loadedExternalTool, contextExternalTool); } diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.spec.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.spec.ts index 28cb093ae2d..819e2895896 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.spec.ts @@ -130,7 +130,7 @@ describe('ContextExternalToolService', () => { }); }); - describe('getContextExternalToolById', () => { + describe('findById', () => { describe('when contextExternalToolId is given', () => { const setup = () => { const schoolId: string = legacySchoolDoFactory.buildWithId().id as string; @@ -151,7 +151,7 @@ describe('ContextExternalToolService', () => { it('should return a contextExternalTool', async () => { const { contextExternalTool } = setup(); - const result: ContextExternalTool = await service.getContextExternalToolById(contextExternalTool.id as string); + const result: ContextExternalTool = await service.findById(contextExternalTool.id as string); expect(result).toEqual(contextExternalTool); }); @@ -165,7 +165,7 @@ describe('ContextExternalToolService', () => { it('should throw a not found exception', async () => { setup(); - const func = () => service.getContextExternalToolById('unknownContextExternalToolId'); + const func = () => service.findById('unknownContextExternalToolId'); await expect(func()).rejects.toThrow(NotFoundException); }); diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.ts index 011e6db2f7a..63618191810 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain'; import { ContextExternalToolRepo } from '@shared/repo'; -import { ContextExternalToolQuery } from '../uc/dto/context-external-tool.types'; import { ContextExternalTool, ContextRef } from '../domain'; +import { ContextExternalToolQuery } from '../uc/dto/context-external-tool.types'; @Injectable() export class ContextExternalToolService { @@ -14,7 +14,7 @@ export class ContextExternalToolService { return contextExternalTools; } - async getContextExternalToolById(contextExternalToolId: EntityId): Promise { + async findById(contextExternalToolId: EntityId): Promise { const tool: ContextExternalTool = await this.contextExternalToolRepo.findById(contextExternalToolId); return tool; diff --git a/apps/server/src/modules/tool/context-external-tool/service/index.ts b/apps/server/src/modules/tool/context-external-tool/service/index.ts index 887cfbe7d9d..31fedbe42af 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/index.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/index.ts @@ -1,3 +1,4 @@ export * from './context-external-tool.service'; export * from './context-external-tool-validation.service'; export * from './context-external-tool-authorizable.service'; +export * from './tool-reference.service'; diff --git a/apps/server/src/modules/tool/context-external-tool/service/tool-reference.service.spec.ts b/apps/server/src/modules/tool/context-external-tool/service/tool-reference.service.spec.ts new file mode 100644 index 00000000000..e434ab49527 --- /dev/null +++ b/apps/server/src/modules/tool/context-external-tool/service/tool-reference.service.spec.ts @@ -0,0 +1,132 @@ +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { ObjectId } from '@mikro-orm/mongodb'; +import { Test, TestingModule } from '@nestjs/testing'; +import { contextExternalToolFactory, externalToolFactory, schoolExternalToolFactory } from '@shared/testing'; +import { ToolConfigurationStatus } from '../../common/enum'; +import { CommonToolService } from '../../common/service'; +import { ExternalToolLogoService, ExternalToolService } from '../../external-tool/service'; +import { SchoolExternalToolService } from '../../school-external-tool/service'; +import { ToolReference } from '../domain'; +import { ContextExternalToolService } from './context-external-tool.service'; +import { ToolReferenceService } from './tool-reference.service'; + +describe('ToolReferenceService', () => { + let module: TestingModule; + let service: ToolReferenceService; + + let externalToolService: DeepMocked; + let schoolExternalToolService: DeepMocked; + let contextExternalToolService: DeepMocked; + let commonToolService: DeepMocked; + let externalToolLogoService: DeepMocked; + + beforeAll(async () => { + module = await Test.createTestingModule({ + providers: [ + ToolReferenceService, + { + provide: ExternalToolService, + useValue: createMock(), + }, + { + provide: SchoolExternalToolService, + useValue: createMock(), + }, + { + provide: ContextExternalToolService, + useValue: createMock(), + }, + { + provide: CommonToolService, + useValue: createMock(), + }, + { + provide: ExternalToolLogoService, + useValue: createMock(), + }, + ], + }).compile(); + + service = module.get(ToolReferenceService); + externalToolService = module.get(ExternalToolService); + schoolExternalToolService = module.get(SchoolExternalToolService); + contextExternalToolService = module.get(ContextExternalToolService); + commonToolService = module.get(CommonToolService); + externalToolLogoService = module.get(ExternalToolLogoService); + }); + + afterAll(async () => { + await module.close(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('getToolReference', () => { + describe('when a context external tool id is provided', () => { + const setup = () => { + const contextExternalToolId = new ObjectId().toHexString(); + const externalTool = externalToolFactory.buildWithId(); + const schoolExternalTool = schoolExternalToolFactory.buildWithId({ + toolId: externalTool.id as string, + }); + const contextExternalTool = contextExternalToolFactory + .withSchoolExternalToolRef(schoolExternalTool.id as string) + .buildWithId(undefined, contextExternalToolId); + const logoUrl = 'logoUrl'; + + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + externalToolService.findById.mockResolvedValueOnce(externalTool); + commonToolService.determineToolConfigurationStatus.mockReturnValue(ToolConfigurationStatus.OUTDATED); + externalToolLogoService.buildLogoUrl.mockReturnValue(logoUrl); + + return { + contextExternalToolId, + externalTool, + schoolExternalTool, + contextExternalTool, + logoUrl, + }; + }; + + it('should determine the tool status', async () => { + const { contextExternalToolId, externalTool, schoolExternalTool, contextExternalTool } = setup(); + + await service.getToolReference(contextExternalToolId); + + expect(commonToolService.determineToolConfigurationStatus).toHaveBeenCalledWith( + externalTool, + schoolExternalTool, + contextExternalTool + ); + }); + + it('should build the logo url', async () => { + const { contextExternalToolId, externalTool } = setup(); + + await service.getToolReference(contextExternalToolId); + + expect(externalToolLogoService.buildLogoUrl).toHaveBeenCalledWith( + '/v3/tools/external-tools/{id}/logo', + externalTool + ); + }); + + it('should return the tool reference', async () => { + const { contextExternalToolId, logoUrl, contextExternalTool, externalTool } = setup(); + + const result: ToolReference = await service.getToolReference(contextExternalToolId); + + expect(result).toEqual({ + logoUrl, + displayName: contextExternalTool.displayName as string, + openInNewTab: externalTool.openNewTab, + status: ToolConfigurationStatus.OUTDATED, + contextToolId: contextExternalToolId, + }); + }); + }); + }); +}); diff --git a/apps/server/src/modules/tool/context-external-tool/service/tool-reference.service.ts b/apps/server/src/modules/tool/context-external-tool/service/tool-reference.service.ts new file mode 100644 index 00000000000..02c6a08677e --- /dev/null +++ b/apps/server/src/modules/tool/context-external-tool/service/tool-reference.service.ts @@ -0,0 +1,50 @@ +import { Injectable } from '@nestjs/common'; +import { EntityId } from '@shared/domain'; +import { ToolConfigurationStatus } from '../../common/enum'; +import { CommonToolService } from '../../common/service'; +import { ExternalTool } from '../../external-tool/domain'; +import { ExternalToolLogoService, ExternalToolService } from '../../external-tool/service'; +import { SchoolExternalTool } from '../../school-external-tool/domain'; +import { SchoolExternalToolService } from '../../school-external-tool/service'; +import { ContextExternalTool, ToolReference } from '../domain'; +import { ToolReferenceMapper } from '../mapper'; +import { ContextExternalToolService } from './context-external-tool.service'; + +@Injectable() +export class ToolReferenceService { + constructor( + private readonly externalToolService: ExternalToolService, + private readonly schoolExternalToolService: SchoolExternalToolService, + private readonly contextExternalToolService: ContextExternalToolService, + private readonly commonToolService: CommonToolService, + private readonly externalToolLogoService: ExternalToolLogoService + ) {} + + async getToolReference(contextExternalToolId: EntityId): Promise { + const contextExternalTool: ContextExternalTool = await this.contextExternalToolService.findById( + contextExternalToolId + ); + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( + contextExternalTool.schoolToolRef.schoolToolId + ); + const externalTool: ExternalTool = await this.externalToolService.findById(schoolExternalTool.toolId); + + const status: ToolConfigurationStatus = this.commonToolService.determineToolConfigurationStatus( + externalTool, + schoolExternalTool, + contextExternalTool + ); + + const toolReference: ToolReference = ToolReferenceMapper.mapToToolReference( + externalTool, + contextExternalTool, + status + ); + toolReference.logoUrl = this.externalToolLogoService.buildLogoUrl( + '/v3/tools/external-tools/{id}/logo', + externalTool + ); + + return toolReference; + } +} diff --git a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts index 7dcdea9d16b..801765f80e5 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts @@ -8,10 +8,10 @@ import { LegacyLogger } from '@src/core/logger'; import { Action, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; import { ForbiddenLoggableException } from '@src/modules/authorization/errors/forbidden.loggable-exception'; import { ToolContextType } from '../../common/enum'; +import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../domain'; import { ContextExternalToolService, ContextExternalToolValidationService } from '../service'; import { ContextExternalToolUc } from './context-external-tool.uc'; -import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; describe('ContextExternalToolUc', () => { let module: TestingModule; @@ -339,7 +339,7 @@ describe('ContextExternalToolUc', () => { const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId(); toolPermissionHelper.ensureContextPermissions.mockResolvedValue(); - contextExternalToolService.getContextExternalToolById.mockResolvedValue(contextExternalTool); + contextExternalToolService.findById.mockResolvedValue(contextExternalTool); return { contextExternalTool, @@ -496,7 +496,7 @@ describe('ContextExternalToolUc', () => { }, }); - contextExternalToolService.getContextExternalToolById.mockResolvedValue(contextExternalTool); + contextExternalToolService.findById.mockResolvedValue(contextExternalTool); toolPermissionHelper.ensureContextPermissions.mockResolvedValue(Promise.resolve()); return { @@ -524,7 +524,7 @@ describe('ContextExternalToolUc', () => { await uc.getContextExternalTool(userId, contextExternalTool.id as string); - expect(contextExternalToolService.getContextExternalToolById).toHaveBeenCalledWith(contextExternalTool.id); + expect(contextExternalToolService.findById).toHaveBeenCalledWith(contextExternalTool.id); }); }); @@ -542,7 +542,7 @@ describe('ContextExternalToolUc', () => { }, }); - contextExternalToolService.getContextExternalToolById.mockResolvedValue(contextExternalTool); + contextExternalToolService.findById.mockResolvedValue(contextExternalTool); toolPermissionHelper.ensureContextPermissions.mockRejectedValue( new ForbiddenLoggableException( userId, diff --git a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts index 903b8197251..04002cf9fc6 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts @@ -1,12 +1,12 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission, User } from '@shared/domain'; -import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; import { LegacyLogger } from '@src/core/logger'; -import { ContextExternalToolService, ContextExternalToolValidationService } from '../service'; -import { ContextExternalToolDto } from './dto/context-external-tool.types'; -import { ContextExternalTool, ContextRef } from '../domain'; +import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; import { ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; +import { ContextExternalTool, ContextRef } from '../domain'; +import { ContextExternalToolService, ContextExternalToolValidationService } from '../service'; +import { ContextExternalToolDto } from './dto/context-external-tool.types'; @Injectable() export class ContextExternalToolUc { @@ -62,9 +62,7 @@ export class ContextExternalToolUc { } async deleteContextExternalTool(userId: EntityId, contextExternalToolId: EntityId): Promise { - const tool: ContextExternalTool = await this.contextExternalToolService.getContextExternalToolById( - contextExternalToolId - ); + const tool: ContextExternalTool = await this.contextExternalToolService.findById(contextExternalToolId); const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); await this.toolPermissionHelper.ensureContextPermissions(userId, tool, context); @@ -85,7 +83,7 @@ export class ContextExternalToolUc { } async getContextExternalTool(userId: EntityId, contextToolId: EntityId) { - const tool: ContextExternalTool = await this.contextExternalToolService.getContextExternalToolById(contextToolId); + const tool: ContextExternalTool = await this.contextExternalToolService.findById(contextToolId); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_ADMIN]); await this.toolPermissionHelper.ensureContextPermissions(userId, tool, context); diff --git a/apps/server/src/modules/tool/context-external-tool/uc/index.ts b/apps/server/src/modules/tool/context-external-tool/uc/index.ts index cd34b162bad..12f2a82a9f1 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/index.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/index.ts @@ -1 +1,2 @@ export * from './context-external-tool.uc'; +export * from './tool-reference.uc'; diff --git a/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.spec.ts b/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.spec.ts new file mode 100644 index 00000000000..e0fbdcf6a6a --- /dev/null +++ b/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.spec.ts @@ -0,0 +1,215 @@ +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { ForbiddenException } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { Permission } from '@shared/domain'; +import { contextExternalToolFactory, externalToolFactory } from '@shared/testing'; +import { AuthorizationContextBuilder } from '@src/modules/authorization'; +import { ToolConfigurationStatus, ToolContextType } from '../../common/enum'; +import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; +import { ExternalTool } from '../../external-tool/domain'; +import { ContextExternalTool, ToolReference } from '../domain'; +import { ContextExternalToolService, ToolReferenceService } from '../service'; +import { ToolReferenceUc } from './tool-reference.uc'; + +describe('ToolReferenceUc', () => { + let module: TestingModule; + let uc: ToolReferenceUc; + + let contextExternalToolService: DeepMocked; + let toolReferenceService: DeepMocked; + let toolPermissionHelper: DeepMocked; + + beforeAll(async () => { + module = await Test.createTestingModule({ + providers: [ + ToolReferenceUc, + { + provide: ContextExternalToolService, + useValue: createMock(), + }, + { + provide: ToolReferenceService, + useValue: createMock(), + }, + { + provide: ToolPermissionHelper, + useValue: createMock(), + }, + ], + }).compile(); + + uc = module.get(ToolReferenceUc); + + contextExternalToolService = module.get(ContextExternalToolService); + toolReferenceService = module.get(ToolReferenceService); + toolPermissionHelper = module.get(ToolPermissionHelper); + }); + + afterAll(async () => { + await module.close(); + }); + + describe('getToolReferencesForContext', () => { + describe('when called with a context type and id', () => { + const setup = () => { + const userId = 'userId'; + + const externalTool: ExternalTool = externalToolFactory.withBase64Logo().buildWithId(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId(); + const toolReference: ToolReference = new ToolReference({ + logoUrl: externalTool.logoUrl, + contextToolId: contextExternalTool.id as string, + displayName: contextExternalTool.displayName as string, + status: ToolConfigurationStatus.LATEST, + openInNewTab: externalTool.openNewTab, + }); + + const contextType: ToolContextType = ToolContextType.COURSE; + const contextId = 'contextId'; + + contextExternalToolService.findAllByContext.mockResolvedValueOnce([contextExternalTool]); + toolPermissionHelper.ensureContextPermissions.mockResolvedValueOnce(); + toolReferenceService.getToolReference.mockResolvedValue(toolReference); + + return { + userId, + contextType, + contextId, + contextExternalTool, + externalTool, + toolReference, + }; + }; + + it('should call toolPermissionHelper.ensureContextPermissions', async () => { + const { userId, contextType, contextId, contextExternalTool } = setup(); + + await uc.getToolReferencesForContext(userId, contextType, contextId); + + expect(toolPermissionHelper.ensureContextPermissions).toHaveBeenCalledWith( + userId, + contextExternalTool, + AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]) + ); + }); + + it('should return a list of tool references', async () => { + const { userId, contextType, contextId, toolReference } = setup(); + + const result: ToolReference[] = await uc.getToolReferencesForContext(userId, contextType, contextId); + + expect(result).toEqual([toolReference]); + }); + }); + + describe('when user does not have permission to a tool', () => { + const setup = () => { + const userId = 'userId'; + + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId(); + + const contextType: ToolContextType = ToolContextType.COURSE; + const contextId = 'contextId'; + + contextExternalToolService.findAllByContext.mockResolvedValueOnce([contextExternalTool]); + toolPermissionHelper.ensureContextPermissions.mockRejectedValueOnce(new ForbiddenException()); + + return { + userId, + contextType, + contextId, + }; + }; + + it('should filter out tool references if a ForbiddenException is thrown', async () => { + const { userId, contextType, contextId } = setup(); + + const result: ToolReference[] = await uc.getToolReferencesForContext(userId, contextType, contextId); + + expect(result).toEqual([]); + }); + }); + }); + + describe('getToolReference', () => { + describe('when called with a context type and id', () => { + const setup = () => { + const userId = 'userId'; + const contextExternalToolId = 'contextExternalToolId'; + + const externalTool: ExternalTool = externalToolFactory.withBase64Logo().buildWithId(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId( + undefined, + contextExternalToolId + ); + const toolReference: ToolReference = new ToolReference({ + logoUrl: externalTool.logoUrl, + contextToolId: contextExternalTool.id as string, + displayName: contextExternalTool.displayName as string, + status: ToolConfigurationStatus.LATEST, + openInNewTab: externalTool.openNewTab, + }); + + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); + toolPermissionHelper.ensureContextPermissions.mockResolvedValueOnce(); + toolReferenceService.getToolReference.mockResolvedValue(toolReference); + + return { + userId, + contextExternalTool, + externalTool, + toolReference, + contextExternalToolId, + }; + }; + + it('should call toolPermissionHelper.ensureContextPermissions', async () => { + const { userId, contextExternalToolId, contextExternalTool } = setup(); + + await uc.getToolReference(userId, contextExternalToolId); + + expect(toolPermissionHelper.ensureContextPermissions).toHaveBeenCalledWith( + userId, + contextExternalTool, + AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]) + ); + }); + + it('should return a list of tool references', async () => { + const { userId, contextExternalToolId, toolReference } = setup(); + + const result: ToolReference = await uc.getToolReference(userId, contextExternalToolId); + + expect(result).toEqual(toolReference); + }); + }); + + describe('when user does not have permission to a tool', () => { + const setup = () => { + const userId = 'userId'; + const contextExternalToolId = 'contextExternalToolId'; + + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId( + undefined, + contextExternalToolId + ); + const error = new ForbiddenException(); + + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); + toolPermissionHelper.ensureContextPermissions.mockRejectedValueOnce(error); + + return { + userId, + contextExternalToolId, + error, + }; + }; + + it('should filter out tool references if a ForbiddenException is thrown', async () => { + const { userId, contextExternalToolId, error } = setup(); + + await expect(uc.getToolReference(userId, contextExternalToolId)).rejects.toThrow(error); + }); + }); + }); +}); diff --git a/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.ts b/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.ts new file mode 100644 index 00000000000..c044e01dfeb --- /dev/null +++ b/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.ts @@ -0,0 +1,82 @@ +import { Injectable } from '@nestjs/common'; +import { EntityId, Permission } from '@shared/domain'; +import { AuthorizationContext, AuthorizationContextBuilder } from '@src/modules/authorization'; +import { ToolContextType } from '../../common/enum'; +import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; +import { ContextExternalTool, ContextRef, ToolReference } from '../domain'; +import { ContextExternalToolService, ToolReferenceService } from '../service'; + +@Injectable() +export class ToolReferenceUc { + constructor( + private readonly contextExternalToolService: ContextExternalToolService, + private readonly toolReferenceService: ToolReferenceService, + private readonly toolPermissionHelper: ToolPermissionHelper + ) {} + + async getToolReferencesForContext( + userId: EntityId, + contextType: ToolContextType, + contextId: EntityId + ): Promise { + const contextRef = new ContextRef({ type: contextType, id: contextId }); + + const contextExternalTools: ContextExternalTool[] = await this.contextExternalToolService.findAllByContext( + contextRef + ); + + const toolReferencesPromises: Promise[] = contextExternalTools.map( + async (contextExternalTool: ContextExternalTool) => this.tryBuildToolReference(userId, contextExternalTool) + ); + + const toolReferencesWithNull: (ToolReference | null)[] = await Promise.all(toolReferencesPromises); + const filteredToolReferences: ToolReference[] = toolReferencesWithNull.filter( + (toolReference: ToolReference | null): toolReference is ToolReference => toolReference !== null + ); + + return filteredToolReferences; + } + + private async tryBuildToolReference( + userId: EntityId, + contextExternalTool: ContextExternalTool + ): Promise { + try { + await this.ensureToolPermissions(userId, contextExternalTool); + + const toolReference: ToolReference = await this.toolReferenceService.getToolReference( + contextExternalTool.id as string + ); + + return toolReference; + } catch (e: unknown) { + return null; + } + } + + async getToolReference(userId: EntityId, contextExternalToolId: EntityId): Promise { + const contextExternalTool: ContextExternalTool = await this.contextExternalToolService.findById( + contextExternalToolId + ); + + await this.ensureToolPermissions(userId, contextExternalTool); + + const toolReference: ToolReference = await this.toolReferenceService.getToolReference( + contextExternalTool.id as string + ); + + return toolReference; + } + + private async ensureToolPermissions(userId: EntityId, contextExternalTool: ContextExternalTool): Promise { + const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); + + const promise: Promise = this.toolPermissionHelper.ensureContextPermissions( + userId, + contextExternalTool, + context + ); + + return promise; + } +} diff --git a/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts b/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts index 6498f66fc1f..0d0c3fb08dc 100644 --- a/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts +++ b/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts @@ -1,41 +1,27 @@ +import { Loaded } from '@mikro-orm/core'; import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { Course, Permission, SchoolEntity } from '@shared/domain'; +import { Permission } from '@shared/domain'; import { cleanupCollections, - contextExternalToolEntityFactory, - courseFactory, externalToolEntityFactory, externalToolFactory, - schoolExternalToolEntityFactory, - schoolFactory, TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; +import { ServerTestModule } from '@src/modules/server'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { Response } from 'supertest'; -import { Loaded } from '@mikro-orm/core'; -import { ServerTestModule } from '@src/modules/server'; import { CustomParameterLocationParams, CustomParameterScopeTypeParams, CustomParameterTypeParams, ToolConfigType, - ToolContextType, } from '../../../common/enum'; -import { - ExternalToolCreateParams, - ExternalToolResponse, - ExternalToolSearchListResponse, - ToolConfigurationStatusResponse, - ToolReferenceListResponse, -} from '../dto'; -import { ContextExternalToolContextParams } from '../../../context-external-tool/controller/dto'; import { ExternalToolEntity } from '../../entity'; -import { ContextExternalToolEntity, ContextExternalToolType } from '../../../context-external-tool/entity'; -import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; +import { ExternalToolCreateParams, ExternalToolResponse, ExternalToolSearchListResponse } from '../dto'; describe('ToolController (API)', () => { let app: INestApplication; @@ -597,126 +583,6 @@ describe('ToolController (API)', () => { }); }); - describe('[GET] tools/external-tools/:contextType/:contextId/references', () => { - describe('when user is not authenticated', () => { - it('should return unauthorized', async () => { - const response: Response = await testApiClient.get(`contextType/${new ObjectId().toHexString()}/references`); - - expect(response.statusCode).toEqual(HttpStatus.UNAUTHORIZED); - }); - }); - - describe('when user has no access to a tool', () => { - const setup = async () => { - const schoolWithoutTool: SchoolEntity = schoolFactory.buildWithId(); - const school: SchoolEntity = schoolFactory.buildWithId(); - const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({ school: schoolWithoutTool }); - const course: Course = courseFactory.buildWithId({ school, teachers: [adminUser] }); - const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId(); - const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ - school, - tool: externalToolEntity, - }); - const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ - schoolTool: schoolExternalToolEntity, - contextId: course.id, - contextType: ContextExternalToolType.COURSE, - }); - - await em.persistAndFlush([ - school, - adminAccount, - adminUser, - course, - externalToolEntity, - schoolExternalToolEntity, - contextExternalToolEntity, - ]); - em.clear(); - - const params: ContextExternalToolContextParams = { - contextId: course.id, - contextType: ToolContextType.COURSE, - }; - - const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); - - return { loggedInClient, params }; - }; - - it('should filter out the tool', async () => { - const { loggedInClient, params } = await setup(); - - const response: Response = await loggedInClient.get(`${params.contextType}/${params.contextId}/references`); - - expect(response.statusCode).toEqual(HttpStatus.OK); - expect(response.body).toEqual({ data: [] }); - }); - }); - - describe('when user has access for a tool', () => { - const setup = async () => { - const school: SchoolEntity = schoolFactory.buildWithId(); - const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({ school }, [ - Permission.CONTEXT_TOOL_USER, - ]); - const course: Course = courseFactory.buildWithId({ school, teachers: [adminUser] }); - const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ logoUrl: undefined }); - const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ - school, - tool: externalToolEntity, - toolVersion: externalToolEntity.version, - }); - const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ - schoolTool: schoolExternalToolEntity, - contextId: course.id, - contextType: ContextExternalToolType.COURSE, - displayName: 'This is a test tool', - toolVersion: schoolExternalToolEntity.toolVersion, - }); - - await em.persistAndFlush([ - school, - adminAccount, - adminUser, - course, - externalToolEntity, - schoolExternalToolEntity, - contextExternalToolEntity, - ]); - em.clear(); - - const params: ContextExternalToolContextParams = { - contextId: course.id, - contextType: ToolContextType.COURSE, - }; - - const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); - - return { loggedInClient, params, contextExternalToolEntity, externalToolEntity }; - }; - - it('should return an ToolReferenceListResponse with data', async () => { - const { loggedInClient, params, contextExternalToolEntity, externalToolEntity } = await setup(); - - const response: Response = await loggedInClient.get(`${params.contextType}/${params.contextId}/references`); - - expect(response.statusCode).toEqual(HttpStatus.OK); - expect(response.body).toEqual({ - data: [ - { - contextToolId: contextExternalToolEntity.id, - displayName: contextExternalToolEntity.displayName as string, - status: ToolConfigurationStatusResponse.LATEST, - logoUrl: externalToolEntity.logoUrl, - openInNewTab: externalToolEntity.openNewTab, - }, - ], - }); - }); - }); - }); - describe('[GET] tools/external-tools/:externalToolId/logo', () => { const setup = async () => { const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.withBase64Logo().buildWithId(); diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/index.ts b/apps/server/src/modules/tool/external-tool/controller/dto/response/index.ts index fbae39a8b33..e9e5fafa376 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/response/index.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/response/index.ts @@ -1,10 +1,7 @@ export * from './config'; export * from './external-tool.response'; -export * from './tool-reference.response'; export * from './custom-parameter.response'; -export * from './tool-reference-list.response'; export * from './external-tool-search-list.response'; -export * from './tool-configuration-status.response'; export * from './context-external-tool-configuration-template.response'; export * from './context-external-tool-configuration-template-list.response'; export * from './school-external-tool-configuration-template.response'; diff --git a/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts b/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts index 3e6ac38fedc..4c3658d8025 100644 --- a/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts +++ b/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts @@ -18,23 +18,20 @@ import { ICurrentUser } from '@src/modules/authentication'; import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; import { Response } from 'express'; import { ExternalToolSearchQuery } from '../../common/interface'; -import { ContextExternalToolContextParams } from '../../context-external-tool/controller/dto'; -import { ExternalTool, ToolReference } from '../domain'; +import { ExternalTool } from '../domain'; import { ExternalToolLogo } from '../domain/external-tool-logo'; import { ExternalToolRequestMapper, ExternalToolResponseMapper } from '../mapper'; -import { ExternalToolCreate, ExternalToolUc, ExternalToolUpdate, ToolReferenceUc } from '../uc'; +import { ExternalToolLogoService } from '../service'; +import { ExternalToolCreate, ExternalToolUc, ExternalToolUpdate } from '../uc'; import { ExternalToolCreateParams, + ExternalToolIdParams, ExternalToolResponse, ExternalToolSearchListResponse, ExternalToolSearchParams, ExternalToolUpdateParams, SortExternalToolParams, - ExternalToolIdParams, - ToolReferenceListResponse, - ToolReferenceResponse, } from './dto'; -import { ExternalToolLogoService } from '../service'; @ApiTags('Tool') @Authenticate('jwt') @@ -43,7 +40,6 @@ export class ToolController { constructor( private readonly externalToolUc: ExternalToolUc, private readonly externalToolDOMapper: ExternalToolRequestMapper, - private readonly toolReferenceUc: ToolReferenceUc, private readonly logger: LegacyLogger, private readonly externalToolLogoService: ExternalToolLogoService ) {} @@ -156,32 +152,6 @@ export class ToolController { return promise; } - @Get('/:contextType/:contextId/references') - @ApiOperation({ summary: 'Get ExternalTool References for a given context' }) - @ApiOkResponse({ - description: 'The Tool References has been successfully fetched.', - type: ToolReferenceListResponse, - }) - @ApiForbiddenResponse({ description: 'User is not allowed to access this resource.' }) - @ApiUnauthorizedResponse({ description: 'User is not logged in.' }) - async getToolReferences( - @CurrentUser() currentUser: ICurrentUser, - @Param() params: ContextExternalToolContextParams - ): Promise { - const toolReferences: ToolReference[] = await this.toolReferenceUc.getToolReferences( - currentUser.userId, - params.contextType, - params.contextId, - '/v3/tools/external-tools/{id}/logo' - ); - - const toolReferenceResponses: ToolReferenceResponse[] = - ExternalToolResponseMapper.mapToToolReferenceResponses(toolReferences); - const toolReferenceListResponse = new ToolReferenceListResponse(toolReferenceResponses); - - return toolReferenceListResponse; - } - @Get('/:externalToolId/logo') @ApiOperation({ summary: 'Gets the logo of an external tool.' }) @ApiOkResponse({ diff --git a/apps/server/src/modules/tool/external-tool/domain/index.ts b/apps/server/src/modules/tool/external-tool/domain/index.ts index 9eaf1f03cbb..e5a1dab735d 100644 --- a/apps/server/src/modules/tool/external-tool/domain/index.ts +++ b/apps/server/src/modules/tool/external-tool/domain/index.ts @@ -1,3 +1,2 @@ export * from './external-tool.do'; export * from './config'; -export * from './tool-reference'; diff --git a/apps/server/src/modules/tool/external-tool/mapper/external-tool-response.mapper.ts b/apps/server/src/modules/tool/external-tool/mapper/external-tool-response.mapper.ts index 2885c2ea0c0..b2035e66477 100644 --- a/apps/server/src/modules/tool/external-tool/mapper/external-tool-response.mapper.ts +++ b/apps/server/src/modules/tool/external-tool/mapper/external-tool-response.mapper.ts @@ -8,16 +8,14 @@ import { CustomParameterType, CustomParameterTypeParams, } from '../../common/enum'; -import { statusMapping } from '../../school-external-tool/mapper'; import { BasicToolConfigResponse, CustomParameterResponse, ExternalToolResponse, Lti11ToolConfigResponse, Oauth2ToolConfigResponse, - ToolReferenceResponse, } from '../controller/dto'; -import { BasicToolConfig, ExternalTool, Lti11ToolConfig, Oauth2ToolConfig, ToolReference } from '../domain'; +import { BasicToolConfig, ExternalTool, Lti11ToolConfig, Oauth2ToolConfig } from '../domain'; const scopeMapping: Record = { [CustomParameterScope.GLOBAL]: CustomParameterScopeTypeParams.GLOBAL, @@ -98,24 +96,4 @@ export class ExternalToolResponseMapper { }; }); } - - static mapToToolReferenceResponses(toolReferences: ToolReference[]): ToolReferenceResponse[] { - const toolReferenceResponses: ToolReferenceResponse[] = toolReferences.map((toolReference: ToolReference) => - this.mapToToolReferenceResponse(toolReference) - ); - - return toolReferenceResponses; - } - - private static mapToToolReferenceResponse(toolReference: ToolReference): ToolReferenceResponse { - const response = new ToolReferenceResponse({ - contextToolId: toolReference.contextToolId, - displayName: toolReference.displayName, - logoUrl: toolReference.logoUrl, - openInNewTab: toolReference.openInNewTab, - status: statusMapping[toolReference.status], - }); - - return response; - } } diff --git a/apps/server/src/modules/tool/external-tool/mapper/index.ts b/apps/server/src/modules/tool/external-tool/mapper/index.ts index 92aff5e73c9..4149a17a519 100644 --- a/apps/server/src/modules/tool/external-tool/mapper/index.ts +++ b/apps/server/src/modules/tool/external-tool/mapper/index.ts @@ -1,3 +1,2 @@ -export * from './tool-reference.mapper'; export * from './external-tool-request.mapper'; export * from './external-tool-response.mapper'; diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-logo-service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-logo-service.spec.ts index 57acd50122f..c53154098a5 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-logo-service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-logo-service.spec.ts @@ -1,4 +1,4 @@ -import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { HttpService } from '@nestjs/axios'; import { HttpException, HttpStatus } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; @@ -9,8 +9,8 @@ import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; import { ExternalToolLogo } from '../domain/external-tool-logo'; import { - ExternalToolLogoFetchFailedLoggableException, ExternalToolLogoFetchedLoggable, + ExternalToolLogoFetchFailedLoggableException, ExternalToolLogoNotFoundLoggableException, ExternalToolLogoSizeExceededLoggableException, ExternalToolLogoWrongFileTypeLoggableException, @@ -329,7 +329,7 @@ describe('ExternalToolLogoService', () => { const setup = () => { const externalTool: ExternalTool = externalToolFactory.withBase64Logo().buildWithId(); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + externalToolService.findById.mockResolvedValue(externalTool); return { externalToolId: externalTool.id as string, @@ -355,7 +355,7 @@ describe('ExternalToolLogoService', () => { const setup = () => { const externalTool: ExternalTool = externalToolFactory.buildWithId({ logo: 'notAValidBase64File' }); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + externalToolService.findById.mockResolvedValue(externalTool); return { externalToolId: externalTool.id as string, @@ -375,7 +375,7 @@ describe('ExternalToolLogoService', () => { const setup = () => { const externalTool: ExternalTool = externalToolFactory.buildWithId(); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + externalToolService.findById.mockResolvedValue(externalTool); return { externalToolId: externalTool.id as string, diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-logo.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-logo.service.ts index f2518e65a3a..b39684fbd1b 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-logo.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-logo.service.ts @@ -1,19 +1,19 @@ +import { HttpService } from '@nestjs/axios'; import { HttpException, Inject } from '@nestjs/common'; +import { EntityId } from '@shared/domain'; +import { Logger } from '@src/core/logger'; import { AxiosResponse } from 'axios'; import { lastValueFrom } from 'rxjs'; -import { HttpService } from '@nestjs/axios'; -import { Logger } from '@src/core/logger'; -import { EntityId } from '@shared/domain'; +import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; +import { ExternalToolLogo } from '../domain/external-tool-logo'; import { ExternalToolLogoFetchedLoggable, + ExternalToolLogoFetchFailedLoggableException, ExternalToolLogoNotFoundLoggableException, ExternalToolLogoSizeExceededLoggableException, ExternalToolLogoWrongFileTypeLoggableException, - ExternalToolLogoFetchFailedLoggableException, } from '../loggable'; -import { IToolFeatures, ToolFeatures } from '../../tool-config'; -import { ExternalToolLogo } from '../domain/external-tool-logo'; import { ExternalToolService } from './external-tool.service'; const contentTypeDetector: Record = { @@ -95,7 +95,7 @@ export class ExternalToolLogoService { } async getExternalToolBinaryLogo(toolId: EntityId): Promise { - const tool: ExternalTool = await this.externalToolService.findExternalToolById(toolId); + const tool: ExternalTool = await this.externalToolService.findById(toolId); if (!tool.logo) { throw new ExternalToolLogoNotFoundLoggableException(toolId); diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-validation.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-validation.service.spec.ts index 8f5f1607df6..42efcd6559a 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-validation.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-validation.service.spec.ts @@ -4,10 +4,10 @@ import { ValidationError } from '@shared/common'; import { externalToolFactory } from '@shared/testing/factory/domainobject/tool/external-tool.factory'; import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; +import { ExternalToolLogoService } from './external-tool-logo.service'; import { ExternalToolParameterValidationService } from './external-tool-parameter-validation.service'; import { ExternalToolValidationService } from './external-tool-validation.service'; import { ExternalToolService } from './external-tool.service'; -import { ExternalToolLogoService } from './external-tool-logo.service'; describe('ExternalToolValidationService', () => { let module: TestingModule; @@ -232,7 +232,7 @@ describe('ExternalToolValidationService', () => { .buildWithId(); externalOauthTool.id = 'toolId'; - externalToolService.findExternalToolById.mockResolvedValue(externalOauthTool); + externalToolService.findById.mockResolvedValue(externalOauthTool); return { externalOauthTool, @@ -266,7 +266,7 @@ describe('ExternalToolValidationService', () => { .withOauth2Config({ clientId: 'ClientId', clientSecret: 'secret' }) .buildWithId(); - externalToolService.findExternalToolById.mockResolvedValue(existingExternalOauthTool); + externalToolService.findById.mockResolvedValue(existingExternalOauthTool); const newExternalTool: ExternalTool = externalToolFactory.buildWithId(); @@ -296,7 +296,7 @@ describe('ExternalToolValidationService', () => { .withOauth2Config({ clientId: 'ClientId', clientSecret: 'secret' }) .buildWithId(); - externalToolService.findExternalToolById.mockResolvedValue(externalOauthTool); + externalToolService.findById.mockResolvedValue(externalOauthTool); return { externalOauthTool }; }; @@ -318,7 +318,7 @@ describe('ExternalToolValidationService', () => { const existingExternalOauthToolDOWithDifferentClientId: ExternalTool = externalToolFactory .withOauth2Config({ clientId: 'DifferentClientId', clientSecret: 'secret' }) .buildWithId(); - externalToolService.findExternalToolById.mockResolvedValue(existingExternalOauthToolDOWithDifferentClientId); + externalToolService.findById.mockResolvedValue(existingExternalOauthToolDOWithDifferentClientId); return { externalOauthTool, @@ -344,7 +344,7 @@ describe('ExternalToolValidationService', () => { const externalLtiToolDO: ExternalTool = externalToolFactory.withLti11Config().buildWithId(); externalLtiToolDO.id = 'toolId'; - externalToolService.findExternalToolById.mockResolvedValue(externalLtiToolDO); + externalToolService.findById.mockResolvedValue(externalLtiToolDO); return { externalLtiToolDO, diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-validation.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-validation.service.ts index 90b8307dc7e..434e7fac86e 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-validation.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-validation.service.ts @@ -2,9 +2,9 @@ import { Inject, Injectable } from '@nestjs/common'; import { ValidationError } from '@shared/common'; import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; +import { ExternalToolLogoService } from './external-tool-logo.service'; import { ExternalToolParameterValidationService } from './external-tool-parameter-validation.service'; import { ExternalToolService } from './external-tool.service'; -import { ExternalToolLogoService } from './external-tool-logo.service'; @Injectable() export class ExternalToolValidationService { @@ -32,7 +32,7 @@ export class ExternalToolValidationService { await this.externalToolParameterValidationService.validateCommon(externalTool); - const loadedTool: ExternalTool = await this.externalToolService.findExternalToolById(toolId); + const loadedTool: ExternalTool = await this.externalToolService.findById(toolId); if ( ExternalTool.isOauth2Config(loadedTool.config) && externalTool.config && diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool.service.spec.ts index d2913e5401a..4db2a5be0b0 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool.service.spec.ts @@ -308,7 +308,7 @@ describe('ExternalToolService', () => { }); }); - describe('findExternalToolById', () => { + describe('findById', () => { describe('when external tool id is set', () => { const setup = () => { const { externalTool } = createTools(); @@ -320,7 +320,7 @@ describe('ExternalToolService', () => { it('should get domain object', async () => { const { externalTool } = setup(); - const result: ExternalTool = await service.findExternalToolById('toolId'); + const result: ExternalTool = await service.findById('toolId'); expect(result).toEqual(externalTool); }); @@ -340,7 +340,7 @@ describe('ExternalToolService', () => { it('should get domain object and add external oauth2 data', async () => { const { externalTool, oauth2ToolConfig } = setup(); - const result: ExternalTool = await service.findExternalToolById('toolId'); + const result: ExternalTool = await service.findById('toolId'); expect(result).toEqual({ ...externalTool, config: oauth2ToolConfig }); }); @@ -362,7 +362,7 @@ describe('ExternalToolService', () => { it('should throw UnprocessableEntityException ', async () => { const { externalTool } = setup(); - const func = () => service.findExternalToolById('toolId'); + const func = () => service.findById('toolId'); await expect(func()).rejects.toThrow(`Could not resolve oauth2Config of tool ${externalTool.name}.`); }); diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool.service.ts index 2a53f8aae45..fcc1a7e2d5c 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool.service.ts @@ -75,7 +75,7 @@ export class ExternalToolService { return tools; } - async findExternalToolById(id: EntityId): Promise { + async findById(id: EntityId): Promise { const tool: ExternalTool = await this.externalToolRepo.findById(id); if (ExternalTool.isOauth2Config(tool.config)) { try { diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.spec.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.spec.ts index 5490f0c546b..0ed3a3317f8 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.spec.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.spec.ts @@ -12,14 +12,14 @@ import { } from '@shared/testing'; import { AuthorizationContextBuilder } from '@src/modules/authorization'; import { CustomParameterScope, ToolContextType } from '../../common/enum'; +import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { ContextExternalToolService } from '../../context-external-tool/service'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { SchoolExternalToolService } from '../../school-external-tool/service'; import { ExternalTool } from '../domain'; -import { ExternalToolLogoService, ExternalToolService, ExternalToolConfigurationService } from '../service'; +import { ExternalToolConfigurationService, ExternalToolLogoService, ExternalToolService } from '../service'; import { ExternalToolConfigurationUc } from './external-tool-configuration.uc'; -import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; describe('ExternalToolConfigurationUc', () => { let module: TestingModule; @@ -439,8 +439,8 @@ describe('ExternalToolConfigurationUc', () => { schoolExternalToolId ); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValueOnce(schoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValueOnce(externalTool); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + externalToolService.findById.mockResolvedValueOnce(externalTool); return { externalTool, @@ -478,7 +478,7 @@ describe('ExternalToolConfigurationUc', () => { schoolExternalToolId ); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValueOnce(schoolExternalTool); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); toolPermissionHelper.ensureSchoolPermissions.mockImplementation(() => { throw new UnauthorizedException(); }); @@ -512,8 +512,8 @@ describe('ExternalToolConfigurationUc', () => { schoolExternalToolId ); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValueOnce(schoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValueOnce(externalTool); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + externalToolService.findById.mockResolvedValueOnce(externalTool); return { schoolExternalToolId, @@ -553,9 +553,9 @@ describe('ExternalToolConfigurationUc', () => { contextExternalToolId ); - contextExternalToolService.getContextExternalToolById.mockResolvedValueOnce(contextExternalTool); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValueOnce(schoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValueOnce(externalTool); + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + externalToolService.findById.mockResolvedValueOnce(externalTool); return { externalTool, @@ -593,7 +593,7 @@ describe('ExternalToolConfigurationUc', () => { contextExternalToolId ); - contextExternalToolService.getContextExternalToolById.mockResolvedValueOnce(contextExternalTool); + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); toolPermissionHelper.ensureContextPermissions.mockImplementation(() => { throw new UnauthorizedException(); }); @@ -632,9 +632,9 @@ describe('ExternalToolConfigurationUc', () => { contextExternalToolId ); - contextExternalToolService.getContextExternalToolById.mockResolvedValueOnce(contextExternalTool); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValueOnce(schoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValueOnce(externalTool); + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + externalToolService.findById.mockResolvedValueOnce(externalTool); return { contextExternalToolId, diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts index 9607beb84df..16dd9626d0c 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts @@ -4,14 +4,14 @@ import { EntityId, Permission } from '@shared/domain'; import { Page } from '@shared/domain/domainobject/page'; import { AuthorizationContext, AuthorizationContextBuilder } from '@src/modules/authorization'; import { CustomParameterScope, ToolContextType } from '../../common/enum'; +import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { ContextExternalToolService } from '../../context-external-tool/service'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { SchoolExternalToolService } from '../../school-external-tool/service'; import { ExternalTool } from '../domain'; -import { ExternalToolLogoService, ExternalToolService, ExternalToolConfigurationService } from '../service'; +import { ExternalToolConfigurationService, ExternalToolLogoService, ExternalToolService } from '../service'; import { ContextExternalToolTemplateInfo } from './dto'; -import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; @Injectable() export class ExternalToolConfigurationUc { @@ -117,14 +117,12 @@ export class ExternalToolConfigurationUc { userId: EntityId, schoolExternalToolId: EntityId ): Promise { - const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.getSchoolExternalToolById( - schoolExternalToolId - ); + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById(schoolExternalToolId); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.SCHOOL_TOOL_ADMIN]); await this.toolPermissionHelper.ensureSchoolPermissions(userId, schoolExternalTool, context); - const externalTool: ExternalTool = await this.externalToolService.findExternalToolById(schoolExternalTool.toolId); + const externalTool: ExternalTool = await this.externalToolService.findById(schoolExternalTool.toolId); if (externalTool.isHidden) { throw new NotFoundException('Could not find the Tool Template'); @@ -139,18 +137,18 @@ export class ExternalToolConfigurationUc { userId: EntityId, contextExternalToolId: EntityId ): Promise { - const contextExternalTool: ContextExternalTool = await this.contextExternalToolService.getContextExternalToolById( + const contextExternalTool: ContextExternalTool = await this.contextExternalToolService.findById( contextExternalToolId ); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_ADMIN]); await this.toolPermissionHelper.ensureContextPermissions(userId, contextExternalTool, context); - const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.getSchoolExternalToolById( + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( contextExternalTool.schoolToolRef.schoolToolId ); - const externalTool: ExternalTool = await this.externalToolService.findExternalToolById(schoolExternalTool.toolId); + const externalTool: ExternalTool = await this.externalToolService.findById(schoolExternalTool.toolId); if (externalTool.isHidden) { throw new NotFoundException('Could not find the Tool Template'); diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts index d0b02f1e4f3..f2baadd885c 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts @@ -301,7 +301,7 @@ describe('ExternalToolUc', () => { it('should fetch a tool', async () => { const { currentUser } = setupAuthorization(); const { externalTool, toolId } = setup(); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + externalToolService.findById.mockResolvedValue(externalTool); const result: ExternalTool = await uc.getExternalTool(currentUser.userId, toolId); @@ -327,7 +327,7 @@ describe('ExternalToolUc', () => { }); externalToolService.updateExternalTool.mockResolvedValue(updatedExternalToolDO); - externalToolService.findExternalToolById.mockResolvedValue(new ExternalTool(externalToolDOtoUpdate)); + externalToolService.findById.mockResolvedValue(new ExternalTool(externalToolDOtoUpdate)); return { externalTool, diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts index 240977b2b38..3fb81e4f74a 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts @@ -35,7 +35,7 @@ export class ExternalToolUc { await this.toolValidationService.validateUpdate(toolId, externalTool); - const loaded: ExternalTool = await this.externalToolService.findExternalToolById(toolId); + const loaded: ExternalTool = await this.externalToolService.findById(toolId); const configToUpdate: ExternalToolConfig = { ...loaded.config, ...externalTool.config }; const toUpdate: ExternalTool = new ExternalTool({ ...loaded, @@ -63,7 +63,7 @@ export class ExternalToolUc { async getExternalTool(userId: EntityId, toolId: EntityId): Promise { await this.ensurePermission(userId, Permission.TOOL_ADMIN); - const tool: ExternalTool = await this.externalToolService.findExternalToolById(toolId); + const tool: ExternalTool = await this.externalToolService.findById(toolId); return tool; } diff --git a/apps/server/src/modules/tool/external-tool/uc/index.ts b/apps/server/src/modules/tool/external-tool/uc/index.ts index 46f3a860080..0a61273b29b 100644 --- a/apps/server/src/modules/tool/external-tool/uc/index.ts +++ b/apps/server/src/modules/tool/external-tool/uc/index.ts @@ -1,4 +1,3 @@ export * from './dto'; export * from './external-tool.uc'; -export * from './tool-reference.uc'; export * from './external-tool-configuration.uc'; diff --git a/apps/server/src/modules/tool/external-tool/uc/tool-reference.uc.spec.ts b/apps/server/src/modules/tool/external-tool/uc/tool-reference.uc.spec.ts deleted file mode 100644 index e06c34e5e8b..00000000000 --- a/apps/server/src/modules/tool/external-tool/uc/tool-reference.uc.spec.ts +++ /dev/null @@ -1,234 +0,0 @@ -import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { Configuration } from '@hpi-schul-cloud/commons/lib'; -import { ForbiddenException } from '@nestjs/common'; -import { Test, TestingModule } from '@nestjs/testing'; -import { Permission } from '@shared/domain'; -import { contextExternalToolFactory, externalToolFactory, schoolExternalToolFactory } from '@shared/testing'; -import { AuthorizationContextBuilder } from '@src/modules/authorization'; -import { ToolReferenceUc } from './tool-reference.uc'; -import { ToolConfigurationStatus, ToolContextType } from '../../common/enum'; -import { CommonToolService } from '../../common/service'; -import { ContextExternalTool } from '../../context-external-tool/domain'; -import { ContextExternalToolService } from '../../context-external-tool/service'; -import { SchoolExternalTool } from '../../school-external-tool/domain'; -import { SchoolExternalToolService } from '../../school-external-tool/service'; -import { ExternalTool, ToolReference } from '../domain'; -import { ExternalToolLogoService, ExternalToolService } from '../service'; -import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; - -describe('ToolReferenceUc', () => { - let module: TestingModule; - let uc: ToolReferenceUc; - - let externalToolService: DeepMocked; - let schoolExternalToolService: DeepMocked; - let contextExternalToolService: DeepMocked; - let toolPermissionHelper: DeepMocked; - let commonToolService: DeepMocked; - let logoService: DeepMocked; - - beforeAll(async () => { - module = await Test.createTestingModule({ - providers: [ - ToolReferenceUc, - { - provide: ExternalToolService, - useValue: createMock(), - }, - { - provide: SchoolExternalToolService, - useValue: createMock(), - }, - { - provide: ContextExternalToolService, - useValue: createMock(), - }, - { - provide: CommonToolService, - useValue: createMock(), - }, - { - provide: ExternalToolLogoService, - useValue: createMock(), - }, - { - provide: ToolPermissionHelper, - useValue: createMock(), - }, - ], - }).compile(); - - uc = module.get(ToolReferenceUc); - - externalToolService = module.get(ExternalToolService); - schoolExternalToolService = module.get(SchoolExternalToolService); - contextExternalToolService = module.get(ContextExternalToolService); - toolPermissionHelper = module.get(ToolPermissionHelper); - commonToolService = module.get(CommonToolService); - logoService = module.get(ExternalToolLogoService); - }); - - afterAll(async () => { - await module.close(); - }); - - describe('getToolReferences', () => { - describe('when called with a context type and id', () => { - const setup = () => { - const userId = 'userId'; - - const externalTool: ExternalTool = externalToolFactory.withBase64Logo().buildWithId(); - const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build({ - toolId: externalTool.id, - }); - const contextExternalTool: ContextExternalTool = contextExternalToolFactory - .withSchoolExternalToolRef('schoolToolId', 'schoolId') - .buildWithId(); - - const contextType: ToolContextType = ToolContextType.COURSE; - const contextId = 'contextId'; - - contextExternalToolService.findAllByContext.mockResolvedValueOnce([contextExternalTool]); - toolPermissionHelper.ensureContextPermissions.mockResolvedValueOnce(); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValueOnce(schoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValueOnce(externalTool); - commonToolService.determineToolConfigurationStatus.mockReturnValueOnce(ToolConfigurationStatus.LATEST); - - return { - userId, - contextType, - contextId, - contextExternalTool, - schoolExternalTool, - externalTool, - externalToolId: externalTool.id as string, - }; - }; - - it('should call toolPermissionHelper.ensureContextPermissions', async () => { - const { userId, contextType, contextId, contextExternalTool } = setup(); - - await uc.getToolReferences(userId, contextType, contextId, '/v3/tools/external-tools/{id}/logo'); - - expect(toolPermissionHelper.ensureContextPermissions).toHaveBeenCalledWith( - userId, - contextExternalTool, - AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]) - ); - }); - - it('should call contextExternalToolService.findAllByContext', async () => { - const { userId, contextType, contextId } = setup(); - - await uc.getToolReferences(userId, contextType, contextId, '/v3/tools/external-tools/{id}/logo'); - - expect(contextExternalToolService.findAllByContext).toHaveBeenCalledWith({ - type: contextType, - id: contextId, - }); - }); - - it('should call schoolExternalToolService.findByExternalToolId', async () => { - const { userId, contextType, contextId, contextExternalTool } = setup(); - - await uc.getToolReferences(userId, contextType, contextId, '/v3/tools/external-tools/{id}/logo'); - - expect(schoolExternalToolService.getSchoolExternalToolById).toHaveBeenCalledWith( - contextExternalTool.schoolToolRef.schoolToolId - ); - }); - - it('should call externalToolService.findById', async () => { - const { userId, contextType, contextId, externalToolId } = setup(); - - await uc.getToolReferences(userId, contextType, contextId, '/v3/tools/external-tools/{id}/logo'); - - expect(externalToolService.findExternalToolById).toHaveBeenCalledWith(externalToolId); - }); - - it('should call commonToolService.determineToolConfigurationStatus', async () => { - const { userId, contextType, contextId, contextExternalTool, schoolExternalTool, externalTool } = setup(); - - await uc.getToolReferences(userId, contextType, contextId, '/v3/tools/external-tools/{id}/logo'); - - expect(commonToolService.determineToolConfigurationStatus).toHaveBeenCalledWith( - externalTool, - schoolExternalTool, - contextExternalTool - ); - }); - - it('should call externalToolLogoService.buildLogoUrl', async () => { - const { userId, contextType, contextId, externalTool } = setup(); - - await uc.getToolReferences(userId, contextType, contextId, '/v3/tools/external-tools/{id}/logo'); - - expect(logoService.buildLogoUrl).toHaveBeenCalledWith('/v3/tools/external-tools/{id}/logo', externalTool); - }); - - it('should return a list of tool references', async () => { - const { userId, contextType, contextId, contextExternalTool, externalTool } = setup(); - - const result: ToolReference[] = await uc.getToolReferences( - userId, - contextType, - contextId, - '/v3/tools/external-tools/{id}/logo' - ); - - expect(result).toEqual([ - { - logoUrl: `${Configuration.get('PUBLIC_BACKEND_URL') as string}/v3/tools/external-tools/${ - externalTool.id as string - }/logo`, - openInNewTab: externalTool.openNewTab, - contextToolId: contextExternalTool.id as string, - displayName: contextExternalTool.displayName as string, - status: ToolConfigurationStatus.LATEST, - }, - ]); - }); - }); - - describe('when user does not have permission to a tool', () => { - const setup = () => { - const userId = 'userId'; - - const externalTool: ExternalTool = externalToolFactory.buildWithId(); - const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build({ - toolId: externalTool.id, - }); - const contextExternalTool: ContextExternalTool = contextExternalToolFactory - .withSchoolExternalToolRef('schoolToolId', 'schoolId') - .buildWithId(); - - const contextType: ToolContextType = ToolContextType.COURSE; - const contextId = 'contextId'; - - contextExternalToolService.findAllByContext.mockResolvedValueOnce([contextExternalTool]); - toolPermissionHelper.ensureContextPermissions.mockRejectedValueOnce(new ForbiddenException()); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValueOnce(schoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValueOnce(externalTool); - - return { - userId, - contextType, - contextId, - }; - }; - - it('should filter out tool references if a ForbiddenException is thrown', async () => { - const { userId, contextType, contextId } = setup(); - - const result: ToolReference[] = await uc.getToolReferences( - userId, - contextType, - contextId, - '/v3/tools/external-tools/{id}/logo' - ); - - expect(result).toEqual([]); - }); - }); - }); -}); diff --git a/apps/server/src/modules/tool/external-tool/uc/tool-reference.uc.ts b/apps/server/src/modules/tool/external-tool/uc/tool-reference.uc.ts deleted file mode 100644 index 5ddf0e467c6..00000000000 --- a/apps/server/src/modules/tool/external-tool/uc/tool-reference.uc.ts +++ /dev/null @@ -1,102 +0,0 @@ -import { ForbiddenException, Injectable } from '@nestjs/common'; -import { EntityId, Permission } from '@shared/domain'; -import { AuthorizationContext, AuthorizationContextBuilder } from '@src/modules/authorization'; -import { ExternalTool, ToolReference } from '../domain'; -import { ToolConfigurationStatus, ToolContextType } from '../../common/enum'; -import { CommonToolService } from '../../common/service'; -import { ContextExternalTool, ContextRef } from '../../context-external-tool/domain'; -import { ContextExternalToolService } from '../../context-external-tool/service'; -import { SchoolExternalTool } from '../../school-external-tool/domain'; -import { SchoolExternalToolService } from '../../school-external-tool/service'; -import { ToolReferenceMapper } from '../mapper/tool-reference.mapper'; -import { ExternalToolLogoService, ExternalToolService } from '../service'; -import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; - -@Injectable() -export class ToolReferenceUc { - constructor( - private readonly externalToolService: ExternalToolService, - private readonly schoolExternalToolService: SchoolExternalToolService, - private readonly contextExternalToolService: ContextExternalToolService, - private readonly toolPermissionHelper: ToolPermissionHelper, - private readonly commonToolService: CommonToolService, - private readonly externalToolLogoService: ExternalToolLogoService - ) {} - - async getToolReferences( - userId: EntityId, - contextType: ToolContextType, - contextId: string, - logoUrlTemplate: string - ): Promise { - const contextRef = new ContextRef({ type: contextType, id: contextId }); - - const contextExternalTools: ContextExternalTool[] = await this.contextExternalToolService.findAllByContext( - contextRef - ); - - const toolReferencesPromises: Promise[] = contextExternalTools.map( - (contextExternalTool: ContextExternalTool) => - this.buildToolReference(userId, contextExternalTool, logoUrlTemplate) - ); - - const toolReferencesWithNull: (ToolReference | null)[] = await Promise.all(toolReferencesPromises); - const filteredToolReferences: ToolReference[] = toolReferencesWithNull.filter( - (toolReference: ToolReference | null): toolReference is ToolReference => toolReference !== null - ); - - return filteredToolReferences; - } - - private async buildToolReference( - userId: EntityId, - contextExternalTool: ContextExternalTool, - logoUrlTemplate: string - ): Promise { - try { - await this.ensureToolPermissions(userId, contextExternalTool); - } catch (e: unknown) { - if (e instanceof ForbiddenException) { - return null; - } - } - - const schoolExternalTool: SchoolExternalTool = await this.fetchSchoolExternalTool(contextExternalTool); - const externalTool: ExternalTool = await this.fetchExternalTool(schoolExternalTool); - - const status: ToolConfigurationStatus = this.commonToolService.determineToolConfigurationStatus( - externalTool, - schoolExternalTool, - contextExternalTool - ); - - const toolReference: ToolReference = ToolReferenceMapper.mapToToolReference( - externalTool, - contextExternalTool, - status - ); - toolReference.logoUrl = this.externalToolLogoService.buildLogoUrl(logoUrlTemplate, externalTool); - - return toolReference; - } - - private async ensureToolPermissions(userId: EntityId, contextExternalTool: ContextExternalTool): Promise { - const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); - - const promise: Promise = this.toolPermissionHelper.ensureContextPermissions( - userId, - contextExternalTool, - context - ); - - return promise; - } - - private async fetchSchoolExternalTool(contextExternalTool: ContextExternalTool): Promise { - return this.schoolExternalToolService.getSchoolExternalToolById(contextExternalTool.schoolToolRef.schoolToolId); - } - - private async fetchExternalTool(schoolExternalTool: SchoolExternalTool): Promise { - return this.externalToolService.findExternalToolById(schoolExternalTool.toolId); - } -} diff --git a/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts b/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts index 89bbe0c2cb7..c2504b42de2 100644 --- a/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts @@ -12,6 +12,9 @@ import { userFactory, } from '@shared/testing'; import { ServerTestModule } from '@src/modules/server'; +import { ToolConfigurationStatusResponse } from '../../../context-external-tool/controller/dto/tool-configuration-status.response'; +import { ExternalToolEntity } from '../../../external-tool/entity'; +import { SchoolExternalToolEntity } from '../../entity'; import { CustomParameterEntryParam, SchoolExternalToolPostParams, @@ -19,9 +22,6 @@ import { SchoolExternalToolSearchListResponse, SchoolExternalToolSearchParams, } from '../dto'; -import { ToolConfigurationStatusResponse } from '../../../external-tool/controller/dto'; -import { SchoolExternalToolEntity } from '../../entity'; -import { ExternalToolEntity } from '../../../external-tool/entity'; describe('ToolSchoolController (API)', () => { let app: INestApplication; diff --git a/apps/server/src/modules/tool/school-external-tool/controller/dto/school-external-tool.response.ts b/apps/server/src/modules/tool/school-external-tool/controller/dto/school-external-tool.response.ts index 62ad203fb02..32dd35f10bd 100644 --- a/apps/server/src/modules/tool/school-external-tool/controller/dto/school-external-tool.response.ts +++ b/apps/server/src/modules/tool/school-external-tool/controller/dto/school-external-tool.response.ts @@ -1,6 +1,6 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { ToolConfigurationStatusResponse } from '../../../context-external-tool/controller/dto/tool-configuration-status.response'; import { CustomParameterEntryResponse } from './custom-parameter-entry.response'; -import { ToolConfigurationStatusResponse } from '../../../external-tool/controller/dto'; export class SchoolExternalToolResponse { @ApiProperty() diff --git a/apps/server/src/modules/tool/school-external-tool/mapper/school-external-tool-response.mapper.spec.ts b/apps/server/src/modules/tool/school-external-tool/mapper/school-external-tool-response.mapper.spec.ts index 916445770e3..ca2296e6df7 100644 --- a/apps/server/src/modules/tool/school-external-tool/mapper/school-external-tool-response.mapper.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/mapper/school-external-tool-response.mapper.spec.ts @@ -1,5 +1,5 @@ import { schoolExternalToolFactory } from '@shared/testing/factory'; -import { ToolConfigurationStatusResponse } from '../../external-tool/controller/dto'; +import { ToolConfigurationStatusResponse } from '../../context-external-tool/controller/dto'; import { SchoolExternalToolResponse, SchoolExternalToolSearchListResponse } from '../controller/dto'; import { SchoolExternalTool } from '../domain'; import { SchoolExternalToolResponseMapper } from './school-external-tool-response.mapper'; diff --git a/apps/server/src/modules/tool/school-external-tool/mapper/school-external-tool-response.mapper.ts b/apps/server/src/modules/tool/school-external-tool/mapper/school-external-tool-response.mapper.ts index 10ee706dd81..7388b1a6a41 100644 --- a/apps/server/src/modules/tool/school-external-tool/mapper/school-external-tool-response.mapper.ts +++ b/apps/server/src/modules/tool/school-external-tool/mapper/school-external-tool-response.mapper.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { CustomParameterEntry } from '../../common/domain'; -import { ToolConfigurationStatus } from '../../common/enum'; -import { ToolConfigurationStatusResponse } from '../../external-tool/controller/dto'; +import { ToolStatusResponseMapper } from '../../common/mapper/tool-status-response.mapper'; +import { ToolConfigurationStatusResponse } from '../../context-external-tool/controller/dto'; import { CustomParameterEntryResponse, SchoolExternalToolResponse, @@ -9,12 +9,6 @@ import { } from '../controller/dto'; import { SchoolExternalTool } from '../domain'; -export const statusMapping: Record = { - [ToolConfigurationStatus.LATEST]: ToolConfigurationStatusResponse.LATEST, - [ToolConfigurationStatus.OUTDATED]: ToolConfigurationStatusResponse.OUTDATED, - [ToolConfigurationStatus.UNKNOWN]: ToolConfigurationStatusResponse.UNKNOWN, -}; - @Injectable() export class SchoolExternalToolResponseMapper { mapToSearchListResponse(externalTools: SchoolExternalTool[]): SchoolExternalToolSearchListResponse { @@ -33,7 +27,7 @@ export class SchoolExternalToolResponseMapper { parameters: this.mapToCustomParameterEntryResponse(schoolExternalTool.parameters), toolVersion: schoolExternalTool.toolVersion, status: schoolExternalTool.status - ? statusMapping[schoolExternalTool.status] + ? ToolStatusResponseMapper.mapToResponse(schoolExternalTool.status) : ToolConfigurationStatusResponse.UNKNOWN, }; } diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-validation.service.spec.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-validation.service.spec.ts index 7ca001675b0..e43bdeb42e0 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-validation.service.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-validation.service.spec.ts @@ -51,7 +51,7 @@ describe('SchoolExternalToolValidationService', () => { ...externalToolFactory.buildWithId(), ...externalToolDoMock, }); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + externalToolService.findById.mockResolvedValue(externalTool); const schoolExternalToolId = schoolExternalTool.id as string; return { schoolExternalTool, @@ -66,7 +66,7 @@ describe('SchoolExternalToolValidationService', () => { await service.validate(schoolExternalTool); - expect(externalToolService.findExternalToolById).toHaveBeenCalledWith(schoolExternalTool.toolId); + expect(externalToolService.findById).toHaveBeenCalledWith(schoolExternalTool.toolId); }); it('should call commonToolValidationService.checkForDuplicateParameters', async () => { diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-validation.service.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-validation.service.ts index 8cc50097d5f..315d738ca64 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-validation.service.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-validation.service.ts @@ -15,9 +15,7 @@ export class SchoolExternalToolValidationService { async validate(schoolExternalTool: SchoolExternalTool): Promise { this.commonToolValidationService.checkForDuplicateParameters(schoolExternalTool); - const loadedExternalTool: ExternalTool = await this.externalToolService.findExternalToolById( - schoolExternalTool.toolId - ); + const loadedExternalTool: ExternalTool = await this.externalToolService.findById(schoolExternalTool.toolId); this.checkVersionMatch(schoolExternalTool.toolVersion, loadedExternalTool.version); diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool.service.spec.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool.service.spec.ts index 7c4031ef0b7..52f9b0a4c02 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool.service.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool.service.spec.ts @@ -3,11 +3,11 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SchoolExternalToolRepo } from '@shared/repo'; import { externalToolFactory } from '@shared/testing/factory/domainobject/tool/external-tool.factory'; import { schoolExternalToolFactory } from '@shared/testing/factory/domainobject/tool/school-external-tool.factory'; -import { ExternalToolService } from '../../external-tool/service'; -import { SchoolExternalToolService } from './school-external-tool.service'; +import { ToolConfigurationStatus } from '../../common/enum'; import { ExternalTool } from '../../external-tool/domain'; +import { ExternalToolService } from '../../external-tool/service'; import { SchoolExternalTool } from '../domain'; -import { ToolConfigurationStatus } from '../../common/enum'; +import { SchoolExternalToolService } from './school-external-tool.service'; describe('SchoolExternalToolService', () => { let module: TestingModule; @@ -77,7 +77,7 @@ describe('SchoolExternalToolService', () => { await service.findSchoolExternalTools(schoolExternalTool); - expect(externalToolService.findExternalToolById).toHaveBeenCalledWith(schoolExternalTool.toolId); + expect(externalToolService.findById).toHaveBeenCalledWith(schoolExternalTool.toolId); }); describe('when determine status', () => { @@ -86,7 +86,7 @@ describe('SchoolExternalToolService', () => { const { schoolExternalTool, externalTool } = setup(); externalTool.version = 1337; schoolExternalToolRepo.find.mockResolvedValue([schoolExternalTool]); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + externalToolService.findById.mockResolvedValue(externalTool); const schoolExternalToolDOs: SchoolExternalTool[] = await service.findSchoolExternalTools(schoolExternalTool); @@ -100,7 +100,7 @@ describe('SchoolExternalToolService', () => { schoolExternalTool.toolVersion = 1; externalTool.version = 0; schoolExternalToolRepo.find.mockResolvedValue([schoolExternalTool]); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + externalToolService.findById.mockResolvedValue(externalTool); const schoolExternalToolDOs: SchoolExternalTool[] = await service.findSchoolExternalTools(schoolExternalTool); @@ -114,7 +114,7 @@ describe('SchoolExternalToolService', () => { schoolExternalTool.toolVersion = 1; externalTool.version = 1; schoolExternalToolRepo.find.mockResolvedValue([schoolExternalTool]); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + externalToolService.findById.mockResolvedValue(externalTool); const schoolExternalToolDOs: SchoolExternalTool[] = await service.findSchoolExternalTools(schoolExternalTool); @@ -136,12 +136,12 @@ describe('SchoolExternalToolService', () => { }); }); - describe('getSchoolExternalToolById', () => { + describe('findById', () => { describe('when schoolExternalToolId is given', () => { it('should call schoolExternalToolRepo.findById', async () => { const { schoolExternalToolId } = setup(); - await service.getSchoolExternalToolById(schoolExternalToolId); + await service.findById(schoolExternalToolId); expect(schoolExternalToolRepo.findById).toHaveBeenCalledWith(schoolExternalToolId); }); @@ -163,7 +163,7 @@ describe('SchoolExternalToolService', () => { await service.saveSchoolExternalTool(schoolExternalTool); - expect(externalToolService.findExternalToolById).toHaveBeenCalledWith(schoolExternalTool.toolId); + expect(externalToolService.findById).toHaveBeenCalledWith(schoolExternalTool.toolId); }); }); }); diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool.service.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool.service.ts index 9ee30d70db6..2f011560f6a 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool.service.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool.service.ts @@ -1,11 +1,11 @@ import { Injectable } from '@nestjs/common'; -import { SchoolExternalToolRepo } from '@shared/repo'; import { EntityId } from '@shared/domain'; -import { SchoolExternalToolQuery } from '../uc/dto/school-external-tool.types'; +import { SchoolExternalToolRepo } from '@shared/repo'; +import { ToolConfigurationStatus } from '../../common/enum'; +import { ExternalTool } from '../../external-tool/domain'; import { ExternalToolService } from '../../external-tool/service'; import { SchoolExternalTool } from '../domain'; -import { ExternalTool } from '../../external-tool/domain'; -import { ToolConfigurationStatus } from '../../common/enum'; +import { SchoolExternalToolQuery } from '../uc/dto/school-external-tool.types'; @Injectable() export class SchoolExternalToolService { @@ -14,7 +14,7 @@ export class SchoolExternalToolService { private readonly externalToolService: ExternalToolService ) {} - async getSchoolExternalToolById(schoolExternalToolId: EntityId): Promise { + async findById(schoolExternalToolId: EntityId): Promise { const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolRepo.findById(schoolExternalToolId); return schoolExternalTool; } @@ -38,7 +38,7 @@ export class SchoolExternalToolService { } private async enrichDataFromExternalTool(tool: SchoolExternalTool): Promise { - const externalTool: ExternalTool = await this.externalToolService.findExternalToolById(tool.toolId); + const externalTool: ExternalTool = await this.externalToolService.findById(tool.toolId); const status: ToolConfigurationStatus = this.determineStatus(tool, externalTool); const schoolExternalTool: SchoolExternalTool = new SchoolExternalTool({ ...tool, diff --git a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts index c0daab13cff..85f26f83679 100644 --- a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts @@ -3,12 +3,12 @@ import { Test, TestingModule } from '@nestjs/testing'; import { EntityId, Permission, User } from '@shared/domain'; import { schoolExternalToolFactory, setupEntities, userFactory } from '@shared/testing'; import { AuthorizationContextBuilder } from '@src/modules/authorization'; -import { SchoolExternalToolUc } from './school-external-tool.uc'; -import { SchoolExternalToolService, SchoolExternalToolValidationService } from '../service'; +import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalToolService } from '../../context-external-tool/service'; -import { SchoolExternalToolQueryInput } from './dto/school-external-tool.types'; import { SchoolExternalTool } from '../domain'; -import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; +import { SchoolExternalToolService, SchoolExternalToolValidationService } from '../service'; +import { SchoolExternalToolQueryInput } from './dto/school-external-tool.types'; +import { SchoolExternalToolUc } from './school-external-tool.uc'; describe('SchoolExternalToolUc', () => { let module: TestingModule; @@ -259,7 +259,7 @@ describe('SchoolExternalToolUc', () => { const tool: SchoolExternalTool = schoolExternalToolFactory.buildWithId(); const user: User = userFactory.buildWithId(); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValue(tool); + schoolExternalToolService.findById.mockResolvedValue(tool); return { user, @@ -285,7 +285,7 @@ describe('SchoolExternalToolUc', () => { const setup = () => { const tool: SchoolExternalTool = schoolExternalToolFactory.buildWithId(); const user: User = userFactory.buildWithId(); - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValue(tool); + schoolExternalToolService.findById.mockResolvedValue(tool); return { user, diff --git a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts index 63067c234d7..2640def12d5 100644 --- a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts +++ b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts @@ -1,11 +1,11 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; import { AuthorizationContext, AuthorizationContextBuilder } from '@src/modules/authorization'; -import { SchoolExternalToolService, SchoolExternalToolValidationService } from '../service'; +import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalToolService } from '../../context-external-tool/service'; -import { SchoolExternalToolDto, SchoolExternalToolQueryInput } from './dto/school-external-tool.types'; import { SchoolExternalTool } from '../domain'; -import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; +import { SchoolExternalToolService, SchoolExternalToolValidationService } from '../service'; +import { SchoolExternalToolDto, SchoolExternalToolQueryInput } from './dto/school-external-tool.types'; @Injectable() export class SchoolExternalToolUc { @@ -57,9 +57,7 @@ export class SchoolExternalToolUc { } async deleteSchoolExternalTool(userId: EntityId, schoolExternalToolId: EntityId): Promise { - const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.getSchoolExternalToolById( - schoolExternalToolId - ); + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById(schoolExternalToolId); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.SCHOOL_TOOL_ADMIN]); await this.toolPermissionHelper.ensureSchoolPermissions(userId, schoolExternalTool, context); @@ -71,9 +69,7 @@ export class SchoolExternalToolUc { } async getSchoolExternalTool(userId: EntityId, schoolExternalToolId: EntityId): Promise { - const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.getSchoolExternalToolById( - schoolExternalToolId - ); + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById(schoolExternalToolId); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.SCHOOL_TOOL_ADMIN]); await this.toolPermissionHelper.ensureSchoolPermissions(userId, schoolExternalTool, context); diff --git a/apps/server/src/modules/tool/tool-api.module.ts b/apps/server/src/modules/tool/tool-api.module.ts index fe775e01fd3..dea3405801d 100644 --- a/apps/server/src/modules/tool/tool-api.module.ts +++ b/apps/server/src/modules/tool/tool-api.module.ts @@ -4,11 +4,14 @@ import { LoggerModule } from '@src/core/logger'; import { AuthorizationModule } from '@src/modules/authorization'; import { LegacySchoolModule } from '@src/modules/legacy-school'; import { UserModule } from '@src/modules/user'; +import { CommonToolModule } from './common'; import { ToolContextController } from './context-external-tool/controller'; -import { ContextExternalToolUc } from './context-external-tool/uc'; +import { ToolReferenceController } from './context-external-tool/controller/tool-reference.controller'; +import { ContextExternalToolUc, ToolReferenceUc } from './context-external-tool/uc'; import { ToolConfigurationController, ToolController } from './external-tool/controller'; import { ExternalToolRequestMapper, ExternalToolResponseMapper } from './external-tool/mapper'; -import { ExternalToolConfigurationUc, ExternalToolUc, ToolReferenceUc } from './external-tool/uc'; +import { ExternalToolConfigurationService } from './external-tool/service'; +import { ExternalToolConfigurationUc, ExternalToolUc } from './external-tool/uc'; import { ToolSchoolController } from './school-external-tool/controller'; import { SchoolExternalToolRequestMapper, SchoolExternalToolResponseMapper } from './school-external-tool/mapper'; import { SchoolExternalToolUc } from './school-external-tool/uc'; @@ -16,8 +19,6 @@ import { ToolConfigModule } from './tool-config.module'; import { ToolLaunchController } from './tool-launch/controller/tool-launch.controller'; import { ToolLaunchUc } from './tool-launch/uc'; import { ToolModule } from './tool.module'; -import { ExternalToolConfigurationService } from './external-tool/service'; -import { CommonToolModule } from './common'; @Module({ imports: [ @@ -34,6 +35,7 @@ import { CommonToolModule } from './common'; ToolConfigurationController, ToolSchoolController, ToolContextController, + ToolReferenceController, ToolController, ], providers: [ diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.spec.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.spec.ts index 7dba13fd2f5..12f8716b5b3 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.spec.ts @@ -133,18 +133,6 @@ describe('AbstractLaunchStrategy', () => { name: 'autoSchoolIdParam', type: CustomParameterType.AUTO_SCHOOLID, }); - const autoCourseIdCustomParameter = customParameterFactory.build({ - scope: CustomParameterScope.GLOBAL, - location: CustomParameterLocation.BODY, - name: 'autoCourseIdParam', - type: CustomParameterType.AUTO_CONTEXTID, - }); - const autoCourseNameCustomParameter = customParameterFactory.build({ - scope: CustomParameterScope.GLOBAL, - location: CustomParameterLocation.BODY, - name: 'autoCourseNameParam', - type: CustomParameterType.AUTO_CONTEXTNAME, - }); const autoSchoolNumberCustomParameter = customParameterFactory.build({ scope: CustomParameterScope.GLOBAL, location: CustomParameterLocation.BODY, @@ -158,8 +146,6 @@ describe('AbstractLaunchStrategy', () => { schoolCustomParameter, contextCustomParameter, autoSchoolIdCustomParameter, - autoCourseIdCustomParameter, - autoCourseNameCustomParameter, autoSchoolNumberCustomParameter, ], }); @@ -191,15 +177,7 @@ describe('AbstractLaunchStrategy', () => { schoolId ); - const course: Course = courseFactory.buildWithId( - { - name: 'testName', - }, - contextExternalTool.contextRef.id - ); - schoolService.getSchoolById.mockResolvedValue(school); - courseService.findById.mockResolvedValue(course); const sortFn = (a: PropertyData, b: PropertyData) => { if (a.name < b.name) { @@ -215,15 +193,12 @@ describe('AbstractLaunchStrategy', () => { globalCustomParameter, schoolCustomParameter, autoSchoolIdCustomParameter, - autoCourseIdCustomParameter, - autoCourseNameCustomParameter, autoSchoolNumberCustomParameter, schoolParameterEntry, contextParameterEntry, externalTool, schoolExternalTool, contextExternalTool, - course, school, sortFn, }; @@ -235,14 +210,11 @@ describe('AbstractLaunchStrategy', () => { schoolCustomParameter, contextParameterEntry, autoSchoolIdCustomParameter, - autoCourseIdCustomParameter, - autoCourseNameCustomParameter, autoSchoolNumberCustomParameter, schoolParameterEntry, externalTool, schoolExternalTool, contextExternalTool, - course, school, sortFn, } = setup(); @@ -280,18 +252,131 @@ describe('AbstractLaunchStrategy', () => { location: PropertyLocation.BODY, }, { - name: autoCourseIdCustomParameter.name, - value: course.id, + name: autoSchoolNumberCustomParameter.name, + value: school.officialSchoolNumber as string, location: PropertyLocation.BODY, }, + { + name: concreteConfigParameter.name, + value: concreteConfigParameter.value, + location: concreteConfigParameter.location, + }, + ].sort(sortFn), + }); + }); + }); + + describe('when launching with context name parameter for the context "course"', () => { + const setup = () => { + const autoCourseNameCustomParameter = customParameterFactory.build({ + scope: CustomParameterScope.GLOBAL, + location: CustomParameterLocation.BODY, + name: 'autoCourseNameParam', + type: CustomParameterType.AUTO_CONTEXTNAME, + }); + + const externalTool: ExternalTool = externalToolFactory.build({ + parameters: [autoCourseNameCustomParameter], + }); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); + + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.build({ + contextRef: { + type: ToolContextType.COURSE, + }, + }); + + const course: Course = courseFactory.buildWithId( + { + name: 'testName', + }, + contextExternalTool.contextRef.id + ); + + courseService.findById.mockResolvedValue(course); + + return { + autoCourseNameCustomParameter, + externalTool, + schoolExternalTool, + contextExternalTool, + course, + }; + }; + + it('should return ToolLaunchData with the course name as parameter value', async () => { + const { externalTool, schoolExternalTool, contextExternalTool, autoCourseNameCustomParameter, course } = + setup(); + + const result: ToolLaunchData = await launchStrategy.createLaunchData('userId', { + externalTool, + schoolExternalTool, + contextExternalTool, + }); + + expect(result).toEqual({ + baseUrl: externalTool.config.baseUrl, + type: ToolLaunchDataType.BASIC, + openNewTab: false, + properties: [ { name: autoCourseNameCustomParameter.name, value: course.name, location: PropertyLocation.BODY, }, { - name: autoSchoolNumberCustomParameter.name, - value: school.officialSchoolNumber as string, + name: concreteConfigParameter.name, + value: concreteConfigParameter.value, + location: concreteConfigParameter.location, + }, + ], + }); + }); + }); + + describe('when launching with context id parameter', () => { + const setup = () => { + const autoContextIdCustomParameter = customParameterFactory.build({ + scope: CustomParameterScope.GLOBAL, + location: CustomParameterLocation.BODY, + name: 'autoContextIdParam', + type: CustomParameterType.AUTO_CONTEXTID, + }); + + const externalTool: ExternalTool = externalToolFactory.build({ + parameters: [autoContextIdCustomParameter], + }); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); + + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.build(); + + return { + autoContextIdCustomParameter, + externalTool, + schoolExternalTool, + contextExternalTool, + }; + }; + + it('should return ToolLaunchData with the context id as parameter value', async () => { + const { externalTool, schoolExternalTool, contextExternalTool, autoContextIdCustomParameter } = setup(); + + const result: ToolLaunchData = await launchStrategy.createLaunchData('userId', { + externalTool, + schoolExternalTool, + contextExternalTool, + }); + + expect(result).toEqual({ + baseUrl: externalTool.config.baseUrl, + type: ToolLaunchDataType.BASIC, + openNewTab: false, + properties: [ + { + name: autoContextIdCustomParameter.name, + value: contextExternalTool.contextRef.id, location: PropertyLocation.BODY, }, { @@ -299,7 +384,7 @@ describe('AbstractLaunchStrategy', () => { value: concreteConfigParameter.value, location: concreteConfigParameter.location, }, - ].sort(sortFn), + ], }); }); }); diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.ts index 9004e461ae2..644105a3c2b 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.ts @@ -215,15 +215,18 @@ export abstract class AbstractLaunchStrategy implements IToolLaunchStrategy { return contextExternalTool.contextRef.id; } case CustomParameterType.AUTO_CONTEXTNAME: { - if (contextExternalTool.contextRef.type === ToolContextType.COURSE) { - const course: Course = await this.courseService.findById(contextExternalTool.contextRef.id); - - return course.name; + switch (contextExternalTool.contextRef.type) { + case ToolContextType.COURSE: { + const course: Course = await this.courseService.findById(contextExternalTool.contextRef.id); + + return course.name; + } + default: { + throw new ParameterTypeNotImplementedLoggableException( + `${customParameter.type}/${contextExternalTool.contextRef.type as string}` + ); + } } - - throw new ParameterTypeNotImplementedLoggableException( - `${customParameter.type}/${contextExternalTool.contextRef.type as string}` - ); } case CustomParameterType.AUTO_SCHOOLNUMBER: { const school: LegacySchoolDo = await this.schoolService.getSchoolById(schoolExternalTool.schoolId); diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.spec.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.spec.ts index a0db37651be..1c31aac36b6 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.spec.ts @@ -9,12 +9,12 @@ import { userDoFactory, } from '@shared/testing'; import { pseudonymFactory } from '@shared/testing/factory/domainobject/pseudonym.factory'; -import { PseudonymService } from '@src/modules/pseudonym/service'; +import { CourseService } from '@src/modules/learnroom/service'; import { LegacySchoolService } from '@src/modules/legacy-school'; +import { PseudonymService } from '@src/modules/pseudonym/service'; import { UserService } from '@src/modules/user'; import { ObjectId } from 'bson'; import { Authorization } from 'oauth-1.0a'; -import { CourseService } from '@src/modules/learnroom/service'; import { LtiMessageType, LtiPrivacyPermission, LtiRole, ToolContextType } from '../../../common/enum'; import { ContextExternalTool } from '../../../context-external-tool/domain'; import { ExternalTool } from '../../../external-tool/domain'; diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.ts index 8c957ca9421..654e14b45d9 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.ts @@ -9,7 +9,7 @@ import { Authorization } from 'oauth-1.0a'; import { LtiRole } from '../../../common/enum'; import { ExternalTool } from '../../../external-tool/domain'; import { LtiRoleMapper } from '../../mapper'; -import { LaunchRequestMethod, PropertyData, PropertyLocation, AuthenticationValues } from '../../types'; +import { AuthenticationValues, LaunchRequestMethod, PropertyData, PropertyLocation } from '../../types'; import { Lti11EncryptionService } from '../lti11-encryption.service'; import { AbstractLaunchStrategy } from './abstract-launch.strategy'; import { IToolLaunchParams } from './tool-launch-params.interface'; diff --git a/apps/server/src/modules/tool/tool-launch/service/tool-launch.service.spec.ts b/apps/server/src/modules/tool/tool-launch/service/tool-launch.service.spec.ts index 02bb484093f..3330b0c9f0e 100644 --- a/apps/server/src/modules/tool/tool-launch/service/tool-launch.service.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/service/tool-launch.service.spec.ts @@ -7,7 +7,14 @@ import { externalToolFactory, schoolExternalToolFactory, } from '@shared/testing'; +import { ToolConfigType, ToolConfigurationStatus } from '../../common/enum'; +import { CommonToolService } from '../../common/service'; import { ContextExternalTool } from '../../context-external-tool/domain'; +import { BasicToolConfig, ExternalTool } from '../../external-tool/domain'; +import { ExternalToolService } from '../../external-tool/service'; +import { SchoolExternalTool } from '../../school-external-tool/domain'; +import { SchoolExternalToolService } from '../../school-external-tool/service'; +import { ToolStatusOutdatedLoggableException } from '../error'; import { LaunchRequestMethod, ToolLaunchData, ToolLaunchDataType, ToolLaunchRequest } from '../types'; import { BasicToolLaunchStrategy, @@ -16,13 +23,6 @@ import { OAuth2ToolLaunchStrategy, } from './strategy'; import { ToolLaunchService } from './tool-launch.service'; -import { ToolStatusOutdatedLoggableException } from '../error'; -import { SchoolExternalToolService } from '../../school-external-tool/service'; -import { ExternalToolService } from '../../external-tool/service'; -import { CommonToolService } from '../../common/service'; -import { SchoolExternalTool } from '../../school-external-tool/domain'; -import { BasicToolConfig, ExternalTool } from '../../external-tool/domain'; -import { ToolConfigType, ToolConfigurationStatus } from '../../common/enum'; describe('ToolLaunchService', () => { let module: TestingModule; @@ -104,8 +104,8 @@ describe('ToolLaunchService', () => { contextExternalTool, }; - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValue(schoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + schoolExternalToolService.findById.mockResolvedValue(schoolExternalTool); + externalToolService.findById.mockResolvedValue(externalTool); basicToolLaunchStrategy.createLaunchData.mockResolvedValue(launchDataDO); commonToolService.determineToolConfigurationStatus.mockReturnValueOnce(ToolConfigurationStatus.LATEST); @@ -136,9 +136,7 @@ describe('ToolLaunchService', () => { await service.getLaunchData('userId', launchParams.contextExternalTool); - expect(schoolExternalToolService.getSchoolExternalToolById).toHaveBeenCalledWith( - launchParams.schoolExternalTool.id - ); + expect(schoolExternalToolService.findById).toHaveBeenCalledWith(launchParams.schoolExternalTool.id); }); it('should call findExternalToolById', async () => { @@ -146,7 +144,7 @@ describe('ToolLaunchService', () => { await service.getLaunchData('userId', launchParams.contextExternalTool); - expect(externalToolService.findExternalToolById).toHaveBeenCalledWith(launchParams.schoolExternalTool.toolId); + expect(externalToolService.findById).toHaveBeenCalledWith(launchParams.schoolExternalTool.toolId); }); }); @@ -165,8 +163,8 @@ describe('ToolLaunchService', () => { contextExternalTool, }; - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValue(schoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + schoolExternalToolService.findById.mockResolvedValue(schoolExternalTool); + externalToolService.findById.mockResolvedValue(externalTool); commonToolService.determineToolConfigurationStatus.mockReturnValueOnce(ToolConfigurationStatus.LATEST); return { @@ -209,8 +207,8 @@ describe('ToolLaunchService', () => { const userId = 'userId'; - schoolExternalToolService.getSchoolExternalToolById.mockResolvedValue(schoolExternalTool); - externalToolService.findExternalToolById.mockResolvedValue(externalTool); + schoolExternalToolService.findById.mockResolvedValue(schoolExternalTool); + externalToolService.findById.mockResolvedValue(externalTool); basicToolLaunchStrategy.createLaunchData.mockResolvedValue(launchDataDO); commonToolService.determineToolConfigurationStatus.mockReturnValueOnce(ToolConfigurationStatus.OUTDATED); diff --git a/apps/server/src/modules/tool/tool-launch/service/tool-launch.service.ts b/apps/server/src/modules/tool/tool-launch/service/tool-launch.service.ts index 3321e782f09..46d2efdeb70 100644 --- a/apps/server/src/modules/tool/tool-launch/service/tool-launch.service.ts +++ b/apps/server/src/modules/tool/tool-launch/service/tool-launch.service.ts @@ -1,6 +1,13 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { EntityId } from '@shared/domain'; +import { ToolConfigType, ToolConfigurationStatus } from '../../common/enum'; import { CommonToolService } from '../../common/service'; +import { ContextExternalTool } from '../../context-external-tool/domain'; +import { ExternalTool } from '../../external-tool/domain'; +import { ExternalToolService } from '../../external-tool/service'; +import { SchoolExternalTool } from '../../school-external-tool/domain'; +import { SchoolExternalToolService } from '../../school-external-tool/service'; +import { ToolStatusOutdatedLoggableException } from '../error'; import { ToolLaunchMapper } from '../mapper'; import { ToolLaunchData, ToolLaunchRequest } from '../types'; import { @@ -9,13 +16,6 @@ import { Lti11ToolLaunchStrategy, OAuth2ToolLaunchStrategy, } from './strategy'; -import { ToolStatusOutdatedLoggableException } from '../error'; -import { SchoolExternalToolService } from '../../school-external-tool/service'; -import { ExternalToolService } from '../../external-tool/service'; -import { ToolConfigType, ToolConfigurationStatus } from '../../common/enum'; -import { ContextExternalTool } from '../../context-external-tool/domain'; -import { ExternalTool } from '../../external-tool/domain'; -import { SchoolExternalTool } from '../../school-external-tool/domain'; @Injectable() export class ToolLaunchService { @@ -73,11 +73,9 @@ export class ToolLaunchService { private async loadToolHierarchy( schoolExternalToolId: string ): Promise<{ schoolExternalTool: SchoolExternalTool; externalTool: ExternalTool }> { - const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.getSchoolExternalToolById( - schoolExternalToolId - ); + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById(schoolExternalToolId); - const externalTool: ExternalTool = await this.externalToolService.findExternalToolById(schoolExternalTool.toolId); + const externalTool: ExternalTool = await this.externalToolService.findById(schoolExternalTool.toolId); return { schoolExternalTool, diff --git a/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts b/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts index 62424d8b8aa..e9b7311e06a 100644 --- a/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts @@ -2,12 +2,12 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { contextExternalToolFactory } from '@shared/testing'; import { ObjectId } from 'bson'; +import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; +import { ContextExternalTool } from '../../context-external-tool/domain'; +import { ContextExternalToolService } from '../../context-external-tool/service'; import { ToolLaunchService } from '../service'; import { ToolLaunchData, ToolLaunchDataType, ToolLaunchRequest } from '../types'; import { ToolLaunchUc } from './tool-launch.uc'; -import { ContextExternalToolService } from '../../context-external-tool/service'; -import { ContextExternalTool } from '../../context-external-tool/domain'; -import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; describe('ToolLaunchUc', () => { let module: TestingModule; @@ -66,7 +66,7 @@ describe('ToolLaunchUc', () => { const userId: string = new ObjectId().toHexString(); toolPermissionHelper.ensureContextPermissions.mockResolvedValueOnce(); - contextExternalToolService.getContextExternalToolById.mockResolvedValueOnce(contextExternalTool); + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); toolLaunchService.getLaunchData.mockResolvedValueOnce(toolLaunchData); return { @@ -82,7 +82,7 @@ describe('ToolLaunchUc', () => { await uc.getToolLaunchRequest(userId, contextExternalToolId); - expect(contextExternalToolService.getContextExternalToolById).toHaveBeenCalledWith(contextExternalToolId); + expect(contextExternalToolService.findById).toHaveBeenCalledWith(contextExternalToolId); }); it('should call service to get data', async () => { diff --git a/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.ts b/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.ts index c397ae1d1af..fed27fa9aad 100644 --- a/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.ts +++ b/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.ts @@ -1,11 +1,11 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; import { AuthorizationContext, AuthorizationContextBuilder } from '@src/modules/authorization'; +import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; +import { ContextExternalTool } from '../../context-external-tool/domain'; +import { ContextExternalToolService } from '../../context-external-tool/service'; import { ToolLaunchService } from '../service'; import { ToolLaunchData, ToolLaunchRequest } from '../types'; -import { ContextExternalToolService } from '../../context-external-tool/service'; -import { ContextExternalTool } from '../../context-external-tool/domain'; -import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; @Injectable() export class ToolLaunchUc { @@ -16,7 +16,7 @@ export class ToolLaunchUc { ) {} async getToolLaunchRequest(userId: EntityId, contextExternalToolId: EntityId): Promise { - const contextExternalTool: ContextExternalTool = await this.contextExternalToolService.getContextExternalToolById( + const contextExternalTool: ContextExternalTool = await this.contextExternalToolService.findById( contextExternalToolId ); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts index 7c02cbc8a75..6806aeb3f71 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts @@ -127,7 +127,7 @@ describe('ContextExternalToolRepo', () => { }); describe('save', () => { - describe('when context is known', () => { + describe('when context is course', () => { function setup() { const domainObject: ContextExternalTool = contextExternalToolFactory.build({ displayName: 'displayName', @@ -159,6 +159,38 @@ describe('ContextExternalToolRepo', () => { }); }); + describe('when context is board card', () => { + function setup() { + const domainObject: ContextExternalTool = contextExternalToolFactory.build({ + displayName: 'displayName', + contextRef: { + id: new ObjectId().toHexString(), + type: ToolContextType.BOARD_ELEMENT, + }, + parameters: [new CustomParameterEntry({ name: 'param', value: 'value' })], + schoolToolRef: { + schoolToolId: new ObjectId().toHexString(), + schoolId: undefined, + }, + toolVersion: 1, + }); + + return { + domainObject, + }; + } + + it('should save a ContextExternalToolDO', async () => { + const { domainObject } = setup(); + const { id, ...expected } = domainObject; + + const result: ContextExternalTool = await repo.save(domainObject); + + expect(result).toMatchObject(expected); + expect(result.id).toBeDefined(); + }); + }); + describe('when context is unknown', () => { const setup = () => { const domainObject: ContextExternalTool = contextExternalToolFactory.build({ diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts index b766828beff..084adb4b727 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts @@ -115,6 +115,8 @@ export class ContextExternalToolRepo extends BaseDORepo< switch (type) { case ToolContextType.COURSE: return ContextExternalToolType.COURSE; + case ToolContextType.BOARD_ELEMENT: + return ContextExternalToolType.BOARD_ELEMENT; default: throw new Error('Unknown ToolContextType'); } @@ -124,6 +126,8 @@ export class ContextExternalToolRepo extends BaseDORepo< switch (type) { case ContextExternalToolType.COURSE: return ToolContextType.COURSE; + case ContextExternalToolType.BOARD_ELEMENT: + return ToolContextType.BOARD_ELEMENT; default: throw new Error('Unknown ContextExternalToolType'); } From fa3a4f7d671acb15f3c90c075b2a97b1e762a358 Mon Sep 17 00:00:00 2001 From: virgilchiriac <17074330+virgilchiriac@users.noreply.github.com> Date: Thu, 19 Oct 2023 12:10:55 +0200 Subject: [PATCH 03/14] BC-5566 - fix merlin feathers service (#4480) Rename the merlinToken feathers service path to a unique path instead of subpath of edu-shraring Not clear why, but feathers service was suddenly mapped wrong and calling edu-sharing service instead of merlinToken service. Changing the order of the services load helped, but then lots of tests fail. --- src/services/edusharing/index.js | 4 +- src/services/lesson/hooks/index.js | 90 ++++++++++--------- .../services/merlinGenerator.test.js | 2 +- 3 files changed, 49 insertions(+), 47 deletions(-) diff --git a/src/services/edusharing/index.js b/src/services/edusharing/index.js index d002686ebea..27a45244494 100644 --- a/src/services/edusharing/index.js +++ b/src/services/edusharing/index.js @@ -35,7 +35,7 @@ class EduSharingPlayer { throw new MethodNotAllowed('This feature is disabled on this instance'); } const esPlayer = EduSharingConnectorV7.getPlayerForNode(uuid); - + return esPlayer; } } @@ -49,7 +49,7 @@ class MerlinToken { module.exports = (app) => { const eduSharingRoute = '/edu-sharing'; const eduSharingPlayerRoute = '/edu-sharing/player'; - const merlinRoute = '/edu-sharing/merlinToken'; + const merlinRoute = '/edu-sharing-merlinToken'; const docRoute = '/edu-sharing/api'; app.use(eduSharingRoute, new EduSharing()); diff --git a/src/services/lesson/hooks/index.js b/src/services/lesson/hooks/index.js index b7b0b0ea5e7..458b64785ab 100644 --- a/src/services/lesson/hooks/index.js +++ b/src/services/lesson/hooks/index.js @@ -60,7 +60,7 @@ const convertMerlinUrl = async (context) => { await Promise.all( content.content.resources.map(async (resource) => { if (resource && resource.merlinReference) { - resource.url = await context.app.service('edu-sharing/merlinToken').find({ + resource.url = await context.app.service('edu-sharing-merlinToken').find({ ...context.params, query: { ...context.params.query, merlinReference: resource.merlinReference }, }); @@ -82,7 +82,7 @@ const convertMerlinUrl = async (context) => { await Promise.all( materialIds.map(async (material) => { if (material.merlinReference) { - material.url = await context.app.service('edu-sharing/merlinToken').find({ + material.url = await context.app.service('edu-sharing-merlinToken').find({ ...context.params, query: { ...context.params.query, @@ -229,48 +229,50 @@ const populateWhitelist = { ], }; -exports.before = () => ({ - all: [authenticate('jwt'), mapUsers], - find: [ - hasPermission('TOPIC_VIEW'), - iff(isProvider('external'), validateLessonFind), - iff(isProvider('external'), getRestrictPopulatesHook(populateWhitelist)), - iff(isProvider('external'), restrictToUsersCoursesLessons), - ], - get: [ - hasPermission('TOPIC_VIEW'), - iff(isProvider('external'), getRestrictPopulatesHook(populateWhitelist)), - iff(isProvider('external'), restrictToUsersCoursesLessons), - ], - create: [ - checkIfCourseGroupLesson.bind(this, 'COURSEGROUP_CREATE', 'TOPIC_CREATE', true), - injectUserId, - checkCorrectCourseOrTeamId, - setPosition, - iff(isProvider('external'), preventPopulate), - ], - update: [ - iff(isProvider('external'), preventPopulate), - permitGroupOperation, - ifNotLocal(checkCorrectCourseOrTeamId), - iff(isProvider('external'), restrictToUsersCoursesLessons), - checkIfCourseGroupLesson.bind(this, 'COURSEGROUP_EDIT', 'TOPIC_EDIT', false), - ], - patch: [ - attachMerlinReferenceToLesson, - checkIfCourseGroupLesson.bind(this, 'COURSEGROUP_EDIT', 'TOPIC_EDIT', false), - permitGroupOperation, - ifNotLocal(checkCorrectCourseOrTeamId), - iff(isProvider('external'), restrictToUsersCoursesLessons), - iff(isProvider('external'), preventPopulate), - ], - remove: [ - checkIfCourseGroupLesson.bind(this, 'COURSEGROUP_CREATE', 'TOPIC_CREATE', false), - permitGroupOperation, - iff(isProvider('external'), restrictToUsersCoursesLessons), - iff(isProvider('external'), preventPopulate), - ], -}); +exports.before = () => { + return { + all: [authenticate('jwt'), mapUsers], + find: [ + hasPermission('TOPIC_VIEW'), + iff(isProvider('external'), validateLessonFind), + iff(isProvider('external'), getRestrictPopulatesHook(populateWhitelist)), + iff(isProvider('external'), restrictToUsersCoursesLessons), + ], + get: [ + hasPermission('TOPIC_VIEW'), + iff(isProvider('external'), getRestrictPopulatesHook(populateWhitelist)), + iff(isProvider('external'), restrictToUsersCoursesLessons), + ], + create: [ + checkIfCourseGroupLesson.bind(this, 'COURSEGROUP_CREATE', 'TOPIC_CREATE', true), + injectUserId, + checkCorrectCourseOrTeamId, + setPosition, + iff(isProvider('external'), preventPopulate), + ], + update: [ + iff(isProvider('external'), preventPopulate), + permitGroupOperation, + ifNotLocal(checkCorrectCourseOrTeamId), + iff(isProvider('external'), restrictToUsersCoursesLessons), + checkIfCourseGroupLesson.bind(this, 'COURSEGROUP_EDIT', 'TOPIC_EDIT', false), + ], + patch: [ + attachMerlinReferenceToLesson, + checkIfCourseGroupLesson.bind(this, 'COURSEGROUP_EDIT', 'TOPIC_EDIT', false), + permitGroupOperation, + ifNotLocal(checkCorrectCourseOrTeamId), + iff(isProvider('external'), restrictToUsersCoursesLessons), + iff(isProvider('external'), preventPopulate), + ], + remove: [ + checkIfCourseGroupLesson.bind(this, 'COURSEGROUP_CREATE', 'TOPIC_CREATE', false), + permitGroupOperation, + iff(isProvider('external'), restrictToUsersCoursesLessons), + iff(isProvider('external'), preventPopulate), + ], + }; +}; exports.after = { all: [], diff --git a/test/services/edusharing/services/merlinGenerator.test.js b/test/services/edusharing/services/merlinGenerator.test.js index 43d5fbde13e..00e83f80461 100644 --- a/test/services/edusharing/services/merlinGenerator.test.js +++ b/test/services/edusharing/services/merlinGenerator.test.js @@ -17,7 +17,7 @@ describe('Merlin Token Generator', () => { before(async () => { app = await appPromise(); - MerlinTokenGeneratorService = app.service('edu-sharing/merlinToken'); + MerlinTokenGeneratorService = app.service('edu-sharing-merlinToken'); server = await app.listen(0); nestServices = await setupNestServices(app); }); From 4e8cfa0df589dd97e8aa7de42df840be46eca040 Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Thu, 19 Oct 2023 17:33:56 +0200 Subject: [PATCH 04/14] BC-4942 - authorization reference service (#4413) * Split reference authorisation service from authorisation service * Replace on some places the authorisation methodes * Move rules into authorisation module to fix dependency issues * Fix invalid tests * Fix invalid imports --- .../src/modules/authorization/README.md | 101 ++--- .../authorization-reference.module.ts | 43 ++ .../authorization/authorization.module.ts | 69 ++-- .../authorization/authorization.service.ts | 100 ----- .../error}/forbidden.loggable-exception.ts | 2 +- .../authorization/domain/error/index.ts | 1 + .../src/modules/authorization/domain/index.ts | 4 + .../authorization-context.builder.spec.ts | 2 +- .../mapper}/authorization-context.builder.ts | 2 +- .../authorization/domain/mapper/index.ts | 1 + .../domain/rules/board-do.rule.spec.ts | 8 +- .../domain/rules/board-do.rule.ts | 8 +- .../rules/context-external-tool.rule.spec.ts | 9 +- .../rules/context-external-tool.rule.ts | 6 +- .../domain/rules/course-group.rule.spec.ts | 4 +- .../domain/rules/course-group.rule.ts | 4 +- .../domain/rules/course.rule.spec.ts | 4 +- .../domain/rules/course.rule.ts | 4 +- .../authorization/domain/rules/index.ts | 16 + .../domain/rules/legacy-school.rule.spec.ts | 4 +- .../domain/rules/legacy-school.rule.ts | 6 +- .../domain/rules/lesson.rule.spec.ts | 108 +++-- .../domain/rules/lesson.rule.ts | 12 +- .../rules/school-external-tool.rule.spec.ts | 8 +- .../domain/rules/school-external-tool.rule.ts | 6 +- .../domain/rules/submission.rule.spec.ts | 43 +- .../domain/rules/submission.rule.ts | 8 +- .../domain/rules/task.rule.spec.ts | 9 +- .../authorization}/domain/rules/task.rule.ts | 4 +- .../domain/rules/team.rule.spec.ts | 9 +- .../authorization}/domain/rules/team.rule.ts | 4 +- .../rules/user-login-migration.rule.spec.ts | 8 +- .../domain/rules/user-login-migration.rule.ts | 8 +- .../domain/rules/user.rule.spec.ts | 4 +- .../authorization}/domain/rules/user.rule.ts | 4 +- .../authorization-reference.service.spec.ts | 183 +++++++++ .../authorization-reference.service.ts | 41 ++ .../service}/authorization.helper.spec.ts | 0 .../service}/authorization.helper.ts | 0 .../service}/authorization.service.spec.ts | 186 ++------- .../domain/service/authorization.service.ts | 59 +++ .../authorization/domain/service/index.ts | 5 + .../service}/reference.loader.spec.ts | 35 +- .../{ => domain/service}/reference.loader.ts | 15 +- .../{ => domain/service}/rule-manager.spec.ts | 8 +- .../{ => domain/service}/rule-manager.ts | 10 +- .../{types => domain/type}/action.enum.ts | 0 .../allowed-authorization-object-type.enum.ts | 0 .../type}/authorization-context.interface.ts | 0 .../type}/authorization-loader-service.ts | 0 .../{types => domain/type}/index.ts | 2 +- .../{types => domain/type}/rule.interface.ts | 0 .../server/src/modules/authorization/index.ts | 20 +- apps/server/src/modules/board/uc/board.uc.ts | 4 +- apps/server/src/modules/board/uc/card.uc.ts | 3 +- .../server/src/modules/board/uc/element.uc.ts | 3 +- .../board/uc/submission-item.uc.spec.ts | 3 +- .../modules/board/uc/submission-item.uc.ts | 3 +- .../src/modules/files-storage/README.md | 2 +- .../files-storage/files-storage-api.module.ts | 4 +- .../mapper/files-storage.mapper.spec.ts | 2 +- .../mapper/files-storage.mapper.ts | 2 +- .../uc/files-storage-copy.uc.spec.ts | 43 +- .../uc/files-storage-delete.uc.spec.ts | 26 +- .../files-storage-download-preview.uc.spec.ts | 14 +- .../uc/files-storage-download.uc.spec.ts | 18 +- .../uc/files-storage-get.uc.spec.ts | 18 +- .../uc/files-storage-restore.uc.spec.ts | 24 +- .../uc/files-storage-update.uc.spec.ts | 14 +- .../uc/files-storage-upload.uc.spec.ts | 19 +- .../files-storage/uc/files-storage.uc.ts | 7 +- .../api-test/rooms-copy-timeout.api.spec.ts | 11 +- apps/server/src/modules/learnroom/index.ts | 1 + .../modules/learnroom/learnroom-api.module.ts | 3 +- .../learnroom/uc/course-copy.uc.spec.ts | 150 +++---- .../modules/learnroom/uc/course-copy.uc.ts | 12 +- .../learnroom/uc/course-export.uc.spec.ts | 83 +++- .../modules/learnroom/uc/course-export.uc.ts | 17 +- .../learnroom/uc/lesson-copy.uc.spec.ts | 388 +++++++++++------- .../modules/learnroom/uc/lesson-copy.uc.ts | 54 +-- .../learnroom/uc/room-board-dto.factory.ts | 3 +- .../legacy-school/uc/legacy-school.uc.spec.ts | 17 +- .../legacy-school/uc/legacy-school.uc.ts | 33 +- .../uc/oauth-provider.client-crud.uc.ts | 2 +- .../api-test/sharing-create-token.api.spec.ts | 32 +- .../api-test/sharing-import-token.api.spec.ts | 55 ++- .../api-test/sharing-lookup-token.api.spec.ts | 255 +++++++----- .../mapper/context-type.mapper.spec.ts | 2 +- .../sharing/mapper/context-type.mapper.ts | 3 +- .../sharing/mapper/parent-type.mapper.spec.ts | 2 +- .../sharing/mapper/parent-type.mapper.ts | 2 +- .../src/modules/sharing/sharing.module.ts | 13 +- .../modules/sharing/uc/share-token.uc.spec.ts | 44 +- .../src/modules/sharing/uc/share-token.uc.ts | 31 +- .../src/modules/task/uc/task-copy.uc.spec.ts | 157 +++---- .../src/modules/task/uc/task-copy.uc.ts | 85 ++-- .../modules/tool/common/common-tool.module.ts | 3 +- .../common/mapper/context-type.mapper.spec.ts | 11 + .../tool/common/mapper/context-type.mapper.ts | 2 +- .../tool/common/uc/tool-permission-helper.ts | 45 +- .../common/uc/tool-permissions-helper.spec.ts | 119 +++++- .../api-test/tool-context.api.spec.ts | 117 +++--- .../controller/tool-context.controller.ts | 1 + .../uc/context-external-tool.uc.spec.ts | 8 +- .../uc/context-external-tool.uc.ts | 14 +- .../api-test/tool-configuration.api.spec.ts | 84 ++-- .../uc/external-tool-configuration.uc.ts | 2 +- .../tool/tool-launch/tool-launch.module.ts | 4 +- .../mapper/video-conference.mapper.ts | 8 +- .../service/video-conference.service.spec.ts | 166 ++++++-- .../service/video-conference.service.ts | 91 ++-- .../uc/video-conference-create.uc.ts | 6 + .../uc/video-conference-deprecated.uc.spec.ts | 13 +- .../uc/video-conference-deprecated.uc.ts | 14 +- .../uc/video-conference-end.uc.ts | 6 + .../uc/video-conference-info.uc.ts | 6 + .../video-conference.module.ts | 2 + apps/server/src/shared/domain/rules/index.ts | 39 -- .../src/shared/infra/antivirus/index.ts | 6 +- .../src/shared/testing/test-api-client.ts | 4 + 120 files changed, 2162 insertions(+), 1470 deletions(-) create mode 100644 apps/server/src/modules/authorization/authorization-reference.module.ts delete mode 100644 apps/server/src/modules/authorization/authorization.service.ts rename apps/server/src/modules/authorization/{errors => domain/error}/forbidden.loggable-exception.ts (94%) create mode 100644 apps/server/src/modules/authorization/domain/error/index.ts create mode 100644 apps/server/src/modules/authorization/domain/index.ts rename apps/server/src/modules/authorization/{ => domain/mapper}/authorization-context.builder.spec.ts (96%) rename apps/server/src/modules/authorization/{ => domain/mapper}/authorization-context.builder.ts (91%) create mode 100644 apps/server/src/modules/authorization/domain/mapper/index.ts rename apps/server/src/{shared => modules/authorization}/domain/rules/board-do.rule.spec.ts (95%) rename apps/server/src/{shared => modules/authorization}/domain/rules/board-do.rule.ts (81%) rename apps/server/src/{shared => modules/authorization}/domain/rules/context-external-tool.rule.spec.ts (93%) rename apps/server/src/{shared => modules/authorization}/domain/rules/context-external-tool.rule.ts (85%) rename apps/server/src/{shared => modules/authorization}/domain/rules/course-group.rule.spec.ts (97%) rename apps/server/src/{shared => modules/authorization}/domain/rules/course-group.rule.ts (84%) rename apps/server/src/{shared => modules/authorization}/domain/rules/course.rule.spec.ts (95%) rename apps/server/src/{shared => modules/authorization}/domain/rules/course.rule.ts (82%) create mode 100644 apps/server/src/modules/authorization/domain/rules/index.ts rename apps/server/src/{shared => modules/authorization}/domain/rules/legacy-school.rule.spec.ts (93%) rename apps/server/src/{shared => modules/authorization}/domain/rules/legacy-school.rule.ts (78%) rename apps/server/src/{shared => modules/authorization}/domain/rules/lesson.rule.spec.ts (50%) rename apps/server/src/{shared => modules/authorization}/domain/rules/lesson.rule.ts (88%) rename apps/server/src/{shared => modules/authorization}/domain/rules/school-external-tool.rule.spec.ts (92%) rename apps/server/src/{shared => modules/authorization}/domain/rules/school-external-tool.rule.ts (85%) rename apps/server/src/{shared => modules/authorization}/domain/rules/submission.rule.spec.ts (93%) rename apps/server/src/{shared => modules/authorization}/domain/rules/submission.rule.ts (88%) rename apps/server/src/{shared => modules/authorization}/domain/rules/task.rule.spec.ts (96%) rename apps/server/src/{shared => modules/authorization}/domain/rules/task.rule.ts (90%) rename apps/server/src/{shared => modules/authorization}/domain/rules/team.rule.spec.ts (91%) rename apps/server/src/{shared => modules/authorization}/domain/rules/team.rule.ts (81%) rename apps/server/src/{shared => modules/authorization}/domain/rules/user-login-migration.rule.spec.ts (94%) rename apps/server/src/{shared => modules/authorization}/domain/rules/user-login-migration.rule.ts (72%) rename apps/server/src/{shared => modules/authorization}/domain/rules/user.rule.spec.ts (94%) rename apps/server/src/{shared => modules/authorization}/domain/rules/user.rule.ts (78%) create mode 100644 apps/server/src/modules/authorization/domain/service/authorization-reference.service.spec.ts create mode 100644 apps/server/src/modules/authorization/domain/service/authorization-reference.service.ts rename apps/server/src/modules/authorization/{ => domain/service}/authorization.helper.spec.ts (100%) rename apps/server/src/modules/authorization/{ => domain/service}/authorization.helper.ts (100%) rename apps/server/src/modules/authorization/{ => domain/service}/authorization.service.spec.ts (60%) create mode 100644 apps/server/src/modules/authorization/domain/service/authorization.service.ts create mode 100644 apps/server/src/modules/authorization/domain/service/index.ts rename apps/server/src/modules/authorization/{ => domain/service}/reference.loader.spec.ts (87%) rename apps/server/src/modules/authorization/{ => domain/service}/reference.loader.ts (88%) rename apps/server/src/modules/authorization/{ => domain/service}/rule-manager.spec.ts (97%) rename apps/server/src/modules/authorization/{ => domain/service}/rule-manager.ts (88%) rename apps/server/src/modules/authorization/{types => domain/type}/action.enum.ts (100%) rename apps/server/src/modules/authorization/{types => domain/type}/allowed-authorization-object-type.enum.ts (100%) rename apps/server/src/modules/authorization/{types => domain/type}/authorization-context.interface.ts (100%) rename apps/server/src/modules/authorization/{types => domain/type}/authorization-loader-service.ts (100%) rename apps/server/src/modules/authorization/{types => domain/type}/index.ts (100%) rename apps/server/src/modules/authorization/{types => domain/type}/rule.interface.ts (100%) create mode 100644 apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts delete mode 100644 apps/server/src/shared/domain/rules/index.ts diff --git a/apps/server/src/modules/authorization/README.md b/apps/server/src/modules/authorization/README.md index 645feed2e64..7d2b69d209b 100644 --- a/apps/server/src/modules/authorization/README.md +++ b/apps/server/src/modules/authorization/README.md @@ -132,17 +132,7 @@ When calling other internal micro service for already authorized operations plea // next orchestration steps ``` -### Example 2 - Execute a Single Operation with Loading Resources - -```javascript -// If you don't have an entity but an entity type and id, you can check permission by reference -await this.authorizationService.checkPermissionByReferences(userId, AllowedEntity.course, courseId, AuthorizationContextBuilder.read([])); -// or -await this.authorizationService.hasPermissionByReferences(userId, AllowedEntity.course, courseId, AuthorizationContextBuilder.read([])); -// next orchestration steps -``` - -### Example 3 - Set Permission(s) of User as Required +### Example 2 - Set Permission(s) of User as Required ```javascript // Multiple permissions can be added. For a successful authorization, the user need all of them. @@ -173,14 +163,13 @@ this.authorizationService.hasPermission(userId, course, PermissionContexts.creat ```ts async createSchoolBySuperhero(userId: EntityId, params: { name: string }) { - const user = this.authorizationService.getUserWithPermissions(userId); - this.authorizationService.hasAllPermissions(user, [Permission.SCHOOL_CREATE]); - - const school = new School(params); + const user = this.authorizationService.getUserWithPermissions(userId); + this.authorizationService.hasAllPermissions(user, [Permission.SCHOOL_CREATE]); - await this.schoolService.save(school); + const school = new School(params); + await this.schoolService.save(school); - return true; + return true; } ``` @@ -191,15 +180,15 @@ async createSchoolBySuperhero(userId: EntityId, params: { name: string }) { async createUserByAdmin(userId: EntityId, params: { email: string, firstName: string, lastName: string, schoolId: EntityId }) { - const user = this.authorizationService.getUserWithPermissions(userId); - - await this.authorizationService.checkPermissionByReferences(userId, AllowedEntity.school, schoolId, AuthorizationContextBuilder.write([Permission.INSTANCE, Permission.CREATE_USER])); - - const newUser = new User(params) + const user = this.authorizationService.getUserWithPermissions(userId); + + const context = AuthorizationContextBuilder.write([Permission.INSTANCE, Permission.CREATE_USER]) + await this.authorizationService.checkPermission(user, school, context); - await this.userService.save(newUser); + const newUser = new User(params) + await this.userService.save(newUser); - return true; + return true; } ``` @@ -210,18 +199,17 @@ async createUserByAdmin(userId: EntityId, params: { email: string, firstName: st // admin async editCourseByAdmin(userId: EntityId, params: { courseId: EntityId, description: string }) { - const course = this.courseService.getCourse(params.courseId); - const user = this.authorizationService.getUserWithPermissions(userId); - - const school = course.school - - this.authorizationService.hasPermissions(user, school, [Permission.INSTANCE, Permission.COURSE_EDIT]); + const course = this.courseService.getCourse(params.courseId); + const user = this.authorizationService.getUserWithPermissions(userId); + const school = course.school; - course.description = params.description; + const context = AuthorizationContextBuilder.write([Permission.INSTANCE, Permission.CREATE_USER]); + this.authorizationService.checkPermissions(user, school, context); - await this.courseService.save(course); + course.description = params.description; + await this.courseService.save(course); - return true; + return true; } ``` @@ -234,18 +222,17 @@ async createCourse(userId: EntityId, params: { schoolId: EntityId }) { const user = this.authorizationService.getUserWithPermissions(userId); const school = this.schoolService.getSchool(params.schoolId); - this.authorizationService.checkPermission(user, school - { - action: Actions.write, - requiredPermissions: [Permission.COURSE_CREATE], - } - ); + this.authorizationService.checkPermission(user, school + { + action: Actions.write, + requiredPermissions: [Permission.COURSE_CREATE], + } + ); - const course = new Course({ school }); + const course = new Course({ school }); + await this.courseService.saveCourse(course); - await this.courseService.saveCourse(course); - - return course; + return course; } ``` @@ -255,21 +242,20 @@ async createCourse(userId: EntityId, params: { schoolId: EntityId }) { ```ts // User can create a lesson to course, so you have a courseId async createLesson(userId: EntityId, params: { courseId: EntityId }) { - const course = this.courseService.getCourse(params.courseId); - const user = this.authorizationService.getUserWithPermissions(userId); + const course = this.courseService.getCourse(params.courseId); + const user = this.authorizationService.getUserWithPermissions(userId); // check authorization for user and course - this.authorizationService.checkPermission(user, course - { - action: Actions.write, - requiredPermissions: [Permission.COURSE_EDIT], - } - ); - - const lesson = new Lesson({course}); + this.authorizationService.checkPermission(user, course + { + action: Actions.write, + requiredPermissions: [Permission.COURSE_EDIT], + } + ); - await this.lessonService.saveLesson(lesson); + const lesson = new Lesson({course}); + await this.lessonService.saveLesson(lesson); - return true; + return true; } ``` @@ -345,8 +331,9 @@ The authorization module is the core of authorization. It collects all needed in ### Reference.loader -For situations where only the id and the domain object (string) type is known, it is possible to use the \*ByReferences methods. -They load the reference directly. +It should be use only inside of the authorization module. +It is use to load registrated ressouces by the id and name of the ressource. +This is needed to solve the API requests from external services. (API implementation is missing for now) > Please keep in mind that it can have an impact on the performance if you use it wrongly. > We keep it as a seperate method to avoid the usage in areas where the domain object should exist, because we see the risk that a developer could be tempted by the ease of only passing the id. diff --git a/apps/server/src/modules/authorization/authorization-reference.module.ts b/apps/server/src/modules/authorization/authorization-reference.module.ts new file mode 100644 index 00000000000..7346f2178dd --- /dev/null +++ b/apps/server/src/modules/authorization/authorization-reference.module.ts @@ -0,0 +1,43 @@ +import { forwardRef, Module } from '@nestjs/common'; +import { + CourseGroupRepo, + CourseRepo, + LessonRepo, + SchoolExternalToolRepo, + LegacySchoolRepo, + SubmissionRepo, + TaskRepo, + TeamsRepo, + UserRepo, +} from '@shared/repo'; +import { ToolModule } from '@src/modules/tool'; +import { LoggerModule } from '@src/core/logger'; +import { BoardModule } from '@src/modules/board'; +import { ReferenceLoader, AuthorizationReferenceService, AuthorizationHelper } from './domain'; +import { AuthorizationModule } from './authorization.module'; + +/** + * This module is part of an intermediate state. In the future it should be replaced by an AuthorizationApiModule. + * For now it is used where the authorization itself needs to load data from the database. + * Avoid using this module and load the needed data in your use cases and then use the normal AuthorizationModule! + */ +@Module({ + // TODO: remove forwardRef to TooModule N21-1055 + imports: [AuthorizationModule, forwardRef(() => ToolModule), forwardRef(() => BoardModule), LoggerModule], + providers: [ + AuthorizationHelper, + ReferenceLoader, + UserRepo, + CourseRepo, + CourseGroupRepo, + TaskRepo, + LegacySchoolRepo, + LessonRepo, + TeamsRepo, + SubmissionRepo, + SchoolExternalToolRepo, + AuthorizationReferenceService, + ], + exports: [AuthorizationReferenceService], +}) +export class AuthorizationReferenceModule {} diff --git a/apps/server/src/modules/authorization/authorization.module.ts b/apps/server/src/modules/authorization/authorization.module.ts index c983ee187fd..37ca0a2b229 100644 --- a/apps/server/src/modules/authorization/authorization.module.ts +++ b/apps/server/src/modules/authorization/authorization.module.ts @@ -1,53 +1,46 @@ -import { forwardRef, Module } from '@nestjs/common'; -import { ALL_RULES } from '@shared/domain/rules'; +import { Module } from '@nestjs/common'; +import { UserRepo } from '@shared/repo'; +import { LoggerModule } from '@src/core/logger'; import { FeathersModule } from '@shared/infra/feathers'; import { - CourseGroupRepo, - CourseRepo, - LessonRepo, - SchoolExternalToolRepo, - LegacySchoolRepo, - SubmissionRepo, - TaskRepo, - TeamsRepo, - UserRepo, -} from '@shared/repo'; -import { LoggerModule } from '@src/core/logger'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; -import { ToolModule } from '@src/modules/tool'; -import { BoardModule } from '../board'; -import { AuthorizationHelper } from './authorization.helper'; -import { AuthorizationService } from './authorization.service'; + BoardDoRule, + ContextExternalToolRule, + CourseGroupRule, + CourseRule, + LessonRule, + SchoolExternalToolRule, + SubmissionRule, + TaskRule, + TeamRule, + UserRule, + UserLoginMigrationRule, + LegacySchoolRule, +} from './domain/rules'; +import { AuthorizationHelper, AuthorizationService, RuleManager } from './domain'; import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; -import { ReferenceLoader } from './reference.loader'; -import { RuleManager } from './rule-manager'; @Module({ - // TODO: remove forwardRef to TooModule N21-1055 - imports: [ - FeathersModule, - LoggerModule, - LegacySchoolModule, - forwardRef(() => ToolModule), - forwardRef(() => BoardModule), - ], + imports: [FeathersModule, LoggerModule], providers: [ FeathersAuthorizationService, FeathersAuthProvider, AuthorizationService, - ...ALL_RULES, - ReferenceLoader, UserRepo, - CourseRepo, - CourseGroupRepo, - TaskRepo, - LegacySchoolRepo, - LessonRepo, - TeamsRepo, - SubmissionRepo, - SchoolExternalToolRepo, RuleManager, AuthorizationHelper, + // rules + BoardDoRule, + ContextExternalToolRule, + CourseGroupRule, + CourseRule, + LessonRule, + SchoolExternalToolRule, + SubmissionRule, + TaskRule, + TeamRule, + UserRule, + UserLoginMigrationRule, + LegacySchoolRule, ], exports: [FeathersAuthorizationService, AuthorizationService], }) diff --git a/apps/server/src/modules/authorization/authorization.service.ts b/apps/server/src/modules/authorization/authorization.service.ts deleted file mode 100644 index b89561f1c30..00000000000 --- a/apps/server/src/modules/authorization/authorization.service.ts +++ /dev/null @@ -1,100 +0,0 @@ -import { ForbiddenException, Injectable, UnauthorizedException } from '@nestjs/common'; -import { BaseDO, EntityId, User } from '@shared/domain'; -import { AuthorizableObject } from '@shared/domain/domain-object'; -import { ErrorUtils } from '@src/core/error/utils'; -import { AuthorizationHelper } from './authorization.helper'; -import { ForbiddenLoggableException } from './errors/forbidden.loggable-exception'; -import { ReferenceLoader } from './reference.loader'; -import { RuleManager } from './rule-manager'; -import { AuthorizableReferenceType, AuthorizationContext } from './types'; - -@Injectable() -export class AuthorizationService { - constructor( - private readonly ruleManager: RuleManager, - private readonly loader: ReferenceLoader, - private readonly authorizationHelper: AuthorizationHelper - ) {} - - public checkPermission(user: User, object: AuthorizableObject | BaseDO, context: AuthorizationContext): void { - if (!this.hasPermission(user, object, context)) { - throw new ForbiddenLoggableException(user.id, object.constructor.name, context); - } - } - - public hasPermission(user: User, object: AuthorizableObject | BaseDO, context: AuthorizationContext): boolean { - const rule = this.ruleManager.selectRule(user, object, context); - const hasPermission = rule.hasPermission(user, object, context); - - return hasPermission; - } - - /** - * @deprecated - */ - public async checkPermissionByReferences( - userId: EntityId, - entityName: AuthorizableReferenceType, - entityId: EntityId, - context: AuthorizationContext - ): Promise { - if (!(await this.hasPermissionByReferences(userId, entityName, entityId, context))) { - throw new ForbiddenLoggableException(userId, entityName, context); - } - } - - /** - * @deprecated - */ - public async hasPermissionByReferences( - userId: EntityId, - entityName: AuthorizableReferenceType, - entityId: EntityId, - context: AuthorizationContext - ): Promise { - // TODO: This try-catch-block should be removed. See ticket: https://ticketsystem.dbildungscloud.de/browse/BC-4023 - try { - const [user, object] = await Promise.all([ - this.getUserWithPermissions(userId), - this.loader.loadAuthorizableObject(entityName, entityId), - ]); - const rule = this.ruleManager.selectRule(user, object, context); - const hasPermission = rule.hasPermission(user, object, context); - - return hasPermission; - } catch (error) { - throw new ForbiddenException( - null, - ErrorUtils.createHttpExceptionOptions(error, 'AuthorizationService:hasPermissionByReferences') - ); - } - } - - public checkAllPermissions(user: User, requiredPermissions: string[]): void { - if (!this.authorizationHelper.hasAllPermissions(user, requiredPermissions)) { - // TODO: Should be ForbiddenException - throw new UnauthorizedException(); - } - } - - public hasAllPermissions(user: User, requiredPermissions: string[]): boolean { - return this.authorizationHelper.hasAllPermissions(user, requiredPermissions); - } - - public checkOneOfPermissions(user: User, requiredPermissions: string[]): void { - if (!this.authorizationHelper.hasOneOfPermissions(user, requiredPermissions)) { - // TODO: Should be ForbiddenException - throw new UnauthorizedException(); - } - } - - public hasOneOfPermissions(user: User, requiredPermissions: string[]): boolean { - return this.authorizationHelper.hasOneOfPermissions(user, requiredPermissions); - } - - public async getUserWithPermissions(userId: EntityId): Promise { - const userWithPermissions = await this.loader.getUserWithPermissions(userId); - - return userWithPermissions; - } -} diff --git a/apps/server/src/modules/authorization/errors/forbidden.loggable-exception.ts b/apps/server/src/modules/authorization/domain/error/forbidden.loggable-exception.ts similarity index 94% rename from apps/server/src/modules/authorization/errors/forbidden.loggable-exception.ts rename to apps/server/src/modules/authorization/domain/error/forbidden.loggable-exception.ts index f775fb903df..9557ed14ede 100644 --- a/apps/server/src/modules/authorization/errors/forbidden.loggable-exception.ts +++ b/apps/server/src/modules/authorization/domain/error/forbidden.loggable-exception.ts @@ -2,7 +2,7 @@ import { ForbiddenException } from '@nestjs/common'; import { EntityId } from '@shared/domain'; import { Loggable } from '@src/core/logger/interfaces'; import { ErrorLogMessage } from '@src/core/logger/types'; -import { AuthorizationContext } from '../types'; +import { AuthorizationContext } from '../type'; export class ForbiddenLoggableException extends ForbiddenException implements Loggable { constructor( diff --git a/apps/server/src/modules/authorization/domain/error/index.ts b/apps/server/src/modules/authorization/domain/error/index.ts new file mode 100644 index 00000000000..f2c782cbe56 --- /dev/null +++ b/apps/server/src/modules/authorization/domain/error/index.ts @@ -0,0 +1 @@ +export * from './forbidden.loggable-exception'; diff --git a/apps/server/src/modules/authorization/domain/index.ts b/apps/server/src/modules/authorization/domain/index.ts new file mode 100644 index 00000000000..0f5cfe67874 --- /dev/null +++ b/apps/server/src/modules/authorization/domain/index.ts @@ -0,0 +1,4 @@ +export * from './service'; +export * from './mapper'; +export * from './error'; +export * from './type'; diff --git a/apps/server/src/modules/authorization/authorization-context.builder.spec.ts b/apps/server/src/modules/authorization/domain/mapper/authorization-context.builder.spec.ts similarity index 96% rename from apps/server/src/modules/authorization/authorization-context.builder.spec.ts rename to apps/server/src/modules/authorization/domain/mapper/authorization-context.builder.spec.ts index 856ec92d4a6..5944d7f22e0 100644 --- a/apps/server/src/modules/authorization/authorization-context.builder.spec.ts +++ b/apps/server/src/modules/authorization/domain/mapper/authorization-context.builder.spec.ts @@ -1,6 +1,6 @@ import { Permission } from '@shared/domain'; import { AuthorizationContextBuilder } from './authorization-context.builder'; -import { Action } from './types'; +import { Action } from '../type'; describe('AuthorizationContextBuilder', () => { it('Should allow to set required permissions.', () => { diff --git a/apps/server/src/modules/authorization/authorization-context.builder.ts b/apps/server/src/modules/authorization/domain/mapper/authorization-context.builder.ts similarity index 91% rename from apps/server/src/modules/authorization/authorization-context.builder.ts rename to apps/server/src/modules/authorization/domain/mapper/authorization-context.builder.ts index 58259aa1b6f..86b16685b58 100644 --- a/apps/server/src/modules/authorization/authorization-context.builder.ts +++ b/apps/server/src/modules/authorization/domain/mapper/authorization-context.builder.ts @@ -1,5 +1,5 @@ import { Permission } from '@shared/domain'; -import { AuthorizationContext, Action } from './types'; +import { AuthorizationContext, Action } from '../type'; export class AuthorizationContextBuilder { private static build(requiredPermissions: Permission[], action: Action): AuthorizationContext { diff --git a/apps/server/src/modules/authorization/domain/mapper/index.ts b/apps/server/src/modules/authorization/domain/mapper/index.ts new file mode 100644 index 00000000000..6f21d79acad --- /dev/null +++ b/apps/server/src/modules/authorization/domain/mapper/index.ts @@ -0,0 +1 @@ +export * from './authorization-context.builder'; diff --git a/apps/server/src/shared/domain/rules/board-do.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/board-do.rule.spec.ts similarity index 95% rename from apps/server/src/shared/domain/rules/board-do.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/board-do.rule.spec.ts index 3574250b67c..bda3680b460 100644 --- a/apps/server/src/shared/domain/rules/board-do.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/board-do.rule.spec.ts @@ -1,10 +1,10 @@ import { Test, TestingModule } from '@nestjs/testing'; import { roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action } from '@src/modules'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; import { ObjectId } from 'bson'; -import { BoardDoAuthorizable, BoardRoles, UserRoleEnum } from '../domainobject'; -import { Permission } from '../interface'; +import { BoardDoAuthorizable, BoardRoles, UserRoleEnum } from '@shared/domain/domainobject'; +import { Permission } from '@shared/domain/interface'; +import { Action } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { BoardDoRule } from './board-do.rule'; describe(BoardDoRule.name, () => { diff --git a/apps/server/src/shared/domain/rules/board-do.rule.ts b/apps/server/src/modules/authorization/domain/rules/board-do.rule.ts similarity index 81% rename from apps/server/src/shared/domain/rules/board-do.rule.ts rename to apps/server/src/modules/authorization/domain/rules/board-do.rule.ts index 575c9f0db5a..2042365e071 100644 --- a/apps/server/src/shared/domain/rules/board-do.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/board-do.rule.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '@src/modules/authorization/types'; -import { BoardDoAuthorizable, BoardRoles } from '../domainobject'; -import { User } from '../entity'; +import { BoardDoAuthorizable, BoardRoles } from '@shared/domain/domainobject'; +import { User } from '@shared/domain/entity'; +import { Action, AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; @Injectable() export class BoardDoRule implements Rule { diff --git a/apps/server/src/shared/domain/rules/context-external-tool.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts similarity index 93% rename from apps/server/src/shared/domain/rules/context-external-tool.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts index 90a25a1ca82..ddd458959ed 100644 --- a/apps/server/src/shared/domain/rules/context-external-tool.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts @@ -7,16 +7,15 @@ import { setupEntities, userFactory, } from '@shared/testing'; - -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action } from '@src/modules/authorization/types'; import { ContextExternalTool } from '@src/modules/tool/context-external-tool/domain'; import { ContextExternalToolEntity } from '@src/modules/tool/context-external-tool/entity'; import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; -import { Role, User } from '../entity'; -import { Permission } from '../interface'; +import { Role, User } from '@shared/domain/entity'; +import { Permission } from '@shared/domain/interface'; import { ContextExternalToolRule } from './context-external-tool.rule'; +import { Action } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; describe('ContextExternalToolRule', () => { let service: ContextExternalToolRule; diff --git a/apps/server/src/shared/domain/rules/context-external-tool.rule.ts b/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts similarity index 85% rename from apps/server/src/shared/domain/rules/context-external-tool.rule.ts rename to apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts index 35be641e550..5d57c95a160 100644 --- a/apps/server/src/shared/domain/rules/context-external-tool.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { AuthorizationContext, Rule } from '@src/modules/authorization/types'; import { ContextExternalTool } from '@src/modules/tool/context-external-tool/domain'; import { ContextExternalToolEntity } from '@src/modules/tool/context-external-tool/entity'; -import { User } from '../entity'; +import { User } from '@shared/domain/entity'; +import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; @Injectable() export class ContextExternalToolRule implements Rule { diff --git a/apps/server/src/shared/domain/rules/course-group.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts similarity index 97% rename from apps/server/src/shared/domain/rules/course-group.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts index 8296f75f917..62c14baa138 100644 --- a/apps/server/src/shared/domain/rules/course-group.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts @@ -2,10 +2,10 @@ import { Test, TestingModule } from '@nestjs/testing'; import { CourseGroup, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory, courseGroupFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action } from '@src/modules/authorization/types'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; +import { Action } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; describe('CourseGroupRule', () => { let service: CourseGroupRule; diff --git a/apps/server/src/shared/domain/rules/course-group.rule.ts b/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts similarity index 84% rename from apps/server/src/shared/domain/rules/course-group.rule.ts rename to apps/server/src/modules/authorization/domain/rules/course-group.rule.ts index 14638862ba2..863d7072ec8 100644 --- a/apps/server/src/shared/domain/rules/course-group.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { CourseGroup, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '@src/modules/authorization/types'; import { CourseRule } from './course.rule'; +import { Action, AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; @Injectable() export class CourseGroupRule implements Rule { diff --git a/apps/server/src/shared/domain/rules/course.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts similarity index 95% rename from apps/server/src/shared/domain/rules/course.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts index eff0bd41fee..1c4dcc7d670 100644 --- a/apps/server/src/shared/domain/rules/course.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts @@ -2,9 +2,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Course, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action } from '@src/modules/authorization/types'; import { CourseRule } from './course.rule'; +import { Action } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; describe('CourseRule', () => { let service: CourseRule; diff --git a/apps/server/src/shared/domain/rules/course.rule.ts b/apps/server/src/modules/authorization/domain/rules/course.rule.ts similarity index 82% rename from apps/server/src/shared/domain/rules/course.rule.ts rename to apps/server/src/modules/authorization/domain/rules/course.rule.ts index 90183dbfd0f..e923e1ab967 100644 --- a/apps/server/src/shared/domain/rules/course.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/course.rule.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Course, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '@src/modules/authorization/types'; +import { Action, AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; @Injectable() export class CourseRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/index.ts b/apps/server/src/modules/authorization/domain/rules/index.ts new file mode 100644 index 00000000000..bd4ffe27a59 --- /dev/null +++ b/apps/server/src/modules/authorization/domain/rules/index.ts @@ -0,0 +1,16 @@ +/** + * Rules are currently placed in authorization module to avoid dependency cycles. + * In future they must be moved to the feature modules and register it in registration service. + */ +export * from './board-do.rule'; +export * from './context-external-tool.rule'; +export * from './course-group.rule'; +export * from './course.rule'; +export * from './legacy-school.rule'; +export * from './lesson.rule'; +export * from './school-external-tool.rule'; +export * from './submission.rule'; +export * from './task.rule'; +export * from './team.rule'; +export * from './user-login-migration.rule'; +export * from './user.rule'; diff --git a/apps/server/src/shared/domain/rules/legacy-school.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts similarity index 93% rename from apps/server/src/shared/domain/rules/legacy-school.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts index c547f772de5..489def39318 100644 --- a/apps/server/src/shared/domain/rules/legacy-school.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts @@ -1,9 +1,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; import { roleFactory, legacySchoolDoFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action } from '@src/modules/authorization/types'; import { ObjectID } from 'bson'; +import { Action } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { LegacySchoolRule } from './legacy-school.rule'; describe('LegacySchoolRule', () => { diff --git a/apps/server/src/shared/domain/rules/legacy-school.rule.ts b/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts similarity index 78% rename from apps/server/src/shared/domain/rules/legacy-school.rule.ts rename to apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts index 5068d327c35..e115727091a 100644 --- a/apps/server/src/shared/domain/rules/legacy-school.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; import { BaseDO, LegacySchoolDo } from '@shared/domain'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { AuthorizationContext, Rule } from '@src/modules/authorization/types'; -import { AuthorizableObject } from '../domain-object'; +import { AuthorizableObject } from '@shared/domain/domain-object'; +import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; /** * @deprecated because it uses the deprecated LegacySchoolDo. diff --git a/apps/server/src/shared/domain/rules/lesson.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts similarity index 50% rename from apps/server/src/shared/domain/rules/lesson.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts index 13c605f77e4..a8e4bdd8038 100644 --- a/apps/server/src/shared/domain/rules/lesson.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts @@ -10,17 +10,20 @@ import { setupEntities, userFactory, } from '@shared/testing'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action } from '@src/modules/authorization/types'; -import { CourseGroupRule, CourseRule } from '.'; +import { NotImplementedException } from '@nestjs/common'; +import { Action, AuthorizationContext } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; +import { CourseGroupRule } from './course-group.rule'; +import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; +import { AuthorizationContextBuilder } from '../mapper'; describe('LessonRule', () => { - let service: LessonRule; + let rule: LessonRule; let authorizationHelper: AuthorizationHelper; let courseRule: DeepPartial; let courseGroupRule: DeepPartial; - let user: User; + let globalUser: User; let entity: LessonEntity; const permissionA = 'a' as Permission; const permissionB = 'b' as Permission; @@ -33,7 +36,7 @@ describe('LessonRule', () => { providers: [AuthorizationHelper, LessonRule, CourseRule, CourseGroupRule], }).compile(); - service = await module.get(LessonRule); + rule = await module.get(LessonRule); authorizationHelper = await module.get(AuthorizationHelper); courseRule = await module.get(CourseRule); courseGroupRule = await module.get(CourseGroupRule); @@ -41,58 +44,117 @@ describe('LessonRule', () => { beforeEach(() => { const role = roleFactory.build({ permissions: [permissionA, permissionB] }); - user = userFactory.build({ roles: [role] }); + globalUser = userFactory.build({ roles: [role] }); }); it('should call hasAllPermissions on AuthorizationHelper', () => { entity = lessonFactory.build(); const spy = jest.spyOn(authorizationHelper, 'hasAllPermissions'); - service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [] }); - expect(spy).toBeCalledWith(user, []); + rule.hasPermission(globalUser, entity, { action: Action.read, requiredPermissions: [] }); + expect(spy).toBeCalledWith(globalUser, []); }); it('should call courseRule.hasPermission', () => { - const course = courseFactory.build({ teachers: [user] }); + const course = courseFactory.build({ teachers: [globalUser] }); entity = lessonFactory.build({ course }); const spy = jest.spyOn(courseRule, 'hasPermission'); - service.hasPermission(user, entity, { action: Action.write, requiredPermissions: [permissionA] }); - expect(spy).toBeCalledWith(user, entity.course, { action: Action.write, requiredPermissions: [] }); + rule.hasPermission(globalUser, entity, { action: Action.write, requiredPermissions: [permissionA] }); + expect(spy).toBeCalledWith(globalUser, entity.course, { action: Action.write, requiredPermissions: [] }); }); it('should call courseGroupRule.hasPermission', () => { - const course = courseFactory.build({ teachers: [user] }); + const course = courseFactory.build({ teachers: [globalUser] }); const courseGroup = courseGroupFactory.build({ course }); entity = lessonFactory.build({ course: undefined, courseGroup }); const spy = jest.spyOn(courseGroupRule, 'hasPermission'); - service.hasPermission(user, entity, { action: Action.write, requiredPermissions: [permissionA] }); - expect(spy).toBeCalledWith(user, entity.courseGroup, { action: Action.write, requiredPermissions: [] }); + rule.hasPermission(globalUser, entity, { action: Action.write, requiredPermissions: [permissionA] }); + expect(spy).toBeCalledWith(globalUser, entity.courseGroup, { action: Action.write, requiredPermissions: [] }); + }); + + describe('Given user request not implemented action', () => { + const getContext = (): AuthorizationContext => { + const context: AuthorizationContext = { + requiredPermissions: [], + // @ts-expect-error Testcase + action: 'not_implemented', + }; + + return context; + }; + + describe('when valid data exists', () => { + const setup = () => { + const user = userFactory.build(); + const course = courseFactory.build({ teachers: [user] }); + const lesson = lessonFactory.build({ course }); + const context = getContext(); + + return { + user, + lesson, + context, + }; + }; + + it('should reject with NotImplementedException', () => { + const { user, lesson, context } = setup(); + + expect(() => rule.hasPermission(user, lesson, context)).toThrowError(NotImplementedException); + }); + }); + }); + + describe('Given user request Action.write', () => { + const getWriteContext = () => AuthorizationContextBuilder.write([]); + + describe('when lesson has no course or coursegroup', () => { + const setup = () => { + const user = userFactory.build(); + const lessonEntity = lessonFactory.build({ course: undefined }); + const context = getWriteContext(); + + return { + user, + lessonEntity, + context, + }; + }; + + it('should return false', () => { + const { user, lessonEntity, context } = setup(); + + const result = rule.hasPermission(user, lessonEntity, context); + + expect(result).toBe(false); + }); + }); }); describe('User [TEACHER]', () => { it('should return "true" if user in scope', () => { - const course = courseFactory.build({ teachers: [user] }); + const course = courseFactory.build({ teachers: [globalUser] }); entity = lessonFactory.build({ course }); - const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [permissionA] }); + const res = rule.hasPermission(globalUser, entity, { action: Action.read, requiredPermissions: [permissionA] }); expect(res).toBe(true); }); it('should return "true" if user has access to hidden entity', () => { - const course = courseFactory.build({ teachers: [user] }); + const course = courseFactory.build({ teachers: [globalUser] }); entity = lessonFactory.build({ course, hidden: true }); - const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [permissionA] }); + const res = rule.hasPermission(globalUser, entity, { action: Action.read, requiredPermissions: [permissionA] }); expect(res).toBe(true); }); it('should return "false" if user has not permission', () => { entity = lessonFactory.build(); - const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [permissionC] }); + const res = rule.hasPermission(globalUser, entity, { action: Action.read, requiredPermissions: [permissionC] }); expect(res).toBe(false); }); it('should return "false" if user has not access to entity', () => { entity = lessonFactory.build(); - const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [permissionC] }); + const res = rule.hasPermission(globalUser, entity, { action: Action.read, requiredPermissions: [permissionC] }); expect(res).toBe(false); }); }); @@ -106,14 +168,14 @@ describe('LessonRule', () => { it('should return "false" if user has access to entity', () => { const course = courseFactory.build({ students: [student] }); entity = lessonFactory.build({ course }); - const res = service.hasPermission(student, entity, { action: Action.read, requiredPermissions: [permissionA] }); + const res = rule.hasPermission(student, entity, { action: Action.read, requiredPermissions: [permissionA] }); expect(res).toBe(true); }); it('should return "false" if user has not access to hidden entity', () => { const course = courseFactory.build({ students: [student] }); entity = lessonFactory.build({ course, hidden: true }); - const res = service.hasPermission(student, entity, { action: Action.read, requiredPermissions: [permissionA] }); + const res = rule.hasPermission(student, entity, { action: Action.read, requiredPermissions: [permissionA] }); expect(res).toBe(false); }); }); diff --git a/apps/server/src/shared/domain/rules/lesson.rule.ts b/apps/server/src/modules/authorization/domain/rules/lesson.rule.ts similarity index 88% rename from apps/server/src/shared/domain/rules/lesson.rule.ts rename to apps/server/src/modules/authorization/domain/rules/lesson.rule.ts index ff264af13ae..1f59f98ad49 100644 --- a/apps/server/src/shared/domain/rules/lesson.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/lesson.rule.ts @@ -1,7 +1,7 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotImplementedException } from '@nestjs/common'; import { Course, CourseGroup, LessonEntity, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '@src/modules/authorization/types'; +import { Action, AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; @@ -27,6 +27,8 @@ export class LessonRule implements Rule { hasLessonPermission = this.lessonReadPermission(user, entity); } else if (action === Action.write) { hasLessonPermission = this.lessonWritePermission(user, entity); + } else { + throw new NotImplementedException('Action is not supported.'); } const hasUserPermission = this.authorizationHelper.hasAllPermissions(user, requiredPermissions); @@ -55,12 +57,14 @@ export class LessonRule implements Rule { } private parentPermission(user: User, entity: LessonEntity, action: Action): boolean { - let result = false; + let result: boolean; if (entity.courseGroup) { result = this.courseGroupPermission(user, entity.courseGroup, action); } else if (entity.course) { result = this.coursePermission(user, entity.course, action); // ask course for student = read || teacher, sub-teacher = write + } else { + result = false; } return result; diff --git a/apps/server/src/shared/domain/rules/school-external-tool.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts similarity index 92% rename from apps/server/src/shared/domain/rules/school-external-tool.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts index b24ed4d0ac8..d1781bb8576 100644 --- a/apps/server/src/shared/domain/rules/school-external-tool.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts @@ -7,13 +7,11 @@ import { userFactory, schoolExternalToolFactory, } from '@shared/testing'; - -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action } from '@src/modules/authorization/types'; import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; -import { Role, User } from '../entity'; -import { Permission } from '../interface'; +import { Role, User, Permission } from '@shared/domain'; +import { Action } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { SchoolExternalToolRule } from './school-external-tool.rule'; describe('SchoolExternalToolRule', () => { diff --git a/apps/server/src/shared/domain/rules/school-external-tool.rule.ts b/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts similarity index 85% rename from apps/server/src/shared/domain/rules/school-external-tool.rule.ts rename to apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts index bd28502faa2..041c8b523e2 100644 --- a/apps/server/src/shared/domain/rules/school-external-tool.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { AuthorizationContext, Rule } from '@src/modules/authorization/types'; import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; -import { User } from '../entity'; +import { User } from '@shared/domain/entity'; +import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; @Injectable() export class SchoolExternalToolRule implements Rule { diff --git a/apps/server/src/shared/domain/rules/submission.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts similarity index 93% rename from apps/server/src/shared/domain/rules/submission.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts index 098f83547e8..8b054671970 100644 --- a/apps/server/src/shared/domain/rules/submission.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts @@ -9,9 +9,14 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action } from '@src/modules/authorization/types'; -import { CourseGroupRule, CourseRule, LessonRule, SubmissionRule, TaskRule } from '.'; +import { NotImplementedException } from '@nestjs/common'; +import { Action, AuthorizationContext } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; +import { SubmissionRule } from './submission.rule'; +import { TaskRule } from './task.rule'; +import { CourseRule } from './course.rule'; +import { LessonRule } from './lesson.rule'; +import { CourseGroupRule } from './course-group.rule'; const buildUserWithPermission = (permission) => { const role = roleFactory.buildWithId({ permissions: [permission] }); @@ -76,6 +81,38 @@ describe('SubmissionRule', () => { }); describe('hasPermission', () => { + describe('Given user request not implemented action', () => { + const getContext = (): AuthorizationContext => { + const context: AuthorizationContext = { + requiredPermissions: [], + // @ts-expect-error Testcase + action: 'not_implemented', + }; + + return context; + }; + + describe('when valid data exists', () => { + const setup = () => { + const user = userFactory.build(); + const submission = submissionFactory.build({ student: user }); + const context = getContext(); + + return { + user, + submission, + context, + }; + }; + + it('should reject with NotImplementedException', () => { + const { user, submission, context } = setup(); + + expect(() => submissionRule.hasPermission(user, submission, context)).toThrowError(NotImplementedException); + }); + }); + }); + describe('when user roles do not contain required permissions', () => { const setup = () => { const permission = 'a' as Permission; diff --git a/apps/server/src/shared/domain/rules/submission.rule.ts b/apps/server/src/modules/authorization/domain/rules/submission.rule.ts similarity index 88% rename from apps/server/src/shared/domain/rules/submission.rule.ts rename to apps/server/src/modules/authorization/domain/rules/submission.rule.ts index 3234f8e8cff..6bff9504f5c 100644 --- a/apps/server/src/shared/domain/rules/submission.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/submission.rule.ts @@ -1,7 +1,7 @@ -import { Injectable } from '@nestjs/common'; +import { Injectable, NotImplementedException } from '@nestjs/common'; import { Submission, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '@src/modules/authorization/types'; +import { Action, AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { TaskRule } from './task.rule'; @Injectable() @@ -31,6 +31,8 @@ export class SubmissionRule implements Rule { hasAccessToSubmission = this.hasWriteAccess(user, submission); } else if (action === Action.read) { hasAccessToSubmission = this.hasReadAccess(user, submission); + } else { + throw new NotImplementedException('Action is not supported.'); } return hasAccessToSubmission; diff --git a/apps/server/src/shared/domain/rules/task.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts similarity index 96% rename from apps/server/src/shared/domain/rules/task.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts index 0fc886df84f..31d68661ff0 100644 --- a/apps/server/src/shared/domain/rules/task.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts @@ -2,9 +2,12 @@ import { DeepPartial } from '@mikro-orm/core'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission, RoleName } from '@shared/domain/interface'; import { courseFactory, lessonFactory, roleFactory, setupEntities, taskFactory, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { CourseGroupRule, CourseRule, LessonRule, TaskRule } from '.'; -import { Action } from '../../../modules/authorization/types/action.enum'; +import { Action } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; +import { CourseGroupRule } from './course-group.rule'; +import { TaskRule } from './task.rule'; +import { CourseRule } from './course.rule'; +import { LessonRule } from './lesson.rule'; describe('TaskRule', () => { let service: TaskRule; diff --git a/apps/server/src/shared/domain/rules/task.rule.ts b/apps/server/src/modules/authorization/domain/rules/task.rule.ts similarity index 90% rename from apps/server/src/shared/domain/rules/task.rule.ts rename to apps/server/src/modules/authorization/domain/rules/task.rule.ts index 4c358593109..3ebc04d9f71 100644 --- a/apps/server/src/shared/domain/rules/task.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/task.rule.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Task, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '@src/modules/authorization/types'; +import { Action, AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; diff --git a/apps/server/src/shared/domain/rules/team.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts similarity index 91% rename from apps/server/src/shared/domain/rules/team.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts index d29eaaaa6f8..da99354a49b 100644 --- a/apps/server/src/shared/domain/rules/team.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts @@ -1,10 +1,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; -import { roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { teamFactory } from '@shared/testing/factory/team.factory'; -import { TeamRule } from '@shared/domain/rules/team.rule'; -import { AuthorizationContextBuilder } from '@src/modules/authorization/authorization-context.builder'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; +import { roleFactory, setupEntities, userFactory, teamFactory } from '@shared/testing'; +import { AuthorizationHelper } from '../service/authorization.helper'; +import { TeamRule } from './team.rule'; +import { AuthorizationContextBuilder } from '../mapper'; describe('TeamRule', () => { let rule: TeamRule; diff --git a/apps/server/src/shared/domain/rules/team.rule.ts b/apps/server/src/modules/authorization/domain/rules/team.rule.ts similarity index 81% rename from apps/server/src/shared/domain/rules/team.rule.ts rename to apps/server/src/modules/authorization/domain/rules/team.rule.ts index 23ad0d55cf7..2d8f5e90edf 100644 --- a/apps/server/src/shared/domain/rules/team.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/team.rule.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { TeamEntity, TeamUserEntity, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { AuthorizationContext, Rule } from '@src/modules/authorization/types'; +import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; @Injectable() export class TeamRule implements Rule { diff --git a/apps/server/src/shared/domain/rules/user-login-migration.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts similarity index 94% rename from apps/server/src/shared/domain/rules/user-login-migration.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts index a7c6b1e5f7a..f7fe9d3c53f 100644 --- a/apps/server/src/shared/domain/rules/user-login-migration.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts @@ -2,10 +2,10 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { schoolFactory, setupEntities, userFactory, userLoginMigrationDOFactory } from '@shared/testing'; -import { Action, AuthorizationContext } from '@src/modules/authorization'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { UserLoginMigrationDO } from '../domainobject'; -import { Permission } from '../interface'; +import { UserLoginMigrationDO } from '@shared/domain/domainobject'; +import { Permission } from '@shared/domain/interface'; +import { Action, AuthorizationContext } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { UserLoginMigrationRule } from './user-login-migration.rule'; describe('UserLoginMigrationRule', () => { diff --git a/apps/server/src/shared/domain/rules/user-login-migration.rule.ts b/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts similarity index 72% rename from apps/server/src/shared/domain/rules/user-login-migration.rule.ts rename to apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts index 084e4d26372..3ae82d02505 100644 --- a/apps/server/src/shared/domain/rules/user-login-migration.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { AuthorizationContext, Rule } from '@src/modules/authorization/types'; -import { UserLoginMigrationDO } from '../domainobject'; -import { User } from '../entity'; +import { UserLoginMigrationDO } from '@shared/domain/domainobject'; +import { User } from '@shared/domain/entity'; +import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; @Injectable() export class UserLoginMigrationRule implements Rule { diff --git a/apps/server/src/shared/domain/rules/user.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts similarity index 94% rename from apps/server/src/shared/domain/rules/user.rule.spec.ts rename to apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts index bd771961ed3..85492348f75 100644 --- a/apps/server/src/shared/domain/rules/user.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts @@ -2,8 +2,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { Action } from '@src/modules/authorization/types'; +import { Action } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { UserRule } from './user.rule'; describe('UserRule', () => { diff --git a/apps/server/src/shared/domain/rules/user.rule.ts b/apps/server/src/modules/authorization/domain/rules/user.rule.ts similarity index 78% rename from apps/server/src/shared/domain/rules/user.rule.ts rename to apps/server/src/modules/authorization/domain/rules/user.rule.ts index 3dd9bc6d229..2a1365881e1 100644 --- a/apps/server/src/shared/domain/rules/user.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/user.rule.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '@src/modules/authorization/authorization.helper'; -import { AuthorizationContext, Rule } from '@src/modules/authorization/types'; +import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; @Injectable() export class UserRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/service/authorization-reference.service.spec.ts b/apps/server/src/modules/authorization/domain/service/authorization-reference.service.spec.ts new file mode 100644 index 00000000000..8ab1719a72d --- /dev/null +++ b/apps/server/src/modules/authorization/domain/service/authorization-reference.service.spec.ts @@ -0,0 +1,183 @@ +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { NotFoundException } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { courseFactory, setupEntities, userFactory } from '@shared/testing'; +import { ObjectId } from 'bson'; +import { AuthorizableReferenceType } from '../type'; +import { AuthorizationService } from './authorization.service'; +import { ReferenceLoader } from './reference.loader'; +import { AuthorizationContextBuilder } from '../mapper'; +import { ForbiddenLoggableException } from '../error'; +import { AuthorizationReferenceService } from './authorization-reference.service'; + +describe('AuthorizationReferenceService', () => { + let service: AuthorizationReferenceService; + let authorizationService: DeepMocked; + let loader: DeepMocked; + + beforeAll(async () => { + await setupEntities(); + + const module: TestingModule = await Test.createTestingModule({ + providers: [ + AuthorizationReferenceService, + { + provide: AuthorizationService, + useValue: createMock(), + }, + { + provide: ReferenceLoader, + useValue: createMock(), + }, + ], + }).compile(); + + service = await module.get(AuthorizationReferenceService); + authorizationService = await module.get(AuthorizationService); + loader = await module.get(ReferenceLoader); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('checkPermissionByReferences', () => { + const setupData = () => { + const entityId = new ObjectId().toHexString(); + const userId = new ObjectId().toHexString(); + const context = AuthorizationContextBuilder.read([]); + const entityName = AuthorizableReferenceType.Course; + + return { context, entityId, userId, entityName }; + }; + + describe('when hasPermissionByReferences returns false', () => { + const setup = () => { + const { entityId, userId, context, entityName } = setupData(); + + const spy = jest.spyOn(service, 'hasPermissionByReferences').mockResolvedValueOnce(false); + + return { context, userId, entityId, entityName, spy }; + }; + + it('should reject with ForbiddenLoggableException', async () => { + const { context, userId, entityId, entityName, spy } = setup(); + + await expect(service.checkPermissionByReferences(userId, entityName, entityId, context)).rejects.toThrow( + new ForbiddenLoggableException(userId, entityName, context) + ); + + spy.mockRestore(); + }); + }); + + describe('when hasPermissionByReferences returns true', () => { + const setup = () => { + const { entityId, userId, context, entityName } = setupData(); + + const spy = jest.spyOn(service, 'hasPermissionByReferences').mockResolvedValueOnce(true); + + return { context, userId, entityId, entityName, spy }; + }; + + it('should resolve without error', async () => { + const { context, userId, entityId, entityName, spy } = setup(); + + await expect(service.checkPermissionByReferences(userId, entityName, entityId, context)).resolves.not.toThrow(); + + spy.mockRestore(); + }); + }); + }); + + describe('hasPermissionByReferences', () => { + const setupData = () => { + const entity = courseFactory.buildWithId(); + const user = userFactory.buildWithId(); + const context = AuthorizationContextBuilder.read([]); + const entityName = AuthorizableReferenceType.Course; + + return { context, entity, user, entityName }; + }; + + describe('when loader throws an error', () => { + const setup = () => { + const { entity, user, context, entityName } = setupData(); + + loader.loadAuthorizableObject.mockRejectedValueOnce(new NotFoundException()); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationService.hasPermission.mockReturnValueOnce(true); + + return { context, userId: user.id, entityId: entity.id, entityName }; + }; + + it('should reject with this error', async () => { + const { context, userId, entityId, entityName } = setup(); + + await expect(service.hasPermissionByReferences(userId, entityName, entityId, context)).rejects.toThrow( + new NotFoundException() + ); + }); + }); + + describe('when authorizationService throws an error', () => { + const setup = () => { + const { entity, user, context, entityName } = setupData(); + + loader.loadAuthorizableObject.mockRejectedValueOnce(entity); + authorizationService.getUserWithPermissions.mockRejectedValueOnce(new NotFoundException()); + authorizationService.hasPermission.mockReturnValueOnce(true); + + return { context, userId: user.id, entityId: entity.id, entityName }; + }; + + it('should reject with this error', async () => { + const { context, userId, entityId, entityName } = setup(); + + await expect(service.hasPermissionByReferences(userId, entityName, entityId, context)).rejects.toThrow( + new NotFoundException() + ); + }); + }); + + describe('when loader can load entites and authorization resolve with true', () => { + const setup = () => { + const { entity, user, context, entityName } = setupData(); + + loader.loadAuthorizableObject.mockResolvedValueOnce(entity); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationService.hasPermission.mockReturnValueOnce(true); + + return { context, userId: user.id, entityId: entity.id, entityName }; + }; + + it('should resolve to true', async () => { + const { context, userId, entityId, entityName } = setup(); + + const result = await service.hasPermissionByReferences(userId, entityName, entityId, context); + + expect(result).toBe(true); + }); + }); + + describe('when loader can load entities and authorization resolve with false', () => { + const setup = () => { + const { entity, user, context, entityName } = setupData(); + + loader.loadAuthorizableObject.mockResolvedValueOnce(entity); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationService.hasPermission.mockReturnValueOnce(false); + + return { context, userId: user.id, entityId: entity.id, entityName }; + }; + + it('should resolve to false', async () => { + const { context, userId, entityId, entityName } = setup(); + + const result = await service.hasPermissionByReferences(userId, entityName, entityId, context); + + expect(result).toBe(false); + }); + }); + }); +}); diff --git a/apps/server/src/modules/authorization/domain/service/authorization-reference.service.ts b/apps/server/src/modules/authorization/domain/service/authorization-reference.service.ts new file mode 100644 index 00000000000..814df9378da --- /dev/null +++ b/apps/server/src/modules/authorization/domain/service/authorization-reference.service.ts @@ -0,0 +1,41 @@ +import { Injectable } from '@nestjs/common'; +import { EntityId } from '@shared/domain'; +import { ReferenceLoader } from './reference.loader'; +import { AuthorizationContext, AuthorizableReferenceType } from '../type'; +import { ForbiddenLoggableException } from '../error'; +import { AuthorizationService } from './authorization.service'; + +/** + * Should by use only internal in authorization module. See ticket: BC-3990 + */ +@Injectable() +export class AuthorizationReferenceService { + constructor(private readonly loader: ReferenceLoader, private readonly authorizationService: AuthorizationService) {} + + public async checkPermissionByReferences( + userId: EntityId, + entityName: AuthorizableReferenceType, + entityId: EntityId, + context: AuthorizationContext + ): Promise { + if (!(await this.hasPermissionByReferences(userId, entityName, entityId, context))) { + throw new ForbiddenLoggableException(userId, entityName, context); + } + } + + public async hasPermissionByReferences( + userId: EntityId, + entityName: AuthorizableReferenceType, + entityId: EntityId, + context: AuthorizationContext + ): Promise { + const [user, object] = await Promise.all([ + this.authorizationService.getUserWithPermissions(userId), + this.loader.loadAuthorizableObject(entityName, entityId), + ]); + + const hasPermission = this.authorizationService.hasPermission(user, object, context); + + return hasPermission; + } +} diff --git a/apps/server/src/modules/authorization/authorization.helper.spec.ts b/apps/server/src/modules/authorization/domain/service/authorization.helper.spec.ts similarity index 100% rename from apps/server/src/modules/authorization/authorization.helper.spec.ts rename to apps/server/src/modules/authorization/domain/service/authorization.helper.spec.ts diff --git a/apps/server/src/modules/authorization/authorization.helper.ts b/apps/server/src/modules/authorization/domain/service/authorization.helper.ts similarity index 100% rename from apps/server/src/modules/authorization/authorization.helper.ts rename to apps/server/src/modules/authorization/domain/service/authorization.helper.ts diff --git a/apps/server/src/modules/authorization/authorization.service.spec.ts b/apps/server/src/modules/authorization/domain/service/authorization.service.spec.ts similarity index 60% rename from apps/server/src/modules/authorization/authorization.service.spec.ts rename to apps/server/src/modules/authorization/domain/service/authorization.service.spec.ts index 766c2c84d23..f113c64472c 100644 --- a/apps/server/src/modules/authorization/authorization.service.spec.ts +++ b/apps/server/src/modules/authorization/domain/service/authorization.service.spec.ts @@ -1,33 +1,34 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { ForbiddenException, InternalServerErrorException, UnauthorizedException } from '@nestjs/common'; +import { UnauthorizedException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; import { setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder } from './authorization-context.builder'; +import { UserRepo } from '@shared/repo'; +import { AuthorizationContextBuilder } from '../mapper'; import { AuthorizationHelper } from './authorization.helper'; import { AuthorizationService } from './authorization.service'; -import { ForbiddenLoggableException } from './errors/forbidden.loggable-exception'; +import { ForbiddenLoggableException } from '../error'; import { ReferenceLoader } from './reference.loader'; import { RuleManager } from './rule-manager'; -import { AuthorizableReferenceType, Rule } from './types'; +import { Rule } from '../type'; -describe('AuthorizationService', () => { - class TestRule implements Rule { - constructor(private returnValueOfhasPermission: boolean) {} +class TestRule implements Rule { + constructor(private returnValueOfhasPermission: boolean) {} - isApplicable(): boolean { - return true; - } + isApplicable(): boolean { + return true; + } - hasPermission(): boolean { - return this.returnValueOfhasPermission; - } + hasPermission(): boolean { + return this.returnValueOfhasPermission; } +} +describe('AuthorizationService', () => { let service: AuthorizationService; let ruleManager: DeepMocked; - let loader: DeepMocked; let authorizationHelper: DeepMocked; + let userRepo: DeepMocked; const testPermission = 'CAN_TEST' as Permission; @@ -49,13 +50,17 @@ describe('AuthorizationService', () => { provide: AuthorizationHelper, useValue: createMock(), }, + { + provide: UserRepo, + useValue: createMock(), + }, ], }).compile(); service = await module.get(AuthorizationService); ruleManager = await module.get(RuleManager); - loader = await module.get(ReferenceLoader); authorizationHelper = await module.get(AuthorizationHelper); + userRepo = await module.get(UserRepo); }); afterEach(() => { @@ -66,7 +71,7 @@ describe('AuthorizationService', () => { describe('when hasPermission returns false', () => { const setup = () => { const context = AuthorizationContextBuilder.read([]); - const user = userFactory.build(); + const user = userFactory.buildWithId(); const spy = jest.spyOn(service, 'hasPermission').mockReturnValueOnce(false); @@ -85,7 +90,7 @@ describe('AuthorizationService', () => { describe('when hasPermission returns true', () => { const setup = () => { const context = AuthorizationContextBuilder.read([]); - const user = userFactory.build(); + const user = userFactory.buildWithId(); const spy = jest.spyOn(service, 'hasPermission').mockReturnValueOnce(true); @@ -106,7 +111,7 @@ describe('AuthorizationService', () => { describe('when the selected rule returns false', () => { const setup = () => { const context = AuthorizationContextBuilder.read([]); - const user = userFactory.build(); + const user = userFactory.buildWithId(); const testRule = new TestRule(false); ruleManager.selectRule.mockReturnValueOnce(testRule); @@ -126,7 +131,7 @@ describe('AuthorizationService', () => { describe('when the selected rule returns true', () => { const setup = () => { const context = AuthorizationContextBuilder.read([]); - const user = userFactory.build(); + const user = userFactory.buildWithId(); const testRule = new TestRule(true); ruleManager.selectRule.mockReturnValueOnce(testRule); @@ -144,123 +149,10 @@ describe('AuthorizationService', () => { }); }); - describe('checkPermissionByReferences', () => { - describe('when hasPermissionByReferences returns false', () => { - const setup = () => { - const context = AuthorizationContextBuilder.read([]); - const userId = 'test'; - const entityId = 'test'; - const entityName = AuthorizableReferenceType.Course; - - const spy = jest.spyOn(service, 'hasPermissionByReferences').mockResolvedValueOnce(false); - - return { context, userId, entityId, entityName, spy }; - }; - - it('should reject with ForbiddenLoggableException', async () => { - const { context, userId, entityId, entityName, spy } = setup(); - - await expect(service.checkPermissionByReferences(userId, entityName, entityId, context)).rejects.toThrow( - ForbiddenLoggableException - ); - - spy.mockRestore(); - }); - }); - - describe('when hasPermissionByReferences returns true', () => { - const setup = () => { - const context = AuthorizationContextBuilder.read([]); - const userId = 'test'; - const entityId = 'test'; - const entityName = AuthorizableReferenceType.Course; - - const spy = jest.spyOn(service, 'hasPermissionByReferences').mockResolvedValueOnce(true); - - return { context, userId, entityId, entityName, spy }; - }; - - it('should resolve', async () => { - const { context, userId, entityId, entityName, spy } = setup(); - - await expect(service.checkPermissionByReferences(userId, entityName, entityId, context)).resolves.not.toThrow(); - - spy.mockRestore(); - }); - }); - }); - - describe('hasPermissionByReferences', () => { - describe('when loader throws an error', () => { - const setup = () => { - const context = AuthorizationContextBuilder.read([]); - const userId = 'test'; - const entityId = 'test'; - const entityName = AuthorizableReferenceType.Course; - - loader.loadAuthorizableObject.mockRejectedValueOnce(InternalServerErrorException); - - return { context, userId, entityId, entityName }; - }; - - it('should reject with ForbiddenException', async () => { - const { context, userId, entityId, entityName } = setup(); - - await expect(service.hasPermissionByReferences(userId, entityName, entityId, context)).rejects.toThrow( - ForbiddenException - ); - }); - }); - - describe('when the selected rule returns true', () => { - const setup = () => { - const context = AuthorizationContextBuilder.read([]); - const userId = 'test'; - const entityId = 'test'; - const entityName = AuthorizableReferenceType.Course; - const testRule = new TestRule(true); - - ruleManager.selectRule.mockReturnValueOnce(testRule); - - return { context, userId, entityId, entityName }; - }; - - it('should resolve to true', async () => { - const { context, userId, entityId, entityName } = setup(); - - const result = await service.hasPermissionByReferences(userId, entityName, entityId, context); - - expect(result).toBe(true); - }); - }); - - describe('when the selected rule returns false', () => { - const setup = () => { - const context = AuthorizationContextBuilder.read([]); - const userId = 'test'; - const entityId = 'test'; - const entityName = AuthorizableReferenceType.Course; - const testRule = new TestRule(false); - - ruleManager.selectRule.mockReturnValueOnce(testRule); - - return { context, userId, entityId, entityName }; - }; - - it('should resolve to false', async () => { - const { context, userId, entityId, entityName } = setup(); - - const result = await service.hasPermissionByReferences(userId, entityName, entityId, context); - - expect(result).toBe(false); - }); - }); - }); - describe('checkAllPermissions', () => { describe('when helper method returns false', () => { const setup = () => { - const user = userFactory.build(); + const user = userFactory.buildWithId(); const requiredPermissions = [testPermission]; authorizationHelper.hasAllPermissions.mockReturnValueOnce(false); @@ -277,7 +169,7 @@ describe('AuthorizationService', () => { describe('when helper method returns true', () => { const setup = () => { - const user = userFactory.build(); + const user = userFactory.buildWithId(); const requiredPermissions = [testPermission]; authorizationHelper.hasAllPermissions.mockReturnValueOnce(true); @@ -296,7 +188,7 @@ describe('AuthorizationService', () => { describe('hasAllPermissions', () => { describe('when helper method returns false', () => { const setup = () => { - const user = userFactory.build(); + const user = userFactory.buildWithId(); const requiredPermissions = [testPermission]; authorizationHelper.hasAllPermissions.mockReturnValueOnce(false); @@ -315,7 +207,7 @@ describe('AuthorizationService', () => { describe('when helper method returns true', () => { const setup = () => { - const user = userFactory.build(); + const user = userFactory.buildWithId(); const requiredPermissions = [testPermission]; authorizationHelper.hasAllPermissions.mockReturnValueOnce(true); @@ -336,7 +228,7 @@ describe('AuthorizationService', () => { describe('checkOneOfPermissions', () => { describe('when helper method returns false', () => { const setup = () => { - const user = userFactory.build(); + const user = userFactory.buildWithId(); const requiredPermissions = [testPermission]; authorizationHelper.hasOneOfPermissions.mockReturnValueOnce(false); @@ -353,7 +245,7 @@ describe('AuthorizationService', () => { describe('when helper method returns true', () => { const setup = () => { - const user = userFactory.build(); + const user = userFactory.buildWithId(); const requiredPermissions = [testPermission]; authorizationHelper.hasOneOfPermissions.mockReturnValueOnce(true); @@ -372,7 +264,7 @@ describe('AuthorizationService', () => { describe('hasOneOfPermissions', () => { describe('when helper method returns false', () => { const setup = () => { - const user = userFactory.build(); + const user = userFactory.buildWithId(); const requiredPermissions = [testPermission]; authorizationHelper.hasOneOfPermissions.mockReturnValueOnce(false); @@ -391,7 +283,7 @@ describe('AuthorizationService', () => { describe('when helper method returns true', () => { const setup = () => { - const user = userFactory.build(); + const user = userFactory.buildWithId(); const requiredPermissions = [testPermission]; authorizationHelper.hasOneOfPermissions.mockReturnValueOnce(true); @@ -410,12 +302,18 @@ describe('AuthorizationService', () => { }); describe('getUserWithPermissions', () => { + const setup = () => { + const user = userFactory.buildWithId(); + + userRepo.findById.mockResolvedValueOnce(user); + + return { user }; + }; + it('should return user received from loader', async () => { - const userId = 'test'; - const user = userFactory.build(); - loader.getUserWithPermissions.mockResolvedValueOnce(user); + const { user } = setup(); - const result = await service.getUserWithPermissions(userId); + const result = await service.getUserWithPermissions(user.id); expect(result).toEqual(user); }); diff --git a/apps/server/src/modules/authorization/domain/service/authorization.service.ts b/apps/server/src/modules/authorization/domain/service/authorization.service.ts new file mode 100644 index 00000000000..5218dffda81 --- /dev/null +++ b/apps/server/src/modules/authorization/domain/service/authorization.service.ts @@ -0,0 +1,59 @@ +import { Injectable, UnauthorizedException } from '@nestjs/common'; +import { BaseDO, EntityId, User } from '@shared/domain'; +import { AuthorizableObject } from '@shared/domain/domain-object'; +import { UserRepo } from '@shared/repo'; +import { AuthorizationHelper } from './authorization.helper'; +import { ForbiddenLoggableException } from '../error'; +import { RuleManager } from './rule-manager'; +import { AuthorizationContext } from '../type'; + +@Injectable() +export class AuthorizationService { + constructor( + private readonly ruleManager: RuleManager, + private readonly authorizationHelper: AuthorizationHelper, + private readonly userRepo: UserRepo + ) {} + + public checkPermission(user: User, object: AuthorizableObject | BaseDO, context: AuthorizationContext): void { + if (!this.hasPermission(user, object, context)) { + throw new ForbiddenLoggableException(user.id, object.constructor.name, context); + } + } + + public hasPermission(user: User, object: AuthorizableObject | BaseDO, context: AuthorizationContext): boolean { + const rule = this.ruleManager.selectRule(user, object, context); + const hasPermission = rule.hasPermission(user, object, context); + + return hasPermission; + } + + public checkAllPermissions(user: User, requiredPermissions: string[]): void { + if (!this.authorizationHelper.hasAllPermissions(user, requiredPermissions)) { + // TODO: Should be ForbiddenLoggableException + throw new UnauthorizedException(); + } + } + + public hasAllPermissions(user: User, requiredPermissions: string[]): boolean { + return this.authorizationHelper.hasAllPermissions(user, requiredPermissions); + } + + public checkOneOfPermissions(user: User, requiredPermissions: string[]): void { + if (!this.authorizationHelper.hasOneOfPermissions(user, requiredPermissions)) { + // TODO: Should be ForbiddenLoggableException + throw new UnauthorizedException(); + } + } + + public hasOneOfPermissions(user: User, requiredPermissions: string[]): boolean { + return this.authorizationHelper.hasOneOfPermissions(user, requiredPermissions); + } + + public async getUserWithPermissions(userId: EntityId): Promise { + // replace with service method getUserWithPermissions BC-5069 + const userWithPopulatedRoles = await this.userRepo.findById(userId, true); + + return userWithPopulatedRoles; + } +} diff --git a/apps/server/src/modules/authorization/domain/service/index.ts b/apps/server/src/modules/authorization/domain/service/index.ts new file mode 100644 index 00000000000..4175cc4b7a7 --- /dev/null +++ b/apps/server/src/modules/authorization/domain/service/index.ts @@ -0,0 +1,5 @@ +export * from './authorization.service'; +export * from './authorization.helper'; +export * from './rule-manager'; +export * from './authorization-reference.service'; +export * from './reference.loader'; diff --git a/apps/server/src/modules/authorization/reference.loader.spec.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts similarity index 87% rename from apps/server/src/modules/authorization/reference.loader.spec.ts rename to apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts index 9bba78b1880..0403ebcbfd5 100644 --- a/apps/server/src/modules/authorization/reference.loader.spec.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts @@ -14,11 +14,11 @@ import { TeamsRepo, UserRepo, } from '@shared/repo'; -import { roleFactory, setupEntities, userFactory } from '@shared/testing'; +import { setupEntities, userFactory } from '@shared/testing'; import { BoardDoAuthorizableService } from '@src/modules/board'; import { ContextExternalToolAuthorizableService } from '@src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service'; import { ReferenceLoader } from './reference.loader'; -import { AuthorizableReferenceType } from './types'; +import { AuthorizableReferenceType } from '../type'; describe('reference.loader', () => { let service: ReferenceLoader; @@ -138,7 +138,7 @@ describe('reference.loader', () => { it('should call userRepo.findById', async () => { await service.loadAuthorizableObject(AuthorizableReferenceType.User, entityId); - expect(userRepo.findById).toBeCalledWith(entityId, true); + expect(userRepo.findById).toBeCalledWith(entityId); }); it('should call lessonRepo.findById', async () => { @@ -192,33 +192,4 @@ describe('reference.loader', () => { ).rejects.toThrow(NotImplementedException); }); }); - - describe('getUserWithPermissions', () => { - describe('when user successfully', () => { - const setup = () => { - const roles = [roleFactory.build()]; - const user = userFactory.buildWithId({ roles }); - userRepo.findById.mockResolvedValue(user); - return { - user, - }; - }; - - it('should call userRepo.findById with specific arguments', async () => { - const { user } = setup(); - - await service.getUserWithPermissions(user.id); - - expect(userRepo.findById).toBeCalledWith(user.id, true); - }); - - it('should return user', async () => { - const { user } = setup(); - - const result = await service.getUserWithPermissions(user.id); - - expect(result).toBe(user); - }); - }); - }); }); diff --git a/apps/server/src/modules/authorization/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts similarity index 88% rename from apps/server/src/modules/authorization/reference.loader.ts rename to apps/server/src/modules/authorization/domain/service/reference.loader.ts index 9afe013fd24..5c38963c6f5 100644 --- a/apps/server/src/modules/authorization/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -1,5 +1,5 @@ import { Injectable, NotImplementedException } from '@nestjs/common'; -import { BaseDO, EntityId, User } from '@shared/domain'; +import { BaseDO, EntityId } from '@shared/domain'; import { AuthorizableObject } from '@shared/domain/domain-object'; import { CourseGroupRepo, @@ -12,11 +12,10 @@ import { TeamsRepo, UserRepo, } from '@shared/repo'; -import { BoardDoAuthorizableService } from '@src/modules/board/service'; +import { BoardDoAuthorizableService } from '@src/modules/board'; import { ContextExternalToolAuthorizableService } from '@src/modules/tool/context-external-tool/service'; -import { AuthorizableReferenceType } from './types'; +import { AuthorizableReferenceType } from '../type'; -// replace later with general "base" do-repo type RepoType = | TaskRepo | CourseRepo @@ -55,7 +54,7 @@ export class ReferenceLoader { this.repos.set(AuthorizableReferenceType.Task, { repo: this.taskRepo }); this.repos.set(AuthorizableReferenceType.Course, { repo: this.courseRepo }); this.repos.set(AuthorizableReferenceType.CourseGroup, { repo: this.courseGroupRepo }); - this.repos.set(AuthorizableReferenceType.User, { repo: this.userRepo, populate: true }); + this.repos.set(AuthorizableReferenceType.User, { repo: this.userRepo }); this.repos.set(AuthorizableReferenceType.School, { repo: this.schoolRepo }); this.repos.set(AuthorizableReferenceType.Lesson, { repo: this.lessonRepo }); this.repos.set(AuthorizableReferenceType.Team, { repo: this.teamsRepo, populate: true }); @@ -90,10 +89,4 @@ export class ReferenceLoader { return object; } - - async getUserWithPermissions(userId: EntityId): Promise { - const user = await this.userRepo.findById(userId, true); - - return user; - } } diff --git a/apps/server/src/modules/authorization/rule-manager.spec.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts similarity index 97% rename from apps/server/src/modules/authorization/rule-manager.spec.ts rename to apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts index 0a2b90c7639..78ef313ade1 100644 --- a/apps/server/src/modules/authorization/rule-manager.spec.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts @@ -1,6 +1,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { InternalServerErrorException, NotImplementedException } from '@nestjs/common'; import { Test } from '@nestjs/testing'; +import { courseFactory, setupEntities, userFactory } from '@shared/testing'; +import { AuthorizationContextBuilder } from '../mapper'; import { BoardDoRule, ContextExternalToolRule, @@ -13,10 +15,8 @@ import { TaskRule, TeamRule, UserRule, -} from '@shared/domain/rules'; -import { UserLoginMigrationRule } from '@shared/domain/rules/user-login-migration.rule'; -import { courseFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder } from './authorization-context.builder'; + UserLoginMigrationRule, +} from '../rules'; import { RuleManager } from './rule-manager'; describe('RuleManager', () => { diff --git a/apps/server/src/modules/authorization/rule-manager.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.ts similarity index 88% rename from apps/server/src/modules/authorization/rule-manager.ts rename to apps/server/src/modules/authorization/domain/service/rule-manager.ts index 3aece68402a..77d09f284c2 100644 --- a/apps/server/src/modules/authorization/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -1,21 +1,21 @@ import { Injectable, InternalServerErrorException, NotImplementedException } from '@nestjs/common'; import { BaseDO, User } from '@shared/domain'; import { AuthorizableObject } from '@shared/domain/domain-object'; // fix import when it is avaible +import type { AuthorizationContext, Rule } from '../type'; import { BoardDoRule, + ContextExternalToolRule, CourseGroupRule, CourseRule, + LegacySchoolRule, LessonRule, SchoolExternalToolRule, - LegacySchoolRule, SubmissionRule, TaskRule, TeamRule, + UserLoginMigrationRule, UserRule, -} from '@shared/domain/rules'; -import { ContextExternalToolRule } from '@shared/domain/rules/context-external-tool.rule'; -import { UserLoginMigrationRule } from '@shared/domain/rules/user-login-migration.rule'; -import { AuthorizationContext, Rule } from './types'; +} from '../rules'; @Injectable() export class RuleManager { diff --git a/apps/server/src/modules/authorization/types/action.enum.ts b/apps/server/src/modules/authorization/domain/type/action.enum.ts similarity index 100% rename from apps/server/src/modules/authorization/types/action.enum.ts rename to apps/server/src/modules/authorization/domain/type/action.enum.ts diff --git a/apps/server/src/modules/authorization/types/allowed-authorization-object-type.enum.ts b/apps/server/src/modules/authorization/domain/type/allowed-authorization-object-type.enum.ts similarity index 100% rename from apps/server/src/modules/authorization/types/allowed-authorization-object-type.enum.ts rename to apps/server/src/modules/authorization/domain/type/allowed-authorization-object-type.enum.ts diff --git a/apps/server/src/modules/authorization/types/authorization-context.interface.ts b/apps/server/src/modules/authorization/domain/type/authorization-context.interface.ts similarity index 100% rename from apps/server/src/modules/authorization/types/authorization-context.interface.ts rename to apps/server/src/modules/authorization/domain/type/authorization-context.interface.ts diff --git a/apps/server/src/modules/authorization/types/authorization-loader-service.ts b/apps/server/src/modules/authorization/domain/type/authorization-loader-service.ts similarity index 100% rename from apps/server/src/modules/authorization/types/authorization-loader-service.ts rename to apps/server/src/modules/authorization/domain/type/authorization-loader-service.ts diff --git a/apps/server/src/modules/authorization/types/index.ts b/apps/server/src/modules/authorization/domain/type/index.ts similarity index 100% rename from apps/server/src/modules/authorization/types/index.ts rename to apps/server/src/modules/authorization/domain/type/index.ts index 92e7b0c8bf5..b1942491098 100644 --- a/apps/server/src/modules/authorization/types/index.ts +++ b/apps/server/src/modules/authorization/domain/type/index.ts @@ -1,5 +1,5 @@ export * from './action.enum'; export * from './authorization-context.interface'; export * from './rule.interface'; -export * from './allowed-authorization-object-type.enum'; export * from './authorization-loader-service'; +export * from './allowed-authorization-object-type.enum'; diff --git a/apps/server/src/modules/authorization/types/rule.interface.ts b/apps/server/src/modules/authorization/domain/type/rule.interface.ts similarity index 100% rename from apps/server/src/modules/authorization/types/rule.interface.ts rename to apps/server/src/modules/authorization/domain/type/rule.interface.ts diff --git a/apps/server/src/modules/authorization/index.ts b/apps/server/src/modules/authorization/index.ts index bee8b7d4bb1..e129df2cd11 100644 --- a/apps/server/src/modules/authorization/index.ts +++ b/apps/server/src/modules/authorization/index.ts @@ -1,5 +1,15 @@ -export * from './authorization.module'; -export * from './authorization.service'; -export * from './authorization-context.builder'; -export * from './types'; -export * from './feathers'; +export { AuthorizationModule } from './authorization.module'; +export { + AuthorizationService, + AuthorizationHelper, + AuthorizationContextBuilder, + ForbiddenLoggableException, + Rule, + AuthorizationContext, + // Action should not be exported, but hard to solve for now. The AuthorizationContextBuilder is the prefared way + Action, + AuthorizationLoaderService, + AuthorizationLoaderServiceGeneric, +} from './domain'; +// Should not used anymore +export { FeathersAuthorizationService } from './feathers'; diff --git a/apps/server/src/modules/board/uc/board.uc.ts b/apps/server/src/modules/board/uc/board.uc.ts index 7c3194916ac..3e39fd32de3 100644 --- a/apps/server/src/modules/board/uc/board.uc.ts +++ b/apps/server/src/modules/board/uc/board.uc.ts @@ -9,8 +9,8 @@ import { EntityId, } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization/authorization.service'; -import { Action } from '@src/modules/authorization/types/action.enum'; +import { AuthorizationService } from '@src/modules/authorization/domain'; +import { Action } from '@src/modules/authorization'; import { CardService, ColumnBoardService, ColumnService } from '../service'; import { BoardDoAuthorizableService } from '../service/board-do-authorizable.service'; diff --git a/apps/server/src/modules/board/uc/card.uc.ts b/apps/server/src/modules/board/uc/card.uc.ts index 577f3a8b963..170469f0cc4 100644 --- a/apps/server/src/modules/board/uc/card.uc.ts +++ b/apps/server/src/modules/board/uc/card.uc.ts @@ -1,8 +1,7 @@ import { Injectable } from '@nestjs/common'; import { AnyBoardDo, AnyContentElementDo, Card, ContentElementType, EntityId } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization/authorization.service'; -import { Action } from '@src/modules/authorization/types/action.enum'; +import { AuthorizationService, Action } from '@src/modules/authorization'; import { BoardDoAuthorizableService, CardService, ContentElementService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/board/uc/element.uc.ts b/apps/server/src/modules/board/uc/element.uc.ts index e5dc039168c..08357b01798 100644 --- a/apps/server/src/modules/board/uc/element.uc.ts +++ b/apps/server/src/modules/board/uc/element.uc.ts @@ -8,8 +8,7 @@ import { UserRoleEnum, } from '@shared/domain'; import { Logger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; -import { Action } from '@src/modules/authorization/types/action.enum'; +import { AuthorizationService, Action } from '@src/modules/authorization'; import { AnyElementContentBody } from '../controller/dto'; import { BoardDoAuthorizableService, ContentElementService } from '../service'; import { SubmissionItemService } from '../service/submission-item.service'; diff --git a/apps/server/src/modules/board/uc/submission-item.uc.spec.ts b/apps/server/src/modules/board/uc/submission-item.uc.spec.ts index 33bc8468fc9..5d06172acee 100644 --- a/apps/server/src/modules/board/uc/submission-item.uc.spec.ts +++ b/apps/server/src/modules/board/uc/submission-item.uc.spec.ts @@ -9,8 +9,7 @@ import { userFactory, } from '@shared/testing'; import { Logger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; -import { Action } from '@src/modules/authorization/types/action.enum'; +import { AuthorizationService, Action } from '@src/modules/authorization'; import { BoardDoAuthorizableService, ContentElementService, SubmissionItemService } from '../service'; import { SubmissionItemUc } from './submission-item.uc'; diff --git a/apps/server/src/modules/board/uc/submission-item.uc.ts b/apps/server/src/modules/board/uc/submission-item.uc.ts index e59afa4b49b..67e7951673f 100644 --- a/apps/server/src/modules/board/uc/submission-item.uc.ts +++ b/apps/server/src/modules/board/uc/submission-item.uc.ts @@ -9,8 +9,7 @@ import { UserRoleEnum, } from '@shared/domain'; import { Logger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; -import { Action } from '@src/modules/authorization/types/action.enum'; +import { AuthorizationService, Action } from '@src/modules/authorization'; import { BoardDoAuthorizableService, ContentElementService, SubmissionItemService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/files-storage/README.md b/apps/server/src/modules/files-storage/README.md index 4bb6fb4ccb8..f44be536309 100644 --- a/apps/server/src/modules/files-storage/README.md +++ b/apps/server/src/modules/files-storage/README.md @@ -88,7 +88,7 @@ folder structure in S3 > schoolId/fileRecordId > .trash/schoolId/fileRecordId (see: ## Goals and Ideas > ### Deleting Files) -### Authorisation Module +### Authorization Module The authorisation is solved by parents. Therefore it is required that the parent types must be known to the authentication service. diff --git a/apps/server/src/modules/files-storage/files-storage-api.module.ts b/apps/server/src/modules/files-storage/files-storage-api.module.ts index 9d5283b47b7..aab383a158f 100644 --- a/apps/server/src/modules/files-storage/files-storage-api.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-api.module.ts @@ -2,13 +2,13 @@ import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { CoreModule } from '@src/core'; import { AuthenticationModule } from '@src/modules/authentication/authentication.module'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationReferenceModule } from '@src/modules/authorization/authorization-reference.module'; import { FileSecurityController, FilesStorageController } from './controller'; import { FilesStorageModule } from './files-storage.module'; import { FilesStorageUC } from './uc'; @Module({ - imports: [AuthorizationModule, FilesStorageModule, AuthenticationModule, CoreModule, HttpModule], + imports: [AuthorizationReferenceModule, FilesStorageModule, AuthenticationModule, CoreModule, HttpModule], controllers: [FilesStorageController, FileSecurityController], providers: [FilesStorageUC], }) diff --git a/apps/server/src/modules/files-storage/mapper/files-storage.mapper.spec.ts b/apps/server/src/modules/files-storage/mapper/files-storage.mapper.spec.ts index 3165ec49021..a26103ae983 100644 --- a/apps/server/src/modules/files-storage/mapper/files-storage.mapper.spec.ts +++ b/apps/server/src/modules/files-storage/mapper/files-storage.mapper.spec.ts @@ -1,6 +1,6 @@ import { NotImplementedException } from '@nestjs/common'; import { fileRecordFactory, setupEntities } from '@shared/testing'; -import { AuthorizableReferenceType } from '@src/modules/authorization'; +import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { DownloadFileParams, FileRecordListResponse, diff --git a/apps/server/src/modules/files-storage/mapper/files-storage.mapper.ts b/apps/server/src/modules/files-storage/mapper/files-storage.mapper.ts index 3d298cd3b2e..9b30acd4ada 100644 --- a/apps/server/src/modules/files-storage/mapper/files-storage.mapper.ts +++ b/apps/server/src/modules/files-storage/mapper/files-storage.mapper.ts @@ -1,5 +1,5 @@ import { NotImplementedException, StreamableFile } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization'; +import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { plainToClass } from 'class-transformer'; import { DownloadFileParams, diff --git a/apps/server/src/modules/files-storage/uc/files-storage-copy.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-copy.uc.spec.ts index 5d4ab900549..612558e80c1 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-copy.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-copy.uc.spec.ts @@ -8,7 +8,8 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { Action, AuthorizationService } from '@src/modules/authorization'; +import { Action } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { FileRecordParams } from '../controller/dto'; import { FileRecord, FileRecordParentType } from '../entity'; import { CopyFileResponseBuilder } from '../mapper'; @@ -68,7 +69,7 @@ describe('FilesStorageUC', () => { let module: TestingModule; let filesStorageUC: FilesStorageUC; let filesStorageService: DeepMocked; - let authorizationService: DeepMocked; + let authorizationReferenceService: DeepMocked; beforeEach(() => { jest.resetAllMocks(); @@ -97,8 +98,8 @@ describe('FilesStorageUC', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: HttpService, @@ -112,7 +113,7 @@ describe('FilesStorageUC', () => { }).compile(); filesStorageUC = module.get(FilesStorageUC); - authorizationService = module.get(AuthorizationService); + authorizationReferenceService = module.get(AuthorizationReferenceService); filesStorageService = module.get(FilesStorageService); }); @@ -134,7 +135,7 @@ describe('FilesStorageUC', () => { const fileResponse = CopyFileResponseBuilder.build(targetFile.id, sourceFile.id, targetFile.name); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce().mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce().mockResolvedValueOnce(); filesStorageService.copyFilesOfParent.mockResolvedValueOnce([[fileResponse], 1]); return { sourceParams, targetParams, userId, fileResponse }; @@ -145,7 +146,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.copyFilesOfParent(userId, sourceParams, targetParams); - expect(authorizationService.checkPermissionByReferences).toHaveBeenNthCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenNthCalledWith( 1, userId, sourceParams.parentType, @@ -159,7 +160,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.copyFilesOfParent(userId, sourceParams, targetParams); - expect(authorizationService.checkPermissionByReferences).toHaveBeenNthCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenNthCalledWith( 2, userId, targetParams.target.parentType, @@ -191,7 +192,7 @@ describe('FilesStorageUC', () => { const targetParams = createTargetParams(); const error = new ForbiddenException(); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(error).mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(error).mockResolvedValueOnce(); return { sourceParams, targetParams, userId, error }; }; @@ -210,7 +211,7 @@ describe('FilesStorageUC', () => { const targetParams = createTargetParams(); const error = new ForbiddenException(); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce().mockRejectedValueOnce(error); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce().mockRejectedValueOnce(error); return { sourceParams, targetParams, userId, error }; }; @@ -229,7 +230,9 @@ describe('FilesStorageUC', () => { const targetParams = createTargetParams(); const error = new ForbiddenException(); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(error).mockRejectedValueOnce(error); + authorizationReferenceService.checkPermissionByReferences + .mockRejectedValueOnce(error) + .mockRejectedValueOnce(error); return { sourceParams, targetParams, userId, error }; }; @@ -249,7 +252,7 @@ describe('FilesStorageUC', () => { const error = new Error('test'); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce().mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce().mockResolvedValueOnce(); filesStorageService.copyFilesOfParent.mockRejectedValueOnce(error); return { sourceParams, targetParams, userId, error }; @@ -289,7 +292,7 @@ describe('FilesStorageUC', () => { ); filesStorageService.getFileRecord.mockResolvedValue(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce().mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce().mockResolvedValueOnce(); filesStorageService.copy.mockResolvedValueOnce([fileResponse]); return { singleFileParams, copyFileParams, userId, fileResponse, fileRecord }; @@ -308,7 +311,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.copyOneFile(userId, singleFileParams, copyFileParams); - expect(authorizationService.checkPermissionByReferences).toHaveBeenNthCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenNthCalledWith( 1, userId, fileRecord.parentType, @@ -322,7 +325,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.copyOneFile(userId, singleFileParams, copyFileParams); - expect(authorizationService.checkPermissionByReferences).toHaveBeenNthCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenNthCalledWith( 2, userId, copyFileParams.target.parentType, @@ -355,7 +358,7 @@ describe('FilesStorageUC', () => { const error = new ForbiddenException(); filesStorageService.getFileRecord.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(error).mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(error).mockResolvedValueOnce(); return { singleFileParams, copyFileParams, userId, fileRecord, error }; }; @@ -375,7 +378,7 @@ describe('FilesStorageUC', () => { const error = new ForbiddenException(); filesStorageService.getFileRecord.mockResolvedValue(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce().mockRejectedValueOnce(error); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce().mockRejectedValueOnce(error); return { singleFileParams, copyFileParams, userId, fileRecord, error }; }; @@ -395,7 +398,9 @@ describe('FilesStorageUC', () => { const error = new ForbiddenException(); filesStorageService.getFileRecord.mockResolvedValue(fileRecord); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(error).mockRejectedValueOnce(error); + authorizationReferenceService.checkPermissionByReferences + .mockRejectedValueOnce(error) + .mockRejectedValueOnce(error); return { singleFileParams, copyFileParams, userId, fileRecord, error }; }; @@ -434,7 +439,7 @@ describe('FilesStorageUC', () => { const error = new Error('test'); filesStorageService.getFileRecord.mockResolvedValue(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce().mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce().mockResolvedValueOnce(); filesStorageService.copy.mockRejectedValueOnce(error); return { singleFileParams, copyFileParams, userId, fileRecord, error }; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-delete.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-delete.uc.spec.ts index eb13f830be6..fc461a50106 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-delete.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-delete.uc.spec.ts @@ -8,7 +8,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { FileRecordParams } from '../controller/dto'; import { FileRecord, FileRecordParentType } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; @@ -57,7 +57,7 @@ describe('FilesStorageUC delete methods', () => { let filesStorageUC: FilesStorageUC; let filesStorageService: DeepMocked; let previewService: DeepMocked; - let authorizationService: DeepMocked; + let authorizationReferenceService: DeepMocked; beforeAll(async () => { await setupEntities([FileRecord]); @@ -82,8 +82,8 @@ describe('FilesStorageUC delete methods', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: HttpService, @@ -97,7 +97,7 @@ describe('FilesStorageUC delete methods', () => { }).compile(); filesStorageUC = module.get(FilesStorageUC); - authorizationService = module.get(AuthorizationService); + authorizationReferenceService = module.get(AuthorizationReferenceService); filesStorageService = module.get(FilesStorageService); previewService = module.get(PreviewService); }); @@ -122,7 +122,7 @@ describe('FilesStorageUC delete methods', () => { const fileRecord = fileRecords[0]; const mockedResult = [[fileRecord], 0] as Counted; - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); filesStorageService.deleteFilesOfParent.mockResolvedValueOnce(mockedResult); return { params, userId, mockedResult, requestParams, fileRecord }; @@ -134,7 +134,7 @@ describe('FilesStorageUC delete methods', () => { await filesStorageUC.deleteFilesOfParent(userId, requestParams); - expect(authorizationService.checkPermissionByReferences).toBeCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toBeCalledWith( userId, allowedType, requestParams.parentId, @@ -171,7 +171,7 @@ describe('FilesStorageUC delete methods', () => { const setup = () => { const { requestParams, userId } = createParams(); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); return { requestParams, userId }; }; @@ -192,7 +192,7 @@ describe('FilesStorageUC delete methods', () => { const { requestParams, userId } = createParams(); const error = new Error('test'); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); filesStorageService.deleteFilesOfParent.mockRejectedValueOnce(error); return { requestParams, userId, error }; @@ -214,7 +214,7 @@ describe('FilesStorageUC delete methods', () => { const requestParams = { fileRecordId: fileRecord.id, parentType: fileRecord.parentType }; filesStorageService.getFileRecord.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); filesStorageService.delete.mockResolvedValueOnce(); return { requestParams, userId, fileRecord }; @@ -227,7 +227,7 @@ describe('FilesStorageUC delete methods', () => { const allowedType = FilesStorageMapper.mapToAllowedAuthorizationEntityType(requestParams.parentType); - expect(authorizationService.checkPermissionByReferences).toBeCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toBeCalledWith( userId, allowedType, fileRecord.parentId, @@ -301,7 +301,7 @@ describe('FilesStorageUC delete methods', () => { const requestParams = { fileRecordId: fileRecord.id, parentType: fileRecord.parentType }; filesStorageService.getFileRecord.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); return { requestParams, userId }; }; @@ -322,7 +322,7 @@ describe('FilesStorageUC delete methods', () => { const error = new Error('test'); filesStorageService.getFileRecord.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); filesStorageService.delete.mockRejectedValueOnce(error); return { requestParams, userId, error }; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-download-preview.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-download-preview.uc.spec.ts index 3a2f6f1ac21..795939e5cb2 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-download-preview.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-download-preview.uc.spec.ts @@ -7,7 +7,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { SingleFileParams } from '../controller/dto'; import { FileRecord } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; @@ -43,7 +43,7 @@ describe('FilesStorageUC', () => { let filesStorageUC: FilesStorageUC; let filesStorageService: DeepMocked; let previewService: DeepMocked; - let authorizationService: DeepMocked; + let authorizationReferenceService: DeepMocked; beforeAll(async () => { await setupEntities([FileRecord]); @@ -72,8 +72,8 @@ describe('FilesStorageUC', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: HttpService, @@ -83,7 +83,7 @@ describe('FilesStorageUC', () => { }).compile(); filesStorageUC = module.get(FilesStorageUC); - authorizationService = module.get(AuthorizationService); + authorizationReferenceService = module.get(AuthorizationReferenceService); filesStorageService = module.get(FilesStorageService); previewService = module.get(PreviewService); }); @@ -143,7 +143,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.downloadPreview(userId, fileDownloadParams, previewParams); const allowedType = FilesStorageMapper.mapToAllowedAuthorizationEntityType(fileRecord.parentType); - expect(authorizationService.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( userId, allowedType, fileRecord.parentId, @@ -190,7 +190,7 @@ describe('FilesStorageUC', () => { filesStorageService.getFileRecord.mockResolvedValueOnce(fileRecord); const error = new ForbiddenException(); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(error); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(error); return { fileDownloadParams, userId, fileRecord, previewParams, error }; }; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-download.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-download.uc.spec.ts index b1aa6d4b437..3e7fa61fd7f 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-download.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-download.uc.spec.ts @@ -7,7 +7,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { SingleFileParams } from '../controller/dto'; import { FileRecord } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; @@ -34,7 +34,7 @@ describe('FilesStorageUC', () => { let module: TestingModule; let filesStorageUC: FilesStorageUC; let filesStorageService: DeepMocked; - let authorizationService: DeepMocked; + let authorizationReferenceService: DeepMocked; beforeAll(async () => { await setupEntities([FileRecord]); @@ -59,8 +59,8 @@ describe('FilesStorageUC', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: HttpService, @@ -74,7 +74,7 @@ describe('FilesStorageUC', () => { }).compile(); filesStorageUC = module.get(FilesStorageUC); - authorizationService = module.get(AuthorizationService); + authorizationReferenceService = module.get(AuthorizationReferenceService); filesStorageService = module.get(FilesStorageService); }); @@ -99,7 +99,7 @@ describe('FilesStorageUC', () => { const fileResponse = createMock(); filesStorageService.getFileRecord.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValue(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValue(); filesStorageService.download.mockResolvedValueOnce(fileResponse); return { fileDownloadParams, userId, fileRecord, fileResponse }; @@ -121,7 +121,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.download(userId, fileDownloadParams); const allowedType = FilesStorageMapper.mapToAllowedAuthorizationEntityType(fileRecord.parentType); - expect(authorizationService.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( userId, allowedType, fileRecord.parentId, @@ -171,7 +171,7 @@ describe('FilesStorageUC', () => { const error = new ForbiddenException(); filesStorageService.getFileRecord.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(error); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(error); return { fileDownloadParams, userId, fileRecord }; }; @@ -190,7 +190,7 @@ describe('FilesStorageUC', () => { const error = new Error('test'); filesStorageService.getFileRecord.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValue(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValue(); filesStorageService.download.mockRejectedValueOnce(error); return { fileDownloadParams, userId, error }; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-get.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-get.uc.spec.ts index 02cdb82ded6..7f372a1fe80 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-get.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-get.uc.spec.ts @@ -6,7 +6,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { FileRecordParams } from '../controller/dto'; import { FileRecord, FileRecordParentType } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; @@ -37,7 +37,7 @@ describe('FilesStorageUC', () => { let module: TestingModule; let filesStorageUC: FilesStorageUC; let filesStorageService: DeepMocked; - let authorizationService: DeepMocked; + let authorizationReferenceService: DeepMocked; beforeAll(async () => { await setupEntities([FileRecord]); @@ -62,8 +62,8 @@ describe('FilesStorageUC', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: HttpService, @@ -77,7 +77,7 @@ describe('FilesStorageUC', () => { }).compile(); filesStorageUC = module.get(FilesStorageUC); - authorizationService = module.get(AuthorizationService); + authorizationReferenceService = module.get(AuthorizationReferenceService); filesStorageService = module.get(FilesStorageService); }); @@ -100,7 +100,7 @@ describe('FilesStorageUC', () => { const { fileRecords, params } = buildFileRecordsWithParams(); filesStorageService.getFileRecordsOfParent.mockResolvedValueOnce([fileRecords, fileRecords.length]); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); return { userId, params, fileRecords }; }; @@ -110,7 +110,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.getFileRecordsOfParent(userId, params); - expect(authorizationService.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( userId, params.parentType, params.parentId, @@ -141,7 +141,7 @@ describe('FilesStorageUC', () => { const { fileRecords, params } = buildFileRecordsWithParams(); filesStorageService.getFileRecordsOfParent.mockResolvedValueOnce([fileRecords, fileRecords.length]); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(new Error('Bla')); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(new Error('Bla')); return { userId, params, fileRecords }; }; @@ -160,7 +160,7 @@ describe('FilesStorageUC', () => { const fileRecords = []; filesStorageService.getFileRecordsOfParent.mockResolvedValueOnce([fileRecords, fileRecords.length]); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); return { userId, params, fileRecords }; }; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-restore.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-restore.uc.spec.ts index be8a6d32561..e01e3116b79 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-restore.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-restore.uc.spec.ts @@ -7,7 +7,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { FileRecordParams, SingleFileParams } from '../controller/dto'; import { FileRecord, FileRecordParentType } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; @@ -52,7 +52,7 @@ describe('FilesStorageUC', () => { let module: TestingModule; let filesStorageUC: FilesStorageUC; let filesStorageService: DeepMocked; - let authorizationService: DeepMocked; + let authorizationReferenceService: DeepMocked; beforeAll(async () => { await setupEntities([FileRecord]); @@ -77,8 +77,8 @@ describe('FilesStorageUC', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: HttpService, @@ -92,7 +92,7 @@ describe('FilesStorageUC', () => { }).compile(); filesStorageUC = module.get(FilesStorageUC); - authorizationService = module.get(AuthorizationService); + authorizationReferenceService = module.get(AuthorizationReferenceService); filesStorageService = module.get(FilesStorageService); }); @@ -113,7 +113,7 @@ describe('FilesStorageUC', () => { const setup = () => { const { params, userId, fileRecords } = buildFileRecordsWithParams(); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); filesStorageService.restoreFilesOfParent.mockResolvedValueOnce([fileRecords, fileRecords.length]); return { params, userId, fileRecords }; @@ -125,7 +125,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.restoreFilesOfParent(userId, params); - expect(authorizationService.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( userId, allowedType, params.parentId, @@ -153,7 +153,7 @@ describe('FilesStorageUC', () => { describe('WHEN user is not authorised ', () => { const setup = () => { const { params, userId } = buildFileRecordsWithParams(); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); return { params, userId }; }; @@ -189,7 +189,7 @@ describe('FilesStorageUC', () => { const { params, userId, fileRecord } = buildFileRecordWithParams(); filesStorageService.getFileRecordMarkedForDelete.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); filesStorageService.restore.mockResolvedValueOnce(); return { params, userId, fileRecord }; @@ -209,7 +209,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.restoreOneFile(userId, params); - expect(authorizationService.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( userId, allowedType, fileRecord.parentId, @@ -239,7 +239,7 @@ describe('FilesStorageUC', () => { const { params, userId, fileRecord } = buildFileRecordWithParams(); filesStorageService.getFileRecordMarkedForDelete.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); return { params, userId }; }; @@ -276,7 +276,7 @@ describe('FilesStorageUC', () => { const error = new Error('test'); filesStorageService.getFileRecordMarkedForDelete.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); filesStorageService.restore.mockRejectedValueOnce(error); return { params, userId, error }; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-update.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-update.uc.spec.ts index 57ec96cff61..19d9984eea8 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-update.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-update.uc.spec.ts @@ -6,7 +6,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { RenameFileParams, ScanResultParams, SingleFileParams } from '../controller/dto'; import { FileRecord } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; @@ -31,7 +31,7 @@ describe('FilesStorageUC', () => { let module: TestingModule; let filesStorageUC: FilesStorageUC; let filesStorageService: DeepMocked; - let authorizationService: DeepMocked; + let authorizationReferenceService: DeepMocked; beforeAll(async () => { await setupEntities([FileRecord]); @@ -56,8 +56,8 @@ describe('FilesStorageUC', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: HttpService, @@ -71,7 +71,7 @@ describe('FilesStorageUC', () => { }).compile(); filesStorageUC = module.get(FilesStorageUC); - authorizationService = module.get(AuthorizationService); + authorizationReferenceService = module.get(AuthorizationReferenceService); filesStorageService = module.get(FilesStorageService); }); @@ -137,7 +137,7 @@ describe('FilesStorageUC', () => { const data: RenameFileParams = { fileName: 'test_new_name.txt' }; filesStorageService.getFileRecord.mockResolvedValueOnce(fileRecord); - authorizationService.checkPermissionByReferences.mockResolvedValueOnce(); + authorizationReferenceService.checkPermissionByReferences.mockResolvedValueOnce(); filesStorageService.patchFilename.mockResolvedValueOnce(fileRecord); return { userId, params, fileRecord, data }; @@ -155,7 +155,7 @@ describe('FilesStorageUC', () => { await filesStorageUC.patchFilename(userId, params, data); - expect(authorizationService.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( userId, fileRecord.parentType, fileRecord.parentId, diff --git a/apps/server/src/modules/files-storage/uc/files-storage-upload.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-upload.uc.spec.ts index 903a2f2a6a6..ed7defb54fb 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-upload.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-upload.uc.spec.ts @@ -8,7 +8,8 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { AxiosHeadersKeyValue, axiosResponseFactory, fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { Action, AuthorizationService } from '@src/modules/authorization'; +import { Action } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { AxiosRequestConfig, AxiosResponse } from 'axios'; import { Request } from 'express'; import { of } from 'rxjs'; @@ -72,7 +73,7 @@ describe('FilesStorageUC upload methods', () => { let module: TestingModule; let filesStorageUC: FilesStorageUC; let filesStorageService: DeepMocked; - let authorizationService: DeepMocked; + let authorizationReferenceService: DeepMocked; let httpService: DeepMocked; beforeAll(async () => { @@ -98,8 +99,8 @@ describe('FilesStorageUC upload methods', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: HttpService, @@ -113,7 +114,7 @@ describe('FilesStorageUC upload methods', () => { }).compile(); filesStorageUC = module.get(FilesStorageUC); - authorizationService = module.get(AuthorizationService); + authorizationReferenceService = module.get(AuthorizationReferenceService); httpService = module.get(HttpService); filesStorageService = module.get(FilesStorageService); }); @@ -171,7 +172,7 @@ describe('FilesStorageUC upload methods', () => { await filesStorageUC.uploadFromUrl(userId, uploadFromUrlParams); - expect(authorizationService.checkPermissionByReferences).toBeCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toBeCalledWith( userId, uploadFromUrlParams.parentType, uploadFromUrlParams.parentId, @@ -218,7 +219,7 @@ describe('FilesStorageUC upload methods', () => { const setup = () => { const { userId, uploadFromUrlParams } = createUploadFromUrlParams(); const error = new Error('test'); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(error); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(error); return { uploadFromUrlParams, userId, error }; }; @@ -300,7 +301,7 @@ describe('FilesStorageUC upload methods', () => { await filesStorageUC.upload(userId, params, request); const allowedType = FilesStorageMapper.mapToAllowedAuthorizationEntityType(params.parentType); - expect(authorizationService.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( userId, allowedType, params.parentId, @@ -365,7 +366,7 @@ describe('FilesStorageUC upload methods', () => { const request = createRequest(); const error = new ForbiddenException(); - authorizationService.checkPermissionByReferences.mockRejectedValueOnce(error); + authorizationReferenceService.checkPermissionByReferences.mockRejectedValueOnce(error); return { params, userId, request, error }; }; diff --git a/apps/server/src/modules/files-storage/uc/files-storage.uc.ts b/apps/server/src/modules/files-storage/uc/files-storage.uc.ts index fa6a27202de..f5e6d372a6b 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage.uc.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage.uc.ts @@ -2,7 +2,8 @@ import { HttpService } from '@nestjs/axios'; import { Injectable, NotFoundException } from '@nestjs/common'; import { Counted, EntityId } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationContext, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContext } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { AxiosRequestConfig, AxiosResponse } from 'axios'; import busboy from 'busboy'; import { Request } from 'express'; @@ -32,7 +33,7 @@ import { PreviewService } from '../service/preview.service'; export class FilesStorageUC { constructor( private logger: LegacyLogger, - private readonly authorizationService: AuthorizationService, + private readonly authorizationReferenceService: AuthorizationReferenceService, private readonly httpService: HttpService, private readonly filesStorageService: FilesStorageService, private readonly previewService: PreviewService @@ -47,7 +48,7 @@ export class FilesStorageUC { context: AuthorizationContext ) { const allowedType = FilesStorageMapper.mapToAllowedAuthorizationEntityType(parentType); - await this.authorizationService.checkPermissionByReferences(userId, allowedType, parentId, context); + await this.authorizationReferenceService.checkPermissionByReferences(userId, allowedType, parentId, context); } // upload diff --git a/apps/server/src/modules/learnroom/controller/api-test/rooms-copy-timeout.api.spec.ts b/apps/server/src/modules/learnroom/controller/api-test/rooms-copy-timeout.api.spec.ts index ed62a2b6ade..3b9f04e07db 100644 --- a/apps/server/src/modules/learnroom/controller/api-test/rooms-copy-timeout.api.spec.ts +++ b/apps/server/src/modules/learnroom/controller/api-test/rooms-copy-timeout.api.spec.ts @@ -1,5 +1,4 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; -import { IConfig } from '@hpi-schul-cloud/commons/lib/interfaces/IConfig'; import { EntityManager } from '@mikro-orm/mongodb'; import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; @@ -16,11 +15,15 @@ import { import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; import { Request } from 'express'; import request from 'supertest'; +import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { createMock } from '@golevelup/ts-jest'; +// config must be set outside before the server module is importat, otherwise the configuration is already set +const configBefore = Configuration.toObject({ plainSecrets: true }); Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); Configuration.set('INCOMING_REQUEST_TIMEOUT_COPY_API', 1); // eslint-disable-next-line import/first -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@src/modules/server'; // This needs to be in a separate test file because of the above configuration. // When we find a way to mock the config, it should be moved alongside the other API tests. @@ -28,10 +31,8 @@ describe('Rooms copy (API)', () => { let app: INestApplication; let em: EntityManager; let currentUser: ICurrentUser; - let configBefore: IConfig; beforeAll(async () => { - configBefore = Configuration.toObject({ plainSecrets: true }); const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [ServerTestModule], }) @@ -43,6 +44,8 @@ describe('Rooms copy (API)', () => { return true; }, }) + .overrideProvider(FilesStorageClientAdapterService) + .useValue(createMock()) .compile(); app = moduleFixture.createNestApplication(); diff --git a/apps/server/src/modules/learnroom/index.ts b/apps/server/src/modules/learnroom/index.ts index f2dc136ce5e..e4d907784d5 100644 --- a/apps/server/src/modules/learnroom/index.ts +++ b/apps/server/src/modules/learnroom/index.ts @@ -1,2 +1,3 @@ export * from './learnroom.module'; export * from './service/course-copy.service'; +export { CourseService } from './service'; diff --git a/apps/server/src/modules/learnroom/learnroom-api.module.ts b/apps/server/src/modules/learnroom/learnroom-api.module.ts index b72db2d7f59..81a514a0a7b 100644 --- a/apps/server/src/modules/learnroom/learnroom-api.module.ts +++ b/apps/server/src/modules/learnroom/learnroom-api.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { BoardRepo, CourseRepo, DashboardModelMapper, DashboardRepo, LessonRepo, UserRepo } from '@shared/repo'; import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationReferenceModule } from '@src/modules/authorization/authorization-reference.module'; import { CopyHelperModule } from '@src/modules/copy-helper'; import { LessonModule } from '@src/modules/lesson'; import { CourseController } from './controller/course.controller'; @@ -20,7 +21,7 @@ import { } from './uc'; @Module({ - imports: [AuthorizationModule, LessonModule, CopyHelperModule, LearnroomModule], + imports: [AuthorizationModule, LessonModule, CopyHelperModule, LearnroomModule, AuthorizationReferenceModule], controllers: [DashboardController, CourseController, RoomsController], providers: [ DashboardUc, diff --git a/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts index 2e7e9f739ad..33beee8c4db 100644 --- a/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts @@ -3,9 +3,9 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { ForbiddenException, InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; -import { boardFactory, courseFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizableReferenceType } from '@src/modules/authorization'; -import { AuthorizationService } from '@src/modules/authorization/authorization.service'; +import { courseFactory, setupEntities, userFactory } from '@shared/testing'; +import { AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationReferenceService, AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { CopyElementType, CopyStatusEnum } from '@src/modules/copy-helper'; import { CourseCopyService } from '../service'; import { CourseCopyUC } from './course-copy.uc'; @@ -13,7 +13,7 @@ import { CourseCopyUC } from './course-copy.uc'; describe('course copy uc', () => { let module: TestingModule; let uc: CourseCopyUC; - let authorization: DeepMocked; + let authorization: DeepMocked; let courseCopyService: DeepMocked; beforeAll(async () => { @@ -22,8 +22,8 @@ describe('course copy uc', () => { providers: [ CourseCopyUC, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: CourseCopyService, @@ -33,7 +33,7 @@ describe('course copy uc', () => { }).compile(); uc = module.get(CourseCopyUC); - authorization = module.get(AuthorizationService); + authorization = module.get(AuthorizationReferenceService); courseCopyService = module.get(CourseCopyService); }); @@ -41,91 +41,99 @@ describe('course copy uc', () => { await module.close(); }); - beforeEach(() => { - Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); - }); + // Please be careful the Configuration.set is effects all tests !!! describe('copy course', () => { - const setup = () => { - const user = userFactory.buildWithId(); - const allCourses = courseFactory.buildList(3, { teachers: [user] }); - const course = allCourses[0]; - const originalBoard = boardFactory.build({ course }); - const courseCopy = courseFactory.buildWithId({ teachers: [user] }); - const boardCopy = boardFactory.build({ course: courseCopy }); - - authorization.getUserWithPermissions.mockResolvedValue(user); - const status = { - title: 'courseCopy', - type: CopyElementType.COURSE, - status: CopyStatusEnum.SUCCESS, - copyEntity: courseCopy, + describe('when authorization to course resolve with void and feature is deactivated', () => { + const setup = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', false); + const user = userFactory.buildWithId(); + const course = courseFactory.buildWithId({ teachers: [user] }); + + return { + userId: user.id, + courseId: course.id, + }; }; - courseCopyService.copyCourse.mockResolvedValue(status); + it('should throw if copy feature is deactivated', async () => { + const { courseId, userId } = setup(); + + await expect(uc.copyCourse(userId, courseId)).rejects.toThrowError( + new InternalServerErrorException('Copy Feature not enabled') + ); + }); + }); - return { - user, - course, - originalBoard, - courseCopy, - boardCopy, - allCourses, - status, + describe('when authorization to course resolve with void and feature is activated', () => { + const setup = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); + const user = userFactory.buildWithId(); + const course = courseFactory.buildWithId({ teachers: [user] }); + const courseCopy = courseFactory.buildWithId({ teachers: [user] }); + + const status = { + title: 'courseCopy', + type: CopyElementType.COURSE, + status: CopyStatusEnum.SUCCESS, + copyEntity: courseCopy, + }; + + authorization.checkPermissionByReferences.mockResolvedValueOnce(); + courseCopyService.copyCourse.mockResolvedValueOnce(status); + + return { + userId: user.id, + courseId: course.id, + status, + }; }; - }; - it('should throw if copy feature is deactivated', async () => { - Configuration.set('FEATURE_COPY_SERVICE_ENABLED', false); - const { course, user } = setup(); - await expect(uc.copyCourse(user.id, course.id)).rejects.toThrowError(InternalServerErrorException); - }); + it('should check permission to create a course', async () => { + const { courseId, userId } = setup(); - it('should check permission to create a course', async () => { - const { course, user } = setup(); - await uc.copyCourse(user.id, course.id); - expect(authorization.checkPermissionByReferences).toBeCalledWith( - user.id, - AuthorizableReferenceType.Course, - course.id, - { - action: Action.write, - requiredPermissions: [Permission.COURSE_CREATE], - } - ); - }); + await uc.copyCourse(userId, courseId); - it('should call course copy service', async () => { - const { course, user } = setup(); - await uc.copyCourse(user.id, course.id); - expect(courseCopyService.copyCourse).toBeCalledWith({ userId: user.id, courseId: course.id }); - }); + const context = AuthorizationContextBuilder.write([Permission.COURSE_CREATE]); + expect(authorization.checkPermissionByReferences).toBeCalledWith( + userId, + AuthorizableReferenceType.Course, + courseId, + context + ); + }); + + it('should call course copy service', async () => { + const { courseId, userId } = setup(); + + await uc.copyCourse(userId, courseId); + + expect(courseCopyService.copyCourse).toBeCalledWith({ userId, courseId }); + }); + + it('should return status', async () => { + const { courseId, userId, status } = setup(); + + const result = await uc.copyCourse(userId, courseId); - it('should return status', async () => { - const { course, user, status } = setup(); - const result = await uc.copyCourse(user.id, course.id); - expect(result).toEqual(status); + expect(result).toEqual(status); + }); }); - describe('when access to course is forbidden', () => { + describe('when authorization to course throw a forbidden exception', () => { const setupWithCourseForbidden = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); const user = userFactory.buildWithId(); const course = courseFactory.buildWithId(); - authorization.checkPermissionByReferences.mockImplementation(() => { - throw new ForbiddenException(); - }); + authorization.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); + return { user, course }; }; it('should throw ForbiddenException', async () => { const { course, user } = setupWithCourseForbidden(); - try { - await uc.copyCourse(user.id, course.id); - throw new Error('should have failed'); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } + await expect(uc.copyCourse(user.id, course.id)).rejects.toThrowError(new ForbiddenException()); }); }); }); diff --git a/apps/server/src/modules/learnroom/uc/course-copy.uc.ts b/apps/server/src/modules/learnroom/uc/course-copy.uc.ts index 0d806c36263..0f700d57f17 100644 --- a/apps/server/src/modules/learnroom/uc/course-copy.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course-copy.uc.ts @@ -1,24 +1,23 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; -import { Action, AuthorizableReferenceType, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationReferenceService, AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { CopyStatus } from '@src/modules/copy-helper'; import { CourseCopyService } from '../service'; @Injectable() export class CourseCopyUC { constructor( - private readonly authorization: AuthorizationService, + private readonly authorization: AuthorizationReferenceService, private readonly courseCopyService: CourseCopyService ) {} async copyCourse(userId: EntityId, courseId: EntityId): Promise { this.checkFeatureEnabled(); - await this.authorization.checkPermissionByReferences(userId, AuthorizableReferenceType.Course, courseId, { - action: Action.write, - requiredPermissions: [Permission.COURSE_CREATE], - }); + const context = AuthorizationContextBuilder.write([Permission.COURSE_CREATE]); + await this.authorization.checkPermissionByReferences(userId, AuthorizableReferenceType.Course, courseId, context); const result = await this.courseCopyService.copyCourse({ userId, courseId }); @@ -26,6 +25,7 @@ export class CourseCopyUC { } private checkFeatureEnabled() { + // @hpi-schul-cloud/commons is deprecated way to get envirements const enabled = Configuration.get('FEATURE_COPY_SERVICE_ENABLED') as boolean; if (!enabled) { throw new InternalServerErrorException('Copy Feature not enabled'); diff --git a/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts index 3d93827f06d..04e3d0de480 100644 --- a/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts @@ -1,7 +1,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { CommonCartridgeExportService } from '@src/modules/learnroom/service/common-cartridge-export.service'; -import { AuthorizationService } from '@src/modules'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { ObjectId } from 'bson'; +import { ForbiddenException } from '@nestjs/common'; import { CourseExportUc } from './course-export.uc'; import { CommonCartridgeVersion } from '../common-cartridge'; @@ -9,7 +11,7 @@ describe('CourseExportUc', () => { let module: TestingModule; let courseExportUc: CourseExportUc; let courseExportServiceMock: DeepMocked; - let authorizationServiceMock: DeepMocked; + let authorizationServiceMock: DeepMocked; beforeAll(async () => { module = await Test.createTestingModule({ @@ -20,33 +22,86 @@ describe('CourseExportUc', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, ], }).compile(); courseExportUc = module.get(CourseExportUc); courseExportServiceMock = module.get(CommonCartridgeExportService); - authorizationServiceMock = module.get(AuthorizationService); + authorizationServiceMock = module.get(AuthorizationReferenceService); }); afterAll(async () => { await module.close(); }); + afterEach(() => { + // is needed to solve buffer test isolation + jest.resetAllMocks(); + }); + describe('exportCourse', () => { - const version: CommonCartridgeVersion = CommonCartridgeVersion.V_1_1_0; - it('should check for permissions', async () => { - authorizationServiceMock.checkPermissionByReferences.mockResolvedValueOnce(); - await expect(courseExportUc.exportCourse('', '', version)).resolves.not.toThrow(); - expect(authorizationServiceMock.checkPermissionByReferences).toBeCalledTimes(1); + const setupParams = () => { + const courseId = new ObjectId().toHexString(); + const userId = new ObjectId().toHexString(); + const version: CommonCartridgeVersion = CommonCartridgeVersion.V_1_1_0; + + return { version, userId, courseId }; + }; + + describe('when authorization throw a error', () => { + const setup = () => { + authorizationServiceMock.checkPermissionByReferences.mockRejectedValueOnce(new ForbiddenException()); + courseExportServiceMock.exportCourse.mockResolvedValueOnce(Buffer.from('')); + + return setupParams(); + }; + + it('should pass this error', async () => { + const { courseId, userId, version } = setup(); + + await expect(courseExportUc.exportCourse(courseId, userId, version)).rejects.toThrowError( + new ForbiddenException() + ); + }); + }); + + describe('when course export service throw a error', () => { + const setup = () => { + authorizationServiceMock.checkPermissionByReferences.mockResolvedValueOnce(); + courseExportServiceMock.exportCourse.mockRejectedValueOnce(new Error()); + + return setupParams(); + }; + + it('should pass this error', async () => { + const { courseId, userId, version } = setup(); + + await expect(courseExportUc.exportCourse(courseId, userId, version)).rejects.toThrowError(new Error()); + }); }); - it('should return a binary file as buffer', async () => { - courseExportServiceMock.exportCourse.mockResolvedValueOnce(Buffer.from('')); - authorizationServiceMock.checkPermissionByReferences.mockResolvedValueOnce(); + describe('when authorization resolve', () => { + const setup = () => { + authorizationServiceMock.checkPermissionByReferences.mockResolvedValueOnce(); + courseExportServiceMock.exportCourse.mockResolvedValueOnce(Buffer.from('')); + + return setupParams(); + }; + + it('should check for permissions', async () => { + const { courseId, userId, version } = setup(); + + await expect(courseExportUc.exportCourse(courseId, userId, version)).resolves.not.toThrow(); + expect(authorizationServiceMock.checkPermissionByReferences).toBeCalledTimes(1); + }); + + it('should return a binary file as buffer', async () => { + const { courseId, userId, version } = setup(); - await expect(courseExportUc.exportCourse('', '', version)).resolves.toBeInstanceOf(Buffer); + await expect(courseExportUc.exportCourse(courseId, userId, version)).resolves.toBeInstanceOf(Buffer); + }); }); }); }); diff --git a/apps/server/src/modules/learnroom/uc/course-export.uc.ts b/apps/server/src/modules/learnroom/uc/course-export.uc.ts index 418812e0cd8..07e427c8fa8 100644 --- a/apps/server/src/modules/learnroom/uc/course-export.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course-export.uc.ts @@ -1,6 +1,7 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; -import { Action, AuthorizableReferenceType, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationReferenceService, AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { CommonCartridgeVersion } from '../common-cartridge'; import { CommonCartridgeExportService } from '../service/common-cartridge-export.service'; @@ -8,14 +9,18 @@ import { CommonCartridgeExportService } from '../service/common-cartridge-export export class CourseExportUc { constructor( private readonly courseExportService: CommonCartridgeExportService, - private readonly authorizationService: AuthorizationService + private readonly authorizationService: AuthorizationReferenceService ) {} async exportCourse(courseId: EntityId, userId: EntityId, version: CommonCartridgeVersion): Promise { - await this.authorizationService.checkPermissionByReferences(userId, AuthorizableReferenceType.Course, courseId, { - action: Action.read, - requiredPermissions: [Permission.COURSE_EDIT], - }); + const context = AuthorizationContextBuilder.read([Permission.COURSE_EDIT]); + await this.authorizationService.checkPermissionByReferences( + userId, + AuthorizableReferenceType.Course, + courseId, + context + ); + return this.courseExportService.exportCourse(courseId, userId, version); } } diff --git a/apps/server/src/modules/learnroom/uc/lesson-copy.uc.spec.ts b/apps/server/src/modules/learnroom/uc/lesson-copy.uc.spec.ts index a00e0be6c26..34d73449b4c 100644 --- a/apps/server/src/modules/learnroom/uc/lesson-copy.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/lesson-copy.uc.spec.ts @@ -3,13 +3,12 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { ObjectId } from '@mikro-orm/mongodb'; import { ForbiddenException, InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { BaseDO, Permission, User } from '@shared/domain'; +import { Permission } from '@shared/domain'; import { CourseRepo, LessonRepo, UserRepo } from '@shared/repo'; import { courseFactory, lessonFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizableReferenceType, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@src/modules/copy-helper'; import { EtherpadService, LessonCopyService } from '@src/modules/lesson/service'; -import { AuthorizableObject } from '@shared/domain/domain-object'; import { LessonCopyUC } from './lesson-copy.uc'; describe('lesson copy uc', () => { @@ -71,193 +70,286 @@ describe('lesson copy uc', () => { copyHelperService = module.get(CopyHelperService); }); - beforeEach(() => { - Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); + afterEach(() => { jest.resetAllMocks(); }); + // Please be careful the Configuration.set is effects all tests !!! + describe('copy lesson', () => { - const setup = () => { - const user = userFactory.buildWithId(); - const course = courseFactory.buildWithId({ teachers: [user] }); - const allLessons = lessonFactory.buildList(3, { course }); - const lesson = allLessons[0]; - - authorisation.getUserWithPermissions.mockResolvedValue(user); - lessonRepo.findById.mockResolvedValue(lesson); - lessonRepo.findAllByCourseIds.mockResolvedValue([allLessons, allLessons.length]); - lessonRepo.save.mockResolvedValue(undefined); - - courseRepo.findById.mockResolvedValue(course); - authorisation.hasPermission.mockReturnValue(true); - const copy = lessonFactory.buildWithId({ course }); - const status = { - title: 'lessonCopy', - type: CopyElementType.LESSON, - status: CopyStatusEnum.SUCCESS, - copyEntity: copy, - }; - lessonCopyService.copyLesson.mockResolvedValue(status); - lessonCopyService.updateCopiedEmbeddedTasks.mockReturnValue(status); - const lessonCopyName = 'Copy'; - copyHelperService.deriveCopyName.mockReturnValue(lessonCopyName); - - return { - user, - course, - lesson, - copy, - status, - lessonCopyName, - allLessons, - userId: user.id, + // missing tests + // when course repo is throw a error + // when lesson repo is throw a error + describe('when feature flag is disabled', () => { + const setup = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', false); + + const user = userFactory.buildWithId(); + const course = courseFactory.buildWithId({ teachers: [user] }); + const lesson = lessonFactory.build({ course }); + + const parentParams = { courseId: course.id, userId: user.id }; + + return { + userId: user.id, + lessonId: lesson.id, + parentParams, + }; }; - }; - - it('should throw if copy feature is deactivated', async () => { - Configuration.set('FEATURE_COPY_SERVICE_ENABLED', false); - const { course, user, lesson, userId } = setup(); - await expect(uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId })).rejects.toThrowError( - InternalServerErrorException - ); - }); - it('should fetch correct user', async () => { - const { course, user, lesson, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId }); - expect(authorisation.getUserWithPermissions).toBeCalledWith(user.id); - }); + it('should throw if copy feature is deactivated', async () => { + const { userId, lessonId, parentParams } = setup(); - it('should fetch correct lesson', async () => { - const { course, user, lesson, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId }); - expect(lessonRepo.findById).toBeCalledWith(lesson.id); + await expect(uc.copyLesson(userId, lessonId, parentParams)).rejects.toThrowError( + new InternalServerErrorException('Copy Feature not enabled') + ); + }); }); - it('should fetch destination course', async () => { - const { course, user, lesson, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId }); - expect(courseRepo.findById).toBeCalledWith(course.id); - }); + describe('when authorization resolve and no destination course is passed', () => { + const setup = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); - it('should pass without destination course', async () => { - const { user, lesson, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { userId }); - expect(courseRepo.findById).not.toHaveBeenCalled(); - }); + const user = userFactory.buildWithId(); + const course = courseFactory.buildWithId({ teachers: [user] }); + const allLessons = lessonFactory.buildList(3, { course }); + const copy = lessonFactory.buildWithId({ course }); + + const lesson = allLessons[0]; + const status = { + title: 'lessonCopy', + type: CopyElementType.LESSON, + status: CopyStatusEnum.SUCCESS, + copyEntity: copy, + }; + const lessonCopyName = 'Copy'; + const parentParams = { userId: user.id }; + + authorisation.getUserWithPermissions.mockResolvedValueOnce(user); + authorisation.hasPermission.mockReturnValue(true); + + lessonRepo.findById.mockResolvedValueOnce(lesson); + lessonRepo.findAllByCourseIds.mockResolvedValueOnce([allLessons, allLessons.length]); + courseRepo.findById.mockResolvedValueOnce(course); + + lessonCopyService.copyLesson.mockResolvedValueOnce(status); + copyHelperService.deriveCopyName.mockReturnValueOnce(lessonCopyName); + + return { + user, + userId: user.id, + course, + courseId: course.id, + lessonId: lesson.id, + parentParams, + }; + }; + + it('should pass without destination course', async () => { + const { lessonId, userId, parentParams } = setup(); - it('should check authorisation for lesson', async () => { - const { course, user, lesson, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId }); - expect(authorisation.hasPermission).toBeCalledWith(user, lesson, { - action: Action.read, - requiredPermissions: [Permission.TOPIC_CREATE], + await uc.copyLesson(userId, lessonId, parentParams); + + expect(courseRepo.findById).not.toHaveBeenCalled(); }); - }); - it('should check authorisation for destination course', async () => { - const { course, user, lesson, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId }); - expect(authorisation.checkPermissionByReferences).toBeCalledWith( - user.id, - AuthorizableReferenceType.Course, - course.id, - { - action: Action.write, - requiredPermissions: [], - } - ); - }); + it('should pass authorisation check without destination course', async () => { + const { course, user, lessonId, userId, parentParams } = setup(); + + await uc.copyLesson(userId, lessonId, parentParams); - it('should pass authorisation check without destination course', async () => { - const { course, user, lesson, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { userId }); - expect(authorisation.hasPermission).not.toBeCalledWith(user, course, { - action: Action.write, - requiredPermissions: [], + const context = AuthorizationContextBuilder.write([]); + expect(authorisation.hasPermission).not.toBeCalledWith(user, course, context); }); }); - it('should call copy service', async () => { - const { course, user, lesson, lessonCopyName, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId }); - expect(lessonCopyService.copyLesson).toBeCalledWith({ - originalLessonId: lesson.id, - destinationCourse: course, - user, - copyName: lessonCopyName, + describe('when authorization resolve', () => { + const setup = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); + + const user = userFactory.buildWithId(); + const course = courseFactory.buildWithId({ teachers: [user] }); + const allLessons = lessonFactory.buildList(3, { course }); + const copy = lessonFactory.buildWithId({ course }); + + const lesson = allLessons[0]; + const status = { + title: 'lessonCopy', + type: CopyElementType.LESSON, + status: CopyStatusEnum.SUCCESS, + copyEntity: copy, + }; + const lessonCopyName = 'Copy'; + const parentParams = { courseId: course.id, userId: user.id }; + + authorisation.getUserWithPermissions.mockResolvedValueOnce(user); + authorisation.hasPermission.mockReturnValue(true); + + lessonRepo.findById.mockResolvedValueOnce(lesson); + lessonRepo.findAllByCourseIds.mockResolvedValueOnce([allLessons, allLessons.length]); + courseRepo.findById.mockResolvedValueOnce(course); + + lessonCopyService.copyLesson.mockResolvedValueOnce(status); + // lessonCopyService.updateCopiedEmbeddedTasks.mockReturnValue(status); + copyHelperService.deriveCopyName.mockReturnValueOnce(lessonCopyName); + + return { + user, + userId: user.id, + course, + courseId: course.id, + lesson, + lessonId: lesson.id, + parentParams, + copy, + status, + lessonCopyName, + allLessons, + }; + }; + + it('should fetch correct user', async () => { + const { lessonId, userId, parentParams } = setup(); + + await uc.copyLesson(userId, lessonId, parentParams); + + expect(authorisation.getUserWithPermissions).toBeCalledWith(userId); }); - }); - it('should return status', async () => { - const { course, user, lesson, status, userId } = setup(); - const result = await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId }); - expect(result).toEqual(status); - }); + it('should fetch correct lesson', async () => { + const { lessonId, userId, parentParams } = setup(); - it('should use copyHelperService', async () => { - const { course, user, lesson, allLessons, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId }); - const existingNames = allLessons.map((l) => l.name); - expect(copyHelperService.deriveCopyName).toHaveBeenCalledWith(lesson.name, existingNames); - }); + await uc.copyLesson(userId, lessonId, parentParams); + + expect(lessonRepo.findById).toBeCalledWith(lessonId); + }); + + it('should fetch destination course', async () => { + const { course, lessonId, userId, parentParams } = setup(); + + await uc.copyLesson(userId, lessonId, parentParams); + + expect(courseRepo.findById).toBeCalledWith(course.id); + }); + + it('should check authorisation for lesson', async () => { + const { lessonId, userId, user, lesson, parentParams } = setup(); + + await uc.copyLesson(userId, lessonId, parentParams); + + const context = AuthorizationContextBuilder.read([Permission.TOPIC_CREATE]); + expect(authorisation.hasPermission).toBeCalledWith(user, lesson, context); + }); + + it('should check authorisation for destination course', async () => { + const { course, user, lessonId, userId, parentParams } = setup(); + + await uc.copyLesson(userId, lessonId, parentParams); + + const context = AuthorizationContextBuilder.write([]); + expect(authorisation.checkPermission).toBeCalledWith(user, course, context); + }); + + it('should call copy service', async () => { + const { course, user, lessonId, lessonCopyName, userId, parentParams } = setup(); + + await uc.copyLesson(userId, lessonId, parentParams); + + expect(lessonCopyService.copyLesson).toBeCalledWith({ + originalLessonId: lessonId, + destinationCourse: course, + user, + copyName: lessonCopyName, + }); + }); - it('should use findAllByCourseIds to determine existing lesson names', async () => { - const { course, user, lesson, userId } = setup(); - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId }); - expect(lessonRepo.findAllByCourseIds).toHaveBeenCalledWith([course.id]); + it('should return status', async () => { + const { lessonId, status, userId, parentParams } = setup(); + + const result = await uc.copyLesson(userId, lessonId, parentParams); + + expect(result).toEqual(status); + }); + + it('should use copyHelperService', async () => { + const { lessonId, allLessons, userId, lesson, parentParams } = setup(); + + await uc.copyLesson(userId, lessonId, parentParams); + + const existingNames = allLessons.map((l) => l.name); + expect(copyHelperService.deriveCopyName).toHaveBeenCalledWith(lesson.name, existingNames); + }); + + it('should use findAllByCourseIds to determine existing lesson names', async () => { + const { courseId, userId, lessonId, parentParams } = setup(); + + await uc.copyLesson(userId, lessonId, parentParams); + + expect(lessonRepo.findAllByCourseIds).toHaveBeenCalledWith([courseId]); + }); }); - describe('when access to lesson is forbidden', () => { - const setupWithLessonForbidden = () => { + describe('when authorization of lesson throw forbidden exception', () => { + const setup = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); + const user = userFactory.buildWithId(); const course = courseFactory.buildWithId(); const lesson = lessonFactory.buildWithId(); - userRepo.findById.mockResolvedValue(user); - lessonRepo.findById.mockResolvedValue(lesson); - // authorisation should not be mocked - authorisation.hasPermission.mockImplementation((u: User, e: AuthorizableObject | BaseDO) => e !== lesson); - return { user, course, lesson }; + const parentParams = { courseId: course.id, userId: new ObjectId().toHexString() }; + + userRepo.findById.mockResolvedValueOnce(user); + lessonRepo.findById.mockResolvedValueOnce(lesson); + courseRepo.findById.mockResolvedValueOnce(course); + authorisation.hasPermission.mockReturnValueOnce(false); + + return { + userId: user.id, + lessonId: lesson.id, + parentParams, + }; }; - it('should throw NotFoundException', async () => { - const { course, user, lesson } = setupWithLessonForbidden(); + it('should throw ForbiddenException', async () => { + const { parentParams, userId, lessonId } = setup(); - try { - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId: new ObjectId().toHexString() }); - throw new Error('should have failed'); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } + await expect(uc.copyLesson(userId, lessonId, parentParams)).rejects.toThrowError( + new ForbiddenException('could not find lesson to copy') + ); }); }); - describe('when access to course is forbidden', () => { - const setupWithCourseForbidden = () => { + describe('when authorization of course throw with forbidden exception', () => { + const setup = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); + const user = userFactory.buildWithId(); const course = courseFactory.buildWithId(); const lesson = lessonFactory.buildWithId(); - userRepo.findById.mockResolvedValue(user); - lessonRepo.findById.mockResolvedValue(lesson); - courseRepo.findById.mockResolvedValue(course); - authorisation.hasPermission.mockReturnValue(true); - authorisation.checkPermissionByReferences.mockImplementation(() => { + + const parentParams = { courseId: course.id, userId: new ObjectId().toHexString() }; + + userRepo.findById.mockResolvedValueOnce(user); + lessonRepo.findById.mockResolvedValueOnce(lesson); + courseRepo.findById.mockResolvedValueOnce(course); + authorisation.checkPermission.mockImplementationOnce(() => { throw new ForbiddenException(); }); - return { user, course, lesson }; + authorisation.hasPermission.mockReturnValueOnce(true); + + return { + userId: user.id, + lessonId: lesson.id, + parentParams, + }; }; - it('should throw Forbidden Exception', async () => { - const { course, user, lesson } = setupWithCourseForbidden(); + it('should pass the forbidden exception', async () => { + const { parentParams, userId, lessonId } = setup(); - try { - await uc.copyLesson(user.id, lesson.id, { courseId: course.id, userId: new ObjectId().toHexString() }); - throw new Error('should have failed'); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } + await expect(uc.copyLesson(userId, lessonId, parentParams)).rejects.toThrowError(new ForbiddenException()); }); }); }); diff --git a/apps/server/src/modules/learnroom/uc/lesson-copy.uc.ts b/apps/server/src/modules/learnroom/uc/lesson-copy.uc.ts index 753200a5718..7ec51f5ef1c 100644 --- a/apps/server/src/modules/learnroom/uc/lesson-copy.uc.ts +++ b/apps/server/src/modules/learnroom/uc/lesson-copy.uc.ts @@ -1,14 +1,9 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { ForbiddenException, Injectable, InternalServerErrorException } from '@nestjs/common'; -import { EntityId } from '@shared/domain'; +import { Course, EntityId, LessonEntity, User } from '@shared/domain'; import { Permission } from '@shared/domain/interface/permission.enum'; import { CourseRepo, LessonRepo } from '@shared/repo'; -import { - Action, - AuthorizableReferenceType, - AuthorizationContextBuilder, - AuthorizationService, -} from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; import { CopyHelperService, CopyStatus } from '@src/modules/copy-helper'; import { LessonCopyParentParams } from '@src/modules/lesson'; import { LessonCopyService } from '@src/modules/lesson/service'; @@ -24,27 +19,24 @@ export class LessonCopyUC { ) {} async copyLesson(userId: EntityId, lessonId: EntityId, parentParams: LessonCopyParentParams): Promise { - this.featureEnabled(); - const user = await this.authorisation.getUserWithPermissions(userId); - const originalLesson = await this.lessonRepo.findById(lessonId); - const context = AuthorizationContextBuilder.read([Permission.TOPIC_CREATE]); - if (!this.authorisation.hasPermission(user, originalLesson, context)) { - throw new ForbiddenException('could not find lesson to copy'); - } + this.checkFeatureEnabled(); + + const [user, originalLesson]: [User, LessonEntity] = await Promise.all([ + this.authorisation.getUserWithPermissions(userId), + this.lessonRepo.findById(lessonId), + ]); + this.checkOriginalLessonAuthorization(user, originalLesson); + + // should be a seperate private method const destinationCourse = parentParams.courseId ? await this.courseRepo.findById(parentParams.courseId) : originalLesson.course; - await this.authorisation.checkPermissionByReferences( - userId, - AuthorizableReferenceType.Course, - destinationCourse.id, - { - action: Action.write, - requiredPermissions: [], - } - ); + // --- + + this.checkDestinationCourseAuthorization(user, destinationCourse); + // should be a seperate private method const [existingLessons] = await this.lessonRepo.findAllByCourseIds([originalLesson.course.id]); const existingNames = existingLessons.map((l) => l.name); const copyName = this.copyHelperService.deriveCopyName(originalLesson.name, existingNames); @@ -55,11 +47,25 @@ export class LessonCopyUC { user, copyName, }); + // --- return copyStatus; } - private featureEnabled() { + private checkOriginalLessonAuthorization(user: User, originalLesson: LessonEntity): void { + const contextReadWithTopicCreate = AuthorizationContextBuilder.read([Permission.TOPIC_CREATE]); + if (!this.authorisation.hasPermission(user, originalLesson, contextReadWithTopicCreate)) { + // error message is not correct, switch to authorisation.checkPermission() makse sense for me + throw new ForbiddenException('could not find lesson to copy'); + } + } + + private checkDestinationCourseAuthorization(user: User, destinationCourse: Course): void { + const contextCanWrite = AuthorizationContextBuilder.write([]); + this.authorisation.checkPermission(user, destinationCourse, contextCanWrite); + } + + private checkFeatureEnabled() { const enabled = Configuration.get('FEATURE_COPY_SERVICE_ENABLED') as boolean; if (!enabled) { throw new InternalServerErrorException('Copy Feature not enabled'); diff --git a/apps/server/src/modules/learnroom/uc/room-board-dto.factory.ts b/apps/server/src/modules/learnroom/uc/room-board-dto.factory.ts index 98a0957f3d3..cafa02e4d20 100644 --- a/apps/server/src/modules/learnroom/uc/room-board-dto.factory.ts +++ b/apps/server/src/modules/learnroom/uc/room-board-dto.factory.ts @@ -14,8 +14,7 @@ import { TaskWithStatusVo, User, } from '@shared/domain'; -import { AuthorizationService } from '@src/modules/authorization/authorization.service'; -import { Action } from '@src/modules/authorization/types/action.enum'; +import { AuthorizationService, Action } from '@src/modules/authorization'; import { ColumnBoardMetaData, LessonMetaData, diff --git a/apps/server/src/modules/legacy-school/uc/legacy-school.uc.spec.ts b/apps/server/src/modules/legacy-school/uc/legacy-school.uc.spec.ts index 17dc2de5fd0..8747a07ada6 100644 --- a/apps/server/src/modules/legacy-school/uc/legacy-school.uc.spec.ts +++ b/apps/server/src/modules/legacy-school/uc/legacy-school.uc.spec.ts @@ -66,6 +66,9 @@ describe('LegacySchoolUc', () => { jest.resetAllMocks(); }); + // Tests with case of authService.checkPermission.mockImplementation(() => throw new ForbiddenException()); + // are missed for both methodes + describe('setMigration is called', () => { describe('when first starting the migration', () => { const setup = () => { @@ -77,7 +80,7 @@ describe('LegacySchoolUc', () => { }); userLoginMigrationService.findMigrationBySchool.mockResolvedValue(null); - authService.checkPermissionByReferences.mockImplementation(() => Promise.resolve()); + authService.checkPermission.mockReturnValueOnce(); schoolService.getSchoolById.mockResolvedValue(school); userLoginMigrationService.setMigration.mockResolvedValue(userLoginMigration); }; @@ -107,7 +110,7 @@ describe('LegacySchoolUc', () => { }); userLoginMigrationService.findMigrationBySchool.mockResolvedValue(userLoginMigration); - authService.checkPermissionByReferences.mockImplementation(() => Promise.resolve()); + authService.checkPermission.mockReturnValueOnce(); schoolService.getSchoolById.mockResolvedValue(school); userLoginMigrationService.setMigration.mockResolvedValue(updatedUserLoginMigration); schoolMigrationService.hasSchoolMigratedUser.mockResolvedValue(true); @@ -138,7 +141,7 @@ describe('LegacySchoolUc', () => { }); userLoginMigrationService.findMigrationBySchool.mockResolvedValue(userLoginMigration); - authService.checkPermissionByReferences.mockImplementation(() => Promise.resolve()); + authService.checkPermission.mockReturnValueOnce(); schoolService.getSchoolById.mockResolvedValue(school); userLoginMigrationService.setMigration.mockResolvedValue(updatedUserLoginMigration); schoolMigrationService.hasSchoolMigratedUser.mockResolvedValue(false); @@ -177,7 +180,7 @@ describe('LegacySchoolUc', () => { }); userLoginMigrationService.findMigrationBySchool.mockResolvedValue(userLoginMigration); - authService.checkPermissionByReferences.mockImplementation(() => Promise.resolve()); + authService.checkPermission.mockReturnValueOnce(); schoolService.getSchoolById.mockResolvedValue(school); userLoginMigrationService.setMigration.mockResolvedValue(updatedUserLoginMigration); }; @@ -208,7 +211,7 @@ describe('LegacySchoolUc', () => { }); userLoginMigrationService.findMigrationBySchool.mockResolvedValue(userLoginMigration); - authService.checkPermissionByReferences.mockImplementation(() => Promise.resolve()); + authService.checkPermission.mockReturnValueOnce(); schoolService.getSchoolById.mockResolvedValue(school); userLoginMigrationService.setMigration.mockResolvedValue(updatedUserLoginMigration); schoolMigrationService.validateGracePeriod.mockImplementation(() => { @@ -241,7 +244,7 @@ describe('LegacySchoolUc', () => { userLoginMigrationService.findMigrationBySchool.mockResolvedValue(userLoginMigration); schoolService.getSchoolById.mockResolvedValue(school); - authService.checkPermissionByReferences.mockImplementation(() => Promise.resolve()); + authService.checkPermission.mockReturnValueOnce(); }; it('should return a migration', async () => { @@ -265,7 +268,7 @@ describe('LegacySchoolUc', () => { userLoginMigrationService.findMigrationBySchool.mockResolvedValue(null); schoolService.getSchoolById.mockResolvedValue(school); - authService.checkPermissionByReferences.mockImplementation(() => Promise.resolve()); + authService.checkPermission.mockReturnValueOnce(); }; it('should return no migration information', async () => { diff --git a/apps/server/src/modules/legacy-school/uc/legacy-school.uc.ts b/apps/server/src/modules/legacy-school/uc/legacy-school.uc.ts index 2ccc9dc5698..d1d13ffb037 100644 --- a/apps/server/src/modules/legacy-school/uc/legacy-school.uc.ts +++ b/apps/server/src/modules/legacy-school/uc/legacy-school.uc.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { Permission, LegacySchoolDo, UserLoginMigrationDO } from '@shared/domain'; -import { Action, AuthorizableReferenceType, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { Permission, LegacySchoolDo, UserLoginMigrationDO, User } from '@shared/domain'; import { SchoolMigrationService, UserLoginMigrationRevertService, @@ -30,10 +30,12 @@ export class LegacySchoolUc { oauthMigrationFinished: boolean, userId: string ): Promise { - await this.authService.checkPermissionByReferences(userId, AuthorizableReferenceType.School, schoolId, { - action: Action.read, - requiredPermissions: [Permission.SCHOOL_EDIT], - }); + const [authorizableUser, school]: [User, LegacySchoolDo] = await Promise.all([ + this.authService.getUserWithPermissions(userId), + this.schoolService.getSchoolById(schoolId), + ]); + + this.checkSchoolAuthorization(authorizableUser, school); const existingUserLoginMigration: UserLoginMigrationDO | null = await this.userLoginMigrationService.findMigrationBySchool(schoolId); @@ -61,8 +63,6 @@ export class LegacySchoolUc { await this.schoolMigrationService.unmarkOutdatedUsers(schoolId); } - const school: LegacySchoolDo = await this.schoolService.getSchoolById(schoolId); - const migrationDto: OauthMigrationDto = new OauthMigrationDto({ oauthMigrationPossible: !updatedUserLoginMigration.closedAt ? updatedUserLoginMigration.startedAt : undefined, oauthMigrationMandatory: updatedUserLoginMigration.mandatorySince, @@ -75,17 +75,17 @@ export class LegacySchoolUc { } async getMigration(schoolId: string, userId: string): Promise { - await this.authService.checkPermissionByReferences(userId, AuthorizableReferenceType.School, schoolId, { - action: Action.read, - requiredPermissions: [Permission.SCHOOL_EDIT], - }); + const [authorizableUser, school]: [User, LegacySchoolDo] = await Promise.all([ + this.authService.getUserWithPermissions(userId), + this.schoolService.getSchoolById(schoolId), + ]); + + this.checkSchoolAuthorization(authorizableUser, school); const userLoginMigration: UserLoginMigrationDO | null = await this.userLoginMigrationService.findMigrationBySchool( schoolId ); - const school: LegacySchoolDo = await this.schoolService.getSchoolById(schoolId); - const migrationDto: OauthMigrationDto = new OauthMigrationDto({ oauthMigrationPossible: userLoginMigration && !userLoginMigration.closedAt ? userLoginMigration.startedAt : undefined, @@ -97,4 +97,9 @@ export class LegacySchoolUc { return migrationDto; } + + private checkSchoolAuthorization(authorizableUser: User, school: LegacySchoolDo): void { + const context = AuthorizationContextBuilder.read([Permission.SCHOOL_EDIT]); + this.authService.checkPermission(authorizableUser, school, context); + } } diff --git a/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.ts b/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.ts index b344f1b6cdc..d0480398ec8 100644 --- a/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.ts +++ b/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { OauthProviderService } from '@shared/infra/oauth-provider/index'; import { Permission, User } from '@shared/domain/index'; -import { AuthorizationService } from '@src/modules/authorization/authorization.service'; +import { AuthorizationService } from '@src/modules/authorization'; import { ProviderOauthClient } from '@shared/infra/oauth-provider/dto'; import { ICurrentUser } from '@src/modules/authentication'; diff --git a/apps/server/src/modules/sharing/controller/api-test/sharing-create-token.api.spec.ts b/apps/server/src/modules/sharing/controller/api-test/sharing-create-token.api.spec.ts index 68c3a141f9b..2abd2019fbf 100644 --- a/apps/server/src/modules/sharing/controller/api-test/sharing-create-token.api.spec.ts +++ b/apps/server/src/modules/sharing/controller/api-test/sharing-create-token.api.spec.ts @@ -1,8 +1,8 @@ import { Request } from 'express'; import request from 'supertest'; import { Configuration } from '@hpi-schul-cloud/commons/lib'; -import { EntityManager } from '@mikro-orm/mongodb'; -import { ExecutionContext, INestApplication } from '@nestjs/common'; +import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; +import { ExecutionContext, HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common'; import { Permission } from '@shared/domain'; @@ -101,27 +101,27 @@ describe(`share token creation (api)`, () => { const response = await api.post({ parentId: course.id, parentType: ShareTokenParentType.Course }); - expect(response.status).toEqual(500); + expect(response.status).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); }); }); - describe('with ivalid request data', () => { + describe('with invalid request data', () => { it('should return status 400 on empty parent id', async () => { const response = await api.post({ parentId: '', parentType: ShareTokenParentType.Course, }); - expect(response.status).toEqual(400); + expect(response.status).toEqual(HttpStatus.BAD_REQUEST); }); - it('should return status 403 when parent id is not found', async () => { + it('should return status 404 when parent id is not found', async () => { const response = await api.post({ - parentId: '000011112222333344445555', + parentId: new ObjectId().toHexString(), parentType: ShareTokenParentType.Course, }); - expect(response.status).toEqual(403); + expect(response.status).toEqual(HttpStatus.NOT_FOUND); }); it('should return status 400 on invalid parent id', async () => { @@ -130,7 +130,7 @@ describe(`share token creation (api)`, () => { parentType: ShareTokenParentType.Course, }); - expect(response.status).toEqual(400); + expect(response.status).toEqual(HttpStatus.BAD_REQUEST); }); it('should return status 400 on invalid parent type', async () => { @@ -142,7 +142,7 @@ describe(`share token creation (api)`, () => { parentType: 'invalid', }); - expect(response.status).toEqual(400); + expect(response.status).toEqual(HttpStatus.BAD_REQUEST); }); it('should return status 400 when expiresInDays is invalid integer', async () => { @@ -155,7 +155,7 @@ describe(`share token creation (api)`, () => { expiresInDays: 'foo', }); - expect(response.status).toEqual(400); + expect(response.status).toEqual(HttpStatus.BAD_REQUEST); }); it('should return status 400 when expiresInDays is negative', async () => { @@ -167,7 +167,7 @@ describe(`share token creation (api)`, () => { expiresInDays: -10, }); - expect(response.status).toEqual(400); + expect(response.status).toEqual(HttpStatus.BAD_REQUEST); }); it('should return status 400 when expiresInDays is not an integer', async () => { @@ -179,7 +179,7 @@ describe(`share token creation (api)`, () => { expiresInDays: 2.5, }); - expect(response.status).toEqual(400); + expect(response.status).toEqual(HttpStatus.BAD_REQUEST); }); }); @@ -189,7 +189,7 @@ describe(`share token creation (api)`, () => { const response = await api.post({ parentId: course.id, parentType: ShareTokenParentType.Course }); - expect(response.status).toEqual(201); + expect(response.status).toEqual(HttpStatus.CREATED); }); it('should return a valid result', async () => { @@ -216,7 +216,7 @@ describe(`share token creation (api)`, () => { schoolExclusive: true, }); - expect(response.status).toEqual(201); + expect(response.status).toEqual(HttpStatus.CREATED); }); it('should return a valid result', async () => { @@ -248,7 +248,7 @@ describe(`share token creation (api)`, () => { expiresInDays: 5, }); - expect(response.status).toEqual(201); + expect(response.status).toEqual(HttpStatus.CREATED); }); it('should return a valid result containg the expiration timestamp', async () => { diff --git a/apps/server/src/modules/sharing/controller/api-test/sharing-import-token.api.spec.ts b/apps/server/src/modules/sharing/controller/api-test/sharing-import-token.api.spec.ts index e58940addda..737a24b022e 100644 --- a/apps/server/src/modules/sharing/controller/api-test/sharing-import-token.api.spec.ts +++ b/apps/server/src/modules/sharing/controller/api-test/sharing-import-token.api.spec.ts @@ -1,6 +1,6 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { EntityManager } from '@mikro-orm/mongodb'; -import { ExecutionContext, INestApplication } from '@nestjs/common'; +import { ExecutionContext, HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common'; import { Permission } from '@shared/domain'; @@ -118,16 +118,17 @@ describe(`share token import (api)`, () => { const response = await api.post({ token }, { newName: 'NewName' }); - expect(response.status).toEqual(500); + expect(response.status).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); }); }); describe('with a valid token', () => { it('should return status 201', async () => { const { token } = await setup(); + const response = await api.post({ token }, { newName: 'NewName' }); - expect(response.status).toEqual(201); + expect(response.status).toEqual(HttpStatus.CREATED); }); it('should return a valid result', async () => { @@ -149,23 +150,53 @@ describe(`share token import (api)`, () => { describe('with invalid token', () => { it('should return status 404', async () => { await setup(); + const response = await api.post({ token: 'invalid_token' }, { newName: 'NewName' }); - expect(response.status).toEqual(404); + + expect(response.status).toEqual(HttpStatus.NOT_FOUND); }); }); describe('with invalid context', () => { - it('should return status 403', async () => { + const setup2 = async () => { + const school = schoolFactory.build(); const otherSchool = schoolFactory.build(); - await em.persistAndFlush(otherSchool); - em.clear(); + const roles = roleFactory.buildList(1, { + permissions: [Permission.COURSE_CREATE], + }); - const { token } = await setup({ + const user = userFactory.build({ school, roles }); + const course = courseFactory.build({ teachers: [user] }); + await em.persistAndFlush([user, course, otherSchool]); + + const context = { contextType: ShareTokenContextType.School, contextId: otherSchool.id, - }); - const response = await api.post({ token }, { newName: 'NewName' }); - expect(response.status).toEqual(403); + }; + + const shareToken = await shareTokenService.createToken( + { + parentType: ShareTokenParentType.Course, + parentId: course.id, + }, + { context } + ); + + em.clear(); + + currentUser = mapUserToCurrentUser(user); + + return { + shareTokenFromDifferentCourse: shareToken.token, + }; + }; + + it('should return status 403', async () => { + const { shareTokenFromDifferentCourse } = await setup2(); + + const response = await api.post({ token: shareTokenFromDifferentCourse }, { newName: 'NewName' }); + + expect(response.status).toEqual(HttpStatus.FORBIDDEN); }); }); @@ -175,7 +206,7 @@ describe(`share token import (api)`, () => { // @ts-expect-error invalid new name const response = await api.post({ token }, { newName: 42 }); - expect(response.status).toEqual(501); + expect(response.status).toEqual(HttpStatus.NOT_IMPLEMENTED); }); }); }); diff --git a/apps/server/src/modules/sharing/controller/api-test/sharing-lookup-token.api.spec.ts b/apps/server/src/modules/sharing/controller/api-test/sharing-lookup-token.api.spec.ts index a5c1304a730..7065c06a026 100644 --- a/apps/server/src/modules/sharing/controller/api-test/sharing-lookup-token.api.spec.ts +++ b/apps/server/src/modules/sharing/controller/api-test/sharing-lookup-token.api.spec.ts @@ -1,167 +1,210 @@ -import { Request } from 'express'; -import request from 'supertest'; -import { Configuration } from '@hpi-schul-cloud/commons/lib'; +import { Configuration } from '@hpi-schul-cloud/commons'; import { EntityManager } from '@mikro-orm/mongodb'; -import { ExecutionContext, INestApplication } from '@nestjs/common'; +import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { ApiValidationError } from '@shared/common'; import { Permission } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; -import { - cleanupCollections, - courseFactory, - mapUserToCurrentUser, - roleFactory, - schoolFactory, - userFactory, -} from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; +import { TestApiClient, UserAndAccountTestFactory, courseFactory, schoolFactory } from '@shared/testing'; import { ServerTestModule } from '@src/modules/server'; import { ShareTokenService } from '../../service'; -import { ShareTokenInfoResponse, ShareTokenResponse, ShareTokenUrlParams } from '../dto'; -import { ShareTokenContext, ShareTokenContextType, ShareTokenParentType } from '../../domainobject/share-token.do'; - -const baseRouteName = '/sharetoken'; - -class API { - app: INestApplication; - - constructor(app: INestApplication) { - this.app = app; - } - - async get(urlParams: ShareTokenUrlParams) { - const response = await request(this.app.getHttpServer()) - .get(`${baseRouteName}/${urlParams.token}`) - .set('Accept', 'application/json'); - - return { - result: response.body as ShareTokenResponse, - error: response.body as ApiValidationError, - status: response.status, - }; - } -} +import { ShareTokenInfoResponse } from '../dto'; +import { ShareTokenContextType, ShareTokenParentType } from '../../domainobject/share-token.do'; describe(`share token lookup (api)`, () => { let app: INestApplication; let em: EntityManager; - let currentUser: ICurrentUser; let shareTokenService: ShareTokenService; - let api: API; + let testApiClient: TestApiClient; beforeAll(async () => { const module: TestingModule = await Test.createTestingModule({ imports: [ServerTestModule], - }) - .overrideGuard(JwtAuthGuard) - .useValue({ - canActivate(context: ExecutionContext) { - const req: Request = context.switchToHttp().getRequest(); - req.user = currentUser; - return true; - }, - }) - .compile(); + }).compile(); app = module.createNestApplication(); await app.init(); em = module.get(EntityManager); shareTokenService = module.get(ShareTokenService); - api = new API(app); + testApiClient = new TestApiClient(app, 'sharetoken'); }); afterAll(async () => { await app.close(); }); - beforeEach(() => { - Configuration.set('FEATURE_COURSE_SHARE_NEW', true); - }); + describe('with the feature disabled', () => { + const setup = async () => { + Configuration.set('FEATURE_COURSE_SHARE_NEW', false); - const setup = async (context?: ShareTokenContext) => { - await cleanupCollections(em); - const school = schoolFactory.build(); - const roles = roleFactory.buildList(1, { - permissions: [Permission.COURSE_CREATE], - }); - const user = userFactory.build({ school, roles }); - const course = courseFactory.build({ teachers: [user] }); - await em.persistAndFlush([user, course]); - - const shareToken = await shareTokenService.createToken( - { - parentType: ShareTokenParentType.Course, - parentId: course.id, - }, - { context } - ); - - em.clear(); - - currentUser = mapUserToCurrentUser(user); - - return { - parentType: ShareTokenParentType.Course, - parentName: course.getMetadata().title, - token: shareToken.token, + const parentType = ShareTokenParentType.Course; + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher({}, [Permission.COURSE_CREATE]); + const course = courseFactory.build({ teachers: [teacherUser] }); + + await em.persistAndFlush([course, teacherAccount, teacherUser]); + em.clear(); + + const shareToken = await shareTokenService.createToken( + { + parentType, + parentId: course.id, + }, + undefined + ); + + const loggedInClient = await testApiClient.login(teacherAccount); + + return { + token: shareToken.token, + loggedInClient, + }; }; - }; - describe('with the feature disabled', () => { it('should return status 500', async () => { - Configuration.set('FEATURE_COURSE_SHARE_NEW', false); - const { token } = await setup(); + const { token, loggedInClient } = await setup(); - const response = await api.get({ token }); + const response = await loggedInClient.get(token); - expect(response.status).toEqual(500); + expect(response.status).toEqual(HttpStatus.INTERNAL_SERVER_ERROR); + expect(response.body).toEqual({ + code: 500, + message: 'Import Course Feature not enabled', + title: 'Internal Server Error', + type: 'INTERNAL_SERVER_ERROR', + }); }); }); + // test and setup for other feature flags are missed + describe('with a valid token', () => { - it('should return status 200', async () => { - const { token } = await setup(); - const response = await api.get({ token }); + const setup = async () => { + Configuration.set('FEATURE_COURSE_SHARE_NEW', true); - expect(response.status).toEqual(200); - }); + const parentType = ShareTokenParentType.Course; + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher({}, [Permission.COURSE_CREATE]); + const course = courseFactory.build({ teachers: [teacherUser] }); - it('should return a valid result', async () => { - const { parentType, parentName, token } = await setup(); - const response = await api.get({ token }); + await em.persistAndFlush([course, teacherAccount, teacherUser]); + em.clear(); + + const shareToken = await shareTokenService.createToken( + { + parentType, + parentId: course.id, + }, + undefined + ); + + const loggedInClient = await testApiClient.login(teacherAccount); const expectedResult: ShareTokenInfoResponse = { - token, + token: shareToken.token, parentType, - parentName, + parentName: course.getMetadata().title, }; - expect(response.result).toEqual(expectedResult); + return { + expectedResult, + token: shareToken.token, + loggedInClient, + }; + }; + + it('should return status 200 with correct formated body', async () => { + const { token, loggedInClient, expectedResult } = await setup(); + + const response = await loggedInClient.get(token); + + expect(response.status).toEqual(HttpStatus.OK); + expect(response.body).toEqual(expectedResult); }); }); describe('with invalid token', () => { + const setup = async () => { + Configuration.set('FEATURE_COURSE_SHARE_NEW', true); + + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher({}, [Permission.COURSE_CREATE]); + const course = courseFactory.build({ teachers: [teacherUser] }); + + await em.persistAndFlush([course, teacherAccount, teacherUser]); + em.clear(); + + const loggedInClient = await testApiClient.login(teacherAccount); + + return { + invalidToken: 'invalid_token', + loggedInClient, + }; + }; + it('should return status 404', async () => { - await setup(); - const response = await api.get({ token: 'invalid_token' }); - expect(response.status).toEqual(404); + const { invalidToken, loggedInClient } = await setup(); + + const response = await loggedInClient.get(invalidToken); + + expect(response.status).toEqual(HttpStatus.NOT_FOUND); + expect(response.body).toEqual({ + code: 404, + message: 'The requested ShareToken: [object Object] has not been found.', + title: 'Not Found', + type: 'NOT_FOUND', + }); }); }); describe('with invalid context', () => { - it('should return status 403', async () => { + const setup = async () => { + Configuration.set('FEATURE_COURSE_SHARE_NEW', true); + + const parentType = ShareTokenParentType.Course; + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher({}, [Permission.COURSE_CREATE]); const otherSchool = schoolFactory.build(); - await em.persistAndFlush(otherSchool); + const course = courseFactory.build({ teachers: [teacherUser] }); + + await em.persistAndFlush([course, teacherAccount, teacherUser, otherSchool]); em.clear(); - const { token } = await setup({ + const context = { contextType: ShareTokenContextType.School, contextId: otherSchool.id, + }; + + const shareToken = await shareTokenService.createToken( + { + parentType, + parentId: course.id, + }, + { context } + ); + + const loggedInClient = await testApiClient.login(teacherAccount); + + const expectedResult: ShareTokenInfoResponse = { + token: shareToken.token, + parentType, + parentName: course.getMetadata().title, + }; + + return { + expectedResult, + token: shareToken.token, + loggedInClient, + }; + }; + + it('should return status 403', async () => { + const { token, loggedInClient } = await setup(); + + const response = await loggedInClient.get(token); + + expect(response.status).toEqual(HttpStatus.FORBIDDEN); + expect(response.body).toEqual({ + code: 403, + message: 'Forbidden', + title: 'Forbidden', + type: 'FORBIDDEN', }); - const response = await api.get({ token }); - expect(response.status).toEqual(403); }); }); }); diff --git a/apps/server/src/modules/sharing/mapper/context-type.mapper.spec.ts b/apps/server/src/modules/sharing/mapper/context-type.mapper.spec.ts index 9684a1dbb58..68c0ccbd972 100644 --- a/apps/server/src/modules/sharing/mapper/context-type.mapper.spec.ts +++ b/apps/server/src/modules/sharing/mapper/context-type.mapper.spec.ts @@ -1,5 +1,5 @@ import { NotImplementedException } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization'; +import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { ShareTokenContextType } from '../domainobject/share-token.do'; import { ShareTokenContextTypeMapper } from './context-type.mapper'; diff --git a/apps/server/src/modules/sharing/mapper/context-type.mapper.ts b/apps/server/src/modules/sharing/mapper/context-type.mapper.ts index 7c9b4c8bb1d..05ed42843c7 100644 --- a/apps/server/src/modules/sharing/mapper/context-type.mapper.ts +++ b/apps/server/src/modules/sharing/mapper/context-type.mapper.ts @@ -1,5 +1,5 @@ import { NotImplementedException } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization'; +import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { ShareTokenContextType } from '../domainobject/share-token.do'; export class ShareTokenContextTypeMapper { @@ -12,6 +12,7 @@ export class ShareTokenContextTypeMapper { if (!res) { throw new NotImplementedException(); } + return res; } } diff --git a/apps/server/src/modules/sharing/mapper/parent-type.mapper.spec.ts b/apps/server/src/modules/sharing/mapper/parent-type.mapper.spec.ts index c6d8669bc70..4f8750245b4 100644 --- a/apps/server/src/modules/sharing/mapper/parent-type.mapper.spec.ts +++ b/apps/server/src/modules/sharing/mapper/parent-type.mapper.spec.ts @@ -1,5 +1,5 @@ import { NotImplementedException } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization'; +import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { ShareTokenParentType } from '../domainobject/share-token.do'; import { ShareTokenParentTypeMapper } from './parent-type.mapper'; diff --git a/apps/server/src/modules/sharing/mapper/parent-type.mapper.ts b/apps/server/src/modules/sharing/mapper/parent-type.mapper.ts index 54d8ceb0470..2ea01ea39f9 100644 --- a/apps/server/src/modules/sharing/mapper/parent-type.mapper.ts +++ b/apps/server/src/modules/sharing/mapper/parent-type.mapper.ts @@ -1,5 +1,5 @@ import { NotImplementedException } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization'; +import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { ShareTokenParentType } from '../domainobject/share-token.do'; export class ShareTokenParentTypeMapper { diff --git a/apps/server/src/modules/sharing/sharing.module.ts b/apps/server/src/modules/sharing/sharing.module.ts index f09214e9cf8..519033065b5 100644 --- a/apps/server/src/modules/sharing/sharing.module.ts +++ b/apps/server/src/modules/sharing/sharing.module.ts @@ -1,6 +1,7 @@ import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationReferenceModule } from '@src/modules/authorization/authorization-reference.module'; import { ShareTokenController } from './controller/share-token.controller'; import { ShareTokenUC } from './uc'; import { ShareTokenService, TokenGenerator } from './service'; @@ -10,7 +11,7 @@ import { LearnroomModule } from '../learnroom'; import { TaskModule } from '../task'; @Module({ - imports: [AuthorizationModule, LoggerModule, LearnroomModule, LessonModule, TaskModule], + imports: [AuthorizationModule, AuthorizationReferenceModule, LoggerModule, LearnroomModule, LessonModule, TaskModule], controllers: [], providers: [ShareTokenService, TokenGenerator, ShareTokenRepo], exports: [ShareTokenService], @@ -18,7 +19,15 @@ import { TaskModule } from '../task'; export class SharingModule {} @Module({ - imports: [SharingModule, AuthorizationModule, LearnroomModule, LessonModule, TaskModule, LoggerModule], + imports: [ + SharingModule, + AuthorizationModule, + AuthorizationReferenceModule, + LearnroomModule, + LessonModule, + TaskModule, + LoggerModule, + ], controllers: [ShareTokenController], providers: [ShareTokenUC], }) diff --git a/apps/server/src/modules/sharing/uc/share-token.uc.spec.ts b/apps/server/src/modules/sharing/uc/share-token.uc.spec.ts index 73234960794..8f33076cff2 100644 --- a/apps/server/src/modules/sharing/uc/share-token.uc.spec.ts +++ b/apps/server/src/modules/sharing/uc/share-token.uc.spec.ts @@ -15,7 +15,8 @@ import { userFactory, } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { Action, AuthorizableReferenceType, AuthorizationService } from '@src/modules/authorization'; +import { Action, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizableReferenceType, AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { CopyElementType, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; import { CourseCopyService } from '@src/modules/learnroom'; import { CourseService } from '@src/modules/learnroom/service'; @@ -33,6 +34,7 @@ describe('ShareTokenUC', () => { let lessonCopyService: DeepMocked; let taskCopyService: DeepMocked; let authorization: DeepMocked; + let authorizationReferenceService: DeepMocked; let courseService: DeepMocked; let lessonRepo: DeepMocked; @@ -48,6 +50,10 @@ describe('ShareTokenUC', () => { provide: AuthorizationService, useValue: createMock(), }, + { + provide: AuthorizationReferenceService, + useValue: createMock(), + }, { provide: CourseCopyService, useValue: createMock(), @@ -81,8 +87,10 @@ describe('ShareTokenUC', () => { lessonCopyService = module.get(LessonCopyService); taskCopyService = module.get(TaskCopyService); authorization = module.get(AuthorizationService); + authorizationReferenceService = module.get(AuthorizationReferenceService); courseService = module.get(CourseService); lessonRepo = module.get(LessonRepo); + await setupEntities(); }); @@ -93,6 +101,7 @@ describe('ShareTokenUC', () => { beforeEach(() => { jest.resetAllMocks(); jest.clearAllMocks(); + // configuration sets must be part of the setup functions and part of the describe when ...and feature x is activated Configuration.set('FEATURE_COURSE_SHARE_NEW', true); Configuration.set('FEATURE_LESSON_SHARE', true); Configuration.set('FEATURE_TASK_SHARE', true); @@ -129,7 +138,7 @@ describe('ShareTokenUC', () => { parentType: ShareTokenParentType.Course, }); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( user.id, AuthorizableReferenceType.Course, course.id, @@ -148,7 +157,7 @@ describe('ShareTokenUC', () => { parentType: ShareTokenParentType.Course, }); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledTimes(1); + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledTimes(1); }); it('should call the service', async () => { @@ -190,7 +199,7 @@ describe('ShareTokenUC', () => { parentType: ShareTokenParentType.Lesson, }); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( user.id, AuthorizableReferenceType.Lesson, lesson.id, @@ -209,7 +218,7 @@ describe('ShareTokenUC', () => { parentType: ShareTokenParentType.Lesson, }); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledTimes(1); + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledTimes(1); }); it('should call the service', async () => { @@ -251,7 +260,7 @@ describe('ShareTokenUC', () => { parentType: ShareTokenParentType.Task, }); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( user.id, AuthorizableReferenceType.Task, task.id, @@ -270,7 +279,7 @@ describe('ShareTokenUC', () => { parentType: ShareTokenParentType.Task, }); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledTimes(1); + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledTimes(1); }); it('should call the service', async () => { @@ -309,7 +318,7 @@ describe('ShareTokenUC', () => { } ); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( user.id, AuthorizableReferenceType.Course, course.id, @@ -337,7 +346,7 @@ describe('ShareTokenUC', () => { } ); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( user.id, AuthorizableReferenceType.School, school.id, @@ -574,7 +583,7 @@ describe('ShareTokenUC', () => { await uc.lookupShareToken(user.id, shareToken.token); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( user.id, AuthorizableReferenceType.School, school.id, @@ -601,7 +610,7 @@ describe('ShareTokenUC', () => { await uc.lookupShareToken(user.id, shareToken.token); - expect(authorization.checkPermissionByReferences).not.toHaveBeenCalled(); + expect(authorizationReferenceService.checkPermissionByReferences).not.toHaveBeenCalled(); }); }); }); @@ -686,7 +695,7 @@ describe('ShareTokenUC', () => { await uc.importShareToken(user.id, shareToken.token, 'NewName'); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( user.id, AuthorizableReferenceType.School, school.id, @@ -706,7 +715,7 @@ describe('ShareTokenUC', () => { await uc.importShareToken(user.id, shareToken.token, 'NewName'); - expect(authorization.checkPermissionByReferences).not.toHaveBeenCalled(); + expect(authorizationReferenceService.checkPermissionByReferences).not.toHaveBeenCalled(); }); }); }); @@ -803,7 +812,7 @@ describe('ShareTokenUC', () => { await uc.importShareToken(user.id, shareToken.token, 'NewName'); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( user.id, AuthorizableReferenceType.School, school.id, @@ -823,7 +832,7 @@ describe('ShareTokenUC', () => { await uc.importShareToken(user.id, shareToken.token, 'NewName'); - expect(authorization.checkPermissionByReferences).not.toHaveBeenCalled(); + expect(authorizationReferenceService.checkPermissionByReferences).not.toHaveBeenCalled(); }); }); }); @@ -919,7 +928,7 @@ describe('ShareTokenUC', () => { await uc.importShareToken(user.id, shareToken.token, 'NewName'); - expect(authorization.checkPermissionByReferences).toHaveBeenCalledWith( + expect(authorizationReferenceService.checkPermissionByReferences).toHaveBeenCalledWith( user.id, AuthorizableReferenceType.School, school.id, @@ -939,7 +948,7 @@ describe('ShareTokenUC', () => { await uc.importShareToken(user.id, shareToken.token, 'NewName'); - expect(authorization.checkPermissionByReferences).not.toHaveBeenCalled(); + expect(authorizationReferenceService.checkPermissionByReferences).not.toHaveBeenCalled(); }); }); }); @@ -962,6 +971,7 @@ describe('ShareTokenUC', () => { service.lookupToken.mockResolvedValue(shareToken); jest.spyOn(ShareTokenUC.prototype as any, 'checkFeatureEnabled').mockReturnValue(undefined); jest.spyOn(ShareTokenUC.prototype as any, 'checkCreatePermission').mockReturnValue(undefined); + await expect(uc.importShareToken('userId', shareToken.token, 'NewName')).rejects.toThrowError( NotImplementedException ); diff --git a/apps/server/src/modules/sharing/uc/share-token.uc.ts b/apps/server/src/modules/sharing/uc/share-token.uc.ts index 0b72be7ce31..b2bbc635403 100644 --- a/apps/server/src/modules/sharing/uc/share-token.uc.ts +++ b/apps/server/src/modules/sharing/uc/share-token.uc.ts @@ -2,7 +2,8 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { BadRequestException, Injectable, InternalServerErrorException, NotImplementedException } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { Action, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { CopyStatus } from '@src/modules/copy-helper'; import { CourseCopyService } from '@src/modules/learnroom'; import { CourseService } from '@src/modules/learnroom/service'; @@ -24,6 +25,7 @@ export class ShareTokenUC { constructor( private readonly shareTokenService: ShareTokenService, private readonly authorizationService: AuthorizationService, + private readonly authorizationReferenceService: AuthorizationReferenceService, private readonly courseCopyService: CourseCopyService, private readonly lessonCopyService: LessonCopyService, private readonly courseService: CourseService, @@ -177,18 +179,26 @@ export class ShareTokenUC { requiredPermissions = [Permission.HOMEWORK_CREATE]; } - await this.authorizationService.checkPermissionByReferences(userId, allowedParentType, payload.parentId, { - action: Action.write, - requiredPermissions, - }); + const authorizationContext = AuthorizationContextBuilder.write(requiredPermissions); + + await this.authorizationReferenceService.checkPermissionByReferences( + userId, + allowedParentType, + payload.parentId, + authorizationContext + ); } private async checkContextReadPermission(userId: EntityId, context: ShareTokenContext) { const allowedContextType = ShareTokenContextTypeMapper.mapToAllowedAuthorizationEntityType(context.contextType); - await this.authorizationService.checkPermissionByReferences(userId, allowedContextType, context.contextId, { - action: Action.read, - requiredPermissions: [], - }); + const authorizationContext = AuthorizationContextBuilder.read([]); + + await this.authorizationReferenceService.checkPermissionByReferences( + userId, + allowedContextType, + context.contextId, + authorizationContext + ); } private async checkCreatePermission(userId: EntityId, parentType: ShareTokenParentType) { @@ -221,16 +231,19 @@ export class ShareTokenUC { private checkFeatureEnabled(parentType: ShareTokenParentType) { switch (parentType) { case ShareTokenParentType.Course: + // Configuration.get is the deprecated way to read envirment variables if (!(Configuration.get('FEATURE_COURSE_SHARE_NEW') as boolean)) { throw new InternalServerErrorException('Import Course Feature not enabled'); } break; case ShareTokenParentType.Lesson: + // Configuration.get is the deprecated way to read envirment variables if (!(Configuration.get('FEATURE_LESSON_SHARE') as boolean)) { throw new InternalServerErrorException('Import Lesson Feature not enabled'); } break; case ShareTokenParentType.Task: + // Configuration.get is the deprecated way to read envirment variables if (!(Configuration.get('FEATURE_TASK_SHARE') as boolean)) { throw new InternalServerErrorException('Import Task Feature not enabled'); } diff --git a/apps/server/src/modules/task/uc/task-copy.uc.spec.ts b/apps/server/src/modules/task/uc/task-copy.uc.spec.ts index a7666d479a9..2ad21c68dc2 100644 --- a/apps/server/src/modules/task/uc/task-copy.uc.spec.ts +++ b/apps/server/src/modules/task/uc/task-copy.uc.spec.ts @@ -3,15 +3,14 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { ObjectId } from '@mikro-orm/mongodb'; import { ForbiddenException, InternalServerErrorException, NotFoundException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { BaseDO, User } from '@shared/domain'; import { CourseRepo, LessonRepo, TaskRepo, UserRepo } from '@shared/repo'; import { courseFactory, lessonFactory, setupEntities, taskFactory, userFactory } from '@shared/testing'; -import { Action, AuthorizableReferenceType, AuthorizationService } from '@src/modules/authorization'; +import { Action, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@src/modules/copy-helper'; import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; -import { AuthorizableObject } from '@shared/domain/domain-object'; import { TaskCopyService } from '../service'; import { TaskCopyUC } from './task-copy.uc'; +import { TaskCopyParentParams } from '../types'; describe('task copy uc', () => { let uc: TaskCopyUC; @@ -92,14 +91,8 @@ describe('task copy uc', () => { const lesson = lessonFactory.buildWithId({ course }); const allTasks = taskFactory.buildList(3, { course }); const task = allTasks[0]; - authorisation.getUserWithPermissions.mockResolvedValue(user); - taskRepo.findById.mockResolvedValue(task); - lessonRepo.findById.mockResolvedValue(lesson); - taskRepo.findBySingleParent.mockResolvedValue([allTasks, allTasks.length]); - courseRepo.findById.mockResolvedValue(course); - authorisation.hasPermission.mockReturnValue(true); const copyName = 'name of the copy'; - copyHelperService.deriveCopyName.mockReturnValue(copyName); + const copy = taskFactory.buildWithId({ creator: user, course }); const status = { title: 'taskCopy', @@ -108,9 +101,16 @@ describe('task copy uc', () => { copyEntity: copy, originalEntity: task, }; - taskCopyService.copyTask.mockResolvedValue(status); - taskRepo.save.mockResolvedValue(undefined); - const userId = user.id; + + authorisation.getUserWithPermissions.mockResolvedValueOnce(user); + taskRepo.findById.mockResolvedValueOnce(task); + lessonRepo.findById.mockResolvedValueOnce(lesson); + taskRepo.findBySingleParent.mockResolvedValueOnce([allTasks, allTasks.length]); + courseRepo.findById.mockResolvedValueOnce(course); + authorisation.hasPermission.mockReturnValueOnce(true).mockReturnValueOnce(true); + copyHelperService.deriveCopyName.mockReturnValueOnce(copyName); + taskCopyService.copyTask.mockResolvedValueOnce(status); + taskRepo.save.mockResolvedValueOnce(); return { user, @@ -121,15 +121,16 @@ describe('task copy uc', () => { copy, allTasks, status, - userId, + userId: user.id, }; }; describe('feature is deactivated', () => { it('should throw InternalServerErrorException', async () => { + const { course, user, task, userId } = setup(); Configuration.set('FEATURE_COPY_SERVICE_ENABLED', false); - await expect(uc.copyTask('user.id', 'task.id', { courseId: 'course.id', userId: 'test' })).rejects.toThrowError( + await expect(uc.copyTask(user.id, task.id, { courseId: course.id, userId })).rejects.toThrowError( InternalServerErrorException ); }); @@ -214,15 +215,9 @@ describe('task copy uc', () => { const { course, user, task, userId } = setup(); await uc.copyTask(user.id, task.id, { courseId: course.id, userId }); - expect(authorisation.checkPermissionByReferences).toBeCalledWith( - user.id, - AuthorizableReferenceType.Course, - course.id, - { - action: Action.write, - requiredPermissions: [], - } - ); + + const context = AuthorizationContextBuilder.write([]); + expect(authorisation.checkPermission).toBeCalledWith(user, course, context); }); it('should pass authorisation check without destination course', async () => { @@ -230,10 +225,8 @@ describe('task copy uc', () => { await uc.copyTask(user.id, task.id, { userId }); - expect(authorisation.hasPermission).not.toBeCalledWith(user, course, { - action: Action.write, - requiredPermissions: [], - }); + const context = AuthorizationContextBuilder.write([]); + expect(authorisation.hasPermission).not.toBeCalledWith(user, course, context); }); it('should check authorisation for destination lesson', async () => { @@ -260,57 +253,64 @@ describe('task copy uc', () => { describe('when access to task is forbidden', () => { const setupWithTaskForbidden = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); + const user = userFactory.buildWithId(); const course = courseFactory.buildWithId(); const lesson = lessonFactory.buildWithId({ course }); const task = taskFactory.buildWithId(); - userRepo.findById.mockResolvedValue(user); - taskRepo.findById.mockResolvedValue(task); - // authorisation should not be mocked - authorisation.hasPermission.mockImplementation((u: User, e: AuthorizableObject | BaseDO) => e !== task); - return { user, course, lesson, task }; + + userRepo.findById.mockResolvedValueOnce(user); + taskRepo.findById.mockResolvedValueOnce(task); + authorisation.hasPermission.mockReturnValueOnce(false); + + const parentParams: TaskCopyParentParams = { + courseId: course.id, + lessonId: lesson.id, + userId: new ObjectId().toHexString(), + }; + + return { user, course, lesson, task, parentParams }; }; it('should throw NotFoundException', async () => { - const { course, lesson, user, task } = setupWithTaskForbidden(); - - try { - await uc.copyTask(user.id, task.id, { - courseId: course.id, - lessonId: lesson.id, - userId: new ObjectId().toHexString(), - }); - throw new Error('should have failed'); - } catch (err) { - expect(err).toBeInstanceOf(NotFoundException); - } + const { user, task, parentParams } = setupWithTaskForbidden(); + + await expect(uc.copyTask(user.id, task.id, parentParams)).rejects.toThrowError( + new NotFoundException('could not find task to copy') + ); }); }); describe('when access to course is forbidden', () => { const setupWithCourseForbidden = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); + const user = userFactory.buildWithId(); const course = courseFactory.buildWithId(); const task = taskFactory.buildWithId(); - userRepo.findById.mockResolvedValue(user); - taskRepo.findById.mockResolvedValue(task); - // authorisation should not be mocked - authorisation.hasPermission.mockImplementation((u: User, e: AuthorizableObject | BaseDO) => e !== course); - authorisation.checkPermissionByReferences.mockImplementation(() => { + + userRepo.findById.mockResolvedValueOnce(user); + taskRepo.findById.mockResolvedValueOnce(task); + courseRepo.findById.mockResolvedValueOnce(course); + authorisation.hasPermission.mockReturnValueOnce(true).mockReturnValueOnce(true); + authorisation.checkPermission.mockImplementationOnce(() => { throw new ForbiddenException(); }); - return { user, course, task }; + + const parentParams: TaskCopyParentParams = { courseId: course.id, userId: new ObjectId().toHexString() }; + + return { + userId: user.id, + taskId: task.id, + parentParams, + }; }; it('should throw Forbidden Exception', async () => { - const { course, user, task } = setupWithCourseForbidden(); - - try { - await uc.copyTask(user.id, task.id, { courseId: course.id, userId: new ObjectId().toHexString() }); - throw new Error('should have failed'); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } + const { userId, taskId, parentParams } = setupWithCourseForbidden(); + + await expect(uc.copyTask(userId, taskId, parentParams)).rejects.toThrowError(new ForbiddenException()); }); }); }); @@ -355,32 +355,35 @@ describe('task copy uc', () => { describe('when access to lesson is forbidden', () => { const setupWithLessonForbidden = () => { + Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); + const user = userFactory.buildWithId(); const course = courseFactory.buildWithId(); const lesson = lessonFactory.buildWithId({ course }); const task = taskFactory.buildWithId(); - userRepo.findById.mockResolvedValue(user); - taskRepo.findById.mockResolvedValue(task); - courseRepo.findById.mockResolvedValue(course); - lessonRepo.findById.mockResolvedValue(lesson); - // Authorisation should not be mocked - authorisation.hasPermission.mockImplementation((u: User, e: AuthorizableObject | BaseDO) => { - if (e === lesson) return false; - return true; - }); - return { user, lesson, task }; + userRepo.findById.mockResolvedValueOnce(user); + taskRepo.findById.mockResolvedValueOnce(task); + courseRepo.findById.mockResolvedValueOnce(course); + lessonRepo.findById.mockResolvedValueOnce(lesson); + // first canReadTask > second canWriteLesson + authorisation.hasPermission.mockReturnValueOnce(true).mockReturnValueOnce(false); + + const parentParams: TaskCopyParentParams = { lessonId: lesson.id, userId: new ObjectId().toHexString() }; + + return { + userId: user.id, + taskId: task.id, + parentParams, + }; }; it('should throw Forbidden Exception', async () => { - const { lesson, user, task } = setupWithLessonForbidden(); - - try { - await uc.copyTask(user.id, task.id, { lessonId: lesson.id, userId: new ObjectId().toHexString() }); - throw new Error('should have failed'); - } catch (err) { - expect(err).toBeInstanceOf(ForbiddenException); - } + const { userId, taskId, parentParams } = setupWithLessonForbidden(); + + await expect(uc.copyTask(userId, taskId, parentParams)).rejects.toThrowError( + new ForbiddenException('you dont have permission to add to this lesson') + ); }); }); }); diff --git a/apps/server/src/modules/task/uc/task-copy.uc.ts b/apps/server/src/modules/task/uc/task-copy.uc.ts index b1cdd212919..94b0a5a3acb 100644 --- a/apps/server/src/modules/task/uc/task-copy.uc.ts +++ b/apps/server/src/modules/task/uc/task-copy.uc.ts @@ -1,13 +1,8 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { ForbiddenException, Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common'; -import { Course, EntityId, LessonEntity, User } from '@shared/domain'; +import { Course, EntityId, Task, LessonEntity, User } from '@shared/domain'; import { CourseRepo, LessonRepo, TaskRepo } from '@shared/repo'; -import { - Action, - AuthorizableReferenceType, - AuthorizationContextBuilder, - AuthorizationService, -} from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; import { CopyHelperService, CopyStatus } from '@src/modules/copy-helper'; import { TaskCopyService } from '../service'; import { TaskCopyParentParams } from '../types'; @@ -24,46 +19,73 @@ export class TaskCopyUC { ) {} async copyTask(userId: EntityId, taskId: EntityId, parentParams: TaskCopyParentParams): Promise { - this.featureEnabled(); - const user = await this.authorisation.getUserWithPermissions(userId); - const originalTask = await this.taskRepo.findById(taskId); - if (!this.authorisation.hasPermission(user, originalTask, AuthorizationContextBuilder.read([]))) { - throw new NotFoundException('could not find task to copy'); - } + this.checkFeatureEnabled(); + + // i put it to promise all, it do not look like any more information can be expose over errors if it is called between the authorizations + // TODO: Add try catch around it with throw BadRequest invalid data + const [authorizableUser, originalTask, destinationCourse]: [User, Task, Course | undefined] = await Promise.all([ + this.authorisation.getUserWithPermissions(userId), + this.taskRepo.findById(taskId), + this.getDestinationCourse(parentParams.courseId), + ]); - const destinationCourse = await this.getDestinationCourse(parentParams.courseId); - if (parentParams.courseId) { - await this.authorisation.checkPermissionByReferences( - userId, - AuthorizableReferenceType.Course, - parentParams.courseId, - { - action: Action.write, - requiredPermissions: [], - } - ); + this.checkOriginalTaskAuthorization(authorizableUser, originalTask); + + if (destinationCourse) { + this.checkDestinationCourseAuthorisation(authorizableUser, destinationCourse); } - const destinationLesson = await this.getDestinationLesson(parentParams.lessonId, user); - const copyName = await this.getCopyName(originalTask.name, parentParams.courseId); + // i think getDestinationLesson can also to a promise.all on top + // then getCopyName can be put into if (destinationCourse) { + // but then the test need to cleanup + const [destinationLesson, copyName]: [LessonEntity | undefined, string | undefined] = await Promise.all([ + this.getDestinationLesson(parentParams.lessonId), + this.getCopyName(originalTask.name, parentParams.courseId), + ]); + + if (destinationLesson) { + this.checkDestinationLessonAuthorization(authorizableUser, destinationLesson); + } const status = await this.taskCopyService.copyTask({ originalTaskId: originalTask.id, destinationCourse, destinationLesson, - user, + user: authorizableUser, copyName, }); return status; } + private checkOriginalTaskAuthorization(authorizableUser: User, originalTask: Task): void { + const context = AuthorizationContextBuilder.read([]); + if (!this.authorisation.hasPermission(authorizableUser, originalTask, context)) { + // error message and erorr type are not correct + throw new NotFoundException('could not find task to copy'); + } + } + + private checkDestinationCourseAuthorisation(authorizableUser: User, destinationCourse: Course): void { + const context = AuthorizationContextBuilder.write([]); + this.authorisation.checkPermission(authorizableUser, destinationCourse, context); + } + + private checkDestinationLessonAuthorization(authorizableUser: User, destinationLesson: LessonEntity): void { + const context = AuthorizationContextBuilder.write([]); + if (!this.authorisation.hasPermission(authorizableUser, destinationLesson, context)) { + throw new ForbiddenException('you dont have permission to add to this lesson'); + } + } + private async getCopyName(originalTaskName: string, parentCourseId: EntityId | undefined) { let existingNames: string[] = []; if (parentCourseId) { + // It should really get an task where the creatorId === '' ? const [existingTasks] = await this.taskRepo.findBySingleParent('', parentCourseId); existingNames = existingTasks.map((t) => t.name); } + return this.copyHelperService.deriveCopyName(originalTaskName, existingNames); } @@ -77,19 +99,18 @@ export class TaskCopyUC { return destinationCourse; } - private async getDestinationLesson(lessonId: string | undefined, user: User): Promise { + private async getDestinationLesson(lessonId: string | undefined): Promise { if (lessonId === undefined) { return undefined; } const destinationLesson = await this.lessonRepo.findById(lessonId); - if (!this.authorisation.hasPermission(user, destinationLesson, AuthorizationContextBuilder.write([]))) { - throw new ForbiddenException('you dont have permission to add to this lesson'); - } + return destinationLesson; } - private featureEnabled() { + private checkFeatureEnabled() { + // This is the deprecated way to read envirement variables const enabled = Configuration.get('FEATURE_COPY_SERVICE_ENABLED') as boolean; if (!enabled) { throw new InternalServerErrorException('Copy Feature not enabled'); diff --git a/apps/server/src/modules/tool/common/common-tool.module.ts b/apps/server/src/modules/tool/common/common-tool.module.ts index cc7f5f86f00..0bd9ba5385b 100644 --- a/apps/server/src/modules/tool/common/common-tool.module.ts +++ b/apps/server/src/modules/tool/common/common-tool.module.ts @@ -3,11 +3,12 @@ import { ContextExternalToolRepo, SchoolExternalToolRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; import { AuthorizationModule } from '@src/modules/authorization'; import { LegacySchoolModule } from '@src/modules/legacy-school'; +import { LearnroomModule } from '@src/modules/learnroom'; import { CommonToolService, CommonToolValidationService } from './service'; import { ToolPermissionHelper } from './uc/tool-permission-helper'; @Module({ - imports: [LoggerModule, forwardRef(() => AuthorizationModule), LegacySchoolModule], + imports: [LoggerModule, forwardRef(() => AuthorizationModule), LegacySchoolModule, LearnroomModule], // TODO: make deletion of entities cascading, adjust ExternalToolService.deleteExternalTool and remove the repos from here providers: [ CommonToolService, diff --git a/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts b/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts new file mode 100644 index 00000000000..78d79f45a1b --- /dev/null +++ b/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts @@ -0,0 +1,11 @@ +import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; +import { ToolContextType } from '../enum'; +import { ContextTypeMapper } from './context-type.mapper'; + +describe('context-type.mapper', () => { + it('should map ToolContextType.COURSE to AuthorizableReferenceType.Course', () => { + const mappedCourse = ContextTypeMapper.mapContextTypeToAllowedAuthorizationEntityType(ToolContextType.COURSE); + + expect(mappedCourse).toEqual(AuthorizableReferenceType.Course); + }); +}); diff --git a/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts b/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts index bf8fb537924..883400f4258 100644 --- a/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts +++ b/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts @@ -1,4 +1,4 @@ -import { AuthorizableReferenceType } from '@src/modules/authorization/types'; +import { AuthorizableReferenceType } from '@src/modules/authorization/domain/'; import { ToolContextType } from '../enum'; const typeMapping: Record = { diff --git a/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts b/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts index dc4f339b4ab..7f0cb68ade8 100644 --- a/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts +++ b/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts @@ -1,16 +1,21 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { EntityId, LegacySchoolDo, User } from '@shared/domain'; -import { AuthorizableReferenceType, AuthorizationContext, AuthorizationService } from '@src/modules/authorization'; +import { Course, EntityId, LegacySchoolDo, User } from '@shared/domain'; +import { AuthorizationContext, AuthorizationService } from '@src/modules/authorization'; import { LegacySchoolService } from '@src/modules/legacy-school'; +import { CourseService } from '@src/modules/learnroom'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; -import { ContextTypeMapper } from '../mapper'; +// import { ContextTypeMapper } from '../mapper'; @Injectable() export class ToolPermissionHelper { constructor( - @Inject(forwardRef(() => AuthorizationService)) private authorizationService: AuthorizationService, - private readonly schoolService: LegacySchoolService + @Inject(forwardRef(() => AuthorizationService)) private readonly authorizationService: AuthorizationService, + private readonly schoolService: LegacySchoolService, + // invalid dependency on this place it is in UC layer in a other module + // loading of ressources should be part of service layer + // if it must resolve different loadings based on the request it can be added in own service and use in UC + private readonly courseService: CourseService ) {} // TODO build interface to get contextDO by contextType @@ -19,21 +24,19 @@ export class ToolPermissionHelper { contextExternalTool: ContextExternalTool, context: AuthorizationContext ): Promise { + // loading of ressources should be part of the UC -> unnessasary awaits + const [authorizableUser, course]: [User, Course] = await Promise.all([ + this.authorizationService.getUserWithPermissions(userId), + this.courseService.findById(contextExternalTool.contextRef.id), + ]); + if (contextExternalTool.id) { - await this.authorizationService.checkPermissionByReferences( - userId, - AuthorizableReferenceType.ContextExternalToolEntity, - contextExternalTool.id, - context - ); + this.authorizationService.checkPermission(authorizableUser, contextExternalTool, context); } - await this.authorizationService.checkPermissionByReferences( - userId, - ContextTypeMapper.mapContextTypeToAllowedAuthorizationEntityType(contextExternalTool.contextRef.type), - contextExternalTool.contextRef.id, - context - ); + // const type = ContextTypeMapper.mapContextTypeToAllowedAuthorizationEntityType(contextExternalTool.contextRef.type); + // no different types possible until it is fixed. + this.authorizationService.checkPermission(authorizableUser, course, context); } public async ensureSchoolPermissions( @@ -41,8 +44,12 @@ export class ToolPermissionHelper { schoolExternalTool: SchoolExternalTool, context: AuthorizationContext ): Promise { - const user: User = await this.authorizationService.getUserWithPermissions(userId); - const school: LegacySchoolDo = await this.schoolService.getSchoolById(schoolExternalTool.schoolId); + // loading of ressources should be part of the UC -> unnessasary awaits + const [user, school]: [User, LegacySchoolDo] = await Promise.all([ + this.authorizationService.getUserWithPermissions(userId), + this.schoolService.getSchoolById(schoolExternalTool.schoolId), + ]); + this.authorizationService.checkPermission(user, school, context); } } diff --git a/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts b/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts index b1567693130..0f2a1192d57 100644 --- a/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts +++ b/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts @@ -2,13 +2,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { contextExternalToolFactory, + courseFactory, legacySchoolDoFactory, schoolExternalToolFactory, setupEntities, + userFactory, } from '@shared/testing'; import { Permission, LegacySchoolDo } from '@shared/domain'; import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; import { LegacySchoolService } from '@src/modules/legacy-school'; +import { ForbiddenException } from '@nestjs/common'; +import { CourseService } from '@src/modules/learnroom'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { ToolPermissionHelper } from './tool-permission-helper'; import { SchoolExternalTool } from '../../school-external-tool/domain'; @@ -18,6 +22,7 @@ describe('ToolPermissionHelper', () => { let helper: ToolPermissionHelper; let authorizationService: DeepMocked; + let courseService: DeepMocked; let schoolService: DeepMocked; beforeAll(async () => { @@ -29,6 +34,10 @@ describe('ToolPermissionHelper', () => { provide: AuthorizationService, useValue: createMock(), }, + { + provide: CourseService, + useValue: createMock(), + }, { provide: LegacySchoolService, useValue: createMock(), @@ -38,6 +47,7 @@ describe('ToolPermissionHelper', () => { helper = module.get(ToolPermissionHelper); authorizationService = module.get(AuthorizationService); + courseService = module.get(CourseService); schoolService = module.get(LegacySchoolService); }); @@ -50,29 +60,98 @@ describe('ToolPermissionHelper', () => { }); describe('ensureContextPermissions', () => { - describe('when context external tool is given', () => { + describe('when context external tool with id is given', () => { + const setup = () => { + const user = userFactory.buildWithId(); + const course = courseFactory.buildWithId(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId(); + const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); + + courseService.findById.mockResolvedValueOnce(course); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationService.checkPermission.mockReturnValueOnce().mockReturnValueOnce(); + + return { + user, + course, + contextExternalTool, + context, + }; + }; + + it('should check permission for context external tool', async () => { + const { user, course, contextExternalTool, context } = setup(); + + await helper.ensureContextPermissions(user.id, contextExternalTool, context); + + expect(authorizationService.checkPermission).toHaveBeenCalledTimes(2); + expect(authorizationService.checkPermission).toHaveBeenNthCalledWith(1, user, contextExternalTool, context); + expect(authorizationService.checkPermission).toHaveBeenNthCalledWith(2, user, course, context); + }); + + it('should return undefined', async () => { + const { user, contextExternalTool, context } = setup(); + + const result = await helper.ensureContextPermissions(user.id, contextExternalTool, context); + + expect(result).toBeUndefined(); + }); + }); + + describe('when context external tool without id is given', () => { const setup = () => { - const userId = 'userId'; + const user = userFactory.buildWithId(); + const course = courseFactory.buildWithId(); const contextExternalTool: ContextExternalTool = contextExternalToolFactory.build(); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); + courseService.findById.mockResolvedValueOnce(course); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + return { - userId, + user, + course, contextExternalTool, context, }; }; it('should check permission for context external tool', async () => { - const { userId, contextExternalTool, context } = setup(); + const { user, course, contextExternalTool, context } = setup(); + + await helper.ensureContextPermissions(user.id, contextExternalTool, context); - await helper.ensureContextPermissions(userId, contextExternalTool, context); + expect(authorizationService.checkPermission).toHaveBeenCalledTimes(1); + expect(authorizationService.checkPermission).toHaveBeenCalledWith(user, course, context); + }); + }); - expect(authorizationService.checkPermissionByReferences).toHaveBeenCalledWith( - userId, - 'courses', - contextExternalTool.contextRef.id, - context + describe('when user is unauthorized', () => { + const setup = () => { + const user = userFactory.buildWithId(); + const course = courseFactory.buildWithId(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId(); + const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); + + courseService.findById.mockResolvedValueOnce(course); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationService.checkPermission.mockImplementationOnce(() => { + throw new ForbiddenException(); + }); + + return { + user, + course, + contextExternalTool, + context, + }; + }; + + it('should check permission for context external tool', async () => { + const { user, contextExternalTool, context } = setup(); + + await expect(helper.ensureContextPermissions(user.id, contextExternalTool, context)).rejects.toThrowError( + new ForbiddenException() ); }); }); @@ -81,15 +160,16 @@ describe('ToolPermissionHelper', () => { describe('ensureSchoolPermissions', () => { describe('when school external tool is given', () => { const setup = () => { - const userId = 'userId'; + const user = userFactory.buildWithId(); const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId(); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.SCHOOL_TOOL_ADMIN]); const school: LegacySchoolDo = legacySchoolDoFactory.build({ id: schoolExternalTool.schoolId }); schoolService.getSchoolById.mockResolvedValue(school); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); return { - userId, + user, schoolExternalTool, school, context, @@ -97,11 +177,20 @@ describe('ToolPermissionHelper', () => { }; it('should check permission for school external tool', async () => { - const { userId, schoolExternalTool, context, school } = setup(); + const { user, schoolExternalTool, context, school } = setup(); + + await helper.ensureSchoolPermissions(user.id, schoolExternalTool, context); + + expect(authorizationService.checkPermission).toHaveBeenCalledTimes(1); + expect(authorizationService.checkPermission).toHaveBeenCalledWith(user, school, context); + }); + + it('should return undefined', async () => { + const { user, schoolExternalTool, context } = setup(); - await helper.ensureSchoolPermissions(userId, schoolExternalTool, context); + const result = await helper.ensureSchoolPermissions(user.id, schoolExternalTool, context); - expect(authorizationService.checkPermission).toHaveBeenCalledWith(userId, school, context); + expect(result).toBeUndefined(); }); }); }); diff --git a/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts index 6e8ca18253f..b5584426763 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts @@ -114,29 +114,29 @@ describe('ToolContextController (API)', () => { }); }); - describe('when creation of contextExternalTool failed', () => { + describe('when user is not authorized for the requested context', () => { const setup = async () => { const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - const course: Course = courseFactory.buildWithId({ teachers: [teacherUser] }); - - const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + const school = schoolFactory.build(); + const course = courseFactory.build({ teachers: [teacherUser] }); + const otherCourse = courseFactory.build(); + const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.build({ schoolParameters: [], toolVersion: 1, + school, }); - const randomTestId = new ObjectId().toString(); + await em.persistAndFlush([course, otherCourse, school, teacherUser, teacherAccount, schoolExternalToolEntity]); + em.clear(); + const postParams: ContextExternalToolPostParams = { - schoolToolId: randomTestId, - contextId: randomTestId, + schoolToolId: school.id, + contextId: otherCourse.id, contextType: ToolContextType.COURSE, parameters: [], toolVersion: 1, }; - await em.persistAndFlush([course, teacherUser, teacherAccount, schoolExternalToolEntity]); - em.clear(); - const loggedInClient: TestApiClient = await testApiClient.login(teacherAccount); return { @@ -145,12 +145,13 @@ describe('ToolContextController (API)', () => { }; }; - it('when user is not authorized, it should return forbidden', async () => { + it('it should return forbidden', async () => { const { postParams, loggedInClient } = await setup(); const response = await loggedInClient.post().send(postParams); expect(response.statusCode).toEqual(HttpStatus.FORBIDDEN); + // expected body is missed }); }); }); @@ -204,23 +205,26 @@ describe('ToolContextController (API)', () => { describe('when deletion of contextExternalTool failed', () => { const setup = async () => { const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - const course: Course = courseFactory.buildWithId({ teachers: [teacherUser] }); - - const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + const course = courseFactory.buildWithId({ teachers: [teacherUser] }); + const schoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ toolVersion: 1, }); - - const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ + const contextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ schoolTool: schoolExternalToolEntity, + contextId: course.id, toolVersion: 1, }); - em.persist([course, teacherUser, teacherAccount, schoolExternalToolEntity, contextExternalToolEntity]); - await em.flush(); + await em.persistAndFlush([ + course, + teacherUser, + teacherAccount, + schoolExternalToolEntity, + contextExternalToolEntity, + ]); em.clear(); - const loggedInClient: TestApiClient = await testApiClient.login(teacherAccount); + const loggedInClient = await testApiClient.login(teacherAccount); return { contextExternalToolEntity, @@ -234,6 +238,7 @@ describe('ToolContextController (API)', () => { const result = await loggedInClient.delete(`${contextExternalToolEntity.id}`); expect(result.statusCode).toEqual(HttpStatus.FORBIDDEN); + // result.body is missed }); }); }); @@ -543,37 +548,29 @@ describe('ToolContextController (API)', () => { describe('when user has not the required permission', () => { const setup = async () => { - const school: SchoolEntity = schoolFactory.buildWithId(); - + const school = schoolFactory.build(); const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent({ school }); - - const course: Course = courseFactory.buildWithId({ + const course = courseFactory.build({ teachers: [studentUser], school, }); - - const externalTool: ExternalToolEntity = externalToolEntityFactory.buildWithId(); - const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + const externalTool: ExternalToolEntity = externalToolEntityFactory.build(); + const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.build({ school, tool: externalTool, toolVersion: 1, }); - const contextExternalTool: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ + + await em.persistAndFlush([school, course, externalTool, schoolExternalTool, studentAccount, studentUser]); + + const contextExternalTool: ContextExternalToolEntity = contextExternalToolEntityFactory.build({ contextId: course.id, schoolTool: schoolExternalTool, toolVersion: 1, contextType: ContextExternalToolType.COURSE, }); - await em.persistAndFlush([ - school, - course, - externalTool, - schoolExternalTool, - contextExternalTool, - studentAccount, - studentUser, - ]); + await em.persistAndFlush([contextExternalTool]); em.clear(); const loggedInClient: TestApiClient = await testApiClient.login(studentAccount); @@ -591,6 +588,7 @@ describe('ToolContextController (API)', () => { const response = await loggedInClient.get(`${contextExternalTool.id}`); expect(response.status).toEqual(HttpStatus.FORBIDDEN); + // body check }); }); }); @@ -688,34 +686,37 @@ describe('ToolContextController (API)', () => { describe('when the user is not authorized', () => { const setup = async () => { - const roleWithoutPermission = roleFactory.buildWithId(); - const { teacherUser, teacherAccount } = UserAndAccountTestFactory.buildTeacher(); - + const roleWithoutPermission = roleFactory.build(); teacherUser.roles.set([roleWithoutPermission]); - const school: SchoolEntity = schoolFactory.buildWithId(); - - const course: Course = courseFactory.buildWithId({ teachers: [teacherUser], school }); - + const school = schoolFactory.build(); + const course = courseFactory.build({ teachers: [teacherUser], school }); const contextParameter = customParameterEntityFactory.build({ scope: CustomParameterScope.CONTEXT, regex: 'testValue123', }); - - const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ + const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.build({ parameters: [contextParameter], version: 2, }); - - const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.build({ tool: externalToolEntity, school, schoolParameters: [], toolVersion: 2, }); - const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ + await em.persistAndFlush([ + course, + school, + teacherUser, + teacherAccount, + externalToolEntity, + schoolExternalToolEntity, + ]); + + const contextExternalToolEntity: ContextExternalToolEntity = contextExternalToolEntityFactory.build({ schoolTool: schoolExternalToolEntity, contextId: course.id, contextType: ContextExternalToolType.COURSE, @@ -724,6 +725,10 @@ describe('ToolContextController (API)', () => { toolVersion: 1, }); + await em.persistAndFlush([contextExternalToolEntity]); + + em.clear(); + const postParams: ContextExternalToolPostParams = { schoolToolId: schoolExternalToolEntity.id, contextId: course.id, @@ -738,17 +743,6 @@ describe('ToolContextController (API)', () => { toolVersion: 2, }; - await em.persistAndFlush([ - course, - school, - teacherUser, - teacherAccount, - externalToolEntity, - schoolExternalToolEntity, - contextExternalToolEntity, - ]); - em.clear(); - const loggedInClient: TestApiClient = await testApiClient.login(teacherAccount); return { loggedInClient, postParams, contextExternalToolEntity }; @@ -760,6 +754,7 @@ describe('ToolContextController (API)', () => { const response = await loggedInClient.put(`${contextExternalToolEntity.id}`).send(postParams); expect(response.status).toEqual(HttpStatus.FORBIDDEN); + // body missed }); }); diff --git a/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts b/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts index 491e7d9f2d6..3e7d8199fa2 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts @@ -58,6 +58,7 @@ export class ToolContextController { ContextExternalToolResponseMapper.mapContextExternalToolResponse(createdTool); this.logger.debug(`ContextExternalTool with id ${response.id} was created by user with id ${currentUser.userId}`); + return response; } diff --git a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts index 801765f80e5..8be134b80f2 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts @@ -5,8 +5,12 @@ import { Test, TestingModule } from '@nestjs/testing'; import { EntityId, Permission, User } from '@shared/domain'; import { contextExternalToolFactory, setupEntities, userFactory } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { Action, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { ForbiddenLoggableException } from '@src/modules/authorization/errors/forbidden.loggable-exception'; +import { + Action, + AuthorizationContextBuilder, + AuthorizationService, + ForbiddenLoggableException, +} from '@src/modules/authorization'; import { ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../domain'; diff --git a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts index 04002cf9fc6..9b6e3e3fe66 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts @@ -61,18 +61,20 @@ export class ContextExternalToolUc { return saved; } - async deleteContextExternalTool(userId: EntityId, contextExternalToolId: EntityId): Promise { + public async deleteContextExternalTool(userId: EntityId, contextExternalToolId: EntityId): Promise { const tool: ContextExternalTool = await this.contextExternalToolService.findById(contextExternalToolId); - const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); + const context = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); await this.toolPermissionHelper.ensureContextPermissions(userId, tool, context); - const promise: Promise = this.contextExternalToolService.deleteContextExternalTool(tool); - - return promise; + await this.contextExternalToolService.deleteContextExternalTool(tool); } - async getContextExternalToolsForContext(userId: EntityId, contextType: ToolContextType, contextId: string) { + public async getContextExternalToolsForContext( + userId: EntityId, + contextType: ToolContextType, + contextId: string + ): Promise { const tools: ContextExternalTool[] = await this.contextExternalToolService.findAllByContext( new ContextRef({ id: contextId, type: contextType }) ); diff --git a/apps/server/src/modules/tool/external-tool/controller/api-test/tool-configuration.api.spec.ts b/apps/server/src/modules/tool/external-tool/controller/api-test/tool-configuration.api.spec.ts index 70ec7d4be67..d0030bf7b46 100644 --- a/apps/server/src/modules/tool/external-tool/controller/api-test/tool-configuration.api.spec.ts +++ b/apps/server/src/modules/tool/external-tool/controller/api-test/tool-configuration.api.spec.ts @@ -33,7 +33,6 @@ describe('ToolConfigurationController (API)', () => { let app: INestApplication; let em: EntityManager; let orm: MikroORM; - let testApiClient: TestApiClient; beforeAll(async () => { @@ -346,22 +345,19 @@ describe('ToolConfigurationController (API)', () => { describe('GET tools/school-external-tools/:schoolExternalToolId/configuration-template', () => { describe('when the user is not authorized', () => { const setup = async () => { - const school: SchoolEntity = schoolFactory.buildWithId(); - - const user: User = userFactory.buildWithId({ school, roles: [] }); - const account: Account = accountFactory.buildWithId({ userId: user.id }); - - const externalTool: ExternalToolEntity = externalToolEntityFactory.buildWithId(); - - const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + const school = schoolFactory.build(); + // not on same school like the tool + const { adminAccount, adminUser } = UserAndAccountTestFactory.buildAdmin({}, []); + const externalTool: ExternalToolEntity = externalToolEntityFactory.build(); + const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.build({ school, tool: externalTool, }); - await em.persistAndFlush([user, account, school, externalTool, schoolExternalTool]); + await em.persistAndFlush([adminAccount, adminUser, school, externalTool, schoolExternalTool]); em.clear(); - const loggedInClient: TestApiClient = await testApiClient.login(account); + const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); return { loggedInClient, @@ -477,51 +473,43 @@ describe('ToolConfigurationController (API)', () => { describe('GET tools/context-external-tools/:contextExternalToolId/configuration-template', () => { describe('when the user is not authorized', () => { const setup = async () => { - const school: SchoolEntity = schoolFactory.buildWithId(); - - const course: Course = courseFactory.buildWithId(); - - const user: User = userFactory.buildWithId({ school, roles: [] }); - const account: Account = accountFactory.buildWithId({ userId: user.id }); - - const externalTool: ExternalToolEntity = externalToolEntityFactory.buildWithId(); - - const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + const school = schoolFactory.build(); + const { adminUser, adminAccount } = UserAndAccountTestFactory.buildAdmin({}, [Permission.SCHOOL_TOOL_ADMIN]); + // user is not part of the course + const course = courseFactory.build(); + const externalTool: ExternalToolEntity = externalToolEntityFactory.build(); + const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.build({ school, tool: externalTool, }); - const contextExternalTool: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ + await em.persistAndFlush([course, adminUser, adminAccount, school, externalTool, schoolExternalTool]); + + const contextExternalTool: ContextExternalToolEntity = contextExternalToolEntityFactory.build({ schoolTool: schoolExternalTool, + contextId: course.id, }); - await em.persistAndFlush([ - user, - account, - school, - externalTool, - schoolExternalTool, - contextExternalTool, - course, - ]); + await em.persistAndFlush([contextExternalTool]); em.clear(); - const loggedInClient: TestApiClient = await testApiClient.login(account); + const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); return { loggedInClient, - contextExternalTool, + contextExternalToolId: contextExternalTool.id, }; }; it('should return a forbidden status', async () => { - const { loggedInClient, contextExternalTool } = await setup(); + const { loggedInClient, contextExternalToolId } = await setup(); const response: Response = await loggedInClient.get( - `context-external-tools/${contextExternalTool.id}/configuration-template` + `context-external-tools/${contextExternalToolId}/configuration-template` ); expect(response.status).toEqual(HttpStatus.FORBIDDEN); + // body }); }); @@ -607,36 +595,26 @@ describe('ToolConfigurationController (API)', () => { describe('when tool is hidden', () => { const setup = async () => { - const school: SchoolEntity = schoolFactory.buildWithId(); - + const school = schoolFactory.build(); const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher({ school }, [ Permission.CONTEXT_TOOL_ADMIN, ]); - - const course: Course = courseFactory.buildWithId({ school, teachers: [teacherUser] }); - - const externalTool: ExternalToolEntity = externalToolEntityFactory.buildWithId({ isHidden: true }); - - const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + const course = courseFactory.build({ school, teachers: [teacherUser] }); + const externalTool: ExternalToolEntity = externalToolEntityFactory.build({ isHidden: true }); + const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.build({ school, tool: externalTool, }); - const contextExternalTool: ContextExternalToolEntity = contextExternalToolEntityFactory.buildWithId({ + await em.persistAndFlush([teacherUser, school, teacherAccount, externalTool, schoolExternalTool, course]); + + const contextExternalTool: ContextExternalToolEntity = contextExternalToolEntityFactory.build({ schoolTool: schoolExternalTool, contextType: ContextExternalToolType.COURSE, contextId: course.id, }); - await em.persistAndFlush([ - teacherUser, - school, - teacherAccount, - externalTool, - schoolExternalTool, - contextExternalTool, - course, - ]); + await em.persistAndFlush([contextExternalTool]); em.clear(); const loggedInClient: TestApiClient = await testApiClient.login(teacherAccount); diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts index 16dd9626d0c..1e91214ccda 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts @@ -140,8 +140,8 @@ export class ExternalToolConfigurationUc { const contextExternalTool: ContextExternalTool = await this.contextExternalToolService.findById( contextExternalToolId ); - const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_ADMIN]); + const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_ADMIN]); await this.toolPermissionHelper.ensureContextPermissions(userId, contextExternalTool, context); const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( diff --git a/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts b/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts index 6799a50bca2..95d8a42ce1f 100644 --- a/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts +++ b/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts @@ -1,4 +1,4 @@ -import { Module } from '@nestjs/common'; +import { Module, forwardRef } from '@nestjs/common'; import { LearnroomModule } from '@src/modules/learnroom'; import { LegacySchoolModule } from '@src/modules/legacy-school'; import { PseudonymModule } from '@src/modules/pseudonym'; @@ -18,7 +18,7 @@ import { BasicToolLaunchStrategy, Lti11ToolLaunchStrategy, OAuth2ToolLaunchStrat ContextExternalToolModule, LegacySchoolModule, UserModule, - PseudonymModule, + forwardRef(() => PseudonymModule), // i do not like this solution, the root problem is on other place but not detectable for me LearnroomModule, ], providers: [ diff --git a/apps/server/src/modules/video-conference/mapper/video-conference.mapper.ts b/apps/server/src/modules/video-conference/mapper/video-conference.mapper.ts index 727cfdfce7d..1f51a8cb3ff 100644 --- a/apps/server/src/modules/video-conference/mapper/video-conference.mapper.ts +++ b/apps/server/src/modules/video-conference/mapper/video-conference.mapper.ts @@ -1,5 +1,4 @@ -import { Permission, VideoConferenceScope } from '@shared/domain'; -import { AuthorizableReferenceType } from '@src/modules/authorization'; +import { Permission } from '@shared/domain'; import { BBBRole } from '../bbb'; import { VideoConferenceCreateParams, @@ -16,11 +15,6 @@ export const PermissionMapping = { [BBBRole.VIEWER]: Permission.JOIN_MEETING, }; -export const PermissionScopeMapping = { - [VideoConferenceScope.COURSE]: AuthorizableReferenceType.Course, - [VideoConferenceScope.EVENT]: AuthorizableReferenceType.Team, -}; - const stateMapping = { [VideoConferenceState.NOT_STARTED]: VideoConferenceStateResponse.NOT_STARTED, [VideoConferenceState.RUNNING]: VideoConferenceStateResponse.RUNNING, diff --git a/apps/server/src/modules/video-conference/service/video-conference.service.spec.ts b/apps/server/src/modules/video-conference/service/video-conference.service.spec.ts index b0c28b6a3c9..280a11976d7 100644 --- a/apps/server/src/modules/video-conference/service/video-conference.service.spec.ts +++ b/apps/server/src/modules/video-conference/service/video-conference.service.spec.ts @@ -14,14 +14,10 @@ import { } from '@shared/domain'; import { CalendarEventDto, CalendarService } from '@shared/infra/calendar'; import { TeamsRepo, VideoConferenceRepo } from '@shared/repo'; -import { - AuthorizableReferenceType, - AuthorizationContextBuilder, - AuthorizationService, -} from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; import { LegacySchoolService } from '@src/modules/legacy-school'; import { UserService } from '@src/modules/user'; -import { courseFactory, roleFactory, setupEntities, userDoFactory } from '@shared/testing'; +import { courseFactory, roleFactory, setupEntities, userDoFactory, userFactory } from '@shared/testing'; import { videoConferenceDOFactory } from '@shared/testing/factory/video-conference.do.factory'; import { ObjectId } from 'bson'; import { teamFactory } from '@shared/testing/factory/team.factory'; @@ -332,64 +328,160 @@ describe('VideoConferenceService', () => { }); describe('checkPermission', () => { - const setup = () => { - const userId = 'user-id'; - const conferenceScope = VideoConferenceScope.COURSE; - const entityId = 'entity-id'; + describe('when user has START_MEETING permission and is in course scope', () => { + const setup = () => { + const user = userFactory.buildWithId(); + const entity = courseFactory.buildWithId(); + const conferenceScope = VideoConferenceScope.COURSE; - return { - userId, - conferenceScope, - entityId, + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationService.hasPermission.mockReturnValueOnce(true).mockReturnValueOnce(false); + courseService.findById.mockResolvedValueOnce(entity); + + return { + user, + userId: user.id, + entity, + entityId: entity.id, + conferenceScope, + }; }; - }; - describe('when user has START_MEETING permission', () => { + it('should call the correct authorization order', async () => { + const { user, entity, userId, conferenceScope, entityId } = setup(); + + await service.determineBbbRole(userId, entityId, conferenceScope); + + expect(authorizationService.hasPermission).toHaveBeenCalledWith( + user, + entity, + AuthorizationContextBuilder.read([Permission.START_MEETING]) + ); + }); + it('should return BBBRole.MODERATOR', async () => { const { userId, conferenceScope, entityId } = setup(); - authorizationService.hasPermissionByReferences.mockResolvedValueOnce(true); - authorizationService.hasPermissionByReferences.mockResolvedValueOnce(false); - - const result: BBBRole = await service.determineBbbRole(userId, entityId, conferenceScope); + const result = await service.determineBbbRole(userId, entityId, conferenceScope); expect(result).toBe(BBBRole.MODERATOR); - expect(authorizationService.hasPermissionByReferences).toHaveBeenCalledWith( - userId, - AuthorizableReferenceType.Course, - entityId, + }); + }); + + // can be removed when team / course / user is passed from UC + // missing when course / team loading throw an error, but also not nessasary if it is passed to UC. + describe('when user has START_MEETING permission and is in team(event) scope', () => { + const setup = () => { + const user = userFactory.buildWithId(); + const entity = teamFactory.buildWithId(); + const conferenceScope = VideoConferenceScope.EVENT; + + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationService.hasPermission.mockReturnValueOnce(true).mockReturnValueOnce(false); + teamsRepo.findById.mockResolvedValueOnce(entity); + + return { + user, + userId: user.id, + entity, + entityId: entity.id, + conferenceScope, + }; + }; + + it('should call the correct authorization order', async () => { + const { user, entity, userId, conferenceScope, entityId } = setup(); + + await service.determineBbbRole(userId, entityId, conferenceScope); + + expect(authorizationService.hasPermission).toHaveBeenCalledWith( + user, + entity, AuthorizationContextBuilder.read([Permission.START_MEETING]) ); }); + + it('should return BBBRole.MODERATOR', async () => { + const { userId, conferenceScope, entityId } = setup(); + + const result = await service.determineBbbRole(userId, entityId, conferenceScope); + + expect(result).toBe(BBBRole.MODERATOR); + }); }); describe('when user has JOIN_MEETING permission', () => { + const setup = () => { + const user = userFactory.buildWithId(); + const entity = courseFactory.buildWithId(); + const conferenceScope = VideoConferenceScope.COURSE; + + authorizationService.hasPermission.mockReturnValueOnce(false).mockReturnValueOnce(true); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + courseService.findById.mockResolvedValueOnce(entity); + + return { + user, + userId: user.id, + entity, + entityId: entity.id, + conferenceScope, + }; + }; + + it('should call the correct authorization order', async () => { + const { user, entity, userId, conferenceScope, entityId } = setup(); + + await service.determineBbbRole(userId, entityId, conferenceScope); + + expect(authorizationService.hasPermission).toHaveBeenNthCalledWith( + 1, + user, + entity, + AuthorizationContextBuilder.read([Permission.START_MEETING]) + ); + expect(authorizationService.hasPermission).toHaveBeenNthCalledWith( + 2, + user, + entity, + AuthorizationContextBuilder.read([Permission.JOIN_MEETING]) + ); + }); + it('should return BBBRole.VIEWER', async () => { const { userId, conferenceScope, entityId } = setup(); - authorizationService.hasPermissionByReferences.mockResolvedValueOnce(false); - authorizationService.hasPermissionByReferences.mockResolvedValueOnce(true); - const result: BBBRole = await service.determineBbbRole(userId, entityId, conferenceScope); + const result = await service.determineBbbRole(userId, entityId, conferenceScope); expect(result).toBe(BBBRole.VIEWER); - expect(authorizationService.hasPermissionByReferences).toHaveBeenCalledWith( - userId, - AuthorizableReferenceType.Course, - entityId, - AuthorizationContextBuilder.read([Permission.JOIN_MEETING]) - ); }); }); describe('when user has neither START_MEETING nor JOIN_MEETING permission', () => { + const setup = () => { + const user = userFactory.buildWithId(); + const entity = courseFactory.buildWithId(); + const conferenceScope = VideoConferenceScope.COURSE; + + authorizationService.hasPermission.mockReturnValueOnce(false).mockReturnValueOnce(false); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + courseService.findById.mockResolvedValueOnce(entity); + + return { + user, + userId: user.id, + entity, + entityId: entity.id, + conferenceScope, + }; + }; + it('should throw a ForbiddenException', async () => { const { userId, conferenceScope, entityId } = setup(); - authorizationService.hasPermissionByReferences.mockResolvedValueOnce(false); - authorizationService.hasPermissionByReferences.mockResolvedValueOnce(false); - const func = () => service.determineBbbRole(userId, entityId, conferenceScope); + const callDetermineBbbRole = () => service.determineBbbRole(userId, entityId, conferenceScope); - await expect(func).rejects.toThrow(new ForbiddenException(ErrorStatus.INSUFFICIENT_PERMISSION)); + await expect(callDetermineBbbRole).rejects.toThrow(new ForbiddenException(ErrorStatus.INSUFFICIENT_PERMISSION)); }); }); }); diff --git a/apps/server/src/modules/video-conference/service/video-conference.service.ts b/apps/server/src/modules/video-conference/service/video-conference.service.ts index 0201b150f33..69a1a7fd74f 100644 --- a/apps/server/src/modules/video-conference/service/video-conference.service.ts +++ b/apps/server/src/modules/video-conference/service/video-conference.service.ts @@ -8,6 +8,7 @@ import { SchoolFeatures, TeamEntity, TeamUserEntity, + User, UserDO, VideoConferenceDO, VideoConferenceOptionsDO, @@ -15,19 +16,13 @@ import { } from '@shared/domain'; import { CalendarEventDto, CalendarService } from '@shared/infra/calendar'; import { TeamsRepo, VideoConferenceRepo } from '@shared/repo'; -import { - Action, - AuthorizableReferenceType, - AuthorizationContextBuilder, - AuthorizationService, -} from '@src/modules/authorization'; -import { CourseService } from '@src/modules/learnroom/service'; +import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { CourseService } from '@src/modules/learnroom'; import { LegacySchoolService } from '@src/modules/legacy-school'; import { UserService } from '@src/modules/user'; import { BBBRole } from '../bbb'; import { ErrorStatus } from '../error'; import { IVideoConferenceSettings, VideoConferenceOptions, VideoConferenceSettings } from '../interface'; -import { PermissionScopeMapping } from '../mapper/video-conference.mapper'; import { IScopeInfo, VideoConferenceState } from '../uc/dto'; @Injectable() @@ -97,39 +92,59 @@ export class VideoConferenceService { return isExpert; } - async determineBbbRole(userId: EntityId, scopeId: EntityId, scope: VideoConferenceScope): Promise { - const permissionMap: Map> = this.hasPermissions( - userId, - PermissionScopeMapping[scope], - scopeId, - [Permission.START_MEETING, Permission.JOIN_MEETING], - Action.read - ); - - if (await permissionMap.get(Permission.START_MEETING)) { - return BBBRole.MODERATOR; - } - if (await permissionMap.get(Permission.JOIN_MEETING)) { - return BBBRole.VIEWER; + // should be public to expose ressources to UC for passing it to authrisation and improve performance + private async loadScopeRessources( + scopeId: EntityId, + scope: VideoConferenceScope + ): Promise { + let scopeRessource: Course | TeamEntity | null = null; + + if (scope === VideoConferenceScope.COURSE) { + scopeRessource = await this.courseService.findById(scopeId); + } else if (scope === VideoConferenceScope.EVENT) { + scopeRessource = await this.teamsRepo.findById(scopeId); + } else { + // Need to be solve the null with throw by it self. } - throw new ForbiddenException(ErrorStatus.INSUFFICIENT_PERMISSION); + + return scopeRessource; + } + + private isNullOrUndefined(value: unknown): value is null { + return !value; } - private hasPermissions( - userId: EntityId, - entityName: AuthorizableReferenceType, - entityId: EntityId, - permissions: Permission[], - action: Action - ): Map> { - const returnMap: Map> = new Map(); - permissions.forEach((perm) => { - const context = - action === Action.read ? AuthorizationContextBuilder.read([perm]) : AuthorizationContextBuilder.write([perm]); - const ret = this.authorizationService.hasPermissionByReferences(userId, entityName, entityId, context); - returnMap.set(perm, ret); - }); - return returnMap; + private hasStartMeetingAndCanRead(authorizableUser: User, entity: Course | TeamEntity): boolean { + const context = AuthorizationContextBuilder.read([Permission.START_MEETING]); + const hasPermission = this.authorizationService.hasPermission(authorizableUser, entity, context); + + return hasPermission; + } + + private hasJoinMeetingAndCanRead(authorizableUser: User, entity: Course | TeamEntity): boolean { + const context = AuthorizationContextBuilder.read([Permission.JOIN_MEETING]); + const hasPermission = this.authorizationService.hasPermission(authorizableUser, entity, context); + + return hasPermission; + } + + async determineBbbRole(userId: EntityId, scopeId: EntityId, scope: VideoConferenceScope): Promise { + // ressource loading need to be move to uc + const [authorizableUser, scopeRessource]: [User, TeamEntity | Course | null] = await Promise.all([ + this.authorizationService.getUserWithPermissions(userId), + this.loadScopeRessources(scopeId, scope), + ]); + + if (!this.isNullOrUndefined(scopeRessource)) { + if (this.hasStartMeetingAndCanRead(authorizableUser, scopeRessource)) { + return BBBRole.MODERATOR; + } + if (this.hasJoinMeetingAndCanRead(authorizableUser, scopeRessource)) { + return BBBRole.VIEWER; + } + } + + throw new ForbiddenException(ErrorStatus.INSUFFICIENT_PERMISSION); } async throwOnFeaturesDisabled(schoolId: EntityId): Promise { diff --git a/apps/server/src/modules/video-conference/uc/video-conference-create.uc.ts b/apps/server/src/modules/video-conference/uc/video-conference-create.uc.ts index e7a28c9a7ca..853bf0f13bc 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-create.uc.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-create.uc.ts @@ -38,6 +38,12 @@ export class VideoConferenceCreateUc { } private async create(currentUserId: EntityId, scope: ScopeRef, options: VideoConferenceOptions): Promise { + /* need to be replace with + const [authorizableUser, scopeRessource]: [User, TeamEntity | Course] = await Promise.all([ + this.authorizationService.getUserWithPermissions(userId), + this.videoConferenceService.loadScopeRessources(scopeId, scope), + ]); + */ const user: UserDO = await this.userService.findById(currentUserId); await this.verifyFeaturesEnabled(user.schoolId); diff --git a/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.spec.ts b/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.spec.ts index 3680c4519da..4ff494a4cc0 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.spec.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.spec.ts @@ -19,9 +19,10 @@ import { CalendarEventDto } from '@shared/infra/calendar/dto/calendar-event.dto' import { TeamsRepo, VideoConferenceRepo } from '@shared/repo'; import { roleFactory, setupEntities, userDoFactory } from '@shared/testing'; import { teamFactory } from '@shared/testing/factory/team.factory'; -import { AuthorizationService, LegacySchoolService, UserService } from '@src/modules'; +import { LegacySchoolService, UserService } from '@src/modules'; +import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; import { ICurrentUser } from '@src/modules/authentication'; -import { CourseService } from '@src/modules/learnroom/service'; +import { CourseService } from '@src/modules/learnroom'; import { IScopeInfo, VideoConference, VideoConferenceJoin, VideoConferenceState } from './dto'; import { VideoConferenceDeprecatedUc } from './video-conference-deprecated.uc'; import { @@ -63,7 +64,7 @@ describe('VideoConferenceUc', () => { let useCase: VideoConferenceDeprecatedUcSpec; let bbbService: DeepMocked; - let authorizationService: DeepMocked; + let authorizationService: DeepMocked; let videoConferenceRepo: DeepMocked; let teamsRepo: DeepMocked; let courseService: DeepMocked; @@ -118,8 +119,8 @@ describe('VideoConferenceUc', () => { useValue: createMock(), }, { - provide: AuthorizationService, - useValue: createMock(), + provide: AuthorizationReferenceService, + useValue: createMock(), }, { provide: VideoConferenceRepo, @@ -149,7 +150,7 @@ describe('VideoConferenceUc', () => { }).compile(); useCase = module.get(VideoConferenceDeprecatedUcSpec); schoolService = module.get(LegacySchoolService); - authorizationService = module.get(AuthorizationService); + authorizationService = module.get(AuthorizationReferenceService); courseService = module.get(CourseService); calendarService = module.get(CalendarService); videoConferenceRepo = module.get(VideoConferenceRepo); diff --git a/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.ts b/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.ts index 7bce5b2f5a4..e6b1f11ed50 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.ts @@ -18,14 +18,10 @@ import { CalendarEventDto } from '@shared/infra/calendar/dto/calendar-event.dto' import { TeamsRepo } from '@shared/repo'; import { VideoConferenceRepo } from '@shared/repo/videoconference/video-conference.repo'; import { ICurrentUser } from '@src/modules/authentication'; -import { - Action, - AuthorizableReferenceType, - AuthorizationContextBuilder, - AuthorizationService, -} from '@src/modules/authorization'; -import { CourseService } from '@src/modules/learnroom/service'; +import { Action, AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationReferenceService, AuthorizableReferenceType } from '@src/modules/authorization/domain'; import { LegacySchoolService } from '@src/modules/legacy-school'; +import { CourseService } from '@src/modules/learnroom'; import { UserService } from '@src/modules/user'; import { BBBBaseMeetingConfig, @@ -62,7 +58,7 @@ export class VideoConferenceDeprecatedUc { constructor( private readonly bbbService: BBBService, - private readonly authorizationService: AuthorizationService, + private readonly authorizationReferenceService: AuthorizationReferenceService, private readonly videoConferenceRepo: VideoConferenceRepo, private readonly teamsRepo: TeamsRepo, private readonly courseService: CourseService, @@ -413,7 +409,7 @@ export class VideoConferenceDeprecatedUc { permissions.forEach((perm) => { const context = action === Action.read ? AuthorizationContextBuilder.read([perm]) : AuthorizationContextBuilder.write([perm]); - const ret = this.authorizationService.hasPermissionByReferences(userId, entityName, entityId, context); + const ret = this.authorizationReferenceService.hasPermissionByReferences(userId, entityName, entityId, context); returnMap.set(perm, ret); }); return returnMap; diff --git a/apps/server/src/modules/video-conference/uc/video-conference-end.uc.ts b/apps/server/src/modules/video-conference/uc/video-conference-end.uc.ts index a9799f67a89..50318c001c0 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-end.uc.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-end.uc.ts @@ -16,6 +16,12 @@ export class VideoConferenceEndUc { ) {} async end(currentUserId: EntityId, scope: ScopeRef): Promise> { + /* need to be replace with + const [authorizableUser, scopeRessource]: [User, TeamEntity | Course] = await Promise.all([ + this.authorizationService.getUserWithPermissions(userId), + this.videoConferenceService.loadScopeRessources(scopeId, scope), + ]); + */ const user: UserDO = await this.userService.findById(currentUserId); const userId: string = user.id as string; diff --git a/apps/server/src/modules/video-conference/uc/video-conference-info.uc.ts b/apps/server/src/modules/video-conference/uc/video-conference-info.uc.ts index 91ebb23ea2b..79a1f95b8d1 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-info.uc.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-info.uc.ts @@ -17,6 +17,12 @@ export class VideoConferenceInfoUc { ) {} async getMeetingInfo(currentUserId: EntityId, scope: ScopeRef): Promise { + /* need to be replace with + const [authorizableUser, scopeRessource]: [User, TeamEntity | Course] = await Promise.all([ + this.authorizationService.getUserWithPermissions(userId), + this.videoConferenceService.loadScopeRessources(scopeId, scope), + ]); + */ const user: UserDO = await this.userService.findById(currentUserId); await this.videoConferenceService.throwOnFeaturesDisabled(user.schoolId); diff --git a/apps/server/src/modules/video-conference/video-conference.module.ts b/apps/server/src/modules/video-conference/video-conference.module.ts index 70a999437c0..6277f0dde0a 100644 --- a/apps/server/src/modules/video-conference/video-conference.module.ts +++ b/apps/server/src/modules/video-conference/video-conference.module.ts @@ -3,6 +3,7 @@ import { HttpModule } from '@nestjs/axios'; import { CalendarModule } from '@shared/infra/calendar'; import { VideoConferenceRepo } from '@shared/repo/videoconference/video-conference.repo'; import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationReferenceModule } from '@src/modules/authorization/authorization-reference.module'; import { TeamsRepo } from '@shared/repo'; import { LegacySchoolModule } from '@src/modules/legacy-school'; import { LoggerModule } from '@src/core/logger'; @@ -19,6 +20,7 @@ import { LearnroomModule } from '../learnroom'; @Module({ imports: [ AuthorizationModule, + AuthorizationReferenceModule, // can be removed wenn video-conference-deprecated is removed CalendarModule, HttpModule, LegacySchoolModule, diff --git a/apps/server/src/shared/domain/rules/index.ts b/apps/server/src/shared/domain/rules/index.ts deleted file mode 100644 index 888b2ee8501..00000000000 --- a/apps/server/src/shared/domain/rules/index.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { BoardDoRule } from './board-do.rule'; -import { ContextExternalToolRule } from './context-external-tool.rule'; -import { CourseGroupRule } from './course-group.rule'; -import { CourseRule } from './course.rule'; -import { LessonRule } from './lesson.rule'; -import { SchoolExternalToolRule } from './school-external-tool.rule'; -import { LegacySchoolRule } from './legacy-school.rule'; -import { SubmissionRule } from './submission.rule'; -import { TaskRule } from './task.rule'; -import { TeamRule } from './team.rule'; -import { UserLoginMigrationRule } from './user-login-migration.rule'; -import { UserRule } from './user.rule'; - -export * from './board-do.rule'; -export * from './course-group.rule'; -export * from './course.rule'; -export * from './lesson.rule'; -export * from './school-external-tool.rule'; -export * from './legacy-school.rule'; -export * from './submission.rule'; -export * from './task.rule'; -export * from './team.rule'; -export * from './user.rule'; -export * from './context-external-tool.rule'; - -export const ALL_RULES = [ - LessonRule, - CourseRule, - CourseGroupRule, - LegacySchoolRule, - SubmissionRule, - TaskRule, - TeamRule, - UserRule, - SchoolExternalToolRule, - BoardDoRule, - ContextExternalToolRule, - UserLoginMigrationRule, -]; diff --git a/apps/server/src/shared/infra/antivirus/index.ts b/apps/server/src/shared/infra/antivirus/index.ts index 833c46d81a7..2816f95ee57 100644 --- a/apps/server/src/shared/infra/antivirus/index.ts +++ b/apps/server/src/shared/infra/antivirus/index.ts @@ -1,3 +1,3 @@ -export * from './interfaces'; -export * from './antivirus.module'; -export * from './antivirus.service'; +export * from './interfaces'; +export * from './antivirus.module'; +export * from './antivirus.service'; diff --git a/apps/server/src/shared/testing/test-api-client.ts b/apps/server/src/shared/testing/test-api-client.ts index 733dcdd5cfc..75be7b7dc5f 100644 --- a/apps/server/src/shared/testing/test-api-client.ts +++ b/apps/server/src/shared/testing/test-api-client.ts @@ -140,6 +140,10 @@ export class TestApiClient { } private getJwtFromResponse(response: Response): string { + if (response.error) { + const error = JSON.stringify(response.error); + throw new Error(error); + } if (!this.isAuthenticationResponse(response.body)) { const body = JSON.stringify(response.body); throw new Error(`${testReqestConst.errorMessage} ${body}`); From 7b42e1efc1c007dea7d3c745c42bbf694806483b Mon Sep 17 00:00:00 2001 From: Martin Schuhmacher <55735359+MartinSchuhmacher@users.noreply.github.com> Date: Fri, 20 Oct 2023 09:56:47 +0200 Subject: [PATCH 05/14] BC-4293 - Topic does not copy with empty content Learning material (#4485) * copy lernstore content without set resource --- .../service/lesson-copy.service.spec.ts | 49 +++++++++++++++++++ .../lesson/service/lesson-copy.service.ts | 39 ++++++++------- .../src/shared/domain/entity/lesson.entity.ts | 2 +- 3 files changed, 71 insertions(+), 19 deletions(-) diff --git a/apps/server/src/modules/lesson/service/lesson-copy.service.spec.ts b/apps/server/src/modules/lesson/service/lesson-copy.service.spec.ts index 39f3f07882a..6097a3b87f6 100644 --- a/apps/server/src/modules/lesson/service/lesson-copy.service.spec.ts +++ b/apps/server/src/modules/lesson/service/lesson-copy.service.spec.ts @@ -531,6 +531,55 @@ describe('lesson copy service', () => { }); }); + describe('when lesson contains LernStore content element without set resource', () => { + const setup = () => { + const lernStoreContent: IComponentProperties = { + title: 'text component 1', + hidden: false, + component: ComponentType.LERNSTORE, + }; + const user = userFactory.build(); + const originalCourse = courseFactory.build({ school: user.school }); + const destinationCourse = courseFactory.build({ school: user.school, teachers: [user] }); + const originalLesson = lessonFactory.build({ + course: originalCourse, + contents: [lernStoreContent], + }); + lessonRepo.findById.mockResolvedValueOnce(originalLesson); + + return { user, originalCourse, destinationCourse, originalLesson, lernStoreContent }; + }; + + it('the content should be fully copied', async () => { + const { user, destinationCourse, originalLesson, lernStoreContent } = setup(); + + const status = await copyService.copyLesson({ + originalLessonId: originalLesson.id, + destinationCourse, + user, + }); + + const copiedLessonContents = (status.copyEntity as LessonEntity).contents as IComponentProperties[]; + expect(copiedLessonContents[0]).toEqual(lernStoreContent); + }); + + it('should set content type to LESSON_CONTENT_LERNSTORE', async () => { + const { user, destinationCourse, originalLesson } = setup(); + + const status = await copyService.copyLesson({ + originalLessonId: originalLesson.id, + destinationCourse, + user, + }); + const contentsStatus = status.elements?.find((el) => el.type === CopyElementType.LESSON_CONTENT_GROUP); + expect(contentsStatus).toBeDefined(); + if (contentsStatus?.elements) { + expect(contentsStatus.elements[0].type).toEqual(CopyElementType.LESSON_CONTENT_LERNSTORE); + expect(contentsStatus.elements[0].status).toEqual(CopyStatusEnum.SUCCESS); + } + }); + }); + describe('when lesson contains geoGebra content element', () => { const setup = () => { const geoGebraContent: IComponentProperties = { diff --git a/apps/server/src/modules/lesson/service/lesson-copy.service.ts b/apps/server/src/modules/lesson/service/lesson-copy.service.ts index 4a6835b05f2..cd83681a2e7 100644 --- a/apps/server/src/modules/lesson/service/lesson-copy.service.ts +++ b/apps/server/src/modules/lesson/service/lesson-copy.service.ts @@ -266,29 +266,32 @@ export class LessonCopyService { } private copyLernStore(element: IComponentProperties): IComponentProperties { - const resources = ((element.content as IComponentLernstoreProperties).resources ?? []).map( - ({ client, description, merlinReference, title, url }) => { - const result = { - client, - description, - merlinReference, - title, - url, - }; - return result; - } - ); - - const lernstore = { + const lernstore: IComponentProperties = { title: element.title, hidden: element.hidden, component: ComponentType.LERNSTORE, user: element.user, // TODO should be params.user - but that made the server crash, but property is normally undefined - content: { - resources, - }, }; - return lernstore as IComponentProperties; + + if (element.content) { + const resources = ((element.content as IComponentLernstoreProperties).resources ?? []).map( + ({ client, description, merlinReference, title, url }) => { + const result = { + client, + description, + merlinReference, + title, + url, + }; + return result; + } + ); + + const lernstoreContent: IComponentLernstoreProperties = { resources }; + lernstore.content = lernstoreContent; + } + + return lernstore; } private static copyGeogebra(originalElement: IComponentProperties): IComponentProperties { diff --git a/apps/server/src/shared/domain/entity/lesson.entity.ts b/apps/server/src/shared/domain/entity/lesson.entity.ts index d83cd2f182a..a47aab2a8b3 100644 --- a/apps/server/src/shared/domain/entity/lesson.entity.ts +++ b/apps/server/src/shared/domain/entity/lesson.entity.ts @@ -73,7 +73,7 @@ export type IComponentProperties = { | { component: ComponentType.ETHERPAD; content: IComponentEtherpadProperties } | { component: ComponentType.GEOGEBRA; content: IComponentGeogebraProperties } | { component: ComponentType.INTERNAL; content: IComponentInternalProperties } - | { component: ComponentType.LERNSTORE; content: IComponentLernstoreProperties } + | { component: ComponentType.LERNSTORE; content?: IComponentLernstoreProperties } | { component: ComponentType.NEXBOARD; content: IComponentNexboardProperties } ); From 0ab2dfbfb2ca4f7a2c7dd5a965c2b720674f800b Mon Sep 17 00:00:00 2001 From: Cedric Evers <12080057+CeEv@users.noreply.github.com> Date: Fri, 20 Oct 2023 11:42:06 +0200 Subject: [PATCH 06/14] Bc 4922 cleanup exports authentication (#4487) --- .../account/controller/account.controller.ts | 3 +-- apps/server/src/modules/account/index.ts | 1 + .../src/modules/account/services/index.ts | 1 + .../controllers/login.controller.ts | 2 +- .../modules/authentication/decorator/index.ts | 1 + .../modules/authentication/errors/index.ts | 4 ++++ .../src/modules/authentication/index.ts | 4 ++-- .../authentication/interface/jwt-payload.ts | 1 + .../modules/authentication/interface/user.ts | 24 ------------------- .../services/authentication.service.ts | 9 +++---- .../api-test/board-delete.api.spec.ts | 2 +- .../api-test/card-delete.api.spec.ts | 2 +- .../controller/board-submission.controller.ts | 5 ++-- .../board/controller/board.controller.ts | 3 +-- .../board/controller/card.controller.ts | 3 +-- .../board/controller/column.controller.ts | 3 +-- .../board/controller/element.controller.ts | 3 +-- .../collaborative-storage.controller.ts | 3 +-- .../controller/files-storage.controller.ts | 3 +-- .../fwu-learning-contents.controller.ts | 2 +- .../group/controller/group.controller.ts | 3 +-- .../controller/h5p-editor.controller.ts | 2 +- .../learnroom/controller/course.controller.ts | 3 +-- .../controller/dashboard.controller.ts | 3 +-- .../learnroom/controller/rooms.controller.ts | 3 +-- .../controller/legacy-school.controller.ts | 3 +-- .../lesson/controller/lesson.controller.ts | 3 +-- .../news/controller/news.controller.ts | 3 +-- .../news/controller/team-news.controller.ts | 5 +--- .../controller/oauth-provider.controller.ts | 22 ++++++++--------- .../oauth/controller/oauth-sso.controller.ts | 7 +++--- .../controller/pseudonym.controller.ts | 3 +-- .../controller/share-token.controller.ts | 4 ++-- .../task/controller/submission.controller.ts | 3 +-- .../task/controller/task.controller.ts | 4 ++-- .../controller/tool-context.controller.ts | 3 +-- .../controller/tool-reference.controller.ts | 3 +-- .../tool-configuration.controller.ts | 3 +-- .../controller/tool.controller.ts | 3 +-- .../controller/tool-school.controller.ts | 3 +-- .../controller/tool-launch.controller.ts | 3 +-- .../controller/import-user.controller.ts | 5 +--- .../user-login-migration.controller.ts | 3 +-- .../controller/dto/resolved-user.response.ts | 9 ++++--- .../user/controller/user.controller.ts | 3 +-- .../src/modules/user/service/user.service.ts | 15 ++++++++---- .../video-conference-deprecated.controller.ts | 3 +-- .../controller/video-conference.controller.ts | 3 +-- .../request-logging.interceptor.ts | 2 +- 49 files changed, 86 insertions(+), 125 deletions(-) create mode 100644 apps/server/src/modules/account/services/index.ts create mode 100644 apps/server/src/modules/authentication/decorator/index.ts create mode 100644 apps/server/src/modules/authentication/errors/index.ts diff --git a/apps/server/src/modules/account/controller/account.controller.ts b/apps/server/src/modules/account/controller/account.controller.ts index 23c07326d82..86d1f8287f8 100644 --- a/apps/server/src/modules/account/controller/account.controller.ts +++ b/apps/server/src/modules/account/controller/account.controller.ts @@ -1,8 +1,7 @@ import { Body, Controller, Delete, Get, Param, Patch, Query } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { EntityNotFoundError, ForbiddenOperationError, ValidationError } from '@shared/common'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { AccountUc } from '../uc/account.uc'; import { AccountByIdBodyParams, diff --git a/apps/server/src/modules/account/index.ts b/apps/server/src/modules/account/index.ts index 3893bd18f88..2fa0bcc2334 100644 --- a/apps/server/src/modules/account/index.ts +++ b/apps/server/src/modules/account/index.ts @@ -1,2 +1,3 @@ export * from './account.module'; export * from './account-config'; +export { AccountService } from './services'; diff --git a/apps/server/src/modules/account/services/index.ts b/apps/server/src/modules/account/services/index.ts new file mode 100644 index 00000000000..72778be1f1e --- /dev/null +++ b/apps/server/src/modules/account/services/index.ts @@ -0,0 +1 @@ +export * from './account.service'; diff --git a/apps/server/src/modules/authentication/controllers/login.controller.ts b/apps/server/src/modules/authentication/controllers/login.controller.ts index 4db3f825acf..68af396233e 100644 --- a/apps/server/src/modules/authentication/controllers/login.controller.ts +++ b/apps/server/src/modules/authentication/controllers/login.controller.ts @@ -2,7 +2,7 @@ import { Body, Controller, HttpCode, HttpStatus, Post, UseGuards } from '@nestjs import { AuthGuard } from '@nestjs/passport'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ForbiddenOperationError, ValidationError } from '@shared/common'; -import { CurrentUser } from '../decorator/auth.decorator'; +import { CurrentUser } from '../decorator'; import type { ICurrentUser, OauthCurrentUser } from '../interface'; import { LoginDto } from '../uc/dto'; import { LoginUc } from '../uc/login.uc'; diff --git a/apps/server/src/modules/authentication/decorator/index.ts b/apps/server/src/modules/authentication/decorator/index.ts new file mode 100644 index 00000000000..9795c6d38ee --- /dev/null +++ b/apps/server/src/modules/authentication/decorator/index.ts @@ -0,0 +1 @@ +export * from './auth.decorator'; diff --git a/apps/server/src/modules/authentication/errors/index.ts b/apps/server/src/modules/authentication/errors/index.ts new file mode 100644 index 00000000000..d87d53df8bf --- /dev/null +++ b/apps/server/src/modules/authentication/errors/index.ts @@ -0,0 +1,4 @@ +export * from './brute-force.error'; +export * from './ldap-connection.error'; +export * from './school-in-migration.error'; +export * from './unauthorized.loggable-exception'; diff --git a/apps/server/src/modules/authentication/index.ts b/apps/server/src/modules/authentication/index.ts index eaaa9dbb61a..904c64ff97b 100644 --- a/apps/server/src/modules/authentication/index.ts +++ b/apps/server/src/modules/authentication/index.ts @@ -1,2 +1,2 @@ -export * from './interface'; -export * from './guard/jwt-auth.guard'; +export { ICurrentUser } from './interface'; +export { JWT, CurrentUser, Authenticate } from './decorator'; diff --git a/apps/server/src/modules/authentication/interface/jwt-payload.ts b/apps/server/src/modules/authentication/interface/jwt-payload.ts index ed69630ccde..aad11700e60 100644 --- a/apps/server/src/modules/authentication/interface/jwt-payload.ts +++ b/apps/server/src/modules/authentication/interface/jwt-payload.ts @@ -5,6 +5,7 @@ export interface CreateJwtPayload { roles: string[]; systemId?: string; // without this the user needs to change his PW during first login support?: boolean; + // support UserId is missed see featherJS } export interface JwtPayload extends CreateJwtPayload { diff --git a/apps/server/src/modules/authentication/interface/user.ts b/apps/server/src/modules/authentication/interface/user.ts index e2874d1a8e0..a070367a43b 100644 --- a/apps/server/src/modules/authentication/interface/user.ts +++ b/apps/server/src/modules/authentication/interface/user.ts @@ -1,29 +1,5 @@ import { EntityId } from '@shared/domain'; -export interface IRole { - name: string; - - id: string; -} - -export interface IResolvedUser { - firstName: string; - - lastName: string; - - id: string; - - createdAt: Date; - - updatedAt: Date; - - roles: IRole[]; - - permissions: string[]; - - schoolId: string; -} - export interface ICurrentUser { /** authenticated users id */ userId: EntityId; diff --git a/apps/server/src/modules/authentication/services/authentication.service.ts b/apps/server/src/modules/authentication/services/authentication.service.ts index f4bdf319e6b..f2c25ff9e9d 100644 --- a/apps/server/src/modules/authentication/services/authentication.service.ts +++ b/apps/server/src/modules/authentication/services/authentication.service.ts @@ -1,14 +1,15 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; -import { AccountService } from '@src/modules/account/services/account.service'; +import { AccountService } from '@src/modules/account'; +// invalid import import { AccountDto } from '@src/modules/account/services/dto'; -import { JwtValidationAdapter } from '@src/modules/authentication/strategy/jwt-validation.adapter'; +// invalid import, can produce dependency cycles import type { IServerConfig } from '@src/modules/server'; import { randomUUID } from 'crypto'; import jwt, { JwtPayload } from 'jsonwebtoken'; -import { BruteForceError } from '../errors/brute-force.error'; -import { UnauthorizedLoggableException } from '../errors/unauthorized.loggable-exception'; +import { JwtValidationAdapter } from '../strategy/jwt-validation.adapter'; +import { BruteForceError, UnauthorizedLoggableException } from '../errors'; import { CreateJwtPayload } from '../interface/jwt-payload'; import { LoginDto } from '../uc/dto'; diff --git a/apps/server/src/modules/board/controller/api-test/board-delete.api.spec.ts b/apps/server/src/modules/board/controller/api-test/board-delete.api.spec.ts index cd5a1d5c34b..24bb1c69999 100644 --- a/apps/server/src/modules/board/controller/api-test/board-delete.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/board-delete.api.spec.ts @@ -13,7 +13,7 @@ import { } from '@shared/testing'; import { ICurrentUser } from '@src/modules/authentication'; import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@src/modules/server'; import { Request } from 'express'; import request from 'supertest'; import { BoardResponse } from '../dto'; diff --git a/apps/server/src/modules/board/controller/api-test/card-delete.api.spec.ts b/apps/server/src/modules/board/controller/api-test/card-delete.api.spec.ts index 297fc296998..2e38143ebc1 100644 --- a/apps/server/src/modules/board/controller/api-test/card-delete.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/card-delete.api.spec.ts @@ -15,7 +15,7 @@ import { } from '@shared/testing'; import { ICurrentUser } from '@src/modules/authentication'; import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@src/modules/server'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/board/controller/board-submission.controller.ts b/apps/server/src/modules/board/controller/board-submission.controller.ts index 56c6ae76ce2..f7b7e5da8ca 100644 --- a/apps/server/src/modules/board/controller/board-submission.controller.ts +++ b/apps/server/src/modules/board/controller/board-submission.controller.ts @@ -1,9 +1,8 @@ import { Body, Controller, ForbiddenException, Get, HttpCode, NotFoundException, Param, Patch } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; -import { SubmissionsResponse } from '@src/modules/board/controller/dto/submission-item/submissions.response'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { SubmissionsResponse } from './dto/submission-item/submissions.response'; import { CardUc } from '../uc'; import { ElementUc } from '../uc/element.uc'; import { SubmissionItemUc } from '../uc/submission-item.uc'; diff --git a/apps/server/src/modules/board/controller/board.controller.ts b/apps/server/src/modules/board/controller/board.controller.ts index f52d03142d7..16cee839964 100644 --- a/apps/server/src/modules/board/controller/board.controller.ts +++ b/apps/server/src/modules/board/controller/board.controller.ts @@ -12,8 +12,7 @@ import { } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { BoardUc } from '../uc'; import { BoardResponse, BoardUrlParams, ColumnResponse, RenameBodyParams } from './dto'; import { BoardContextResponse } from './dto/board/board-context.reponse'; diff --git a/apps/server/src/modules/board/controller/card.controller.ts b/apps/server/src/modules/board/controller/card.controller.ts index 38a979dbf1e..94007d9296e 100644 --- a/apps/server/src/modules/board/controller/card.controller.ts +++ b/apps/server/src/modules/board/controller/card.controller.ts @@ -14,8 +14,7 @@ import { } from '@nestjs/common'; import { ApiExtraModels, ApiOperation, ApiResponse, ApiTags, getSchemaPath } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { BoardUc, CardUc } from '../uc'; import { AnyContentElementResponse, diff --git a/apps/server/src/modules/board/controller/column.controller.ts b/apps/server/src/modules/board/controller/column.controller.ts index 57359f4cc93..fd7239e517a 100644 --- a/apps/server/src/modules/board/controller/column.controller.ts +++ b/apps/server/src/modules/board/controller/column.controller.ts @@ -12,8 +12,7 @@ import { } from '@nestjs/common'; import { ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { BoardUc } from '../uc'; import { CardResponse, ColumnUrlParams, MoveColumnBodyParams, RenameBodyParams } from './dto'; import { CardResponseMapper } from './mapper'; diff --git a/apps/server/src/modules/board/controller/element.controller.ts b/apps/server/src/modules/board/controller/element.controller.ts index 2dacd2cf539..25a3c553ef9 100644 --- a/apps/server/src/modules/board/controller/element.controller.ts +++ b/apps/server/src/modules/board/controller/element.controller.ts @@ -12,8 +12,7 @@ import { } from '@nestjs/common'; import { ApiBody, ApiExtraModels, ApiOperation, ApiResponse, ApiTags, getSchemaPath } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { CardUc } from '../uc'; import { ElementUc } from '../uc/element.uc'; import { diff --git a/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.ts b/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.ts index 8293ea7452f..ecfd50ffda2 100644 --- a/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.ts +++ b/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.ts @@ -1,8 +1,7 @@ import { ApiResponse, ApiTags } from '@nestjs/swagger'; import { Body, Controller, Param, Patch } from '@nestjs/common'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { LegacyLogger } from '@src/core/logger'; -import { ICurrentUser } from '../../authentication/interface/user'; import { CollaborativeStorageUc } from '../uc/collaborative-storage.uc'; import { TeamPermissionsBody } from './dto/team-permissions.body.params'; import { TeamRoleDto } from './dto/team-role.params'; diff --git a/apps/server/src/modules/files-storage/controller/files-storage.controller.ts b/apps/server/src/modules/files-storage/controller/files-storage.controller.ts index c1c17d685b4..a8bd1b35e9b 100644 --- a/apps/server/src/modules/files-storage/controller/files-storage.controller.ts +++ b/apps/server/src/modules/files-storage/controller/files-storage.controller.ts @@ -24,8 +24,7 @@ import { import { ApiConsumes, ApiHeader, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError, RequestLoggingInterceptor } from '@shared/common'; import { PaginationParams } from '@shared/controller'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { Request, Response } from 'express'; import { GetFileResponse } from '../interface'; import { FilesStorageMapper } from '../mapper'; diff --git a/apps/server/src/modules/fwu-learning-contents/controller/fwu-learning-contents.controller.ts b/apps/server/src/modules/fwu-learning-contents/controller/fwu-learning-contents.controller.ts index 169d9d136b6..8dedd3e5f96 100644 --- a/apps/server/src/modules/fwu-learning-contents/controller/fwu-learning-contents.controller.ts +++ b/apps/server/src/modules/fwu-learning-contents/controller/fwu-learning-contents.controller.ts @@ -10,7 +10,7 @@ import { StreamableFile, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { Authenticate } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate } from '@src/modules/authentication'; import { Request, Response } from 'express'; import { FwuLearningContentsUc } from '../uc/fwu-learning-contents.uc'; import { GetFwuLearningContentParams } from './dto/fwu-learning-contents.params'; diff --git a/apps/server/src/modules/group/controller/group.controller.ts b/apps/server/src/modules/group/controller/group.controller.ts index e810e200d85..344fbe6b98f 100644 --- a/apps/server/src/modules/group/controller/group.controller.ts +++ b/apps/server/src/modules/group/controller/group.controller.ts @@ -3,8 +3,7 @@ import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { PaginationParams } from '@shared/controller'; import { Page } from '@shared/domain'; import { ErrorResponse } from '@src/core/error/dto'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { GroupUc } from '../uc'; import { ClassInfoDto } from '../uc/dto'; import { ClassInfoSearchListResponse, ClassSortParams } from './dto'; diff --git a/apps/server/src/modules/h5p-editor/controller/h5p-editor.controller.ts b/apps/server/src/modules/h5p-editor/controller/h5p-editor.controller.ts index 9f2a15c1a1d..6161b7d7c23 100644 --- a/apps/server/src/modules/h5p-editor/controller/h5p-editor.controller.ts +++ b/apps/server/src/modules/h5p-editor/controller/h5p-editor.controller.ts @@ -1,7 +1,7 @@ import { BadRequestException, Controller, ForbiddenException, Get, InternalServerErrorException } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { Authenticate } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate } from '@src/modules/authentication'; // Dummy html response so we can test i-frame integration const dummyResponse = (title: string) => ` diff --git a/apps/server/src/modules/learnroom/controller/course.controller.ts b/apps/server/src/modules/learnroom/controller/course.controller.ts index 14ae2b595f0..7b09111edff 100644 --- a/apps/server/src/modules/learnroom/controller/course.controller.ts +++ b/apps/server/src/modules/learnroom/controller/course.controller.ts @@ -1,8 +1,7 @@ import { Controller, Get, NotFoundException, Param, Query, Res, StreamableFile } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { PaginationParams } from '@shared/controller/'; -import { ICurrentUser } from '@src/modules/authentication'; import { Response } from 'express'; import { ConfigService } from '@nestjs/config'; import { CourseUc } from '../uc/course.uc'; diff --git a/apps/server/src/modules/learnroom/controller/dashboard.controller.ts b/apps/server/src/modules/learnroom/controller/dashboard.controller.ts index dae1120be8a..da667a85f20 100644 --- a/apps/server/src/modules/learnroom/controller/dashboard.controller.ts +++ b/apps/server/src/modules/learnroom/controller/dashboard.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, Get, Param, Patch, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { DashboardMapper } from '../mapper/dashboard.mapper'; import { DashboardUc } from '../uc/dashboard.uc'; import { DashboardResponse, DashboardUrlParams, MoveElementParams, PatchGroupParams } from './dto'; diff --git a/apps/server/src/modules/learnroom/controller/rooms.controller.ts b/apps/server/src/modules/learnroom/controller/rooms.controller.ts index bf0013dbc54..e01950e7570 100644 --- a/apps/server/src/modules/learnroom/controller/rooms.controller.ts +++ b/apps/server/src/modules/learnroom/controller/rooms.controller.ts @@ -1,8 +1,7 @@ import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { RequestTimeout } from '@shared/common'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { CopyApiResponse, CopyMapper } from '@src/modules/copy-helper'; import { serverConfig } from '@src/modules/server/server.config'; import { RoomBoardResponseMapper } from '../mapper/room-board-response.mapper'; diff --git a/apps/server/src/modules/legacy-school/controller/legacy-school.controller.ts b/apps/server/src/modules/legacy-school/controller/legacy-school.controller.ts index a91d8662752..91d6892741e 100644 --- a/apps/server/src/modules/legacy-school/controller/legacy-school.controller.ts +++ b/apps/server/src/modules/legacy-school/controller/legacy-school.controller.ts @@ -6,8 +6,7 @@ import { ApiTags, ApiUnauthorizedResponse, } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { MigrationMapper } from '../mapper/migration.mapper'; import { OauthMigrationDto } from '../uc/dto/oauth-migration.dto'; import { LegacySchoolUc } from '../uc'; diff --git a/apps/server/src/modules/lesson/controller/lesson.controller.ts b/apps/server/src/modules/lesson/controller/lesson.controller.ts index 8082060cbb7..7b65eb8afc1 100644 --- a/apps/server/src/modules/lesson/controller/lesson.controller.ts +++ b/apps/server/src/modules/lesson/controller/lesson.controller.ts @@ -1,7 +1,6 @@ import { Controller, Delete, Param } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { LessonUC } from '../uc'; import { LessonUrlParams } from './dto'; diff --git a/apps/server/src/modules/news/controller/news.controller.ts b/apps/server/src/modules/news/controller/news.controller.ts index 2d2781fbef5..325a5c3f6fc 100644 --- a/apps/server/src/modules/news/controller/news.controller.ts +++ b/apps/server/src/modules/news/controller/news.controller.ts @@ -1,8 +1,7 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { PaginationParams } from '@shared/controller'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { NewsMapper } from '../mapper/news.mapper'; import { NewsUc } from '../uc/news.uc'; import { diff --git a/apps/server/src/modules/news/controller/team-news.controller.ts b/apps/server/src/modules/news/controller/team-news.controller.ts index 7fad91e88c2..29199932eaf 100644 --- a/apps/server/src/modules/news/controller/team-news.controller.ts +++ b/apps/server/src/modules/news/controller/team-news.controller.ts @@ -1,10 +1,7 @@ import { Controller, Get, Param, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; - +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; import { PaginationParams } from '@shared/controller'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; - import { NewsMapper } from '../mapper/news.mapper'; import { NewsUc } from '../uc'; import { FilterNewsParams, NewsListResponse, TeamUrlParams } from './dto'; diff --git a/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.ts b/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.ts index b603739357a..5c1a0e8e1e8 100644 --- a/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.ts +++ b/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.ts @@ -1,22 +1,22 @@ -import { Configuration } from '@hpi-schul-cloud/commons/lib'; +import { Configuration } from '@hpi-schul-cloud/commons'; import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; -import { OauthProviderLogoutFlowUc } from '@src/modules/oauth-provider/uc/oauth-provider.logout-flow.uc'; -import { OauthProviderLoginFlowUc } from '@src/modules/oauth-provider/uc/oauth-provider.login-flow.uc'; -import { OauthProviderResponseMapper } from '@src/modules/oauth-provider/mapper/oauth-provider-response.mapper'; -import { OauthProviderConsentFlowUc } from '@src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc'; +import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +// import should be @shared/infra/oauth-provider import { ProviderConsentResponse, ProviderLoginResponse, ProviderOauthClient, ProviderRedirectResponse, + ProviderConsentSessionResponse, } from '@shared/infra/oauth-provider/dto'; -import { ConsentResponse } from '@src/modules/oauth-provider/controller/dto/response/consent.response'; -import { ICurrentUser } from '@src/modules/authentication'; -import { OauthProviderClientCrudUc } from '@src/modules/oauth-provider/uc/oauth-provider.client-crud.uc'; -import { RedirectResponse } from '@src/modules/oauth-provider/controller/dto/response/redirect.response'; -import { ProviderConsentSessionResponse } from '@shared/infra/oauth-provider/dto/response/consent-session.response'; import { ApiTags } from '@nestjs/swagger'; +import { OauthProviderLogoutFlowUc } from '../uc/oauth-provider.logout-flow.uc'; +import { OauthProviderLoginFlowUc } from '../uc/oauth-provider.login-flow.uc'; +import { OauthProviderResponseMapper } from '../mapper/oauth-provider-response.mapper'; +import { OauthProviderConsentFlowUc } from '../uc/oauth-provider.consent-flow.uc'; +import { ConsentResponse } from './dto/response/consent.response'; +import { OauthProviderClientCrudUc } from '../uc/oauth-provider.client-crud.uc'; +import { RedirectResponse } from './dto/response/redirect.response'; import { OauthProviderUc } from '../uc/oauth-provider.uc'; import { AcceptQuery, diff --git a/apps/server/src/modules/oauth/controller/oauth-sso.controller.ts b/apps/server/src/modules/oauth/controller/oauth-sso.controller.ts index 3c30984cc7a..a2c1b116d6d 100644 --- a/apps/server/src/modules/oauth/controller/oauth-sso.controller.ts +++ b/apps/server/src/modules/oauth/controller/oauth-sso.controller.ts @@ -14,13 +14,12 @@ import { import { ApiOkResponse, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ISession } from '@shared/domain/types/session'; import { LegacyLogger } from '@src/core/logger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser, JWT } from '@src/modules/authentication/decorator/auth.decorator'; -import { UserMigrationResponse } from '@src/modules/oauth/controller/dto/user-migration.response'; -import { HydraOauthUc } from '@src/modules/oauth/uc/hydra-oauth.uc'; +import { ICurrentUser, Authenticate, CurrentUser, JWT } from '@src/modules/authentication'; import { OAuthMigrationError } from '@src/modules/user-login-migration/error/oauth-migration.error'; import { MigrationDto } from '@src/modules/user-login-migration/service/dto'; import { CookieOptions, Request, Response } from 'express'; +import { HydraOauthUc } from '../uc/hydra-oauth.uc'; +import { UserMigrationResponse } from './dto/user-migration.response'; import { OAuthSSOError } from '../loggable/oauth-sso.error'; import { OAuthTokenDto } from '../interface'; import { OauthLoginStateMapper } from '../mapper/oauth-login-state.mapper'; diff --git a/apps/server/src/modules/pseudonym/controller/pseudonym.controller.ts b/apps/server/src/modules/pseudonym/controller/pseudonym.controller.ts index 02aade8a446..16d55fba9db 100644 --- a/apps/server/src/modules/pseudonym/controller/pseudonym.controller.ts +++ b/apps/server/src/modules/pseudonym/controller/pseudonym.controller.ts @@ -7,8 +7,7 @@ import { ApiUnauthorizedResponse, } from '@nestjs/swagger'; import { Pseudonym } from '@shared/domain'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; -import { ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { PseudonymMapper } from '../mapper/pseudonym.mapper'; import { PseudonymUc } from '../uc'; import { PseudonymResponse } from './dto'; diff --git a/apps/server/src/modules/sharing/controller/share-token.controller.ts b/apps/server/src/modules/sharing/controller/share-token.controller.ts index bd627977aad..b4120dc02ae 100644 --- a/apps/server/src/modules/sharing/controller/share-token.controller.ts +++ b/apps/server/src/modules/sharing/controller/share-token.controller.ts @@ -11,9 +11,9 @@ import { } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError, RequestTimeout } from '@shared/common'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { CopyApiResponse, CopyMapper } from '@src/modules/copy-helper'; +// invalid import can produce dependency cycles import { serverConfig } from '@src/modules/server/server.config'; import { ShareTokenInfoResponseMapper, ShareTokenResponseMapper } from '../mapper'; import { ShareTokenUC } from '../uc'; diff --git a/apps/server/src/modules/task/controller/submission.controller.ts b/apps/server/src/modules/task/controller/submission.controller.ts index df1d08f9953..66d65261ded 100644 --- a/apps/server/src/modules/task/controller/submission.controller.ts +++ b/apps/server/src/modules/task/controller/submission.controller.ts @@ -1,7 +1,6 @@ import { Controller, Delete, Get, Param } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { SubmissionMapper } from '../mapper'; import { SubmissionUc } from '../uc'; import { SubmissionStatusListResponse, SubmissionUrlParams, TaskUrlParams } from './dto'; diff --git a/apps/server/src/modules/task/controller/task.controller.ts b/apps/server/src/modules/task/controller/task.controller.ts index b62400d0426..c438526ede1 100644 --- a/apps/server/src/modules/task/controller/task.controller.ts +++ b/apps/server/src/modules/task/controller/task.controller.ts @@ -2,9 +2,9 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestj import { ApiTags } from '@nestjs/swagger'; import { RequestTimeout } from '@shared/common'; import { PaginationParams } from '@shared/controller/'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { CopyApiResponse, CopyMapper } from '@src/modules/copy-helper'; +// invalid import can produce dependency cycles import { serverConfig } from '@src/modules/server/server.config'; import { TaskMapper } from '../mapper'; import { TaskCopyUC } from '../uc/task-copy.uc'; diff --git a/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts b/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts index 3e7d8199fa2..e42b9b59932 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts @@ -12,8 +12,7 @@ import { } from '@nestjs/swagger'; import { ValidationError } from '@shared/common'; import { LegacyLogger } from '@src/core/logger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { ContextExternalTool } from '../domain'; import { ContextExternalToolRequestMapper, ContextExternalToolResponseMapper } from '../mapper'; import { ContextExternalToolUc } from '../uc'; diff --git a/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts b/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts index c414f4423de..0b9658b1182 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts @@ -1,7 +1,6 @@ import { Controller, Get, Param } from '@nestjs/common'; import { ApiForbiddenResponse, ApiOkResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { ToolReference } from '../domain'; import { ContextExternalToolResponseMapper } from '../mapper'; import { ToolReferenceUc } from '../uc'; diff --git a/apps/server/src/modules/tool/external-tool/controller/tool-configuration.controller.ts b/apps/server/src/modules/tool/external-tool/controller/tool-configuration.controller.ts index ecdcc32f2a2..a82d7353b1e 100644 --- a/apps/server/src/modules/tool/external-tool/controller/tool-configuration.controller.ts +++ b/apps/server/src/modules/tool/external-tool/controller/tool-configuration.controller.ts @@ -7,8 +7,7 @@ import { ApiTags, ApiUnauthorizedResponse, } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { ExternalTool } from '../domain'; import { ToolConfigurationMapper } from '../mapper/tool-configuration.mapper'; import { ContextExternalToolTemplateInfo, ExternalToolConfigurationUc } from '../uc'; diff --git a/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts b/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts index 4c3658d8025..1400da6490a 100644 --- a/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts +++ b/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts @@ -14,8 +14,7 @@ import { ValidationError } from '@shared/common'; import { PaginationParams } from '@shared/controller'; import { IFindOptions, Page } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { Response } from 'express'; import { ExternalToolSearchQuery } from '../../common/interface'; import { ExternalTool } from '../domain'; diff --git a/apps/server/src/modules/tool/school-external-tool/controller/tool-school.controller.ts b/apps/server/src/modules/tool/school-external-tool/controller/tool-school.controller.ts index c34b669f75c..0977cdf478f 100644 --- a/apps/server/src/modules/tool/school-external-tool/controller/tool-school.controller.ts +++ b/apps/server/src/modules/tool/school-external-tool/controller/tool-school.controller.ts @@ -12,9 +12,8 @@ import { } from '@nestjs/swagger'; import { Body, Controller, Delete, Get, Param, Post, Query, Put, HttpCode, HttpStatus } from '@nestjs/common'; import { ValidationError } from '@shared/common'; -import { ICurrentUser } from '@src/modules/authentication'; import { LegacyLogger } from '@src/core/logger'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { SchoolExternalToolRequestMapper, SchoolExternalToolResponseMapper } from '../mapper'; import { ExternalToolSearchListResponse } from '../../external-tool/controller/dto'; import { diff --git a/apps/server/src/modules/tool/tool-launch/controller/tool-launch.controller.ts b/apps/server/src/modules/tool/tool-launch/controller/tool-launch.controller.ts index 957ec92de92..73ce027b654 100644 --- a/apps/server/src/modules/tool/tool-launch/controller/tool-launch.controller.ts +++ b/apps/server/src/modules/tool/tool-launch/controller/tool-launch.controller.ts @@ -7,8 +7,7 @@ import { ApiTags, ApiUnauthorizedResponse, } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { ToolLaunchUc } from '../uc'; import { ToolLaunchParams, ToolLaunchRequestResponse } from './dto'; import { ToolLaunchMapper } from '../mapper'; diff --git a/apps/server/src/modules/user-import/controller/import-user.controller.ts b/apps/server/src/modules/user-import/controller/import-user.controller.ts index 6b87a9f66d2..03aa2741fc2 100644 --- a/apps/server/src/modules/user-import/controller/import-user.controller.ts +++ b/apps/server/src/modules/user-import/controller/import-user.controller.ts @@ -1,11 +1,8 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; - import { PaginationParams } from '@shared/controller'; import { IFindOptions, ImportUser, User } from '@shared/domain'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; -import { ICurrentUser } from '@src/modules/authentication'; - +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { ImportUserMapper } from '../mapper/import-user.mapper'; import { UserMatchMapper } from '../mapper/user-match.mapper'; import { UserImportUc } from '../uc/user-import.uc'; diff --git a/apps/server/src/modules/user-login-migration/controller/user-login-migration.controller.ts b/apps/server/src/modules/user-login-migration/controller/user-login-migration.controller.ts index 19098167d96..d94a79b5b94 100644 --- a/apps/server/src/modules/user-login-migration/controller/user-login-migration.controller.ts +++ b/apps/server/src/modules/user-login-migration/controller/user-login-migration.controller.ts @@ -11,8 +11,7 @@ import { ApiUnprocessableEntityResponse, } from '@nestjs/swagger'; import { Page, UserLoginMigrationDO } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser, JWT } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser, JWT } from '@src/modules/authentication'; import { SchoolNumberMissingLoggableException, UserLoginMigrationAlreadyClosedLoggableException, diff --git a/apps/server/src/modules/user/controller/dto/resolved-user.response.ts b/apps/server/src/modules/user/controller/dto/resolved-user.response.ts index 31347ad0054..e61114be6bf 100644 --- a/apps/server/src/modules/user/controller/dto/resolved-user.response.ts +++ b/apps/server/src/modules/user/controller/dto/resolved-user.response.ts @@ -1,9 +1,12 @@ import { ApiProperty } from '@nestjs/swagger'; -import { IResolvedUser, IRole } from '@src/modules/authentication/interface/user'; -export type Role = IRole; +export type Role = { + name: string; -export class ResolvedUserResponse implements IResolvedUser { + id: string; +}; + +export class ResolvedUserResponse { @ApiProperty() firstName!: string; diff --git a/apps/server/src/modules/user/controller/user.controller.ts b/apps/server/src/modules/user/controller/user.controller.ts index 1cf26a90465..ffe5115bc8f 100644 --- a/apps/server/src/modules/user/controller/user.controller.ts +++ b/apps/server/src/modules/user/controller/user.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, Get, Patch } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { ResolvedUserMapper } from '../mapper'; import { UserUc } from '../uc'; import { ChangeLanguageParams, ResolvedUserResponse, SuccessfulResponse } from './dto'; diff --git a/apps/server/src/modules/user/service/user.service.ts b/apps/server/src/modules/user/service/user.service.ts index 60f62e5ee28..199e8966a61 100644 --- a/apps/server/src/modules/user/service/user.service.ts +++ b/apps/server/src/modules/user/service/user.service.ts @@ -1,13 +1,12 @@ import { ConfigService } from '@nestjs/config'; import { EntityId, IFindOptions, LanguageType, User } from '@shared/domain'; -import { RoleReference } from '@shared/domain/domainobject'; -import { Page } from '@shared/domain/domainobject/page'; -import { UserDO } from '@shared/domain/domainobject/user.do'; +import { RoleReference, Page, UserDO } from '@shared/domain/domainobject'; import { UserRepo } from '@shared/repo'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; -import { AccountService } from '@src/modules/account/services/account.service'; +import { AccountService } from '@src/modules/account'; import { AccountDto } from '@src/modules/account/services/dto'; import { ICurrentUser } from '@src/modules/authentication'; +// invalid import import { CurrentUserMapper } from '@src/modules/authentication/mapper'; import { RoleDto } from '@src/modules/role/service/dto/role.dto'; import { RoleService } from '@src/modules/role/service/role.service'; @@ -40,6 +39,7 @@ export class UserService { async getUser(id: string): Promise { const userEntity = await this.userRepo.findById(id, true); const userDto = UserMapper.mapFromEntityToDto(userEntity); + return userDto; } @@ -48,36 +48,43 @@ export class UserService { const account: AccountDto = await this.accountService.findByUserIdOrFail(userId); const resolvedUser: ICurrentUser = CurrentUserMapper.userToICurrentUser(account.id, user, account.systemId); + return resolvedUser; } async findById(id: string): Promise { const userDO = await this.userDORepo.findById(id, true); + return userDO; } async save(user: UserDO): Promise { const savedUser: Promise = this.userDORepo.save(user); + return savedUser; } async saveAll(users: UserDO[]): Promise { const savedUsers: Promise = this.userDORepo.saveAll(users); + return savedUsers; } async findUsers(query: UserQuery, options?: IFindOptions): Promise> { const users: Page = await this.userDORepo.find(query, options); + return users; } async findByExternalId(externalId: string, systemId: EntityId): Promise { const user: Promise = this.userDORepo.findByExternalId(externalId, systemId); + return user; } async findByEmail(email: string): Promise { const user: Promise = this.userRepo.findByEmail(email); + return user; } diff --git a/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.ts b/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.ts index 79c22cc21e1..1f6657c28b2 100644 --- a/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.ts +++ b/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.ts @@ -11,8 +11,7 @@ import { } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { VideoConferenceScope } from '@shared/domain/interface'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; -import { ICurrentUser } from '@src/modules/authentication/interface'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { BBBBaseResponse } from '../bbb'; import { defaultVideoConferenceOptions } from '../interface'; import { VideoConferenceResponseDeprecatedMapper } from '../mapper/vc-deprecated-response.mapper'; diff --git a/apps/server/src/modules/video-conference/controller/video-conference.controller.ts b/apps/server/src/modules/video-conference/controller/video-conference.controller.ts index 884be5512cb..76b8a5c53ad 100644 --- a/apps/server/src/modules/video-conference/controller/video-conference.controller.ts +++ b/apps/server/src/modules/video-conference/controller/video-conference.controller.ts @@ -1,7 +1,6 @@ import { Body, Controller, Get, HttpStatus, Param, Put, Req } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; +import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; import { Request } from 'express'; import { InvalidOriginForLogoutUrlLoggableException } from '../error'; import { VideoConferenceOptions } from '../interface'; diff --git a/apps/server/src/shared/common/interceptor/request-logging.interceptor.ts b/apps/server/src/shared/common/interceptor/request-logging.interceptor.ts index bfcb738847a..4c9a7bf7135 100644 --- a/apps/server/src/shared/common/interceptor/request-logging.interceptor.ts +++ b/apps/server/src/shared/common/interceptor/request-logging.interceptor.ts @@ -3,7 +3,7 @@ import { LegacyLogger, RequestLoggingBody } from '@src/core/logger'; import { Request } from 'express'; import { Observable, throwError } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; -import { ICurrentUser } from '@src/modules/authentication/interface/user'; +import { ICurrentUser } from '@src/modules/authentication'; @Injectable() export class RequestLoggingInterceptor implements NestInterceptor { From fc68ac096e56f3471cbcef798b954a4d9d9294c4 Mon Sep 17 00:00:00 2001 From: ezzato Date: Fri, 20 Oct 2023 14:11:07 +0200 Subject: [PATCH 07/14] BC-2804 remap imports modules folder (#4468) * update tsconfig, remove index file * split imports from bundles import @module * update jest config to support @modules path * mass change imports --- .../src/apps/files-storage-consumer.app.ts | 2 +- apps/server/src/apps/files-storage.app.ts | 2 +- .../src/apps/fwu-learning-contents.app.ts | 2 +- apps/server/src/apps/h5p-editor.app.ts | 2 +- apps/server/src/apps/management.app.ts | 2 +- apps/server/src/apps/server.app.ts | 18 ++++---- .../api-test/test-bootstrap.console.ts | 2 +- apps/server/src/console/console.module.ts | 10 ++-- .../account/controller/account.controller.ts | 3 +- .../controller/api-test/account.api.spec.ts | 4 +- .../mapper/account-response.mapper.spec.ts | 2 +- .../account/mapper/account-response.mapper.ts | 2 +- .../services/account-db.service.spec.ts | 6 +-- .../account-idm.service.integration.spec.ts | 2 +- .../services/account-lookup.service.ts | 2 +- .../account/services/account.service.spec.ts | 2 +- .../src/modules/account/uc/account.uc.spec.ts | 8 ++-- .../src/modules/account/uc/account.uc.ts | 6 +-- .../authentication/authentication.module.ts | 8 ++-- .../controllers/api-test/login.api.spec.ts | 6 +-- .../decorator/auth.decorator.spec.ts | 4 +- .../services/authentication.service.spec.ts | 6 +-- .../services/authentication.service.ts | 6 +-- .../strategy/ldap.strategy.spec.ts | 2 +- .../authentication/strategy/ldap.strategy.ts | 2 +- .../strategy/local.strategy.spec.ts | 6 +-- .../authentication/strategy/local.strategy.ts | 2 +- .../strategy/oauth2.strategy.spec.ts | 8 ++-- .../strategy/oauth2.strategy.ts | 8 ++-- .../authorization-reference.module.ts | 4 +- .../rules/context-external-tool.rule.spec.ts | 8 ++-- .../rules/context-external-tool.rule.ts | 4 +- .../rules/school-external-tool.rule.spec.ts | 4 +- .../domain/rules/school-external-tool.rule.ts | 4 +- .../domain/service/reference.loader.spec.ts | 4 +- .../domain/service/reference.loader.ts | 4 +- .../src/modules/board/board-api.module.ts | 2 +- .../api-test/board-context.api.spec.ts | 2 +- .../api-test/board-delete.api.spec.ts | 6 +-- .../api-test/board-lookup.api.spec.ts | 6 +-- .../api-test/board-update-title.api.spec.ts | 2 +- .../api-test/card-create.api.spec.ts | 6 +-- .../api-test/card-delete.api.spec.ts | 6 +-- .../api-test/card-lookup.api.spec.ts | 6 +-- .../controller/api-test/card-move.api.spec.ts | 6 +-- .../api-test/card-update-height.api.spec.ts | 2 +- .../api-test/card-update-title.api.spec.ts | 2 +- .../api-test/column-create.api.spec.ts | 6 +-- .../api-test/column-delete.api.spec.ts | 6 +-- .../api-test/column-move.api.spec.ts | 6 +-- .../api-test/column-update-title.api.spec.ts | 2 +- .../content-element-create.api.spec.ts | 2 +- .../content-element-delete.api.spec.ts | 6 +-- .../api-test/content-element-move.api.spec.ts | 6 +-- .../content-element-update-content.spec.ts | 2 +- .../submission-item-create.api.spec.ts | 2 +- .../submission-item-lookup.api.spec.ts | 2 +- .../submission-item-update.api.spec.ts | 2 +- .../controller/board-submission.controller.ts | 2 +- .../board/controller/board.controller.ts | 2 +- .../board/controller/card.controller.ts | 2 +- .../board/controller/column.controller.ts | 2 +- .../board/controller/element.controller.ts | 2 +- .../modules/board/repo/board-do.repo.spec.ts | 2 +- .../repo/recursive-delete.visitor.spec.ts | 2 +- .../board/repo/recursive-delete.vistor.ts | 2 +- .../board/repo/recursive-save.visitor.ts | 2 +- .../service/board-do-authorizable.service.ts | 2 +- .../board-do-copy.service.spec.ts | 2 +- .../board-do-copy.service.ts | 2 +- .../recursive-copy.visitor.ts | 2 +- ...specific-file-copy-service.factory.spec.ts | 4 +- ...hool-specific-file-copy-service.factory.ts | 2 +- .../school-specific-file-copy.interface.ts | 4 +- .../school-specific-file-copy.service.ts | 4 +- .../service/column-board-copy.service.spec.ts | 4 +- .../service/column-board-copy.service.ts | 4 +- .../board/uc/board-management.uc.spec.ts | 2 +- .../src/modules/board/uc/board.uc.spec.ts | 2 +- apps/server/src/modules/board/uc/board.uc.ts | 4 +- .../src/modules/board/uc/card.uc.spec.ts | 2 +- apps/server/src/modules/board/uc/card.uc.ts | 2 +- .../src/modules/board/uc/element.uc.spec.ts | 2 +- .../server/src/modules/board/uc/element.uc.ts | 2 +- .../board/uc/submission-item.uc.spec.ts | 2 +- .../modules/board/uc/submission-item.uc.ts | 2 +- .../class/entity/testing/class.entity.spec.ts | 2 +- .../testing/factory/class.entity.factory.ts | 2 +- .../modules/class/repo/classes.repo.spec.ts | 2 +- .../class/service/class.service.spec.ts | 2 +- .../collaborative-storage.module.ts | 10 ++-- .../collaborative-storage.controller.spec.ts | 6 +-- .../collaborative-storage.controller.ts | 2 +- .../mapper/team-permissions.mapper.spec.ts | 4 +- .../mapper/team.mapper.spec.ts | 2 +- .../collaborative-storage.service.spec.ts | 10 ++-- .../services/collaborative-storage.service.ts | 4 +- .../uc/collaborative-storage.uc.spec.ts | 12 ++--- .../uc/collaborative-storage.uc.ts | 6 +-- .../modules/copy-helper/dto/copy.response.ts | 2 +- .../copy-helper/mapper/copy.mapper.spec.ts | 10 ++-- .../modules/copy-helper/mapper/copy.mapper.ts | 8 ++-- .../files-storage-client.module.ts | 2 +- .../service/copy-files.service.spec.ts | 2 +- .../service/copy-files.service.ts | 2 +- .../api-test/files-security.api.spec.ts | 8 ++-- .../files-storage-copy-files.api.spec.ts | 8 ++-- .../files-storage-delete-files.api.spec.ts | 8 ++-- .../files-storage-download-upload.api.spec.ts | 8 ++-- .../files-storage-list-files.api.spec.ts | 8 ++-- .../files-storage-preview.api.spec.ts | 8 ++-- .../files-storage-rename-file.api.spec.ts | 8 ++-- .../files-storage-restore-files.api.spec.ts | 8 ++-- .../controller/files-storage.controller.ts | 2 +- .../files-storage/files-storage-api.module.ts | 4 +- .../files-storage-test.module.ts | 4 +- .../mapper/files-storage.mapper.spec.ts | 2 +- .../mapper/files-storage.mapper.ts | 2 +- .../uc/files-storage-copy.uc.spec.ts | 4 +- .../uc/files-storage-delete.uc.spec.ts | 2 +- .../files-storage-download-preview.uc.spec.ts | 2 +- .../uc/files-storage-download.uc.spec.ts | 2 +- .../uc/files-storage-get.uc.spec.ts | 2 +- .../uc/files-storage-restore.uc.spec.ts | 2 +- .../uc/files-storage-update.uc.spec.ts | 2 +- .../uc/files-storage-upload.uc.spec.ts | 4 +- .../files-storage/uc/files-storage.uc.ts | 4 +- .../modules/files/entity/file.entity.spec.ts | 2 +- .../fwu-learning-contents.api.spec.ts | 2 +- .../fwu-learning-contents.controller.ts | 2 +- .../fwu-learning-contents-test.module.ts | 4 +- .../fwu-learning-contents.module.ts | 2 +- .../controller/api-test/group.api.spec.ts | 6 +-- .../group/controller/group.controller.ts | 2 +- .../src/modules/group/group-api.module.ts | 12 ++--- .../modules/group/service/group.service.ts | 2 +- .../group/uc/dto/resolved-group-user.ts | 2 +- .../src/modules/group/uc/group.uc.spec.ts | 18 ++++---- apps/server/src/modules/group/uc/group.uc.ts | 16 +++---- .../group/uc/mapper/group-uc.mapper.ts | 4 +- .../api-test/h5p-editor.api.spec.ts | 2 +- .../controller/h5p-editor.controller.ts | 2 +- .../h5p-editor/h5p-editor-test.module.ts | 4 +- .../modules/h5p-editor/h5p-editor.module.ts | 2 +- apps/server/src/modules/index.ts | 25 ---------- .../controller/api-test/course.api.spec.ts | 4 +- .../controller/api-test/dashboard.api.spec.ts | 8 ++-- .../api-test/rooms-copy-timeout.api.spec.ts | 8 ++-- .../controller/api-test/rooms.api.spec.ts | 12 ++--- .../learnroom/controller/course.controller.ts | 2 +- .../controller/dashboard.controller.spec.ts | 2 +- .../controller/dashboard.controller.ts | 2 +- .../board-element.response.ts | 2 +- .../controller/rooms.controller.spec.ts | 4 +- .../learnroom/controller/rooms.controller.ts | 6 +-- .../modules/learnroom/learnroom-api.module.ts | 8 ++-- .../src/modules/learnroom/learnroom.module.ts | 8 ++-- .../service/board-copy.service.spec.ts | 8 ++-- .../learnroom/service/board-copy.service.ts | 10 ++-- .../column-board-target.service.spec.ts | 2 +- .../service/column-board-target.service.ts | 2 +- .../common-cartridge-export.service.spec.ts | 8 ++-- .../common-cartridge-export.service.ts | 4 +- .../service/course-copy.service.spec.ts | 4 +- .../learnroom/service/course-copy.service.ts | 2 +- .../learnroom/service/rooms.service.spec.ts | 4 +- .../learnroom/service/rooms.service.ts | 4 +- .../learnroom/uc/course-copy.uc.spec.ts | 6 +-- .../modules/learnroom/uc/course-copy.uc.ts | 6 +-- .../learnroom/uc/course-export.uc.spec.ts | 4 +- .../modules/learnroom/uc/course-export.uc.ts | 4 +- .../learnroom/uc/lesson-copy.uc.spec.ts | 6 +-- .../modules/learnroom/uc/lesson-copy.uc.ts | 8 ++-- .../uc/room-board-dto.factory.spec.ts | 2 +- .../learnroom/uc/room-board-dto.factory.ts | 2 +- .../legacy-school.controller.spec.ts | 2 +- .../controller/legacy-school.controller.ts | 2 +- .../legacy-school/legacy-school-api.module.ts | 4 +- .../legacy-school/uc/legacy-school.uc.spec.ts | 8 ++-- .../legacy-school/uc/legacy-school.uc.ts | 4 +- .../api-test/lesson-delete.api.spec.ts | 4 +- .../lesson/controller/lesson.controller.ts | 2 +- .../src/modules/lesson/lesson-api.module.ts | 2 +- .../src/modules/lesson/lesson.module.ts | 6 +-- .../service/lesson-copy.service.spec.ts | 6 +-- .../lesson/service/lesson-copy.service.ts | 14 ++---- .../lesson/service/lesson.service.spec.ts | 2 +- .../modules/lesson/service/lesson.service.ts | 2 +- .../src/modules/lesson/uc/lesson.uc.spec.ts | 2 +- .../server/src/modules/lesson/uc/lesson.uc.ts | 2 +- .../api-test/database-management.api.spec.ts | 2 +- .../modules/management/management.module.ts | 2 +- .../news/controller/api-test/news.api.spec.ts | 8 ++-- .../news/controller/news.controller.ts | 2 +- .../news/controller/team-news.controller.ts | 2 +- apps/server/src/modules/news/news.module.ts | 2 +- .../src/modules/news/uc/news.uc.spec.ts | 2 +- apps/server/src/modules/news/uc/news.uc.ts | 2 +- .../dto/request/oauth-client.body.ts | 4 +- .../dto/response/consent.response.ts | 4 +- .../controller/dto/response/login.response.ts | 4 +- .../oauth-provider.controller.spec.ts | 12 ++--- .../controller/oauth-provider.controller.ts | 2 +- .../mapper/oauth-provider-request.mapper.ts | 2 +- .../oauth-provider-response.mapper.spec.ts | 4 +- .../mapper/oauth-provider-response.mapper.ts | 2 +- .../oauth-provider-api.module.ts | 6 +-- .../oauth-provider/oauth-provider.module.ts | 10 ++-- .../service/id-token.service.spec.ts | 12 ++--- .../service/id-token.service.ts | 6 +-- .../oauth-provider.login-flow.service.spec.ts | 8 ++-- .../oauth-provider.login-flow.service.ts | 8 ++-- .../uc/oauth-provider.client-crud.uc.spec.ts | 4 +- .../uc/oauth-provider.client-crud.uc.ts | 4 +- .../uc/oauth-provider.consent-flow.uc.spec.ts | 10 ++-- .../uc/oauth-provider.consent-flow.uc.ts | 8 ++-- .../uc/oauth-provider.login-flow.uc.spec.ts | 8 ++-- .../uc/oauth-provider.login-flow.uc.ts | 12 ++--- .../uc/oauth-provider.logout-flow.uc.spec.ts | 2 +- .../uc/oauth-provider.uc.spec.ts | 2 +- .../controller/api-test/oauth-sso.api.spec.ts | 8 ++-- .../controller/oauth-sso.controller.spec.ts | 4 +- .../oauth/controller/oauth-sso.controller.ts | 6 +-- .../oauth/mapper/user-migration.mapper.ts | 2 +- .../src/modules/oauth/oauth-api.module.ts | 14 +++--- apps/server/src/modules/oauth/oauth.module.ts | 12 ++--- .../oauth/service/dto/hydra.redirect.dto.ts | 2 +- .../oauth/service/hydra.service.spec.ts | 6 +-- .../modules/oauth/service/hydra.service.ts | 6 +-- .../oauth/service/oauth.service.spec.ts | 16 +++---- .../modules/oauth/service/oauth.service.ts | 14 +++--- .../modules/oauth/uc/hydra-oauth.uc.spec.ts | 6 +-- .../src/modules/oauth/uc/hydra-oauth.uc.ts | 2 +- .../src/modules/oauth/uc/oauth.uc.spec.ts | 26 +++++------ apps/server/src/modules/oauth/uc/oauth.uc.ts | 20 ++++---- .../provisioning/dto/external-group.dto.ts | 2 +- .../provisioning-system-input.mapper.spec.ts | 2 +- .../provisioning-system-input.mapper.ts | 2 +- .../provisioning/provisioning.module.ts | 12 ++--- .../service/provisioning.service.spec.ts | 4 +- .../service/provisioning.service.ts | 4 +- .../strategy/iserv/iserv.strategy.spec.ts | 6 +-- .../strategy/iserv/iserv.strategy.ts | 6 +-- .../oidc-mock/oidc-mock.strategy.spec.ts | 2 +- .../strategy/oidc-mock/oidc-mock.strategy.ts | 2 +- .../service/oidc-provisioning.service.spec.ts | 14 +++--- .../oidc/service/oidc-provisioning.service.ts | 16 +++---- .../sanis/sanis-response.mapper.spec.ts | 2 +- .../strategy/sanis/sanis-response.mapper.ts | 2 +- .../strategy/sanis/sanis.strategy.spec.ts | 2 +- .../controller/api-test/pseudonym.api.spec.ts | 4 +- .../controller/pseudonym.controller.ts | 2 +- .../modules/pseudonym/pseudonym-api.module.ts | 4 +- .../src/modules/pseudonym/pseudonym.module.ts | 8 ++-- .../service/feathers-roster.service.spec.ts | 18 ++++---- .../service/feathers-roster.service.ts | 18 ++++---- .../service/pseudonym.service.spec.ts | 2 +- .../pseudonym/service/pseudonym.service.ts | 2 +- .../modules/pseudonym/uc/pseudonym.uc.spec.ts | 4 +- .../src/modules/pseudonym/uc/pseudonym.uc.ts | 4 +- .../modules/role/mapper/role.mapper.spec.ts | 4 +- .../src/modules/role/mapper/role.mapper.ts | 2 +- apps/server/src/modules/role/role.module.ts | 4 +- .../src/modules/role/uc/role.uc.spec.ts | 6 +-- apps/server/src/modules/role/uc/role.uc.ts | 4 +- .../controller/api-test/server.api.spec.ts | 2 +- .../src/modules/server/server.config.ts | 6 ++- .../src/modules/server/server.module.ts | 46 +++++++++---------- .../api-test/sharing-create-token.api.spec.ts | 6 +-- .../api-test/sharing-import-token.api.spec.ts | 8 ++-- .../api-test/sharing-lookup-token.api.spec.ts | 2 +- .../controller/share-token.controller.spec.ts | 4 +- .../controller/share-token.controller.ts | 6 +-- .../mapper/context-type.mapper.spec.ts | 2 +- .../sharing/mapper/context-type.mapper.ts | 2 +- .../sharing/mapper/parent-type.mapper.spec.ts | 2 +- .../sharing/mapper/parent-type.mapper.ts | 2 +- .../service/share-token.service.spec.ts | 6 +-- .../sharing/service/share-token.service.ts | 6 +-- .../src/modules/sharing/sharing.module.ts | 4 +- .../modules/sharing/uc/share-token.uc.spec.ts | 14 +++--- .../src/modules/sharing/uc/share-token.uc.ts | 14 +++--- .../controller/api-test/system.api.spec.ts | 6 +-- .../controller/dto/public-system-response.ts | 2 +- .../mapper/system-response.mapper.ts | 6 +-- .../system/controller/system.controller.ts | 2 +- .../system/mapper/system-oidc.mapper.spec.ts | 2 +- .../system/mapper/system-oidc.mapper.ts | 2 +- .../system/mapper/system.mapper.spec.ts | 2 +- .../modules/system/mapper/system.mapper.ts | 4 +- .../modules/system/service/dto/system.dto.ts | 2 +- .../system/service/system-oidc.service.ts | 2 +- .../modules/system/service/system.service.ts | 4 +- .../src/modules/system/system-api.module.ts | 4 +- .../src/modules/system/system.module.ts | 2 +- .../src/modules/system/uc/system.uc.spec.ts | 8 ++-- .../server/src/modules/system/uc/system.uc.ts | 4 +- .../api-test/submission.api.spec.ts | 10 ++-- .../api-test/task-copy-timeout.api.spec.ts | 6 +-- .../api-test/task-delete.api.spec.ts | 4 +- .../api-test/task-finish.api.spec.ts | 2 +- .../api-test/task-finished.api.spec.ts | 8 ++-- .../api-test/task-restore.api.spec.ts | 2 +- .../task-revert-published.api.spec.ts | 2 +- .../task/controller/api-test/task.api.spec.ts | 4 +- .../task/controller/submission.controller.ts | 2 +- .../task/controller/task.controller.spec.ts | 6 +-- .../task/controller/task.controller.ts | 6 +-- .../task/service/submission.service.spec.ts | 2 +- .../task/service/submission.service.ts | 2 +- .../task/service/task-copy.service.spec.ts | 4 +- .../modules/task/service/task-copy.service.ts | 6 +-- .../modules/task/service/task.service.spec.ts | 2 +- .../src/modules/task/service/task.service.ts | 2 +- .../src/modules/task/task-api.module.ts | 4 +- apps/server/src/modules/task/task.module.ts | 6 +-- .../src/modules/task/uc/submission.uc.spec.ts | 2 +- .../src/modules/task/uc/submission.uc.ts | 2 +- .../src/modules/task/uc/task-copy.uc.spec.ts | 6 +-- .../src/modules/task/uc/task-copy.uc.ts | 4 +- .../src/modules/task/uc/task.uc.spec.ts | 2 +- apps/server/src/modules/task/uc/task.uc.ts | 2 +- .../src/modules/teams/teams-api.module.ts | 2 +- .../modules/tool/common/common-tool.module.ts | 6 +-- .../common/mapper/context-type.mapper.spec.ts | 2 +- .../tool/common/mapper/context-type.mapper.ts | 2 +- .../tool/common/uc/tool-permission-helper.ts | 6 +-- .../common/uc/tool-permissions-helper.spec.ts | 6 +-- .../api-test/tool-context.api.spec.ts | 2 +- .../api-test/tool-reference.api.spec.ts | 2 +- .../controller/tool-context.controller.ts | 2 +- .../controller/tool-reference.controller.ts | 2 +- ...text-external-tool-authorizable.service.ts | 2 +- .../context-external-tool.service.spec.ts | 2 +- .../uc/context-external-tool.uc.spec.ts | 2 +- .../uc/context-external-tool.uc.ts | 2 +- .../uc/tool-reference.uc.spec.ts | 2 +- .../uc/tool-reference.uc.ts | 2 +- .../api-test/tool-configuration.api.spec.ts | 4 +- .../controller/api-test/tool.api.spec.ts | 2 +- .../tool-configuration.controller.ts | 2 +- .../controller/tool.controller.ts | 2 +- .../uc/external-tool-configuration.uc.spec.ts | 2 +- .../uc/external-tool-configuration.uc.ts | 2 +- .../external-tool/uc/external-tool.uc.spec.ts | 4 +- .../tool/external-tool/uc/external-tool.uc.ts | 2 +- .../api-test/tool-school.api.spec.ts | 2 +- .../controller/tool-school.controller.ts | 2 +- .../uc/school-external-tool.uc.spec.ts | 2 +- .../uc/school-external-tool.uc.ts | 2 +- .../src/modules/tool/tool-api.module.ts | 6 +-- .../tool-launch.controller.api.spec.ts | 2 +- .../controller/tool-launch.controller.ts | 2 +- .../strategy/abstract-launch.strategy.spec.ts | 4 +- .../strategy/abstract-launch.strategy.ts | 4 +- .../basic-tool-launch.strategy.spec.ts | 4 +- .../lti11-tool-launch.strategy.spec.ts | 8 ++-- .../strategy/lti11-tool-launch.strategy.ts | 8 ++-- .../oauth2-tool-launch.strategy.spec.ts | 4 +- .../tool/tool-launch/tool-launch.module.ts | 8 ++-- .../tool/tool-launch/uc/tool-launch.uc.ts | 2 +- .../api-test/import-user.api.spec.ts | 8 ++-- .../controller/import-user.controller.spec.ts | 6 +-- .../controller/import-user.controller.ts | 2 +- .../user-import/uc/user-import.uc.spec.ts | 6 +-- .../modules/user-import/uc/user-import.uc.ts | 8 ++-- .../modules/user-import/user-import.module.ts | 2 +- .../api-test/user-login-migration.api.spec.ts | 6 +-- .../user-login-migration.controller.ts | 2 +- .../error/oauth-migration.error.ts | 2 +- .../service/migration-check.service.spec.ts | 4 +- .../service/migration-check.service.ts | 4 +- .../service/school-migration.service.spec.ts | 8 ++-- .../service/school-migration.service.ts | 4 +- ...ser-login-migration-revert.service.spec.ts | 2 +- .../user-login-migration-revert.service.ts | 2 +- .../user-login-migration.service.spec.ts | 8 ++-- .../service/user-login-migration.service.ts | 6 +-- .../service/user-migration.service.spec.ts | 14 +++--- .../service/user-migration.service.ts | 10 ++-- .../uc/close-user-login-migration.uc.spec.ts | 2 +- .../uc/close-user-login-migration.uc.ts | 2 +- .../restart-user-login-migration.uc.spec.ts | 4 +- .../uc/restart-user-login-migration.uc.ts | 4 +- .../uc/start-user-login-migration.uc.spec.ts | 4 +- .../uc/start-user-login-migration.uc.ts | 4 +- .../uc/toggle-user-login-migration.uc.spec.ts | 4 +- .../uc/toggle-user-login-migration.uc.ts | 4 +- .../uc/user-login-migration.uc.spec.ts | 14 +++--- .../uc/user-login-migration.uc.ts | 12 ++--- .../user-login-migration-api.module.ts | 10 ++-- .../user-login-migration.module.ts | 8 ++-- .../api-test/user-language.api.spec.ts | 6 +-- .../controller/api-test/user-me.api.spec.ts | 8 ++-- .../user/controller/user.controller.ts | 2 +- .../modules/user/mapper/user.mapper.spec.ts | 4 +- .../src/modules/user/mapper/user.mapper.ts | 2 +- .../modules/user/service/user.service.spec.ts | 12 ++--- .../src/modules/user/service/user.service.ts | 12 ++--- .../src/modules/user/uc/user.uc.spec.ts | 2 +- apps/server/src/modules/user/user.module.ts | 6 +-- .../api-test/video-conference.api.spec.ts | 2 +- ...o-conference-deprecated.controller.spec.ts | 2 +- .../video-conference-deprecated.controller.ts | 2 +- .../controller/video-conference.controller.ts | 2 +- .../service/video-conference.service.spec.ts | 8 ++-- .../service/video-conference.service.ts | 8 ++-- .../uc/video-conference-create.uc.spec.ts | 2 +- .../uc/video-conference-create.uc.ts | 2 +- .../uc/video-conference-deprecated.uc.spec.ts | 9 ++-- .../uc/video-conference-deprecated.uc.ts | 12 ++--- .../uc/video-conference-end.uc.spec.ts | 2 +- .../uc/video-conference-end.uc.ts | 4 +- .../uc/video-conference-info.uc.spec.ts | 2 +- .../uc/video-conference-info.uc.ts | 4 +- .../uc/video-conference-join.uc.spec.ts | 2 +- .../uc/video-conference-join.uc.ts | 4 +- .../video-conference-api.module.ts | 4 +- .../video-conference.module.ts | 8 ++-- .../request-logging.interceptor.ts | 2 +- .../src/shared/controller/swagger.spec.ts | 2 +- .../src/shared/domain/entity/all-entities.ts | 14 +++--- .../external-tool-element-node.entity.ts | 2 +- .../src/shared/domain/entity/course.entity.ts | 4 +- .../infra/antivirus/antivirus.service.ts | 2 +- .../collaborative-storage-adapter.module.ts | 6 +-- .../collaborative-storage.adapter.spec.ts | 2 +- .../collaborative-storage.adapter.ts | 6 +-- .../collaborative-storage-adapter.mapper.ts | 6 +-- .../strategy/base.interface.strategy.ts | 2 +- .../nextcloud/nextcloud.strategy.spec.ts | 8 ++-- .../strategy/nextcloud/nextcloud.strategy.ts | 10 ++-- .../identity-management-oauth.service.ts | 2 +- .../keycloak-configuration.controller.spec.ts | 2 +- .../keycloak-configuration.module.ts | 3 +- .../mapper/identity-provider.mapper.spec.ts | 2 +- .../mapper/identity-provider.mapper.ts | 2 +- ...-configuration.service.integration.spec.ts | 2 +- .../keycloak-configuration.service.spec.ts | 4 +- .../service/keycloak-configuration.service.ts | 6 +-- .../keycloak-migration.service.spec.ts | 4 +- .../service/keycloak-migration.service.ts | 4 +- ...cloak-identity-management-oauth.service.ts | 2 +- ...ity-management.service.integration.spec.ts | 2 +- .../request/accept-consent-request.body.ts | 2 +- ...ext-external-tool.repo.integration.spec.ts | 12 ++--- .../context-external-tool.repo.ts | 12 ++--- .../context-external-tool.scope.spec.ts | 4 +- .../context-external-tool.scope.ts | 4 +- .../external-tool-sorting.mapper.spec.ts | 4 +- .../external-tool-sorting.mapper.ts | 4 +- .../external-tool.repo.integration.spec.ts | 15 ++---- .../externaltool/external-tool.repo.mapper.ts | 15 ++---- .../repo/externaltool/external-tool.repo.ts | 8 ++-- .../repo/externaltool/external-tool.scope.ts | 2 +- ...ool-external-tool.repo.integration.spec.ts | 10 ++-- .../school-external-tool.repo.ts | 8 ++-- .../school-external-tool.scope.ts | 2 +- .../user/user-do.repo.integration.spec.ts | 2 +- .../src/shared/repo/user/user-do.repo.ts | 2 +- .../testing/factory/account-dto.factory.ts | 2 +- .../context-external-tool-entity.factory.ts | 4 +- .../domainobject/groups/group.factory.ts | 2 +- .../tool/context-external-tool.factory.ts | 6 +-- .../tool/external-tool.factory.ts | 6 +-- .../tool/school-external-tool.factory.ts | 4 +- .../factory/external-group-dto.factory.ts | 4 +- .../factory/external-tool-entity.factory.ts | 4 +- .../external-tool-pseudonym.factory.ts | 2 +- .../testing/factory/filerecord.factory.ts | 2 +- .../testing/factory/group-entity.factory.ts | 2 +- .../testing/factory/pseudonym.factory.ts | 2 +- .../testing/factory/role-dto.factory.ts | 2 +- .../school-external-tool-entity.factory.ts | 2 +- .../testing/factory/share-token.do.factory.ts | 2 +- .../testing/map-user-to-current-user.ts | 2 +- jest.config.ts | 1 + tsconfig.json | 3 +- 478 files changed, 1121 insertions(+), 1155 deletions(-) delete mode 100644 apps/server/src/modules/index.ts diff --git a/apps/server/src/apps/files-storage-consumer.app.ts b/apps/server/src/apps/files-storage-consumer.app.ts index 777176e1176..a18b5f4604b 100644 --- a/apps/server/src/apps/files-storage-consumer.app.ts +++ b/apps/server/src/apps/files-storage-consumer.app.ts @@ -3,7 +3,7 @@ import { NestFactory } from '@nestjs/core'; // register source-map-support for debugging -import { FilesStorageAMQPModule } from '@src/modules/files-storage'; +import { FilesStorageAMQPModule } from '@modules/files-storage'; import { install as sourceMapInstall } from 'source-map-support'; async function bootstrap() { diff --git a/apps/server/src/apps/files-storage.app.ts b/apps/server/src/apps/files-storage.app.ts index e10a2b3a162..2d2f9343ac2 100644 --- a/apps/server/src/apps/files-storage.app.ts +++ b/apps/server/src/apps/files-storage.app.ts @@ -10,7 +10,7 @@ import { install as sourceMapInstall } from 'source-map-support'; // application imports import { SwaggerDocumentOptions } from '@nestjs/swagger'; import { LegacyLogger } from '@src/core/logger'; -import { API_VERSION_PATH, FilesStorageApiModule } from '@src/modules/files-storage'; +import { API_VERSION_PATH, FilesStorageApiModule } from '@modules/files-storage'; import { enableOpenApiDocs } from '@src/shared/controller/swagger'; async function bootstrap() { diff --git a/apps/server/src/apps/fwu-learning-contents.app.ts b/apps/server/src/apps/fwu-learning-contents.app.ts index b7f2e9f1e25..15279392f12 100644 --- a/apps/server/src/apps/fwu-learning-contents.app.ts +++ b/apps/server/src/apps/fwu-learning-contents.app.ts @@ -9,7 +9,7 @@ import { install as sourceMapInstall } from 'source-map-support'; // application imports import { LegacyLogger } from '@src/core/logger'; -import { FwuLearningContentsModule } from '@src/modules/fwu-learning-contents'; +import { FwuLearningContentsModule } from '@modules/fwu-learning-contents'; import { enableOpenApiDocs } from '@src/shared/controller/swagger'; async function bootstrap() { diff --git a/apps/server/src/apps/h5p-editor.app.ts b/apps/server/src/apps/h5p-editor.app.ts index fcd550c34fa..518eb5c45bc 100644 --- a/apps/server/src/apps/h5p-editor.app.ts +++ b/apps/server/src/apps/h5p-editor.app.ts @@ -9,7 +9,7 @@ import { install as sourceMapInstall } from 'source-map-support'; // application imports import { LegacyLogger } from '@src/core/logger'; -import { H5PEditorModule } from '@src/modules/h5p-editor'; +import { H5PEditorModule } from '@modules/h5p-editor'; import { enableOpenApiDocs } from '@src/shared/controller/swagger'; async function bootstrap() { diff --git a/apps/server/src/apps/management.app.ts b/apps/server/src/apps/management.app.ts index 8be7cfdd866..dd66443300a 100644 --- a/apps/server/src/apps/management.app.ts +++ b/apps/server/src/apps/management.app.ts @@ -9,7 +9,7 @@ import { install as sourceMapInstall } from 'source-map-support'; // application imports import { LegacyLogger } from '@src/core/logger'; -import { ManagementServerModule } from '@src/modules/management'; +import { ManagementServerModule } from '@modules/management'; import { enableOpenApiDocs } from '@src/shared/controller/swagger'; async function bootstrap() { diff --git a/apps/server/src/apps/server.app.ts b/apps/server/src/apps/server.app.ts index c486adc1915..bd235d5261f 100644 --- a/apps/server/src/apps/server.app.ts +++ b/apps/server/src/apps/server.app.ts @@ -7,20 +7,20 @@ import { ExpressAdapter } from '@nestjs/platform-express'; import { enableOpenApiDocs } from '@shared/controller/swagger'; import { Mail, MailService } from '@shared/infra/mail'; import { LegacyLogger, Logger } from '@src/core/logger'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { TeamService } from '@src/modules/teams/service/team.service'; -import { AccountValidationService } from '@src/modules/account/services/account.validation.service'; -import { AccountUc } from '@src/modules/account/uc/account.uc'; -import { CollaborativeStorageUc } from '@src/modules/collaborative-storage/uc/collaborative-storage.uc'; -import { GroupService } from '@src/modules/group'; -import { RocketChatService } from '@src/modules/rocketchat'; -import { ServerModule } from '@src/modules/server'; +import { AccountService } from '@modules/account/services/account.service'; +import { TeamService } from '@modules/teams/service/team.service'; +import { AccountValidationService } from '@modules/account/services/account.validation.service'; +import { AccountUc } from '@modules/account/uc/account.uc'; +import { CollaborativeStorageUc } from '@modules/collaborative-storage/uc/collaborative-storage.uc'; +import { GroupService } from '@modules/group'; +import { RocketChatService } from '@modules/rocketchat'; +import { ServerModule } from '@modules/server'; import express from 'express'; import { join } from 'path'; // register source-map-support for debugging import { install as sourceMapInstall } from 'source-map-support'; -import { FeathersRosterService } from '@src/modules/pseudonym'; +import { FeathersRosterService } from '@modules/pseudonym'; import legacyAppPromise = require('../../../../src/app'); import { AppStartLoggable } from './helpers/app-start-loggable'; diff --git a/apps/server/src/console/api-test/test-bootstrap.console.ts b/apps/server/src/console/api-test/test-bootstrap.console.ts index 282d448b05d..edb196b6a54 100644 --- a/apps/server/src/console/api-test/test-bootstrap.console.ts +++ b/apps/server/src/console/api-test/test-bootstrap.console.ts @@ -1,7 +1,7 @@ import { createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { ConsoleWriterService } from '@shared/infra/console'; -import { DatabaseManagementUc } from '@src/modules/management/uc/database-management.uc'; +import { DatabaseManagementUc } from '@modules/management/uc/database-management.uc'; import { AbstractBootstrapConsole, BootstrapConsole } from 'nestjs-console'; export class TestBootstrapConsole extends AbstractBootstrapConsole { diff --git a/apps/server/src/console/console.module.ts b/apps/server/src/console/console.module.ts index 2aed716e719..2cad08943eb 100644 --- a/apps/server/src/console/console.module.ts +++ b/apps/server/src/console/console.module.ts @@ -7,11 +7,11 @@ import { ALL_ENTITIES } from '@shared/domain'; import { ConsoleWriterModule } from '@shared/infra/console/console-writer/console-writer.module'; import { KeycloakModule } from '@shared/infra/identity-management/keycloak/keycloak.module'; import { DB_PASSWORD, DB_URL, DB_USERNAME, createConfigModuleOptions } from '@src/config'; -import { FilesModule } from '@src/modules/files'; -import { FileEntity } from '@src/modules/files/entity'; -import { FileRecord } from '@src/modules/files-storage/entity'; -import { ManagementModule } from '@src/modules/management/management.module'; -import { serverConfig } from '@src/modules/server'; +import { FilesModule } from '@modules/files'; +import { FileEntity } from '@modules/files/entity'; +import { FileRecord } from '@modules/files-storage/entity'; +import { ManagementModule } from '@modules/management/management.module'; +import { serverConfig } from '@modules/server'; import { ConsoleModule } from 'nestjs-console'; import { ServerConsole } from './server.console'; diff --git a/apps/server/src/modules/account/controller/account.controller.ts b/apps/server/src/modules/account/controller/account.controller.ts index 86d1f8287f8..2f45de2403e 100644 --- a/apps/server/src/modules/account/controller/account.controller.ts +++ b/apps/server/src/modules/account/controller/account.controller.ts @@ -1,7 +1,8 @@ import { Body, Controller, Delete, Get, Param, Patch, Query } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { EntityNotFoundError, ForbiddenOperationError, ValidationError } from '@shared/common'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; +import { Authenticate, CurrentUser } from '@modules/authentication/decorator/auth.decorator'; import { AccountUc } from '../uc/account.uc'; import { AccountByIdBodyParams, diff --git a/apps/server/src/modules/account/controller/api-test/account.api.spec.ts b/apps/server/src/modules/account/controller/api-test/account.api.spec.ts index 9e7dd1a7d18..44d47a3c3d2 100644 --- a/apps/server/src/modules/account/controller/api-test/account.api.spec.ts +++ b/apps/server/src/modules/account/controller/api-test/account.api.spec.ts @@ -16,8 +16,8 @@ import { AccountSearchType, PatchMyAccountParams, PatchMyPasswordParams, -} from '@src/modules/account/controller/dto'; -import { ServerTestModule } from '@src/modules/server/server.module'; +} from '@modules/account/controller/dto'; +import { ServerTestModule } from '@modules/server/server.module'; describe('Account Controller (API)', () => { const basePath = '/account'; diff --git a/apps/server/src/modules/account/mapper/account-response.mapper.spec.ts b/apps/server/src/modules/account/mapper/account-response.mapper.spec.ts index 05c345f166b..64858623c67 100644 --- a/apps/server/src/modules/account/mapper/account-response.mapper.spec.ts +++ b/apps/server/src/modules/account/mapper/account-response.mapper.spec.ts @@ -1,5 +1,5 @@ import { Account } from '@shared/domain'; -import { AccountDto } from '@src/modules/account/services/dto/account.dto'; +import { AccountDto } from '@modules/account/services/dto/account.dto'; import { accountDtoFactory, accountFactory } from '@shared/testing'; import { AccountResponseMapper } from '.'; diff --git a/apps/server/src/modules/account/mapper/account-response.mapper.ts b/apps/server/src/modules/account/mapper/account-response.mapper.ts index 94437737df9..84e20519bfe 100644 --- a/apps/server/src/modules/account/mapper/account-response.mapper.ts +++ b/apps/server/src/modules/account/mapper/account-response.mapper.ts @@ -1,5 +1,5 @@ import { Account } from '@shared/domain'; -import { AccountDto } from '@src/modules/account/services/dto/account.dto'; +import { AccountDto } from '@modules/account/services/dto/account.dto'; import { AccountResponse } from '../controller/dto'; export class AccountResponseMapper { diff --git a/apps/server/src/modules/account/services/account-db.service.spec.ts b/apps/server/src/modules/account/services/account-db.service.spec.ts index 8cc6a33cbb6..7fa4c4e44a8 100644 --- a/apps/server/src/modules/account/services/account-db.service.spec.ts +++ b/apps/server/src/modules/account/services/account-db.service.spec.ts @@ -6,9 +6,9 @@ import { EntityNotFoundError } from '@shared/common'; import { Account, EntityId } from '@shared/domain'; import { IdentityManagementService } from '@shared/infra/identity-management/identity-management.service'; import { accountFactory, setupEntities, userFactory } from '@shared/testing'; -import { AccountEntityToDtoMapper } from '@src/modules/account/mapper'; -import { AccountDto } from '@src/modules/account/services/dto'; -import { IServerConfig } from '@src/modules/server'; +import { AccountEntityToDtoMapper } from '@modules/account/mapper'; +import { AccountDto } from '@modules/account/services/dto'; +import { IServerConfig } from '@modules/server'; import bcrypt from 'bcryptjs'; import { LegacyLogger } from '../../../core/logger'; import { AccountRepo } from '../repo/account.repo'; diff --git a/apps/server/src/modules/account/services/account-idm.service.integration.spec.ts b/apps/server/src/modules/account/services/account-idm.service.integration.spec.ts index f50e8fcc07e..2249a485f98 100644 --- a/apps/server/src/modules/account/services/account-idm.service.integration.spec.ts +++ b/apps/server/src/modules/account/services/account-idm.service.integration.spec.ts @@ -5,7 +5,7 @@ import { ConfigModule } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { IdmAccount } from '@shared/domain'; import { KeycloakAdministrationService } from '@shared/infra/identity-management/keycloak-administration/service/keycloak-administration.service'; -import { AccountSaveDto } from '@src/modules/account/services/dto'; +import { AccountSaveDto } from '@modules/account/services/dto'; import { LoggerModule } from '@src/core/logger'; import { IdentityManagementModule } from '@shared/infra/identity-management'; import { IdentityManagementService } from '../../../shared/infra/identity-management/identity-management.service'; diff --git a/apps/server/src/modules/account/services/account-lookup.service.ts b/apps/server/src/modules/account/services/account-lookup.service.ts index a0e18870177..b1549590c5b 100644 --- a/apps/server/src/modules/account/services/account-lookup.service.ts +++ b/apps/server/src/modules/account/services/account-lookup.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { EntityId } from '@shared/domain'; import { IdentityManagementService } from '@shared/infra/identity-management'; -import { IServerConfig } from '@src/modules/server/server.config'; +import { IServerConfig } from '@modules/server/server.config'; import { ObjectId } from 'bson'; /** diff --git a/apps/server/src/modules/account/services/account.service.spec.ts b/apps/server/src/modules/account/services/account.service.spec.ts index 834bc5b0f89..67851d0a6b0 100644 --- a/apps/server/src/modules/account/services/account.service.spec.ts +++ b/apps/server/src/modules/account/services/account.service.spec.ts @@ -1,7 +1,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; -import { IServerConfig } from '@src/modules/server'; +import { IServerConfig } from '@modules/server'; import { LegacyLogger } from '../../../core/logger'; import { AccountServiceDb } from './account-db.service'; import { AccountServiceIdm } from './account-idm.service'; diff --git a/apps/server/src/modules/account/uc/account.uc.spec.ts b/apps/server/src/modules/account/uc/account.uc.spec.ts index e210a5c9ab5..526720c43f7 100644 --- a/apps/server/src/modules/account/uc/account.uc.spec.ts +++ b/apps/server/src/modules/account/uc/account.uc.spec.ts @@ -16,10 +16,10 @@ import { import { UserRepo } from '@shared/repo'; import { accountFactory, schoolFactory, setupEntities, systemFactory, userFactory } from '@shared/testing'; import { BruteForcePrevention } from '@src/imports-from-feathers'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountSaveDto } from '@src/modules/account/services/dto'; -import { AccountDto } from '@src/modules/account/services/dto/account.dto'; -import { ICurrentUser } from '@src/modules/authentication'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountSaveDto } from '@modules/account/services/dto'; +import { AccountDto } from '@modules/account/services/dto/account.dto'; +import { ICurrentUser } from '@modules/authentication'; import { ObjectId } from 'bson'; import { AccountByIdBodyParams, diff --git a/apps/server/src/modules/account/uc/account.uc.ts b/apps/server/src/modules/account/uc/account.uc.ts index 2db4ed8843f..f9e21b28c63 100644 --- a/apps/server/src/modules/account/uc/account.uc.ts +++ b/apps/server/src/modules/account/uc/account.uc.ts @@ -9,11 +9,11 @@ import { import { Account, EntityId, Permission, PermissionService, Role, RoleName, SchoolEntity, User } from '@shared/domain'; import { UserRepo } from '@shared/repo'; // TODO: module internals should be imported with relative paths -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto } from '@src/modules/account/services/dto/account.dto'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto } from '@modules/account/services/dto/account.dto'; import { BruteForcePrevention } from '@src/imports-from-feathers'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { ObjectId } from 'bson'; import { IAccountConfig } from '../account-config'; import { diff --git a/apps/server/src/modules/authentication/authentication.module.ts b/apps/server/src/modules/authentication/authentication.module.ts index 1cfdca45fde..26d20a4dfc8 100644 --- a/apps/server/src/modules/authentication/authentication.module.ts +++ b/apps/server/src/modules/authentication/authentication.module.ts @@ -5,10 +5,10 @@ import { CacheWrapperModule } from '@shared/infra/cache'; import { IdentityManagementModule } from '@shared/infra/identity-management'; import { LegacySchoolRepo, SystemRepo, UserRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { AccountModule } from '@src/modules/account'; -import { OauthModule } from '@src/modules/oauth/oauth.module'; -import { RoleModule } from '@src/modules/role'; -import { SystemModule } from '@src/modules/system'; +import { AccountModule } from '@modules/account'; +import { OauthModule } from '@modules/oauth/oauth.module'; +import { RoleModule } from '@modules/role'; +import { SystemModule } from '@modules/system'; import { Algorithm, SignOptions } from 'jsonwebtoken'; import { jwtConstants } from './constants'; import { AuthenticationService } from './services/authentication.service'; diff --git a/apps/server/src/modules/authentication/controllers/api-test/login.api.spec.ts b/apps/server/src/modules/authentication/controllers/api-test/login.api.spec.ts index 8d8ea8b305d..253d692055d 100644 --- a/apps/server/src/modules/authentication/controllers/api-test/login.api.spec.ts +++ b/apps/server/src/modules/authentication/controllers/api-test/login.api.spec.ts @@ -3,9 +3,9 @@ import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Account, RoleName, SchoolEntity, SystemEntity, User } from '@shared/domain'; import { accountFactory, roleFactory, schoolFactory, systemFactory, userFactory } from '@shared/testing'; -import { SSOErrorCode } from '@src/modules/oauth/loggable'; -import { OauthTokenResponse } from '@src/modules/oauth/service/dto'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { SSOErrorCode } from '@modules/oauth/loggable'; +import { OauthTokenResponse } from '@modules/oauth/service/dto'; +import { ServerTestModule } from '@modules/server/server.module'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import crypto, { KeyPairKeyObjectResult } from 'crypto'; diff --git a/apps/server/src/modules/authentication/decorator/auth.decorator.spec.ts b/apps/server/src/modules/authentication/decorator/auth.decorator.spec.ts index bc5a9361b78..f193bd67516 100644 --- a/apps/server/src/modules/authentication/decorator/auth.decorator.spec.ts +++ b/apps/server/src/modules/authentication/decorator/auth.decorator.spec.ts @@ -2,8 +2,8 @@ import { Controller, ExecutionContext, ForbiddenException, Get, INestApplication } from '@nestjs/common'; import request from 'supertest'; import { Test, TestingModule } from '@nestjs/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { ServerTestModule } from '@modules/server/server.module'; import { JwtAuthGuard } from '../guard/jwt-auth.guard'; import { Authenticate, CurrentUser, JWT } from './auth.decorator'; diff --git a/apps/server/src/modules/authentication/services/authentication.service.spec.ts b/apps/server/src/modules/authentication/services/authentication.service.spec.ts index 461fe0e7246..3d5b6d3a1b7 100644 --- a/apps/server/src/modules/authentication/services/authentication.service.spec.ts +++ b/apps/server/src/modules/authentication/services/authentication.service.spec.ts @@ -3,9 +3,9 @@ import { UnauthorizedException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; import { Test, TestingModule } from '@nestjs/testing'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto } from '@src/modules/account/services/dto'; -import { ICurrentUser } from '@src/modules/authentication'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto } from '@modules/account/services/dto'; +import { ICurrentUser } from '@modules/authentication'; import jwt from 'jsonwebtoken'; import { BruteForceError } from '../errors/brute-force.error'; import { JwtValidationAdapter } from '../strategy/jwt-validation.adapter'; diff --git a/apps/server/src/modules/authentication/services/authentication.service.ts b/apps/server/src/modules/authentication/services/authentication.service.ts index f2c25ff9e9d..41aab6153ea 100644 --- a/apps/server/src/modules/authentication/services/authentication.service.ts +++ b/apps/server/src/modules/authentication/services/authentication.service.ts @@ -1,11 +1,11 @@ import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { JwtService } from '@nestjs/jwt'; -import { AccountService } from '@src/modules/account'; +import { AccountService } from '@modules/account'; // invalid import -import { AccountDto } from '@src/modules/account/services/dto'; +import { AccountDto } from '@modules/account/services/dto'; // invalid import, can produce dependency cycles -import type { IServerConfig } from '@src/modules/server'; +import type { IServerConfig } from '@modules/server'; import { randomUUID } from 'crypto'; import jwt, { JwtPayload } from 'jsonwebtoken'; import { JwtValidationAdapter } from '../strategy/jwt-validation.adapter'; diff --git a/apps/server/src/modules/authentication/strategy/ldap.strategy.spec.ts b/apps/server/src/modules/authentication/strategy/ldap.strategy.spec.ts index db4a1a60878..d686dfcac72 100644 --- a/apps/server/src/modules/authentication/strategy/ldap.strategy.spec.ts +++ b/apps/server/src/modules/authentication/strategy/ldap.strategy.spec.ts @@ -15,7 +15,7 @@ import { userFactory, } from '@shared/testing'; import { Logger } from '@src/core/logger'; -import { AccountDto } from '@src/modules/account/services/dto'; +import { AccountDto } from '@modules/account/services/dto'; import { LdapAuthorizationBodyParams } from '../controllers/dto'; import { ICurrentUser } from '../interface'; import { AuthenticationService } from '../services/authentication.service'; diff --git a/apps/server/src/modules/authentication/strategy/ldap.strategy.ts b/apps/server/src/modules/authentication/strategy/ldap.strategy.ts index 5edc650ee9c..1622e434310 100644 --- a/apps/server/src/modules/authentication/strategy/ldap.strategy.ts +++ b/apps/server/src/modules/authentication/strategy/ldap.strategy.ts @@ -4,7 +4,7 @@ import { LegacySchoolDo, SystemEntity, User } from '@shared/domain'; import { LegacySchoolRepo, SystemRepo, UserRepo } from '@shared/repo'; import { ErrorLoggable } from '@src/core/error/loggable/error.loggable'; import { Logger } from '@src/core/logger'; -import { AccountDto } from '@src/modules/account/services/dto'; +import { AccountDto } from '@modules/account/services/dto'; import { Strategy } from 'passport-custom'; import { LdapAuthorizationBodyParams } from '../controllers/dto'; import { ICurrentUser } from '../interface'; diff --git a/apps/server/src/modules/authentication/strategy/local.strategy.spec.ts b/apps/server/src/modules/authentication/strategy/local.strategy.spec.ts index cac3d204a4b..d1330270fb7 100644 --- a/apps/server/src/modules/authentication/strategy/local.strategy.spec.ts +++ b/apps/server/src/modules/authentication/strategy/local.strategy.spec.ts @@ -5,9 +5,9 @@ import { RoleName, User } from '@shared/domain'; import { IdentityManagementOauthService } from '@shared/infra/identity-management'; import { UserRepo } from '@shared/repo'; import { accountFactory, setupEntities, userFactory } from '@shared/testing'; -import { AccountEntityToDtoMapper } from '@src/modules/account/mapper'; -import { AccountDto } from '@src/modules/account/services/dto'; -import { IServerConfig } from '@src/modules/server'; +import { AccountEntityToDtoMapper } from '@modules/account/mapper'; +import { AccountDto } from '@modules/account/services/dto'; +import { IServerConfig } from '@modules/server'; import bcrypt from 'bcryptjs'; import { AuthenticationService } from '../services/authentication.service'; import { LocalStrategy } from './local.strategy'; diff --git a/apps/server/src/modules/authentication/strategy/local.strategy.ts b/apps/server/src/modules/authentication/strategy/local.strategy.ts index ab34fe05b64..7963a5166e7 100644 --- a/apps/server/src/modules/authentication/strategy/local.strategy.ts +++ b/apps/server/src/modules/authentication/strategy/local.strategy.ts @@ -4,7 +4,7 @@ import { PassportStrategy } from '@nestjs/passport'; import { Injectable, UnauthorizedException } from '@nestjs/common'; import bcrypt from 'bcryptjs'; import { UserRepo } from '@shared/repo'; -import { AccountDto } from '@src/modules/account/services/dto'; +import { AccountDto } from '@modules/account/services/dto'; import { GuardAgainst } from '@shared/common/utils/guard-against'; import { IdentityManagementOauthService, IIdentityManagementConfig } from '@shared/infra/identity-management'; import { CurrentUserMapper } from '../mapper'; diff --git a/apps/server/src/modules/authentication/strategy/oauth2.strategy.spec.ts b/apps/server/src/modules/authentication/strategy/oauth2.strategy.spec.ts index 722ba125574..8fd8f096dbe 100644 --- a/apps/server/src/modules/authentication/strategy/oauth2.strategy.spec.ts +++ b/apps/server/src/modules/authentication/strategy/oauth2.strategy.spec.ts @@ -4,10 +4,10 @@ import { Test, TestingModule } from '@nestjs/testing'; import { EntityId, RoleName } from '@shared/domain'; import { UserDO } from '@shared/domain/domainobject/user.do'; import { userDoFactory } from '@shared/testing'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto } from '@src/modules/account/services/dto'; -import { OAuthTokenDto } from '@src/modules/oauth'; -import { OAuthService } from '@src/modules/oauth/service/oauth.service'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto } from '@modules/account/services/dto'; +import { OAuthTokenDto } from '@modules/oauth'; +import { OAuthService } from '@modules/oauth/service/oauth.service'; import { SchoolInMigrationError } from '../errors/school-in-migration.error'; import { ICurrentUser, OauthCurrentUser } from '../interface'; import { Oauth2Strategy } from './oauth2.strategy'; diff --git a/apps/server/src/modules/authentication/strategy/oauth2.strategy.ts b/apps/server/src/modules/authentication/strategy/oauth2.strategy.ts index 6d532538e68..599744cc1a7 100644 --- a/apps/server/src/modules/authentication/strategy/oauth2.strategy.ts +++ b/apps/server/src/modules/authentication/strategy/oauth2.strategy.ts @@ -1,10 +1,10 @@ import { Injectable, UnauthorizedException } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { UserDO } from '@shared/domain/domainobject/user.do'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto } from '@src/modules/account/services/dto'; -import { OAuthTokenDto } from '@src/modules/oauth'; -import { OAuthService } from '@src/modules/oauth/service/oauth.service'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto } from '@modules/account/services/dto'; +import { OAuthTokenDto } from '@modules/oauth'; +import { OAuthService } from '@modules/oauth/service/oauth.service'; import { Strategy } from 'passport-custom'; import { Oauth2AuthorizationBodyParams } from '../controllers/dto'; import { SchoolInMigrationError } from '../errors/school-in-migration.error'; diff --git a/apps/server/src/modules/authorization/authorization-reference.module.ts b/apps/server/src/modules/authorization/authorization-reference.module.ts index 7346f2178dd..e253587af7b 100644 --- a/apps/server/src/modules/authorization/authorization-reference.module.ts +++ b/apps/server/src/modules/authorization/authorization-reference.module.ts @@ -10,9 +10,9 @@ import { TeamsRepo, UserRepo, } from '@shared/repo'; -import { ToolModule } from '@src/modules/tool'; +import { ToolModule } from '@modules/tool'; import { LoggerModule } from '@src/core/logger'; -import { BoardModule } from '@src/modules/board'; +import { BoardModule } from '@modules/board'; import { ReferenceLoader, AuthorizationReferenceService, AuthorizationHelper } from './domain'; import { AuthorizationModule } from './authorization.module'; diff --git a/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts index ddd458959ed..c47f5c0ef1e 100644 --- a/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts @@ -7,10 +7,10 @@ import { setupEntities, userFactory, } from '@shared/testing'; -import { ContextExternalTool } from '@src/modules/tool/context-external-tool/domain'; -import { ContextExternalToolEntity } from '@src/modules/tool/context-external-tool/entity'; -import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; -import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; +import { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; +import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; +import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { ContextExternalToolRule } from './context-external-tool.rule'; diff --git a/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts b/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts index 5d57c95a160..0aee8334aac 100644 --- a/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { ContextExternalTool } from '@src/modules/tool/context-external-tool/domain'; -import { ContextExternalToolEntity } from '@src/modules/tool/context-external-tool/entity'; +import { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; +import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; import { User } from '@shared/domain/entity'; import { AuthorizationContext, Rule } from '../type'; import { AuthorizationHelper } from '../service/authorization.helper'; diff --git a/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts index d1781bb8576..453f77fc968 100644 --- a/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts @@ -7,8 +7,8 @@ import { userFactory, schoolExternalToolFactory, } from '@shared/testing'; -import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; -import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; +import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { Role, User, Permission } from '@shared/domain'; import { Action } from '../type'; import { AuthorizationHelper } from '../service/authorization.helper'; diff --git a/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts b/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts index 041c8b523e2..46126fe0589 100644 --- a/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; -import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; -import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; +import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { User } from '@shared/domain/entity'; import { AuthorizationContext, Rule } from '../type'; import { AuthorizationHelper } from '../service/authorization.helper'; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts index 0403ebcbfd5..e2e77212cab 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts @@ -15,8 +15,8 @@ import { UserRepo, } from '@shared/repo'; import { setupEntities, userFactory } from '@shared/testing'; -import { BoardDoAuthorizableService } from '@src/modules/board'; -import { ContextExternalToolAuthorizableService } from '@src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service'; +import { BoardDoAuthorizableService } from '@modules/board'; +import { ContextExternalToolAuthorizableService } from '@modules/tool/context-external-tool/service/context-external-tool-authorizable.service'; import { ReferenceLoader } from './reference.loader'; import { AuthorizableReferenceType } from '../type'; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts index 5c38963c6f5..d584561be9e 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -12,8 +12,8 @@ import { TeamsRepo, UserRepo, } from '@shared/repo'; -import { BoardDoAuthorizableService } from '@src/modules/board'; -import { ContextExternalToolAuthorizableService } from '@src/modules/tool/context-external-tool/service'; +import { BoardDoAuthorizableService } from '@modules/board'; +import { ContextExternalToolAuthorizableService } from '@modules/tool/context-external-tool/service'; import { AuthorizableReferenceType } from '../type'; type RepoType = diff --git a/apps/server/src/modules/board/board-api.module.ts b/apps/server/src/modules/board/board-api.module.ts index 2ac4581dfd0..10b868e67b3 100644 --- a/apps/server/src/modules/board/board-api.module.ts +++ b/apps/server/src/modules/board/board-api.module.ts @@ -1,6 +1,6 @@ import { forwardRef, Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationModule } from '@modules/authorization'; import { BoardModule } from './board.module'; import { BoardController, diff --git a/apps/server/src/modules/board/controller/api-test/board-context.api.spec.ts b/apps/server/src/modules/board/controller/api-test/board-context.api.spec.ts index 99bb5403692..0cbdad3b8c2 100644 --- a/apps/server/src/modules/board/controller/api-test/board-context.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/board-context.api.spec.ts @@ -9,7 +9,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import { BoardContextResponse } from '../dto/board/board-context.reponse'; const baseRouteName = '/boards'; diff --git a/apps/server/src/modules/board/controller/api-test/board-delete.api.spec.ts b/apps/server/src/modules/board/controller/api-test/board-delete.api.spec.ts index 24bb1c69999..e92bb645872 100644 --- a/apps/server/src/modules/board/controller/api-test/board-delete.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/board-delete.api.spec.ts @@ -11,9 +11,9 @@ import { mapUserToCurrentUser, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server'; import { Request } from 'express'; import request from 'supertest'; import { BoardResponse } from '../dto'; diff --git a/apps/server/src/modules/board/controller/api-test/board-lookup.api.spec.ts b/apps/server/src/modules/board/controller/api-test/board-lookup.api.spec.ts index ebcb9c280a0..8423d68f793 100644 --- a/apps/server/src/modules/board/controller/api-test/board-lookup.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/board-lookup.api.spec.ts @@ -12,9 +12,9 @@ import { mapUserToCurrentUser, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; import { BoardResponse } from '../dto'; diff --git a/apps/server/src/modules/board/controller/api-test/board-update-title.api.spec.ts b/apps/server/src/modules/board/controller/api-test/board-update-title.api.spec.ts index 4dc93d91417..9b77f2f3824 100644 --- a/apps/server/src/modules/board/controller/api-test/board-update-title.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/board-update-title.api.spec.ts @@ -9,7 +9,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@modules/server/server.module'; const baseRouteName = '/boards'; diff --git a/apps/server/src/modules/board/controller/api-test/card-create.api.spec.ts b/apps/server/src/modules/board/controller/api-test/card-create.api.spec.ts index a108d352759..4b0f57c361d 100644 --- a/apps/server/src/modules/board/controller/api-test/card-create.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/card-create.api.spec.ts @@ -11,9 +11,9 @@ import { mapUserToCurrentUser, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; import { CardResponse } from '../dto'; diff --git a/apps/server/src/modules/board/controller/api-test/card-delete.api.spec.ts b/apps/server/src/modules/board/controller/api-test/card-delete.api.spec.ts index 2e38143ebc1..3fff41b3660 100644 --- a/apps/server/src/modules/board/controller/api-test/card-delete.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/card-delete.api.spec.ts @@ -13,9 +13,9 @@ import { richTextElementNodeFactory, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/board/controller/api-test/card-lookup.api.spec.ts b/apps/server/src/modules/board/controller/api-test/card-lookup.api.spec.ts index 8f2c7bee9a9..711ccf1ac8f 100644 --- a/apps/server/src/modules/board/controller/api-test/card-lookup.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/card-lookup.api.spec.ts @@ -15,9 +15,9 @@ import { schoolFactory, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; import { CardIdsParams, CardListResponse } from '../dto'; diff --git a/apps/server/src/modules/board/controller/api-test/card-move.api.spec.ts b/apps/server/src/modules/board/controller/api-test/card-move.api.spec.ts index 892234c122a..e9e55b27dd2 100644 --- a/apps/server/src/modules/board/controller/api-test/card-move.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/card-move.api.spec.ts @@ -12,9 +12,9 @@ import { mapUserToCurrentUser, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/board/controller/api-test/card-update-height.api.spec.ts b/apps/server/src/modules/board/controller/api-test/card-update-height.api.spec.ts index 6e0de6e8739..2d4ab15b365 100644 --- a/apps/server/src/modules/board/controller/api-test/card-update-height.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/card-update-height.api.spec.ts @@ -11,7 +11,7 @@ import { columnNodeFactory, courseFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@modules/server/server.module'; describe(`card update height (api)`, () => { let app: INestApplication; diff --git a/apps/server/src/modules/board/controller/api-test/card-update-title.api.spec.ts b/apps/server/src/modules/board/controller/api-test/card-update-title.api.spec.ts index 5939c9c0cc0..bc598d69051 100644 --- a/apps/server/src/modules/board/controller/api-test/card-update-title.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/card-update-title.api.spec.ts @@ -11,7 +11,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@modules/server/server.module'; const baseRouteName = '/cards'; diff --git a/apps/server/src/modules/board/controller/api-test/column-create.api.spec.ts b/apps/server/src/modules/board/controller/api-test/column-create.api.spec.ts index 1f6777ab447..52aaa4608ca 100644 --- a/apps/server/src/modules/board/controller/api-test/column-create.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/column-create.api.spec.ts @@ -10,9 +10,9 @@ import { mapUserToCurrentUser, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; import { ColumnResponse } from '../dto'; diff --git a/apps/server/src/modules/board/controller/api-test/column-delete.api.spec.ts b/apps/server/src/modules/board/controller/api-test/column-delete.api.spec.ts index 1d03e6579ba..86e3e666c84 100644 --- a/apps/server/src/modules/board/controller/api-test/column-delete.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/column-delete.api.spec.ts @@ -12,9 +12,9 @@ import { mapUserToCurrentUser, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/board/controller/api-test/column-move.api.spec.ts b/apps/server/src/modules/board/controller/api-test/column-move.api.spec.ts index 2f65a6f2a0e..6fda90f739a 100644 --- a/apps/server/src/modules/board/controller/api-test/column-move.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/column-move.api.spec.ts @@ -12,9 +12,9 @@ import { mapUserToCurrentUser, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/board/controller/api-test/column-update-title.api.spec.ts b/apps/server/src/modules/board/controller/api-test/column-update-title.api.spec.ts index 82fa2264f7a..350406041c7 100644 --- a/apps/server/src/modules/board/controller/api-test/column-update-title.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/column-update-title.api.spec.ts @@ -10,7 +10,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@modules/server/server.module'; const baseRouteName = '/columns'; diff --git a/apps/server/src/modules/board/controller/api-test/content-element-create.api.spec.ts b/apps/server/src/modules/board/controller/api-test/content-element-create.api.spec.ts index 0f7a3795580..57ef692ace1 100644 --- a/apps/server/src/modules/board/controller/api-test/content-element-create.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/content-element-create.api.spec.ts @@ -11,7 +11,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@modules/server/server.module'; import { AnyContentElementResponse, SubmissionContainerElementResponse } from '../dto'; const baseRouteName = '/cards'; diff --git a/apps/server/src/modules/board/controller/api-test/content-element-delete.api.spec.ts b/apps/server/src/modules/board/controller/api-test/content-element-delete.api.spec.ts index 107c12a723a..6b99a64e7ab 100644 --- a/apps/server/src/modules/board/controller/api-test/content-element-delete.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/content-element-delete.api.spec.ts @@ -13,9 +13,9 @@ import { richTextElementNodeFactory, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/board/controller/api-test/content-element-move.api.spec.ts b/apps/server/src/modules/board/controller/api-test/content-element-move.api.spec.ts index 0793dce8951..dbcd9acbc31 100644 --- a/apps/server/src/modules/board/controller/api-test/content-element-move.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/content-element-move.api.spec.ts @@ -13,9 +13,9 @@ import { richTextElementNodeFactory, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/board/controller/api-test/content-element-update-content.spec.ts b/apps/server/src/modules/board/controller/api-test/content-element-update-content.spec.ts index 6a292ffa93d..3be8e7dda15 100644 --- a/apps/server/src/modules/board/controller/api-test/content-element-update-content.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/content-element-update-content.spec.ts @@ -22,7 +22,7 @@ import { richTextElementNodeFactory, submissionContainerElementNodeFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@modules/server/server.module'; describe(`content element update content (api)`, () => { let app: INestApplication; diff --git a/apps/server/src/modules/board/controller/api-test/submission-item-create.api.spec.ts b/apps/server/src/modules/board/controller/api-test/submission-item-create.api.spec.ts index 49cd3af657c..0fb70869e18 100644 --- a/apps/server/src/modules/board/controller/api-test/submission-item-create.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/submission-item-create.api.spec.ts @@ -13,7 +13,7 @@ import { submissionContainerElementNodeFactory, userFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import { SubmissionItemResponse } from '../dto'; const baseRouteName = '/elements'; diff --git a/apps/server/src/modules/board/controller/api-test/submission-item-lookup.api.spec.ts b/apps/server/src/modules/board/controller/api-test/submission-item-lookup.api.spec.ts index 4686f953d29..c119ace1d3c 100644 --- a/apps/server/src/modules/board/controller/api-test/submission-item-lookup.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/submission-item-lookup.api.spec.ts @@ -14,7 +14,7 @@ import { submissionItemNodeFactory, userFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import { SubmissionsResponse } from '../dto'; const baseRouteName = '/board-submissions'; diff --git a/apps/server/src/modules/board/controller/api-test/submission-item-update.api.spec.ts b/apps/server/src/modules/board/controller/api-test/submission-item-update.api.spec.ts index dfa8bf017b3..f8058a3ee4b 100644 --- a/apps/server/src/modules/board/controller/api-test/submission-item-update.api.spec.ts +++ b/apps/server/src/modules/board/controller/api-test/submission-item-update.api.spec.ts @@ -13,7 +13,7 @@ import { submissionContainerElementNodeFactory, submissionItemNodeFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import { SubmissionItemResponse } from '../dto'; const baseRouteName = '/board-submissions'; diff --git a/apps/server/src/modules/board/controller/board-submission.controller.ts b/apps/server/src/modules/board/controller/board-submission.controller.ts index f7b7e5da8ca..cffdcd64467 100644 --- a/apps/server/src/modules/board/controller/board-submission.controller.ts +++ b/apps/server/src/modules/board/controller/board-submission.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, ForbiddenException, Get, HttpCode, NotFoundException, Param, Patch } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { SubmissionsResponse } from './dto/submission-item/submissions.response'; import { CardUc } from '../uc'; import { ElementUc } from '../uc/element.uc'; diff --git a/apps/server/src/modules/board/controller/board.controller.ts b/apps/server/src/modules/board/controller/board.controller.ts index 16cee839964..0d77aa80b3d 100644 --- a/apps/server/src/modules/board/controller/board.controller.ts +++ b/apps/server/src/modules/board/controller/board.controller.ts @@ -12,7 +12,7 @@ import { } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { BoardUc } from '../uc'; import { BoardResponse, BoardUrlParams, ColumnResponse, RenameBodyParams } from './dto'; import { BoardContextResponse } from './dto/board/board-context.reponse'; diff --git a/apps/server/src/modules/board/controller/card.controller.ts b/apps/server/src/modules/board/controller/card.controller.ts index 94007d9296e..62afa262439 100644 --- a/apps/server/src/modules/board/controller/card.controller.ts +++ b/apps/server/src/modules/board/controller/card.controller.ts @@ -14,7 +14,7 @@ import { } from '@nestjs/common'; import { ApiExtraModels, ApiOperation, ApiResponse, ApiTags, getSchemaPath } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { BoardUc, CardUc } from '../uc'; import { AnyContentElementResponse, diff --git a/apps/server/src/modules/board/controller/column.controller.ts b/apps/server/src/modules/board/controller/column.controller.ts index fd7239e517a..9862ef23a74 100644 --- a/apps/server/src/modules/board/controller/column.controller.ts +++ b/apps/server/src/modules/board/controller/column.controller.ts @@ -12,7 +12,7 @@ import { } from '@nestjs/common'; import { ApiBody, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { BoardUc } from '../uc'; import { CardResponse, ColumnUrlParams, MoveColumnBodyParams, RenameBodyParams } from './dto'; import { CardResponseMapper } from './mapper'; diff --git a/apps/server/src/modules/board/controller/element.controller.ts b/apps/server/src/modules/board/controller/element.controller.ts index 25a3c553ef9..229d2d6f2e1 100644 --- a/apps/server/src/modules/board/controller/element.controller.ts +++ b/apps/server/src/modules/board/controller/element.controller.ts @@ -12,7 +12,7 @@ import { } from '@nestjs/common'; import { ApiBody, ApiExtraModels, ApiOperation, ApiResponse, ApiTags, getSchemaPath } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { CardUc } from '../uc'; import { ElementUc } from '../uc/element.uc'; import { diff --git a/apps/server/src/modules/board/repo/board-do.repo.spec.ts b/apps/server/src/modules/board/repo/board-do.repo.spec.ts index 446d8b8cfa3..aa1c49224fe 100644 --- a/apps/server/src/modules/board/repo/board-do.repo.spec.ts +++ b/apps/server/src/modules/board/repo/board-do.repo.spec.ts @@ -26,7 +26,7 @@ import { richTextElementFactory, richTextElementNodeFactory, } from '@shared/testing'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { BoardDoRepo } from './board-do.repo'; import { BoardNodeRepo } from './board-node.repo'; import { RecursiveDeleteVisitor } from './recursive-delete.vistor'; diff --git a/apps/server/src/modules/board/repo/recursive-delete.visitor.spec.ts b/apps/server/src/modules/board/repo/recursive-delete.visitor.spec.ts index d94e7ae5557..9142cb33553 100644 --- a/apps/server/src/modules/board/repo/recursive-delete.visitor.spec.ts +++ b/apps/server/src/modules/board/repo/recursive-delete.visitor.spec.ts @@ -12,7 +12,7 @@ import { submissionContainerElementFactory, submissionItemFactory, } from '@shared/testing'; -import { FileDto, FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FileDto, FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { RecursiveDeleteVisitor } from './recursive-delete.vistor'; describe(RecursiveDeleteVisitor.name, () => { diff --git a/apps/server/src/modules/board/repo/recursive-delete.vistor.ts b/apps/server/src/modules/board/repo/recursive-delete.vistor.ts index c2177e5dd1c..1c407391da4 100644 --- a/apps/server/src/modules/board/repo/recursive-delete.vistor.ts +++ b/apps/server/src/modules/board/repo/recursive-delete.vistor.ts @@ -14,7 +14,7 @@ import { SubmissionItem, } from '@shared/domain'; import { LinkElement } from '@shared/domain/domainobject/board/link-element.do'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; @Injectable() export class RecursiveDeleteVisitor implements BoardCompositeVisitorAsync { diff --git a/apps/server/src/modules/board/repo/recursive-save.visitor.ts b/apps/server/src/modules/board/repo/recursive-save.visitor.ts index 7b2c7901605..5e8249f1fee 100644 --- a/apps/server/src/modules/board/repo/recursive-save.visitor.ts +++ b/apps/server/src/modules/board/repo/recursive-save.visitor.ts @@ -24,7 +24,7 @@ import { } from '@shared/domain'; import { LinkElement } from '@shared/domain/domainobject/board/link-element.do'; import { LinkElementNode } from '@shared/domain/entity/boardnode/link-element-node.entity'; -import { ContextExternalToolEntity } from '@src/modules/tool/context-external-tool/entity'; +import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; import { BoardNodeRepo } from './board-node.repo'; type ParentData = { diff --git a/apps/server/src/modules/board/service/board-do-authorizable.service.ts b/apps/server/src/modules/board/service/board-do-authorizable.service.ts index 7b8b653f9cf..46ef7d3d45b 100644 --- a/apps/server/src/modules/board/service/board-do-authorizable.service.ts +++ b/apps/server/src/modules/board/service/board-do-authorizable.service.ts @@ -11,7 +11,7 @@ import { UserRoleEnum, } from '@shared/domain'; import { CourseRepo } from '@shared/repo'; -import { AuthorizationLoaderService } from '@src/modules/authorization'; +import { AuthorizationLoaderService } from '@modules/authorization'; import { BoardDoRepo } from '../repo'; @Injectable() diff --git a/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.spec.ts b/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.spec.ts index 4b5393854d2..d7c71352166 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.spec.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.spec.ts @@ -31,7 +31,7 @@ import { submissionContainerElementFactory, submissionItemFactory, } from '@shared/testing'; -import { CopyElementType, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; +import { CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; import { ObjectId } from 'bson'; import { BoardDoCopyService } from './board-do-copy.service'; import { SchoolSpecificFileCopyService } from './school-specific-file-copy.interface'; diff --git a/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.ts b/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.ts index 0d457436f44..b2458dd6cfb 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/board-do-copy.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { AnyBoardDo } from '@shared/domain'; -import { CopyStatus } from '@src/modules/copy-helper'; +import { CopyStatus } from '@modules/copy-helper'; import { RecursiveCopyVisitor } from './recursive-copy.visitor'; import { SchoolSpecificFileCopyService } from './school-specific-file-copy.interface'; diff --git a/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.ts b/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.ts index 7b17194c15f..ba76693bb93 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/recursive-copy.visitor.ts @@ -13,7 +13,7 @@ import { } from '@shared/domain'; import { LinkElement } from '@shared/domain/domainobject/board/link-element.do'; import { FileRecordParentType } from '@shared/infra/rabbitmq'; -import { CopyElementType, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; +import { CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; import { ObjectId } from 'bson'; import { SchoolSpecificFileCopyService } from './school-specific-file-copy.interface'; diff --git a/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy-service.factory.spec.ts b/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy-service.factory.spec.ts index 9d6eaf1b24e..c780f9b9c50 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy-service.factory.spec.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy-service.factory.spec.ts @@ -1,8 +1,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { setupEntities } from '@shared/testing'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; -import { FileRecordParentType } from '@src/modules/files-storage/entity'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; +import { FileRecordParentType } from '@modules/files-storage/entity'; import { ObjectId } from 'bson'; import { SchoolSpecificFileCopyServiceFactory } from './school-specific-file-copy-service.factory'; import { SchoolSpecificFileCopyServiceImpl } from './school-specific-file-copy.service'; diff --git a/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy-service.factory.ts b/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy-service.factory.ts index dda07c8c1f9..424033fa974 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy-service.factory.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy-service.factory.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { SchoolSpecificFileCopyService, SchoolSpecificFileCopyServiceProps, diff --git a/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy.interface.ts b/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy.interface.ts index e3b03d0b059..ce7870f6c86 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy.interface.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy.interface.ts @@ -1,6 +1,6 @@ import { EntityId } from '@shared/domain'; -import { CopyFileDto } from '@src/modules/files-storage-client/dto'; -import { FileRecordParentType } from '@src/modules/files-storage/entity'; +import { CopyFileDto } from '@modules/files-storage-client/dto'; +import { FileRecordParentType } from '@modules/files-storage/entity'; export type SchoolSpecificFileCopyServiceCopyParams = { sourceParentId: EntityId; diff --git a/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy.service.ts b/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy.service.ts index c162dbafdc7..1f3fa5f5193 100644 --- a/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy.service.ts +++ b/apps/server/src/modules/board/service/board-do-copy-service/school-specific-file-copy.service.ts @@ -1,5 +1,5 @@ -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; -import { CopyFileDto } from '@src/modules/files-storage-client/dto'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; +import { CopyFileDto } from '@modules/files-storage-client/dto'; import { SchoolSpecificFileCopyService, SchoolSpecificFileCopyServiceCopyParams, diff --git a/apps/server/src/modules/board/service/column-board-copy.service.spec.ts b/apps/server/src/modules/board/service/column-board-copy.service.spec.ts index 6824b82e133..bbfeb27a1f3 100644 --- a/apps/server/src/modules/board/service/column-board-copy.service.spec.ts +++ b/apps/server/src/modules/board/service/column-board-copy.service.spec.ts @@ -3,8 +3,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { BoardExternalReferenceType, ColumnBoard, UserDO } from '@shared/domain'; import { CourseRepo } from '@shared/repo'; import { columnBoardFactory, courseFactory, schoolFactory, setupEntities, userFactory } from '@shared/testing'; -import { CopyElementType, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; -import { UserService } from '@src/modules/user'; +import { CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { UserService } from '@modules/user'; import { BoardDoRepo } from '../repo'; import { BoardDoCopyService, diff --git a/apps/server/src/modules/board/service/column-board-copy.service.ts b/apps/server/src/modules/board/service/column-board-copy.service.ts index c57e01d6c26..79ef29f4752 100644 --- a/apps/server/src/modules/board/service/column-board-copy.service.ts +++ b/apps/server/src/modules/board/service/column-board-copy.service.ts @@ -7,8 +7,8 @@ import { isColumnBoard, } from '@shared/domain'; import { CourseRepo } from '@shared/repo'; -import { CopyStatus } from '@src/modules/copy-helper'; -import { UserService } from '@src/modules/user'; +import { CopyStatus } from '@modules/copy-helper'; +import { UserService } from '@modules/user'; import { BoardDoRepo } from '../repo'; import { BoardDoCopyService, SchoolSpecificFileCopyServiceFactory } from './board-do-copy-service'; diff --git a/apps/server/src/modules/board/uc/board-management.uc.spec.ts b/apps/server/src/modules/board/uc/board-management.uc.spec.ts index 3d0d1fa835f..7c3464a8a52 100644 --- a/apps/server/src/modules/board/uc/board-management.uc.spec.ts +++ b/apps/server/src/modules/board/uc/board-management.uc.spec.ts @@ -4,7 +4,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ConsoleWriterService } from '@shared/infra/console'; import { MongoMemoryDatabaseModule } from '@shared/infra/database'; import { courseFactory } from '@shared/testing'; -import { BoardManagementUc } from '@src/modules/management/uc/board-management.uc'; +import { BoardManagementUc } from '@modules/management/uc/board-management.uc'; describe(BoardManagementUc.name, () => { let module: TestingModule; diff --git a/apps/server/src/modules/board/uc/board.uc.spec.ts b/apps/server/src/modules/board/uc/board.uc.spec.ts index efd41e49ce6..c644bb120da 100644 --- a/apps/server/src/modules/board/uc/board.uc.spec.ts +++ b/apps/server/src/modules/board/uc/board.uc.spec.ts @@ -5,7 +5,7 @@ import { BoardDoAuthorizable, BoardRoles, ContentElementType, UserRoleEnum } fro import { setupEntities, userFactory } from '@shared/testing'; import { cardFactory, columnBoardFactory, columnFactory } from '@shared/testing/factory/domainobject'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationService } from '@modules/authorization'; import { ObjectId } from 'bson'; import { ContentElementService } from '../service'; import { BoardDoAuthorizableService } from '../service/board-do-authorizable.service'; diff --git a/apps/server/src/modules/board/uc/board.uc.ts b/apps/server/src/modules/board/uc/board.uc.ts index 3e39fd32de3..36d45dcd5fc 100644 --- a/apps/server/src/modules/board/uc/board.uc.ts +++ b/apps/server/src/modules/board/uc/board.uc.ts @@ -9,8 +9,8 @@ import { EntityId, } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization/domain'; -import { Action } from '@src/modules/authorization'; +import { AuthorizationService } from '@modules/authorization/domain'; +import { Action } from '@modules/authorization'; import { CardService, ColumnBoardService, ColumnService } from '../service'; import { BoardDoAuthorizableService } from '../service/board-do-authorizable.service'; diff --git a/apps/server/src/modules/board/uc/card.uc.spec.ts b/apps/server/src/modules/board/uc/card.uc.spec.ts index 130a7feac40..fce595085c7 100644 --- a/apps/server/src/modules/board/uc/card.uc.spec.ts +++ b/apps/server/src/modules/board/uc/card.uc.spec.ts @@ -4,7 +4,7 @@ import { BoardDoAuthorizable, BoardRoles, ContentElementType, UserRoleEnum } fro import { setupEntities, userFactory } from '@shared/testing'; import { cardFactory, richTextElementFactory } from '@shared/testing/factory/domainobject'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationService } from '@modules/authorization'; import { ObjectId } from 'bson'; import { BoardDoAuthorizableService, ContentElementService } from '../service'; import { CardService } from '../service/card.service'; diff --git a/apps/server/src/modules/board/uc/card.uc.ts b/apps/server/src/modules/board/uc/card.uc.ts index 170469f0cc4..488f93fd4d8 100644 --- a/apps/server/src/modules/board/uc/card.uc.ts +++ b/apps/server/src/modules/board/uc/card.uc.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { AnyBoardDo, AnyContentElementDo, Card, ContentElementType, EntityId } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService, Action } from '@src/modules/authorization'; +import { AuthorizationService, Action } from '@modules/authorization'; import { BoardDoAuthorizableService, CardService, ContentElementService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/board/uc/element.uc.spec.ts b/apps/server/src/modules/board/uc/element.uc.spec.ts index 9c4b2706ab6..03124305dfa 100644 --- a/apps/server/src/modules/board/uc/element.uc.spec.ts +++ b/apps/server/src/modules/board/uc/element.uc.spec.ts @@ -10,7 +10,7 @@ import { userFactory, } from '@shared/testing'; import { Logger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationService } from '@modules/authorization'; import { ObjectId } from 'bson'; import { BoardDoAuthorizableService, ContentElementService } from '../service'; import { SubmissionItemService } from '../service/submission-item.service'; diff --git a/apps/server/src/modules/board/uc/element.uc.ts b/apps/server/src/modules/board/uc/element.uc.ts index 08357b01798..6f71f202e66 100644 --- a/apps/server/src/modules/board/uc/element.uc.ts +++ b/apps/server/src/modules/board/uc/element.uc.ts @@ -8,7 +8,7 @@ import { UserRoleEnum, } from '@shared/domain'; import { Logger } from '@src/core/logger'; -import { AuthorizationService, Action } from '@src/modules/authorization'; +import { AuthorizationService, Action } from '@modules/authorization'; import { AnyElementContentBody } from '../controller/dto'; import { BoardDoAuthorizableService, ContentElementService } from '../service'; import { SubmissionItemService } from '../service/submission-item.service'; diff --git a/apps/server/src/modules/board/uc/submission-item.uc.spec.ts b/apps/server/src/modules/board/uc/submission-item.uc.spec.ts index 5d06172acee..8e9b0d052b7 100644 --- a/apps/server/src/modules/board/uc/submission-item.uc.spec.ts +++ b/apps/server/src/modules/board/uc/submission-item.uc.spec.ts @@ -9,7 +9,7 @@ import { userFactory, } from '@shared/testing'; import { Logger } from '@src/core/logger'; -import { AuthorizationService, Action } from '@src/modules/authorization'; +import { AuthorizationService, Action } from '@modules/authorization'; import { BoardDoAuthorizableService, ContentElementService, SubmissionItemService } from '../service'; import { SubmissionItemUc } from './submission-item.uc'; diff --git a/apps/server/src/modules/board/uc/submission-item.uc.ts b/apps/server/src/modules/board/uc/submission-item.uc.ts index 67e7951673f..4748b64d84e 100644 --- a/apps/server/src/modules/board/uc/submission-item.uc.ts +++ b/apps/server/src/modules/board/uc/submission-item.uc.ts @@ -9,7 +9,7 @@ import { UserRoleEnum, } from '@shared/domain'; import { Logger } from '@src/core/logger'; -import { AuthorizationService, Action } from '@src/modules/authorization'; +import { AuthorizationService, Action } from '@modules/authorization'; import { BoardDoAuthorizableService, ContentElementService, SubmissionItemService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/class/entity/testing/class.entity.spec.ts b/apps/server/src/modules/class/entity/testing/class.entity.spec.ts index eb16f608238..7339e74ddb4 100644 --- a/apps/server/src/modules/class/entity/testing/class.entity.spec.ts +++ b/apps/server/src/modules/class/entity/testing/class.entity.spec.ts @@ -1,6 +1,6 @@ /* eslint-disable no-new */ import { setupEntities } from '@shared/testing'; -import { classEntityFactory } from '@src/modules/class/entity/testing/factory/class.entity.factory'; +import { classEntityFactory } from '@modules/class/entity/testing/factory/class.entity.factory'; import { ObjectId } from '@mikro-orm/mongodb'; import { ClassEntity } from '../class.entity'; diff --git a/apps/server/src/modules/class/entity/testing/factory/class.entity.factory.ts b/apps/server/src/modules/class/entity/testing/factory/class.entity.factory.ts index 68e7514d3bc..b98d20853fc 100644 --- a/apps/server/src/modules/class/entity/testing/factory/class.entity.factory.ts +++ b/apps/server/src/modules/class/entity/testing/factory/class.entity.factory.ts @@ -1,6 +1,6 @@ import { DeepPartial } from 'fishery'; import { BaseFactory } from '@shared/testing/factory/base.factory'; -import { ClassEntity, ClassSourceOptionsEntity, IClassEntityProps } from '@src/modules/class/entity'; +import { ClassEntity, ClassSourceOptionsEntity, IClassEntityProps } from '@modules/class/entity'; import { ObjectId } from 'bson'; class ClassEntityFactory extends BaseFactory { diff --git a/apps/server/src/modules/class/repo/classes.repo.spec.ts b/apps/server/src/modules/class/repo/classes.repo.spec.ts index 8059cdf8557..302a8f2de8e 100644 --- a/apps/server/src/modules/class/repo/classes.repo.spec.ts +++ b/apps/server/src/modules/class/repo/classes.repo.spec.ts @@ -4,7 +4,7 @@ import { TestingModule } from '@nestjs/testing/testing-module'; import { SchoolEntity } from '@shared/domain'; import { MongoMemoryDatabaseModule } from '@shared/infra/database'; import { cleanupCollections, schoolFactory } from '@shared/testing'; -import { classEntityFactory } from '@src/modules/class/entity/testing/factory/class.entity.factory'; +import { classEntityFactory } from '@modules/class/entity/testing/factory/class.entity.factory'; import { Class } from '../domain'; import { ClassEntity } from '../entity'; import { ClassesRepo } from './classes.repo'; diff --git a/apps/server/src/modules/class/service/class.service.spec.ts b/apps/server/src/modules/class/service/class.service.spec.ts index 3d1e851bd32..850eaf655a6 100644 --- a/apps/server/src/modules/class/service/class.service.spec.ts +++ b/apps/server/src/modules/class/service/class.service.spec.ts @@ -4,7 +4,7 @@ import { InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityId } from '@shared/domain'; import { setupEntities } from '@shared/testing'; -import { classEntityFactory } from '@src/modules/class/entity/testing/factory/class.entity.factory'; +import { classEntityFactory } from '@modules/class/entity/testing/factory/class.entity.factory'; import { Class } from '../domain'; import { classFactory } from '../domain/testing/factory/class.factory'; import { ClassesRepo } from '../repo'; diff --git a/apps/server/src/modules/collaborative-storage/collaborative-storage.module.ts b/apps/server/src/modules/collaborative-storage/collaborative-storage.module.ts index d8580f9292b..eedf1b5638e 100644 --- a/apps/server/src/modules/collaborative-storage/collaborative-storage.module.ts +++ b/apps/server/src/modules/collaborative-storage/collaborative-storage.module.ts @@ -2,11 +2,11 @@ import { Module } from '@nestjs/common'; import { CollaborativeStorageAdapterModule } from '@shared/infra/collaborative-storage/collaborative-storage-adapter.module'; import { TeamsRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { TeamPermissionsMapper } from '@src/modules/collaborative-storage/mapper/team-permissions.mapper'; -import { TeamMapper } from '@src/modules/collaborative-storage/mapper/team.mapper'; -import { CollaborativeStorageService } from '@src/modules/collaborative-storage/services/collaborative-storage.service'; -import { RoleModule } from '@src/modules/role/role.module'; +import { AuthorizationModule } from '@modules/authorization'; +import { TeamPermissionsMapper } from '@modules/collaborative-storage/mapper/team-permissions.mapper'; +import { TeamMapper } from '@modules/collaborative-storage/mapper/team.mapper'; +import { CollaborativeStorageService } from '@modules/collaborative-storage/services/collaborative-storage.service'; +import { RoleModule } from '@modules/role/role.module'; import { CollaborativeStorageController } from './controller/collaborative-storage.controller'; import { CollaborativeStorageUc } from './uc/collaborative-storage.uc'; diff --git a/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.spec.ts b/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.spec.ts index b3f940747fa..4622fed4c00 100644 --- a/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.spec.ts +++ b/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.spec.ts @@ -1,8 +1,8 @@ -import { CollaborativeStorageController } from '@src/modules/collaborative-storage/controller/collaborative-storage.controller'; +import { CollaborativeStorageController } from '@modules/collaborative-storage/controller/collaborative-storage.controller'; import { Test, TestingModule } from '@nestjs/testing'; -import { CollaborativeStorageUc } from '@src/modules/collaborative-storage/uc/collaborative-storage.uc'; +import { CollaborativeStorageUc } from '@modules/collaborative-storage/uc/collaborative-storage.uc'; import { createMock } from '@golevelup/ts-jest'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { LegacyLogger } from '@src/core/logger'; describe('CollaborativeStorage Controller', () => { diff --git a/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.ts b/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.ts index ecfd50ffda2..19fbccbed1c 100644 --- a/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.ts +++ b/apps/server/src/modules/collaborative-storage/controller/collaborative-storage.controller.ts @@ -1,6 +1,6 @@ import { ApiResponse, ApiTags } from '@nestjs/swagger'; import { Body, Controller, Param, Patch } from '@nestjs/common'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { LegacyLogger } from '@src/core/logger'; import { CollaborativeStorageUc } from '../uc/collaborative-storage.uc'; import { TeamPermissionsBody } from './dto/team-permissions.body.params'; diff --git a/apps/server/src/modules/collaborative-storage/mapper/team-permissions.mapper.spec.ts b/apps/server/src/modules/collaborative-storage/mapper/team-permissions.mapper.spec.ts index 195319c0d89..3e3e8c434e5 100644 --- a/apps/server/src/modules/collaborative-storage/mapper/team-permissions.mapper.spec.ts +++ b/apps/server/src/modules/collaborative-storage/mapper/team-permissions.mapper.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { TeamPermissionsBody } from '@src/modules/collaborative-storage/controller/dto/team-permissions.body.params'; -import { TeamPermissionsMapper } from '@src/modules/collaborative-storage/mapper/team-permissions.mapper'; +import { TeamPermissionsBody } from '@modules/collaborative-storage/controller/dto/team-permissions.body.params'; +import { TeamPermissionsMapper } from '@modules/collaborative-storage/mapper/team-permissions.mapper'; describe('TeamMapper', () => { let module: TestingModule; diff --git a/apps/server/src/modules/collaborative-storage/mapper/team.mapper.spec.ts b/apps/server/src/modules/collaborative-storage/mapper/team.mapper.spec.ts index c46577499f0..a2d9808f5a6 100644 --- a/apps/server/src/modules/collaborative-storage/mapper/team.mapper.spec.ts +++ b/apps/server/src/modules/collaborative-storage/mapper/team.mapper.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { setupEntities } from '@shared/testing'; import { teamFactory } from '@shared/testing/factory/team.factory'; -import { TeamMapper } from '@src/modules/collaborative-storage/mapper/team.mapper'; +import { TeamMapper } from '@modules/collaborative-storage/mapper/team.mapper'; describe('TeamMapper', () => { let module: TestingModule; diff --git a/apps/server/src/modules/collaborative-storage/services/collaborative-storage.service.spec.ts b/apps/server/src/modules/collaborative-storage/services/collaborative-storage.service.spec.ts index 1ed1aa3635c..4f95ae44a11 100644 --- a/apps/server/src/modules/collaborative-storage/services/collaborative-storage.service.spec.ts +++ b/apps/server/src/modules/collaborative-storage/services/collaborative-storage.service.spec.ts @@ -8,11 +8,11 @@ import { TeamsRepo } from '@shared/repo'; import { setupEntities } from '@shared/testing'; import { teamFactory } from '@shared/testing/factory/team.factory'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationService } from '@src/modules/authorization'; -import { TeamMapper } from '@src/modules/collaborative-storage/mapper/team.mapper'; -import { CollaborativeStorageService } from '@src/modules/collaborative-storage/services/collaborative-storage.service'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; -import { RoleService } from '@src/modules/role/service/role.service'; +import { AuthorizationService } from '@modules/authorization'; +import { TeamMapper } from '@modules/collaborative-storage/mapper/team.mapper'; +import { CollaborativeStorageService } from '@modules/collaborative-storage/services/collaborative-storage.service'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; +import { RoleService } from '@modules/role/service/role.service'; import { TeamDto } from './dto/team.dto'; describe('Collaborative Storage Service', () => { diff --git a/apps/server/src/modules/collaborative-storage/services/collaborative-storage.service.ts b/apps/server/src/modules/collaborative-storage/services/collaborative-storage.service.ts index dccb28a3bb3..f9807cf691c 100644 --- a/apps/server/src/modules/collaborative-storage/services/collaborative-storage.service.ts +++ b/apps/server/src/modules/collaborative-storage/services/collaborative-storage.service.ts @@ -3,8 +3,8 @@ import { EntityId, Permission } from '@shared/domain'; import { CollaborativeStorageAdapter } from '@shared/infra/collaborative-storage'; import { TeamsRepo } from '@shared/repo'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { RoleService } from '@src/modules/role/service/role.service'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { RoleService } from '@modules/role/service/role.service'; import { TeamMapper } from '../mapper/team.mapper'; import { TeamPermissionsDto } from './dto/team-permissions.dto'; import { TeamDto } from './dto/team.dto'; diff --git a/apps/server/src/modules/collaborative-storage/uc/collaborative-storage.uc.spec.ts b/apps/server/src/modules/collaborative-storage/uc/collaborative-storage.uc.spec.ts index cef601131b9..bfc52a11a59 100644 --- a/apps/server/src/modules/collaborative-storage/uc/collaborative-storage.uc.spec.ts +++ b/apps/server/src/modules/collaborative-storage/uc/collaborative-storage.uc.spec.ts @@ -1,11 +1,11 @@ import { createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; -import { TeamPermissionsBody } from '@src/modules/collaborative-storage/controller/dto/team-permissions.body.params'; -import { TeamRoleDto } from '@src/modules/collaborative-storage/controller/dto/team-role.params'; -import { TeamPermissionsMapper } from '@src/modules/collaborative-storage/mapper/team-permissions.mapper'; -import { CollaborativeStorageService } from '@src/modules/collaborative-storage/services/collaborative-storage.service'; -import { TeamDto } from '@src/modules/collaborative-storage/services/dto/team.dto'; -import { CollaborativeStorageUc } from '@src/modules/collaborative-storage/uc/collaborative-storage.uc'; +import { TeamPermissionsBody } from '@modules/collaborative-storage/controller/dto/team-permissions.body.params'; +import { TeamRoleDto } from '@modules/collaborative-storage/controller/dto/team-role.params'; +import { TeamPermissionsMapper } from '@modules/collaborative-storage/mapper/team-permissions.mapper'; +import { CollaborativeStorageService } from '@modules/collaborative-storage/services/collaborative-storage.service'; +import { TeamDto } from '@modules/collaborative-storage/services/dto/team.dto'; +import { CollaborativeStorageUc } from '@modules/collaborative-storage/uc/collaborative-storage.uc'; describe('TeamStorageUc', () => { let module: TestingModule; diff --git a/apps/server/src/modules/collaborative-storage/uc/collaborative-storage.uc.ts b/apps/server/src/modules/collaborative-storage/uc/collaborative-storage.uc.ts index 046c2f22112..137f89a54c6 100644 --- a/apps/server/src/modules/collaborative-storage/uc/collaborative-storage.uc.ts +++ b/apps/server/src/modules/collaborative-storage/uc/collaborative-storage.uc.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; -import { CollaborativeStorageService } from '@src/modules/collaborative-storage/services/collaborative-storage.service'; -import { TeamPermissionsMapper } from '@src/modules/collaborative-storage/mapper/team-permissions.mapper'; -import { TeamDto } from '@src/modules/collaborative-storage/services/dto/team.dto'; +import { CollaborativeStorageService } from '@modules/collaborative-storage/services/collaborative-storage.service'; +import { TeamPermissionsMapper } from '@modules/collaborative-storage/mapper/team-permissions.mapper'; +import { TeamDto } from '@modules/collaborative-storage/services/dto/team.dto'; import { TeamPermissionsBody } from '../controller/dto/team-permissions.body.params'; import { TeamRoleDto } from '../controller/dto/team-role.params'; diff --git a/apps/server/src/modules/copy-helper/dto/copy.response.ts b/apps/server/src/modules/copy-helper/dto/copy.response.ts index b343042f6e3..549dcac7014 100644 --- a/apps/server/src/modules/copy-helper/dto/copy.response.ts +++ b/apps/server/src/modules/copy-helper/dto/copy.response.ts @@ -1,5 +1,5 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; -import { CopyElementType, CopyStatusEnum } from '@src/modules/copy-helper/types/copy.types'; +import { CopyElementType, CopyStatusEnum } from '@modules/copy-helper/types/copy.types'; /** * DTO for returning a copy status document via api. diff --git a/apps/server/src/modules/copy-helper/mapper/copy.mapper.spec.ts b/apps/server/src/modules/copy-helper/mapper/copy.mapper.spec.ts index b2cd8db4212..bb82db63761 100644 --- a/apps/server/src/modules/copy-helper/mapper/copy.mapper.spec.ts +++ b/apps/server/src/modules/copy-helper/mapper/copy.mapper.spec.ts @@ -1,11 +1,11 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { setupEntities } from '@shared/testing'; -import { CopyElementType, CopyStatusEnum } from '@src/modules/copy-helper'; -import { LessonCopyApiParams } from '@src/modules/learnroom/controller/dto/lesson/lesson-copy.params'; -import { LessonCopyParentParams } from '@src/modules/lesson'; -import { TaskCopyApiParams } from '@src/modules/task/controller/dto/task-copy.params'; -import { TaskCopyParentParams } from '@src/modules/task/types'; +import { CopyElementType, CopyStatusEnum } from '@modules/copy-helper'; +import { LessonCopyApiParams } from '@modules/learnroom/controller/dto/lesson/lesson-copy.params'; +import { LessonCopyParentParams } from '@modules/lesson'; +import { TaskCopyApiParams } from '@modules/task/controller/dto/task-copy.params'; +import { TaskCopyParentParams } from '@modules/task/types'; import { CopyApiResponse } from '../dto/copy.response'; import { CopyMapper } from './copy.mapper'; diff --git a/apps/server/src/modules/copy-helper/mapper/copy.mapper.ts b/apps/server/src/modules/copy-helper/mapper/copy.mapper.ts index dcc266d607a..20aae3e888b 100644 --- a/apps/server/src/modules/copy-helper/mapper/copy.mapper.ts +++ b/apps/server/src/modules/copy-helper/mapper/copy.mapper.ts @@ -1,8 +1,8 @@ import { EntityId, LessonEntity, Task } from '@shared/domain'; -import { LessonCopyApiParams } from '@src/modules/learnroom/controller/dto/lesson/lesson-copy.params'; -import { LessonCopyParentParams } from '@src/modules/lesson/types'; -import { TaskCopyApiParams } from '@src/modules/task/controller/dto/task-copy.params'; -import { TaskCopyParentParams } from '@src/modules/task/types'; +import { LessonCopyApiParams } from '@modules/learnroom/controller/dto/lesson/lesson-copy.params'; +import { LessonCopyParentParams } from '@modules/lesson/types'; +import { TaskCopyApiParams } from '@modules/task/controller/dto/task-copy.params'; +import { TaskCopyParentParams } from '@modules/task/types'; import { CopyApiResponse } from '../dto/copy.response'; import { CopyStatus, CopyStatusEnum } from '../types/copy.types'; diff --git a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts index 6c9036f91f9..11c8eccdd3b 100644 --- a/apps/server/src/modules/files-storage-client/files-storage-client.module.ts +++ b/apps/server/src/modules/files-storage-client/files-storage-client.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; -import { CopyHelperModule } from '@src/modules/copy-helper'; +import { CopyHelperModule } from '@modules/copy-helper'; import { CopyFilesService } from './service/copy-files.service'; import { FilesStorageClientAdapterService } from './service/files-storage-client.service'; import { FilesStorageProducer } from './service/files-storage.producer'; diff --git a/apps/server/src/modules/files-storage-client/service/copy-files.service.spec.ts b/apps/server/src/modules/files-storage-client/service/copy-files.service.spec.ts index 0dadb1a87e1..5bfc98a361a 100644 --- a/apps/server/src/modules/files-storage-client/service/copy-files.service.spec.ts +++ b/apps/server/src/modules/files-storage-client/service/copy-files.service.spec.ts @@ -8,7 +8,7 @@ import { legacyFileEntityMockFactory, setupEntities, } from '@shared/testing'; -import { CopyElementType, CopyHelperService } from '@src/modules/copy-helper'; +import { CopyElementType, CopyHelperService } from '@modules/copy-helper'; import { CopyFilesService } from './copy-files.service'; import { FilesStorageClientAdapterService } from './files-storage-client.service'; diff --git a/apps/server/src/modules/files-storage-client/service/copy-files.service.ts b/apps/server/src/modules/files-storage-client/service/copy-files.service.ts index 60cbf06c27d..1dc5904eb9b 100644 --- a/apps/server/src/modules/files-storage-client/service/copy-files.service.ts +++ b/apps/server/src/modules/files-storage-client/service/copy-files.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain'; -import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; +import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; import { CopyFileDto } from '../dto'; import { EntityWithEmbeddedFiles } from '../interfaces'; import { CopyFilesOfParentParamBuilder, FileParamBuilder } from '../mapper'; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts index 956697a11d4..d6b6f4b7479 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-security.api.spec.ts @@ -3,7 +3,7 @@ import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common'; import { Permission } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, fileRecordFactory, @@ -12,9 +12,9 @@ import { schoolFactory, userFactory, } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { FilesStorageTestModule } from '@src/modules/files-storage'; -import { FileRecordListResponse, ScanResultParams } from '@src/modules/files-storage/controller/dto'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { FilesStorageTestModule } from '@modules/files-storage'; +import { FileRecordListResponse, ScanResultParams } from '@modules/files-storage/controller/dto'; import { Request } from 'express'; import request from 'supertest'; import { FileRecord, FileRecordParentType } from '../../entity'; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts index 659197c73d9..e1b792dea36 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-copy-files.api.spec.ts @@ -15,15 +15,15 @@ import { schoolFactory, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@src/modules/files-storage'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@modules/files-storage'; import { CopyFileParams, CopyFilesOfParentParams, FileRecordListResponse, FileRecordResponse, -} from '@src/modules/files-storage/controller/dto'; +} from '@modules/files-storage/controller/dto'; import { Request } from 'express'; import FileType from 'file-type-cjs/file-type-cjs-index'; import request from 'supertest'; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts index e63bc036510..7a25915b3d5 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-delete-files.api.spec.ts @@ -14,10 +14,10 @@ import { schoolFactory, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@src/modules/files-storage'; -import { FileRecordListResponse, FileRecordResponse } from '@src/modules/files-storage/controller/dto'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@modules/files-storage'; +import { FileRecordListResponse, FileRecordResponse } from '@modules/files-storage/controller/dto'; import { Request } from 'express'; import FileType from 'file-type-cjs/file-type-cjs-index'; import request from 'supertest'; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts index de8cda56198..331282060e2 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-download-upload.api.spec.ts @@ -7,10 +7,10 @@ import { EntityId, Permission } from '@shared/domain'; import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { cleanupCollections, mapUserToCurrentUser, roleFactory, schoolFactory, userFactory } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@src/modules/files-storage'; -import { FileRecordResponse } from '@src/modules/files-storage/controller/dto'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@modules/files-storage'; +import { FileRecordResponse } from '@modules/files-storage/controller/dto'; import { Request } from 'express'; import FileType from 'file-type-cjs/file-type-cjs-index'; import request from 'supertest'; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts index 6cc436fa7f6..b4d974fb24e 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-list-files.api.spec.ts @@ -11,10 +11,10 @@ import { schoolFactory, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { FilesStorageTestModule } from '@src/modules/files-storage'; -import { FileRecordListResponse, FileRecordResponse } from '@src/modules/files-storage/controller/dto'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { FilesStorageTestModule } from '@modules/files-storage'; +import { FileRecordListResponse, FileRecordResponse } from '@modules/files-storage/controller/dto'; import { Request } from 'express'; import request from 'supertest'; import { FileRecordParentType, PreviewStatus } from '../../entity'; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts index f905cae5399..82cc8545a97 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-preview.api.spec.ts @@ -7,10 +7,10 @@ import { EntityId, Permission } from '@shared/domain'; import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { cleanupCollections, mapUserToCurrentUser, roleFactory, schoolFactory, userFactory } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@src/modules/files-storage'; -import { FileRecordResponse } from '@src/modules/files-storage/controller/dto'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@modules/files-storage'; +import { FileRecordResponse } from '@modules/files-storage/controller/dto'; import { Request } from 'express'; import FileType from 'file-type-cjs/file-type-cjs-index'; import request from 'supertest'; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts index 2b3cb75f55d..e28f4dc327f 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-rename-file.api.spec.ts @@ -3,7 +3,7 @@ import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common'; import { Permission } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, fileRecordFactory, @@ -12,9 +12,9 @@ import { schoolFactory, userFactory, } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { FilesStorageTestModule } from '@src/modules/files-storage'; -import { FileRecordResponse, RenameFileParams } from '@src/modules/files-storage/controller/dto'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { FilesStorageTestModule } from '@modules/files-storage'; +import { FileRecordResponse, RenameFileParams } from '@modules/files-storage/controller/dto'; import { Request } from 'express'; import request from 'supertest'; import { FileRecord, FileRecordParentType } from '../../entity'; diff --git a/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts b/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts index 7835c0217d4..f6e9a694ad3 100644 --- a/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts +++ b/apps/server/src/modules/files-storage/controller/api-test/files-storage-restore-files.api.spec.ts @@ -14,10 +14,10 @@ import { schoolFactory, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@src/modules/files-storage'; -import { FileRecordListResponse, FileRecordResponse } from '@src/modules/files-storage/controller/dto'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { FILES_STORAGE_S3_CONNECTION, FilesStorageTestModule } from '@modules/files-storage'; +import { FileRecordListResponse, FileRecordResponse } from '@modules/files-storage/controller/dto'; import { Request } from 'express'; import FileType from 'file-type-cjs/file-type-cjs-index'; import request from 'supertest'; diff --git a/apps/server/src/modules/files-storage/controller/files-storage.controller.ts b/apps/server/src/modules/files-storage/controller/files-storage.controller.ts index a8bd1b35e9b..564919670e4 100644 --- a/apps/server/src/modules/files-storage/controller/files-storage.controller.ts +++ b/apps/server/src/modules/files-storage/controller/files-storage.controller.ts @@ -24,7 +24,7 @@ import { import { ApiConsumes, ApiHeader, ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError, RequestLoggingInterceptor } from '@shared/common'; import { PaginationParams } from '@shared/controller'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { Request, Response } from 'express'; import { GetFileResponse } from '../interface'; import { FilesStorageMapper } from '../mapper'; diff --git a/apps/server/src/modules/files-storage/files-storage-api.module.ts b/apps/server/src/modules/files-storage/files-storage-api.module.ts index aab383a158f..3bf18aa4047 100644 --- a/apps/server/src/modules/files-storage/files-storage-api.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-api.module.ts @@ -1,8 +1,8 @@ import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { CoreModule } from '@src/core'; -import { AuthenticationModule } from '@src/modules/authentication/authentication.module'; -import { AuthorizationReferenceModule } from '@src/modules/authorization/authorization-reference.module'; +import { AuthenticationModule } from '@modules/authentication/authentication.module'; +import { AuthorizationReferenceModule } from '@modules/authorization/authorization-reference.module'; import { FileSecurityController, FilesStorageController } from './controller'; import { FilesStorageModule } from './files-storage.module'; import { FilesStorageUC } from './uc'; diff --git a/apps/server/src/modules/files-storage/files-storage-test.module.ts b/apps/server/src/modules/files-storage/files-storage-test.module.ts index ac3777a041f..6f3d865ebb2 100644 --- a/apps/server/src/modules/files-storage/files-storage-test.module.ts +++ b/apps/server/src/modules/files-storage/files-storage-test.module.ts @@ -5,8 +5,8 @@ import { MongoDatabaseModuleOptions } from '@shared/infra/database/mongo-memory- import { RabbitMQWrapperTestModule } from '@shared/infra/rabbitmq/rabbitmq.module'; import { CoreModule } from '@src/core'; import { LoggerModule } from '@src/core/logger'; -import { AuthenticationModule } from '@src/modules/authentication/authentication.module'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthenticationModule } from '@modules/authentication/authentication.module'; +import { AuthorizationModule } from '@modules/authorization'; import { FileRecord } from './entity'; import { FilesStorageApiModule } from './files-storage-api.module'; diff --git a/apps/server/src/modules/files-storage/mapper/files-storage.mapper.spec.ts b/apps/server/src/modules/files-storage/mapper/files-storage.mapper.spec.ts index a26103ae983..1f681c371d1 100644 --- a/apps/server/src/modules/files-storage/mapper/files-storage.mapper.spec.ts +++ b/apps/server/src/modules/files-storage/mapper/files-storage.mapper.spec.ts @@ -1,6 +1,6 @@ import { NotImplementedException } from '@nestjs/common'; import { fileRecordFactory, setupEntities } from '@shared/testing'; -import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; import { DownloadFileParams, FileRecordListResponse, diff --git a/apps/server/src/modules/files-storage/mapper/files-storage.mapper.ts b/apps/server/src/modules/files-storage/mapper/files-storage.mapper.ts index 9b30acd4ada..5b786aff96e 100644 --- a/apps/server/src/modules/files-storage/mapper/files-storage.mapper.ts +++ b/apps/server/src/modules/files-storage/mapper/files-storage.mapper.ts @@ -1,5 +1,5 @@ import { NotImplementedException, StreamableFile } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; import { plainToClass } from 'class-transformer'; import { DownloadFileParams, diff --git a/apps/server/src/modules/files-storage/uc/files-storage-copy.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-copy.uc.spec.ts index 612558e80c1..2b7f1052121 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-copy.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-copy.uc.spec.ts @@ -8,8 +8,8 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { Action } from '@src/modules/authorization'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { Action } from '@modules/authorization'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { FileRecordParams } from '../controller/dto'; import { FileRecord, FileRecordParentType } from '../entity'; import { CopyFileResponseBuilder } from '../mapper'; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-delete.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-delete.uc.spec.ts index fc461a50106..a1aaf0342ee 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-delete.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-delete.uc.spec.ts @@ -8,7 +8,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { FileRecordParams } from '../controller/dto'; import { FileRecord, FileRecordParentType } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-download-preview.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-download-preview.uc.spec.ts index 795939e5cb2..81b54553d1e 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-download-preview.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-download-preview.uc.spec.ts @@ -7,7 +7,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { SingleFileParams } from '../controller/dto'; import { FileRecord } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-download.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-download.uc.spec.ts index 3e7fa61fd7f..51ad0fd0b77 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-download.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-download.uc.spec.ts @@ -7,7 +7,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { SingleFileParams } from '../controller/dto'; import { FileRecord } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-get.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-get.uc.spec.ts index 7f372a1fe80..60d3fdd1a64 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-get.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-get.uc.spec.ts @@ -6,7 +6,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { FileRecordParams } from '../controller/dto'; import { FileRecord, FileRecordParentType } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-restore.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-restore.uc.spec.ts index e01e3116b79..b66c9c8821d 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-restore.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-restore.uc.spec.ts @@ -7,7 +7,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { FileRecordParams, SingleFileParams } from '../controller/dto'; import { FileRecord, FileRecordParentType } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-update.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-update.uc.spec.ts index 19d9984eea8..c59f37d2599 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-update.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-update.uc.spec.ts @@ -6,7 +6,7 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { RenameFileParams, ScanResultParams, SingleFileParams } from '../controller/dto'; import { FileRecord } from '../entity'; import { FileStorageAuthorizationContext } from '../files-storage.const'; diff --git a/apps/server/src/modules/files-storage/uc/files-storage-upload.uc.spec.ts b/apps/server/src/modules/files-storage/uc/files-storage-upload.uc.spec.ts index ed7defb54fb..43d9e9b7750 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage-upload.uc.spec.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage-upload.uc.spec.ts @@ -8,8 +8,8 @@ import { AntivirusService } from '@shared/infra/antivirus'; import { S3ClientAdapter } from '@shared/infra/s3-client'; import { AxiosHeadersKeyValue, axiosResponseFactory, fileRecordFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { Action } from '@src/modules/authorization'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { Action } from '@modules/authorization'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { AxiosRequestConfig, AxiosResponse } from 'axios'; import { Request } from 'express'; import { of } from 'rxjs'; diff --git a/apps/server/src/modules/files-storage/uc/files-storage.uc.ts b/apps/server/src/modules/files-storage/uc/files-storage.uc.ts index f5e6d372a6b..833d7575bdf 100644 --- a/apps/server/src/modules/files-storage/uc/files-storage.uc.ts +++ b/apps/server/src/modules/files-storage/uc/files-storage.uc.ts @@ -2,8 +2,8 @@ import { HttpService } from '@nestjs/axios'; import { Injectable, NotFoundException } from '@nestjs/common'; import { Counted, EntityId } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationContext } from '@src/modules/authorization'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { AuthorizationContext } from '@modules/authorization'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { AxiosRequestConfig, AxiosResponse } from 'axios'; import busboy from 'busboy'; import { Request } from 'express'; diff --git a/apps/server/src/modules/files/entity/file.entity.spec.ts b/apps/server/src/modules/files/entity/file.entity.spec.ts index 1f6150b12c5..ea9649f4c66 100644 --- a/apps/server/src/modules/files/entity/file.entity.spec.ts +++ b/apps/server/src/modules/files/entity/file.entity.spec.ts @@ -1,6 +1,6 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { setupEntities, storageProviderFactory } from '@shared/testing'; -import { FileOwnerModel } from '@src/modules/files/domain'; +import { FileOwnerModel } from '@modules/files/domain'; import { fileEntityFactory, filePermissionEntityFactory } from './testing'; import { FileEntity } from './file.entity'; import { FileSecurityCheckEntity } from './file-security-check.entity'; diff --git a/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts b/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts index 9f64e07129a..9eee09a30af 100644 --- a/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts +++ b/apps/server/src/modules/fwu-learning-contents/controller/api-test/fwu-learning-contents.api.spec.ts @@ -3,7 +3,7 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { INestApplication, NotFoundException } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { S3ClientAdapter } from '@shared/infra/s3-client'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; import { Readable } from 'stream'; import request from 'supertest'; import { FwuLearningContentsTestModule } from '../../fwu-learning-contents-test.module'; diff --git a/apps/server/src/modules/fwu-learning-contents/controller/fwu-learning-contents.controller.ts b/apps/server/src/modules/fwu-learning-contents/controller/fwu-learning-contents.controller.ts index 8dedd3e5f96..f9e7bd6a238 100644 --- a/apps/server/src/modules/fwu-learning-contents/controller/fwu-learning-contents.controller.ts +++ b/apps/server/src/modules/fwu-learning-contents/controller/fwu-learning-contents.controller.ts @@ -10,7 +10,7 @@ import { StreamableFile, } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { Authenticate } from '@src/modules/authentication'; +import { Authenticate } from '@modules/authentication'; import { Request, Response } from 'express'; import { FwuLearningContentsUc } from '../uc/fwu-learning-contents.uc'; import { GetFwuLearningContentParams } from './dto/fwu-learning-contents.params'; diff --git a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts index 5b6efa3bbb1..62e25bef4e2 100644 --- a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts +++ b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents-test.module.ts @@ -9,8 +9,8 @@ import { S3ClientModule } from '@shared/infra/s3-client'; import { createConfigModuleOptions } from '@src/config'; import { CoreModule } from '@src/core'; import { LoggerModule } from '@src/core/logger'; -import { AuthenticationModule } from '@src/modules/authentication/authentication.module'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthenticationModule } from '@modules/authentication/authentication.module'; +import { AuthorizationModule } from '@modules/authorization'; import { FwuLearningContentsController } from './controller/fwu-learning-contents.controller'; import { config, s3Config } from './fwu-learning-contents.config'; import { FwuLearningContentsUc } from './uc/fwu-learning-contents.uc'; diff --git a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.module.ts b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.module.ts index 2f4cd148d1b..b15c8a04054 100644 --- a/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.module.ts +++ b/apps/server/src/modules/fwu-learning-contents/fwu-learning-contents.module.ts @@ -9,7 +9,7 @@ import { S3ClientModule } from '@shared/infra/s3-client'; import { DB_PASSWORD, DB_URL, DB_USERNAME, createConfigModuleOptions } from '@src/config'; import { CoreModule } from '@src/core'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationModule } from '@modules/authorization'; import { AuthenticationModule } from '../authentication/authentication.module'; import { FwuLearningContentsController } from './controller/fwu-learning-contents.controller'; import { config, s3Config } from './fwu-learning-contents.config'; diff --git a/apps/server/src/modules/group/controller/api-test/group.api.spec.ts b/apps/server/src/modules/group/controller/api-test/group.api.spec.ts index 39bb86a4caa..471d8b348af 100644 --- a/apps/server/src/modules/group/controller/api-test/group.api.spec.ts +++ b/apps/server/src/modules/group/controller/api-test/group.api.spec.ts @@ -12,9 +12,9 @@ import { UserAndAccountTestFactory, userFactory, } from '@shared/testing'; -import { ClassEntity } from '@src/modules/class/entity'; -import { classEntityFactory } from '@src/modules/class/entity/testing/factory/class.entity.factory'; -import { ServerTestModule } from '@src/modules/server'; +import { ClassEntity } from '@modules/class/entity'; +import { classEntityFactory } from '@modules/class/entity/testing/factory/class.entity.factory'; +import { ServerTestModule } from '@modules/server'; import { GroupEntity, GroupEntityTypes } from '../../entity'; import { ClassRootType } from '../../uc/dto/class-root-type'; import { ClassInfoSearchListResponse, ClassSortBy } from '../dto'; diff --git a/apps/server/src/modules/group/controller/group.controller.ts b/apps/server/src/modules/group/controller/group.controller.ts index 344fbe6b98f..553d07b0d5c 100644 --- a/apps/server/src/modules/group/controller/group.controller.ts +++ b/apps/server/src/modules/group/controller/group.controller.ts @@ -3,7 +3,7 @@ import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { PaginationParams } from '@shared/controller'; import { Page } from '@shared/domain'; import { ErrorResponse } from '@src/core/error/dto'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { GroupUc } from '../uc'; import { ClassInfoDto } from '../uc/dto'; import { ClassInfoSearchListResponse, ClassSortParams } from './dto'; diff --git a/apps/server/src/modules/group/group-api.module.ts b/apps/server/src/modules/group/group-api.module.ts index 913fb2ef903..14a564b4741 100644 --- a/apps/server/src/modules/group/group-api.module.ts +++ b/apps/server/src/modules/group/group-api.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { ClassModule } from '@src/modules/class'; -import { RoleModule } from '@src/modules/role'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; -import { SystemModule } from '@src/modules/system'; -import { UserModule } from '@src/modules/user'; +import { AuthorizationModule } from '@modules/authorization'; +import { ClassModule } from '@modules/class'; +import { RoleModule } from '@modules/role'; +import { LegacySchoolModule } from '@modules/legacy-school'; +import { SystemModule } from '@modules/system'; +import { UserModule } from '@modules/user'; import { GroupController } from './controller'; import { GroupModule } from './group.module'; import { GroupUc } from './uc'; diff --git a/apps/server/src/modules/group/service/group.service.ts b/apps/server/src/modules/group/service/group.service.ts index f3ce6a287e8..0ccded2442b 100644 --- a/apps/server/src/modules/group/service/group.service.ts +++ b/apps/server/src/modules/group/service/group.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { NotFoundLoggableException } from '@shared/common/loggable-exception'; import { EntityId, type UserDO } from '@shared/domain'; -import { AuthorizationLoaderServiceGeneric } from '@src/modules/authorization'; +import { AuthorizationLoaderServiceGeneric } from '@modules/authorization'; import { Group } from '../domain'; import { GroupRepo } from '../repo'; diff --git a/apps/server/src/modules/group/uc/dto/resolved-group-user.ts b/apps/server/src/modules/group/uc/dto/resolved-group-user.ts index 862abdba594..80db4973ef2 100644 --- a/apps/server/src/modules/group/uc/dto/resolved-group-user.ts +++ b/apps/server/src/modules/group/uc/dto/resolved-group-user.ts @@ -1,5 +1,5 @@ import { UserDO } from '@shared/domain'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; export class ResolvedGroupUser { user: UserDO; diff --git a/apps/server/src/modules/group/uc/group.uc.spec.ts b/apps/server/src/modules/group/uc/group.uc.spec.ts index ed089007a72..3de4d262679 100644 --- a/apps/server/src/modules/group/uc/group.uc.spec.ts +++ b/apps/server/src/modules/group/uc/group.uc.spec.ts @@ -13,15 +13,15 @@ import { userDoFactory, userFactory, } from '@shared/testing'; -import { Action, AuthorizationContext, AuthorizationService } from '@src/modules/authorization'; -import { ClassService } from '@src/modules/class'; -import { Class } from '@src/modules/class/domain'; -import { classFactory } from '@src/modules/class/domain/testing/factory/class.factory'; -import { LegacySchoolService, SchoolYearService } from '@src/modules/legacy-school'; -import { RoleService } from '@src/modules/role'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; -import { SystemDto, SystemService } from '@src/modules/system'; -import { UserService } from '@src/modules/user'; +import { Action, AuthorizationContext, AuthorizationService } from '@modules/authorization'; +import { ClassService } from '@modules/class'; +import { Class } from '@modules/class/domain'; +import { classFactory } from '@modules/class/domain/testing/factory/class.factory'; +import { LegacySchoolService, SchoolYearService } from '@modules/legacy-school'; +import { RoleService } from '@modules/role'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; +import { SystemDto, SystemService } from '@modules/system'; +import { UserService } from '@modules/user'; import { Group } from '../domain'; import { GroupService } from '../service'; import { ClassInfoDto } from './dto'; diff --git a/apps/server/src/modules/group/uc/group.uc.ts b/apps/server/src/modules/group/uc/group.uc.ts index 1d884c5a325..23cac984a6d 100644 --- a/apps/server/src/modules/group/uc/group.uc.ts +++ b/apps/server/src/modules/group/uc/group.uc.ts @@ -1,13 +1,13 @@ import { Injectable } from '@nestjs/common'; import { EntityId, LegacySchoolDo, Page, Permission, SchoolYearEntity, SortOrder, User, UserDO } from '@shared/domain'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { ClassService } from '@src/modules/class'; -import { Class } from '@src/modules/class/domain'; -import { LegacySchoolService, SchoolYearService } from '@src/modules/legacy-school'; -import { RoleService } from '@src/modules/role'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; -import { SystemDto, SystemService } from '@src/modules/system'; -import { UserService } from '@src/modules/user'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { ClassService } from '@modules/class'; +import { Class } from '@modules/class/domain'; +import { LegacySchoolService, SchoolYearService } from '@modules/legacy-school'; +import { RoleService } from '@modules/role'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; +import { SystemDto, SystemService } from '@modules/system'; +import { UserService } from '@modules/user'; import { Group, GroupUser } from '../domain'; import { GroupService } from '../service'; import { SortHelper } from '../util'; diff --git a/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts b/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts index 596302c4a5c..d5a415498db 100644 --- a/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts +++ b/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts @@ -1,6 +1,6 @@ import { RoleName, SchoolYearEntity, UserDO } from '@shared/domain'; -import { Class } from '@src/modules/class/domain'; -import { SystemDto } from '@src/modules/system'; +import { Class } from '@modules/class/domain'; +import { SystemDto } from '@modules/system'; import { Group } from '../../domain'; import { ClassInfoDto, ResolvedGroupUser } from '../dto'; import { ClassRootType } from '../dto/class-root-type'; diff --git a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor.api.spec.ts b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor.api.spec.ts index f2e40310645..57a8a66b347 100644 --- a/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor.api.spec.ts +++ b/apps/server/src/modules/h5p-editor/controller/api-test/h5p-editor.api.spec.ts @@ -2,7 +2,7 @@ import { EntityManager } from '@mikro-orm/core'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { TestApiClient, UserAndAccountTestFactory } from '@shared/testing'; -import { H5PEditorTestModule } from '@src/modules/h5p-editor/h5p-editor-test.module'; +import { H5PEditorTestModule } from '@modules/h5p-editor/h5p-editor-test.module'; describe('H5PEditor Controller (api)', () => { let app: INestApplication; diff --git a/apps/server/src/modules/h5p-editor/controller/h5p-editor.controller.ts b/apps/server/src/modules/h5p-editor/controller/h5p-editor.controller.ts index 6161b7d7c23..519f96e75e1 100644 --- a/apps/server/src/modules/h5p-editor/controller/h5p-editor.controller.ts +++ b/apps/server/src/modules/h5p-editor/controller/h5p-editor.controller.ts @@ -1,7 +1,7 @@ import { BadRequestException, Controller, ForbiddenException, Get, InternalServerErrorException } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError } from '@shared/common'; -import { Authenticate } from '@src/modules/authentication'; +import { Authenticate } from '@modules/authentication/decorator/auth.decorator'; // Dummy html response so we can test i-frame integration const dummyResponse = (title: string) => ` diff --git a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts index dfe1b1ec846..fccb5e2841b 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor-test.module.ts @@ -5,8 +5,8 @@ import { MongoDatabaseModuleOptions } from '@shared/infra/database/mongo-memory- import { RabbitMQWrapperTestModule } from '@shared/infra/rabbitmq'; import { CoreModule } from '@src/core'; import { LoggerModule } from '@src/core/logger'; -import { AuthenticationModule } from '@src/modules/authentication/authentication.module'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthenticationModule } from '@modules/authentication/authentication.module'; +import { AuthorizationModule } from '@modules/authorization'; import { AuthenticationApiModule } from '../authentication/authentication-api.module'; import { H5PEditorModule } from './h5p-editor.module'; diff --git a/apps/server/src/modules/h5p-editor/h5p-editor.module.ts b/apps/server/src/modules/h5p-editor/h5p-editor.module.ts index 869f76d3a86..442f0a04409 100644 --- a/apps/server/src/modules/h5p-editor/h5p-editor.module.ts +++ b/apps/server/src/modules/h5p-editor/h5p-editor.module.ts @@ -6,7 +6,7 @@ import { Account, Role, SchoolEntity, SchoolYearEntity, SystemEntity, User } fro import { DB_PASSWORD, DB_URL, DB_USERNAME, createConfigModuleOptions } from '@src/config'; import { CoreModule } from '@src/core'; import { Logger } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationModule } from '@modules/authorization'; import { AuthenticationModule } from '../authentication/authentication.module'; import { H5PEditorController } from './controller/h5p-editor.controller'; import { config } from './h5p-editor.config'; diff --git a/apps/server/src/modules/index.ts b/apps/server/src/modules/index.ts deleted file mode 100644 index 111a64d9f33..00000000000 --- a/apps/server/src/modules/index.ts +++ /dev/null @@ -1,25 +0,0 @@ -export * from './account'; -export * from './authentication'; -export * from './authorization'; -export * from './board'; -export * from './collaborative-storage'; -export * from './files-storage'; -export * from './files-storage-client'; -export * from './fwu-learning-contents'; -export * from './learnroom'; -export * from './lesson'; -export * from './news'; -export * from './oauth'; -export * from './oauth-provider'; -export * from './provisioning'; -export * from './rocketchat'; -export * from './role'; -export * from './legacy-school'; -export * from './sharing'; -export * from './system'; -export * from './task'; -export * from './tool'; -export * from './user'; -export * from './user-import'; -export * from './user-login-migration'; -export * from './video-conference'; diff --git a/apps/server/src/modules/learnroom/controller/api-test/course.api.spec.ts b/apps/server/src/modules/learnroom/controller/api-test/course.api.spec.ts index 0d6518878f7..964df864abb 100644 --- a/apps/server/src/modules/learnroom/controller/api-test/course.api.spec.ts +++ b/apps/server/src/modules/learnroom/controller/api-test/course.api.spec.ts @@ -3,8 +3,8 @@ import { INestApplication, StreamableFile } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; import { cleanupCollections, courseFactory, UserAndAccountTestFactory, TestApiClient } from '@shared/testing'; -import { CourseMetadataListResponse } from '@src/modules/learnroom/controller/dto'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { CourseMetadataListResponse } from '@modules/learnroom/controller/dto'; +import { ServerTestModule } from '@modules/server/server.module'; const createStudent = () => { const { studentUser, studentAccount } = UserAndAccountTestFactory.buildStudent({}, [Permission.COURSE_VIEW]); diff --git a/apps/server/src/modules/learnroom/controller/api-test/dashboard.api.spec.ts b/apps/server/src/modules/learnroom/controller/api-test/dashboard.api.spec.ts index c3e9dc3d5e0..b6c27736d93 100644 --- a/apps/server/src/modules/learnroom/controller/api-test/dashboard.api.spec.ts +++ b/apps/server/src/modules/learnroom/controller/api-test/dashboard.api.spec.ts @@ -2,12 +2,12 @@ import { EntityManager, ObjectId } from '@mikro-orm/mongodb'; import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { DashboardEntity, GridElement, Permission, User, RoleName } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { IDashboardRepo } from '@shared/repo'; import { courseFactory, mapUserToCurrentUser, roleFactory, userFactory } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { DashboardResponse } from '@src/modules/learnroom/controller/dto'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { DashboardResponse } from '@modules/learnroom/controller/dto'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/learnroom/controller/api-test/rooms-copy-timeout.api.spec.ts b/apps/server/src/modules/learnroom/controller/api-test/rooms-copy-timeout.api.spec.ts index 3b9f04e07db..2941e185ca1 100644 --- a/apps/server/src/modules/learnroom/controller/api-test/rooms-copy-timeout.api.spec.ts +++ b/apps/server/src/modules/learnroom/controller/api-test/rooms-copy-timeout.api.spec.ts @@ -3,7 +3,7 @@ import { EntityManager } from '@mikro-orm/mongodb'; import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, courseFactory, @@ -12,10 +12,10 @@ import { roleFactory, userFactory, } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; import { Request } from 'express'; import request from 'supertest'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { createMock } from '@golevelup/ts-jest'; // config must be set outside before the server module is importat, otherwise the configuration is already set @@ -23,7 +23,7 @@ const configBefore = Configuration.toObject({ plainSecrets: true }); Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); Configuration.set('INCOMING_REQUEST_TIMEOUT_COPY_API', 1); // eslint-disable-next-line import/first -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; // This needs to be in a separate test file because of the above configuration. // When we find a way to mock the config, it should be moved alongside the other API tests. diff --git a/apps/server/src/modules/learnroom/controller/api-test/rooms.api.spec.ts b/apps/server/src/modules/learnroom/controller/api-test/rooms.api.spec.ts index 2c62b74c9fd..6db5100405a 100644 --- a/apps/server/src/modules/learnroom/controller/api-test/rooms.api.spec.ts +++ b/apps/server/src/modules/learnroom/controller/api-test/rooms.api.spec.ts @@ -14,12 +14,12 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { CopyApiResponse } from '@src/modules/copy-helper'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; -import { SingleColumnBoardResponse } from '@src/modules/learnroom/controller/dto'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { CopyApiResponse } from '@modules/copy-helper'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; +import { SingleColumnBoardResponse } from '@modules/learnroom/controller/dto'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/learnroom/controller/course.controller.ts b/apps/server/src/modules/learnroom/controller/course.controller.ts index 7b09111edff..dfb4e920957 100644 --- a/apps/server/src/modules/learnroom/controller/course.controller.ts +++ b/apps/server/src/modules/learnroom/controller/course.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, NotFoundException, Param, Query, Res, StreamableFile } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { PaginationParams } from '@shared/controller/'; import { Response } from 'express'; import { ConfigService } from '@nestjs/config'; diff --git a/apps/server/src/modules/learnroom/controller/dashboard.controller.spec.ts b/apps/server/src/modules/learnroom/controller/dashboard.controller.spec.ts index 59445f2c1f8..284f18ef862 100644 --- a/apps/server/src/modules/learnroom/controller/dashboard.controller.spec.ts +++ b/apps/server/src/modules/learnroom/controller/dashboard.controller.spec.ts @@ -7,7 +7,7 @@ import { LearnroomMetadata, LearnroomTypes, } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { DashboardUc } from '../uc/dashboard.uc'; import { DashboardController } from './dashboard.controller'; import { DashboardResponse } from './dto'; diff --git a/apps/server/src/modules/learnroom/controller/dashboard.controller.ts b/apps/server/src/modules/learnroom/controller/dashboard.controller.ts index da667a85f20..224f6c41ca7 100644 --- a/apps/server/src/modules/learnroom/controller/dashboard.controller.ts +++ b/apps/server/src/modules/learnroom/controller/dashboard.controller.ts @@ -1,6 +1,6 @@ import { Body, Controller, Get, Param, Patch, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { DashboardMapper } from '../mapper/dashboard.mapper'; import { DashboardUc } from '../uc/dashboard.uc'; import { DashboardResponse, DashboardUrlParams, MoveElementParams, PatchGroupParams } from './dto'; diff --git a/apps/server/src/modules/learnroom/controller/dto/single-column-board/board-element.response.ts b/apps/server/src/modules/learnroom/controller/dto/single-column-board/board-element.response.ts index fb110ff95d9..4e9d139124b 100644 --- a/apps/server/src/modules/learnroom/controller/dto/single-column-board/board-element.response.ts +++ b/apps/server/src/modules/learnroom/controller/dto/single-column-board/board-element.response.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { RoomBoardElementTypes } from '@src/modules/learnroom/types'; +import { RoomBoardElementTypes } from '@modules/learnroom/types'; import { BoardColumnBoardResponse } from './board-column-board.response'; import { BoardLessonResponse } from './board-lesson.response'; import { BoardTaskResponse } from './board-task.response'; diff --git a/apps/server/src/modules/learnroom/controller/rooms.controller.spec.ts b/apps/server/src/modules/learnroom/controller/rooms.controller.spec.ts index 5692cc3427b..c63e2e380ae 100644 --- a/apps/server/src/modules/learnroom/controller/rooms.controller.spec.ts +++ b/apps/server/src/modules/learnroom/controller/rooms.controller.spec.ts @@ -1,8 +1,8 @@ import { createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityId } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; -import { CopyApiResponse, CopyElementType, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; +import { ICurrentUser } from '@modules/authentication'; +import { CopyApiResponse, CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; import { RoomBoardResponseMapper } from '../mapper/room-board-response.mapper'; import { RoomBoardDTO } from '../types'; import { CourseCopyUC } from '../uc/course-copy.uc'; diff --git a/apps/server/src/modules/learnroom/controller/rooms.controller.ts b/apps/server/src/modules/learnroom/controller/rooms.controller.ts index e01950e7570..0e0b2f7c7a0 100644 --- a/apps/server/src/modules/learnroom/controller/rooms.controller.ts +++ b/apps/server/src/modules/learnroom/controller/rooms.controller.ts @@ -1,9 +1,9 @@ import { Body, Controller, Get, Param, Patch, Post } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { RequestTimeout } from '@shared/common'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; -import { CopyApiResponse, CopyMapper } from '@src/modules/copy-helper'; -import { serverConfig } from '@src/modules/server/server.config'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; +import { CopyApiResponse, CopyMapper } from '@modules/copy-helper'; +import { serverConfig } from '@modules/server/server.config'; import { RoomBoardResponseMapper } from '../mapper/room-board-response.mapper'; import { CourseCopyUC } from '../uc/course-copy.uc'; import { LessonCopyUC } from '../uc/lesson-copy.uc'; diff --git a/apps/server/src/modules/learnroom/learnroom-api.module.ts b/apps/server/src/modules/learnroom/learnroom-api.module.ts index 81a514a0a7b..5cfaada65b8 100644 --- a/apps/server/src/modules/learnroom/learnroom-api.module.ts +++ b/apps/server/src/modules/learnroom/learnroom-api.module.ts @@ -1,9 +1,9 @@ import { Module } from '@nestjs/common'; import { BoardRepo, CourseRepo, DashboardModelMapper, DashboardRepo, LessonRepo, UserRepo } from '@shared/repo'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { AuthorizationReferenceModule } from '@src/modules/authorization/authorization-reference.module'; -import { CopyHelperModule } from '@src/modules/copy-helper'; -import { LessonModule } from '@src/modules/lesson'; +import { AuthorizationModule } from '@modules/authorization'; +import { AuthorizationReferenceModule } from '@modules/authorization/authorization-reference.module'; +import { CopyHelperModule } from '@modules/copy-helper'; +import { LessonModule } from '@modules/lesson'; import { CourseController } from './controller/course.controller'; import { DashboardController } from './controller/dashboard.controller'; import { RoomsController } from './controller/rooms.controller'; diff --git a/apps/server/src/modules/learnroom/learnroom.module.ts b/apps/server/src/modules/learnroom/learnroom.module.ts index 1149b477c30..c84310ba05e 100644 --- a/apps/server/src/modules/learnroom/learnroom.module.ts +++ b/apps/server/src/modules/learnroom/learnroom.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { BoardRepo, CourseRepo, DashboardModelMapper, DashboardRepo, LessonRepo, UserRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { BoardModule } from '@src/modules/board'; -import { CopyHelperModule } from '@src/modules/copy-helper'; -import { LessonModule } from '@src/modules/lesson'; -import { TaskModule } from '@src/modules/task'; +import { BoardModule } from '@modules/board'; +import { CopyHelperModule } from '@modules/copy-helper'; +import { LessonModule } from '@modules/lesson'; +import { TaskModule } from '@modules/task'; import { BoardCopyService, ColumnBoardTargetService, diff --git a/apps/server/src/modules/learnroom/service/board-copy.service.spec.ts b/apps/server/src/modules/learnroom/service/board-copy.service.spec.ts index 87542f7efb2..51ebc404d83 100644 --- a/apps/server/src/modules/learnroom/service/board-copy.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/board-copy.service.spec.ts @@ -17,10 +17,10 @@ import { userFactory, } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { ColumnBoardCopyService } from '@src/modules/board/service/column-board-copy.service'; -import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; -import { LessonCopyService } from '@src/modules/lesson/service'; -import { TaskCopyService } from '@src/modules/task/service'; +import { ColumnBoardCopyService } from '@modules/board/service/column-board-copy.service'; +import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { LessonCopyService } from '@modules/lesson/service'; +import { TaskCopyService } from '@modules/task/service'; import { BoardCopyService } from './board-copy.service'; describe('board copy service', () => { diff --git a/apps/server/src/modules/learnroom/service/board-copy.service.ts b/apps/server/src/modules/learnroom/service/board-copy.service.ts index da31f4cef4e..f695dfd2c05 100644 --- a/apps/server/src/modules/learnroom/service/board-copy.service.ts +++ b/apps/server/src/modules/learnroom/service/board-copy.service.ts @@ -19,11 +19,11 @@ import { } from '@shared/domain'; import { BoardRepo } from '@shared/repo'; import { LegacyLogger } from '@src/core/logger'; -import { ColumnBoardCopyService } from '@src/modules/board/service/column-board-copy.service'; -import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; -import { getResolvedValues } from '@src/modules/files-storage/helper'; -import { LessonCopyService } from '@src/modules/lesson/service'; -import { TaskCopyService } from '@src/modules/task/service'; +import { ColumnBoardCopyService } from '@modules/board/service/column-board-copy.service'; +import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { getResolvedValues } from '@modules/files-storage/helper'; +import { LessonCopyService } from '@modules/lesson/service'; +import { TaskCopyService } from '@modules/task/service'; import { sortBy } from 'lodash'; type BoardCopyParams = { diff --git a/apps/server/src/modules/learnroom/service/column-board-target.service.spec.ts b/apps/server/src/modules/learnroom/service/column-board-target.service.spec.ts index 59a6169a2a3..64db859ddac 100644 --- a/apps/server/src/modules/learnroom/service/column-board-target.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/column-board-target.service.spec.ts @@ -4,7 +4,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ColumnBoardTarget } from '@shared/domain'; import { MongoMemoryDatabaseModule } from '@shared/infra/database'; import { cleanupCollections, columnBoardTargetFactory } from '@shared/testing'; -import { ColumnBoardService } from '@src/modules/board'; +import { ColumnBoardService } from '@modules/board'; import { ColumnBoardTargetService } from './column-board-target.service'; describe(ColumnBoardTargetService.name, () => { diff --git a/apps/server/src/modules/learnroom/service/column-board-target.service.ts b/apps/server/src/modules/learnroom/service/column-board-target.service.ts index 8f72cb3e44e..79b476c4fe6 100644 --- a/apps/server/src/modules/learnroom/service/column-board-target.service.ts +++ b/apps/server/src/modules/learnroom/service/column-board-target.service.ts @@ -2,7 +2,7 @@ import { FilterQuery } from '@mikro-orm/core'; import { EntityManager } from '@mikro-orm/mongodb'; import { Injectable } from '@nestjs/common'; import { ColumnBoardTarget, EntityId } from '@shared/domain'; -import { ColumnBoardService } from '@src/modules/board'; +import { ColumnBoardService } from '@modules/board'; @Injectable() export class ColumnBoardTargetService { diff --git a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.spec.ts b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.spec.ts index e8e3d43f9bc..af1bc727d6a 100644 --- a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.spec.ts @@ -9,10 +9,10 @@ import { Task, } from '@shared/domain'; import { courseFactory, lessonFactory, setupEntities, taskFactory } from '@shared/testing'; -import { CommonCartridgeExportService } from '@src/modules/learnroom/service/common-cartridge-export.service'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LessonService } from '@src/modules/lesson/service'; -import { TaskService } from '@src/modules/task/service/task.service'; +import { CommonCartridgeExportService } from '@modules/learnroom/service/common-cartridge-export.service'; +import { CourseService } from '@modules/learnroom/service'; +import { LessonService } from '@modules/lesson/service'; +import { TaskService } from '@modules/task/service/task.service'; import AdmZip from 'adm-zip'; import { CommonCartridgeVersion } from '../common-cartridge'; diff --git a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts index b41b6314fa8..e25d2a62367 100644 --- a/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts +++ b/apps/server/src/modules/learnroom/service/common-cartridge-export.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { Course, EntityId, IComponentProperties, Task } from '@shared/domain'; -import { LessonService } from '@src/modules/lesson/service'; +import { LessonService } from '@modules/lesson/service'; import { ComponentType } from '@src/shared/domain/entity/lesson.entity'; -import { TaskService } from '@src/modules/task/service'; +import { TaskService } from '@modules/task/service'; import { CommonCartridgeFileBuilder, CommonCartridgeIntendedUseType, diff --git a/apps/server/src/modules/learnroom/service/course-copy.service.spec.ts b/apps/server/src/modules/learnroom/service/course-copy.service.spec.ts index 866ba655d97..969360dcc2e 100644 --- a/apps/server/src/modules/learnroom/service/course-copy.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/course-copy.service.spec.ts @@ -10,8 +10,8 @@ import { setupEntities, userFactory, } from '@shared/testing'; -import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@src/modules/copy-helper'; -import { LessonCopyService } from '@src/modules/lesson/service'; +import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@modules/copy-helper'; +import { LessonCopyService } from '@modules/lesson/service'; import { BoardCopyService } from './board-copy.service'; import { CourseCopyService } from './course-copy.service'; import { RoomsService } from './rooms.service'; diff --git a/apps/server/src/modules/learnroom/service/course-copy.service.ts b/apps/server/src/modules/learnroom/service/course-copy.service.ts index a8a0c236673..51f98bb436b 100644 --- a/apps/server/src/modules/learnroom/service/course-copy.service.ts +++ b/apps/server/src/modules/learnroom/service/course-copy.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Course, EntityId, User } from '@shared/domain'; import { BoardRepo, CourseRepo, UserRepo } from '@shared/repo'; -import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; +import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; import { BoardCopyService } from './board-copy.service'; import { RoomsService } from './rooms.service'; diff --git a/apps/server/src/modules/learnroom/service/rooms.service.spec.ts b/apps/server/src/modules/learnroom/service/rooms.service.spec.ts index 789edcb9099..2358e2b2067 100644 --- a/apps/server/src/modules/learnroom/service/rooms.service.spec.ts +++ b/apps/server/src/modules/learnroom/service/rooms.service.spec.ts @@ -6,8 +6,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { BoardExternalReference, BoardExternalReferenceType, EntityId } from '@shared/domain'; import { BoardRepo, LessonRepo } from '@shared/repo'; import { boardFactory, courseFactory, lessonFactory, setupEntities, taskFactory, userFactory } from '@shared/testing'; -import { CardService, ColumnBoardService, ColumnService, ContentElementService } from '@src/modules/board'; -import { TaskService } from '@src/modules/task/service'; +import { CardService, ColumnBoardService, ColumnService, ContentElementService } from '@modules/board'; +import { TaskService } from '@modules/task/service'; import { ColumnBoardTargetService } from './column-board-target.service'; import { RoomsService } from './rooms.service'; diff --git a/apps/server/src/modules/learnroom/service/rooms.service.ts b/apps/server/src/modules/learnroom/service/rooms.service.ts index 24bffb03f4f..cc8b95e09b0 100644 --- a/apps/server/src/modules/learnroom/service/rooms.service.ts +++ b/apps/server/src/modules/learnroom/service/rooms.service.ts @@ -2,8 +2,8 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { Injectable } from '@nestjs/common'; import { Board, BoardExternalReferenceType, ColumnBoardTarget, EntityId } from '@shared/domain'; import { BoardRepo, LessonRepo } from '@shared/repo'; -import { ColumnBoardService } from '@src/modules/board'; -import { TaskService } from '@src/modules/task/service'; +import { ColumnBoardService } from '@modules/board'; +import { TaskService } from '@modules/task/service'; import { ColumnBoardTargetService } from './column-board-target.service'; @Injectable() diff --git a/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts index 33beee8c4db..9e051dae6da 100644 --- a/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts @@ -4,9 +4,9 @@ import { ForbiddenException, InternalServerErrorException } from '@nestjs/common import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; import { courseFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder } from '@src/modules/authorization'; -import { AuthorizationReferenceService, AuthorizableReferenceType } from '@src/modules/authorization/domain'; -import { CopyElementType, CopyStatusEnum } from '@src/modules/copy-helper'; +import { AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationReferenceService, AuthorizableReferenceType } from '@modules/authorization/domain'; +import { CopyElementType, CopyStatusEnum } from '@modules/copy-helper'; import { CourseCopyService } from '../service'; import { CourseCopyUC } from './course-copy.uc'; diff --git a/apps/server/src/modules/learnroom/uc/course-copy.uc.ts b/apps/server/src/modules/learnroom/uc/course-copy.uc.ts index 0f700d57f17..19b94a9c238 100644 --- a/apps/server/src/modules/learnroom/uc/course-copy.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course-copy.uc.ts @@ -1,9 +1,9 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; -import { AuthorizationContextBuilder } from '@src/modules/authorization'; -import { AuthorizationReferenceService, AuthorizableReferenceType } from '@src/modules/authorization/domain'; -import { CopyStatus } from '@src/modules/copy-helper'; +import { AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationReferenceService, AuthorizableReferenceType } from '@modules/authorization/domain'; +import { CopyStatus } from '@modules/copy-helper'; import { CourseCopyService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts index 04e3d0de480..8ca36158f5d 100644 --- a/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { CommonCartridgeExportService } from '@src/modules/learnroom/service/common-cartridge-export.service'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; +import { CommonCartridgeExportService } from '@modules/learnroom/service/common-cartridge-export.service'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; import { ObjectId } from 'bson'; import { ForbiddenException } from '@nestjs/common'; import { CourseExportUc } from './course-export.uc'; diff --git a/apps/server/src/modules/learnroom/uc/course-export.uc.ts b/apps/server/src/modules/learnroom/uc/course-export.uc.ts index 07e427c8fa8..758b1b550d7 100644 --- a/apps/server/src/modules/learnroom/uc/course-export.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course-export.uc.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; -import { AuthorizationContextBuilder } from '@src/modules/authorization'; -import { AuthorizationReferenceService, AuthorizableReferenceType } from '@src/modules/authorization/domain'; +import { AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationReferenceService, AuthorizableReferenceType } from '@modules/authorization/domain'; import { CommonCartridgeVersion } from '../common-cartridge'; import { CommonCartridgeExportService } from '../service/common-cartridge-export.service'; diff --git a/apps/server/src/modules/learnroom/uc/lesson-copy.uc.spec.ts b/apps/server/src/modules/learnroom/uc/lesson-copy.uc.spec.ts index 34d73449b4c..49c7c53435c 100644 --- a/apps/server/src/modules/learnroom/uc/lesson-copy.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/lesson-copy.uc.spec.ts @@ -6,9 +6,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; import { CourseRepo, LessonRepo, UserRepo } from '@shared/repo'; import { courseFactory, lessonFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@src/modules/copy-helper'; -import { EtherpadService, LessonCopyService } from '@src/modules/lesson/service'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@modules/copy-helper'; +import { EtherpadService, LessonCopyService } from '@modules/lesson/service'; import { LessonCopyUC } from './lesson-copy.uc'; describe('lesson copy uc', () => { diff --git a/apps/server/src/modules/learnroom/uc/lesson-copy.uc.ts b/apps/server/src/modules/learnroom/uc/lesson-copy.uc.ts index 7ec51f5ef1c..ae41fb6881b 100644 --- a/apps/server/src/modules/learnroom/uc/lesson-copy.uc.ts +++ b/apps/server/src/modules/learnroom/uc/lesson-copy.uc.ts @@ -3,10 +3,10 @@ import { ForbiddenException, Injectable, InternalServerErrorException } from '@n import { Course, EntityId, LessonEntity, User } from '@shared/domain'; import { Permission } from '@shared/domain/interface/permission.enum'; import { CourseRepo, LessonRepo } from '@shared/repo'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { CopyHelperService, CopyStatus } from '@src/modules/copy-helper'; -import { LessonCopyParentParams } from '@src/modules/lesson'; -import { LessonCopyService } from '@src/modules/lesson/service'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { CopyHelperService, CopyStatus } from '@modules/copy-helper'; +import { LessonCopyParentParams } from '@modules/lesson'; +import { LessonCopyService } from '@modules/lesson/service'; @Injectable() export class LessonCopyUC { diff --git a/apps/server/src/modules/learnroom/uc/room-board-dto.factory.spec.ts b/apps/server/src/modules/learnroom/uc/room-board-dto.factory.spec.ts index 9c7ddf0c742..f983342f4f8 100644 --- a/apps/server/src/modules/learnroom/uc/room-board-dto.factory.spec.ts +++ b/apps/server/src/modules/learnroom/uc/room-board-dto.factory.spec.ts @@ -13,7 +13,7 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationService } from '@modules/authorization'; import { LessonMetaData } from '../types'; import { RoomBoardDTOFactory } from './room-board-dto.factory'; import { RoomsAuthorisationService } from './rooms.authorisation.service'; diff --git a/apps/server/src/modules/learnroom/uc/room-board-dto.factory.ts b/apps/server/src/modules/learnroom/uc/room-board-dto.factory.ts index cafa02e4d20..91bd043d472 100644 --- a/apps/server/src/modules/learnroom/uc/room-board-dto.factory.ts +++ b/apps/server/src/modules/learnroom/uc/room-board-dto.factory.ts @@ -14,7 +14,7 @@ import { TaskWithStatusVo, User, } from '@shared/domain'; -import { AuthorizationService, Action } from '@src/modules/authorization'; +import { AuthorizationService, Action } from '@modules/authorization'; import { ColumnBoardMetaData, LessonMetaData, diff --git a/apps/server/src/modules/legacy-school/controller/legacy-school.controller.spec.ts b/apps/server/src/modules/legacy-school/controller/legacy-school.controller.spec.ts index 1777ad02faa..764d71b6abf 100644 --- a/apps/server/src/modules/legacy-school/controller/legacy-school.controller.spec.ts +++ b/apps/server/src/modules/legacy-school/controller/legacy-school.controller.spec.ts @@ -1,7 +1,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { MigrationMapper } from '../mapper/migration.mapper'; import { OauthMigrationDto } from '../uc/dto/oauth-migration.dto'; import { LegacySchoolUc } from '../uc'; diff --git a/apps/server/src/modules/legacy-school/controller/legacy-school.controller.ts b/apps/server/src/modules/legacy-school/controller/legacy-school.controller.ts index 91d6892741e..58b591faea1 100644 --- a/apps/server/src/modules/legacy-school/controller/legacy-school.controller.ts +++ b/apps/server/src/modules/legacy-school/controller/legacy-school.controller.ts @@ -6,7 +6,7 @@ import { ApiTags, ApiUnauthorizedResponse, } from '@nestjs/swagger'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { MigrationMapper } from '../mapper/migration.mapper'; import { OauthMigrationDto } from '../uc/dto/oauth-migration.dto'; import { LegacySchoolUc } from '../uc'; diff --git a/apps/server/src/modules/legacy-school/legacy-school-api.module.ts b/apps/server/src/modules/legacy-school/legacy-school-api.module.ts index 3072fa8f6ca..aaf1f6acad2 100644 --- a/apps/server/src/modules/legacy-school/legacy-school-api.module.ts +++ b/apps/server/src/modules/legacy-school/legacy-school-api.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationModule } from '@modules/authorization'; import { LoggerModule } from '@src/core/logger'; -import { UserLoginMigrationModule } from '@src/modules/user-login-migration'; +import { UserLoginMigrationModule } from '@modules/user-login-migration'; import { LegacySchoolUc } from './uc'; import { LegacySchoolModule } from './legacy-school.module'; import { LegacySchoolController } from './controller/legacy-school.controller'; diff --git a/apps/server/src/modules/legacy-school/uc/legacy-school.uc.spec.ts b/apps/server/src/modules/legacy-school/uc/legacy-school.uc.spec.ts index 8747a07ada6..138bcd81a0a 100644 --- a/apps/server/src/modules/legacy-school/uc/legacy-school.uc.spec.ts +++ b/apps/server/src/modules/legacy-school/uc/legacy-school.uc.spec.ts @@ -3,14 +3,14 @@ import { UnprocessableEntityException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { LegacySchoolDo, UserLoginMigrationDO } from '@shared/domain'; import { legacySchoolDoFactory, userLoginMigrationDOFactory } from '@shared/testing/factory'; -import { AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school/service'; -import { LegacySchoolUc } from '@src/modules/legacy-school/uc'; +import { AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school/service'; +import { LegacySchoolUc } from '@modules/legacy-school/uc'; import { SchoolMigrationService, UserLoginMigrationRevertService, UserLoginMigrationService, -} from '@src/modules/user-login-migration'; +} from '@modules/user-login-migration'; import { OauthMigrationDto } from './dto/oauth-migration.dto'; describe('LegacySchoolUc', () => { diff --git a/apps/server/src/modules/legacy-school/uc/legacy-school.uc.ts b/apps/server/src/modules/legacy-school/uc/legacy-school.uc.ts index d1d13ffb037..50fd7faf5a5 100644 --- a/apps/server/src/modules/legacy-school/uc/legacy-school.uc.ts +++ b/apps/server/src/modules/legacy-school/uc/legacy-school.uc.ts @@ -1,11 +1,11 @@ import { Injectable } from '@nestjs/common'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { Permission, LegacySchoolDo, UserLoginMigrationDO, User } from '@shared/domain'; import { SchoolMigrationService, UserLoginMigrationRevertService, UserLoginMigrationService, -} from '@src/modules/user-login-migration'; +} from '@modules/user-login-migration'; import { LegacySchoolService } from '../service'; import { OauthMigrationDto } from './dto/oauth-migration.dto'; diff --git a/apps/server/src/modules/lesson/controller/api-test/lesson-delete.api.spec.ts b/apps/server/src/modules/lesson/controller/api-test/lesson-delete.api.spec.ts index 96b9875cf15..d4a707420de 100644 --- a/apps/server/src/modules/lesson/controller/api-test/lesson-delete.api.spec.ts +++ b/apps/server/src/modules/lesson/controller/api-test/lesson-delete.api.spec.ts @@ -10,8 +10,8 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; -import { ServerTestModule } from '@src/modules/server'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; +import { ServerTestModule } from '@modules/server'; import { ObjectId } from 'bson'; describe('Lesson Controller (API) - delete', () => { diff --git a/apps/server/src/modules/lesson/controller/lesson.controller.ts b/apps/server/src/modules/lesson/controller/lesson.controller.ts index 7b65eb8afc1..14762a47d8c 100644 --- a/apps/server/src/modules/lesson/controller/lesson.controller.ts +++ b/apps/server/src/modules/lesson/controller/lesson.controller.ts @@ -1,6 +1,6 @@ import { Controller, Delete, Param } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { LessonUC } from '../uc'; import { LessonUrlParams } from './dto'; diff --git a/apps/server/src/modules/lesson/lesson-api.module.ts b/apps/server/src/modules/lesson/lesson-api.module.ts index 6185b5da2a4..1f17893f582 100644 --- a/apps/server/src/modules/lesson/lesson-api.module.ts +++ b/apps/server/src/modules/lesson/lesson-api.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationModule } from '@modules/authorization'; import { LessonController } from './controller'; import { LessonModule } from './lesson.module'; import { LessonUC } from './uc'; diff --git a/apps/server/src/modules/lesson/lesson.module.ts b/apps/server/src/modules/lesson/lesson.module.ts index 021f4ab4efa..2e246c63211 100644 --- a/apps/server/src/modules/lesson/lesson.module.ts +++ b/apps/server/src/modules/lesson/lesson.module.ts @@ -2,9 +2,9 @@ import { Module } from '@nestjs/common'; import { FeathersServiceProvider } from '@shared/infra/feathers'; import { LessonRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { CopyHelperModule } from '@src/modules/copy-helper'; -import { FilesStorageClientModule } from '@src/modules/files-storage-client'; -import { TaskModule } from '@src/modules/task'; +import { CopyHelperModule } from '@modules/copy-helper'; +import { FilesStorageClientModule } from '@modules/files-storage-client'; +import { TaskModule } from '@modules/task'; import { EtherpadService, LessonCopyService, LessonService, NexboardService } from './service'; @Module({ diff --git a/apps/server/src/modules/lesson/service/lesson-copy.service.spec.ts b/apps/server/src/modules/lesson/service/lesson-copy.service.spec.ts index 6097a3b87f6..34392c91c0c 100644 --- a/apps/server/src/modules/lesson/service/lesson-copy.service.spec.ts +++ b/apps/server/src/modules/lesson/service/lesson-copy.service.spec.ts @@ -24,9 +24,9 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; -import { CopyFilesService } from '@src/modules/files-storage-client'; -import { TaskCopyService } from '@src/modules/task/service'; +import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { CopyFilesService } from '@modules/files-storage-client'; +import { TaskCopyService } from '@modules/task/service'; import { EtherpadService } from './etherpad.service'; import { LessonCopyService } from './lesson-copy.service'; import { NexboardService } from './nexboard.service'; diff --git a/apps/server/src/modules/lesson/service/lesson-copy.service.ts b/apps/server/src/modules/lesson/service/lesson-copy.service.ts index cd83681a2e7..b6d7e7849c7 100644 --- a/apps/server/src/modules/lesson/service/lesson-copy.service.ts +++ b/apps/server/src/modules/lesson/service/lesson-copy.service.ts @@ -12,16 +12,10 @@ import { Material, } from '@shared/domain'; import { LessonRepo } from '@shared/repo'; -import { - CopyDictionary, - CopyElementType, - CopyHelperService, - CopyStatus, - CopyStatusEnum, -} from '@src/modules/copy-helper'; -import { CopyFilesService } from '@src/modules/files-storage-client'; -import { FileUrlReplacement } from '@src/modules/files-storage-client/service/copy-files.service'; -import { TaskCopyService } from '@src/modules/task/service/task-copy.service'; +import { CopyDictionary, CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { CopyFilesService } from '@modules/files-storage-client'; +import { FileUrlReplacement } from '@modules/files-storage-client/service/copy-files.service'; +import { TaskCopyService } from '@modules/task/service/task-copy.service'; import { randomBytes } from 'crypto'; import { LessonCopyParams } from '../types'; import { EtherpadService } from './etherpad.service'; diff --git a/apps/server/src/modules/lesson/service/lesson.service.spec.ts b/apps/server/src/modules/lesson/service/lesson.service.spec.ts index 7f0179640b5..a94ecfe9c8b 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.spec.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.spec.ts @@ -2,7 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { LessonRepo } from '@shared/repo'; import { lessonFactory, setupEntities } from '@shared/testing'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { ObjectId } from '@mikro-orm/mongodb'; import { ComponentType, IComponentProperties } from '@shared/domain'; import { LessonService } from './lesson.service'; diff --git a/apps/server/src/modules/lesson/service/lesson.service.ts b/apps/server/src/modules/lesson/service/lesson.service.ts index 5a69a19f305..2dee6f05563 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Counted, EntityId, IComponentProperties, LessonEntity } from '@shared/domain'; import { LessonRepo } from '@shared/repo'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; @Injectable() export class LessonService { diff --git a/apps/server/src/modules/lesson/uc/lesson.uc.spec.ts b/apps/server/src/modules/lesson/uc/lesson.uc.spec.ts index cd6b347b048..72448f8b770 100644 --- a/apps/server/src/modules/lesson/uc/lesson.uc.spec.ts +++ b/apps/server/src/modules/lesson/uc/lesson.uc.spec.ts @@ -2,7 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; import { lessonFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { LessonService } from '../service'; import { LessonUC } from './lesson.uc'; diff --git a/apps/server/src/modules/lesson/uc/lesson.uc.ts b/apps/server/src/modules/lesson/uc/lesson.uc.ts index 68519a4e0e1..063a43ce9b9 100644 --- a/apps/server/src/modules/lesson/uc/lesson.uc.ts +++ b/apps/server/src/modules/lesson/uc/lesson.uc.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { LessonService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/management/controller/api-test/database-management.api.spec.ts b/apps/server/src/modules/management/controller/api-test/database-management.api.spec.ts index d4c9967dcf4..012ee8f3cf2 100644 --- a/apps/server/src/modules/management/controller/api-test/database-management.api.spec.ts +++ b/apps/server/src/modules/management/controller/api-test/database-management.api.spec.ts @@ -1,7 +1,7 @@ import { MikroORM } from '@mikro-orm/core'; import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { ManagementServerTestModule } from '@src/modules/management/management-server.module'; +import { ManagementServerTestModule } from '@modules/management/management-server.module'; import request from 'supertest'; describe('Database Management Controller (API)', () => { diff --git a/apps/server/src/modules/management/management.module.ts b/apps/server/src/modules/management/management.module.ts index 3fb5bf4e3c2..fc4c8bc08d3 100644 --- a/apps/server/src/modules/management/management.module.ts +++ b/apps/server/src/modules/management/management.module.ts @@ -8,7 +8,7 @@ import { FileSystemModule } from '@shared/infra/file-system'; import { KeycloakConfigurationModule } from '@shared/infra/identity-management/keycloak-configuration/keycloak-configuration.module'; import { createConfigModuleOptions } from '@src/config'; import { LoggerModule } from '@src/core/logger'; -import { serverConfig } from '@src/modules/server'; +import { serverConfig } from '@modules/server'; import { BoardManagementConsole } from './console/board-management.console'; import { DatabaseManagementConsole } from './console/database-management.console'; import { DatabaseManagementController } from './controller/database-management.controller'; diff --git a/apps/server/src/modules/news/controller/api-test/news.api.spec.ts b/apps/server/src/modules/news/controller/api-test/news.api.spec.ts index 644dde5a371..31e4a10af9a 100644 --- a/apps/server/src/modules/news/controller/api-test/news.api.spec.ts +++ b/apps/server/src/modules/news/controller/api-test/news.api.spec.ts @@ -3,10 +3,10 @@ import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityId, News, NewsTargetModel } from '@shared/domain'; import { API_VALIDATION_ERROR_TYPE } from '@src/core/error/server-error-types'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { FeathersAuthorizationService } from '@src/modules/authorization'; -import { CreateNewsParams, NewsListResponse, NewsResponse, UpdateNewsParams } from '@src/modules/news/controller/dto'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { FeathersAuthorizationService } from '@modules/authorization'; +import { CreateNewsParams, NewsListResponse, NewsResponse, UpdateNewsParams } from '@modules/news/controller/dto'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import moment from 'moment'; import request from 'supertest'; diff --git a/apps/server/src/modules/news/controller/news.controller.ts b/apps/server/src/modules/news/controller/news.controller.ts index 325a5c3f6fc..2f1c227401a 100644 --- a/apps/server/src/modules/news/controller/news.controller.ts +++ b/apps/server/src/modules/news/controller/news.controller.ts @@ -1,7 +1,7 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; import { PaginationParams } from '@shared/controller'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { NewsMapper } from '../mapper/news.mapper'; import { NewsUc } from '../uc/news.uc'; import { diff --git a/apps/server/src/modules/news/controller/team-news.controller.ts b/apps/server/src/modules/news/controller/team-news.controller.ts index 29199932eaf..2344d6f2ac9 100644 --- a/apps/server/src/modules/news/controller/team-news.controller.ts +++ b/apps/server/src/modules/news/controller/team-news.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, Param, Query } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { PaginationParams } from '@shared/controller'; import { NewsMapper } from '../mapper/news.mapper'; import { NewsUc } from '../uc'; diff --git a/apps/server/src/modules/news/news.module.ts b/apps/server/src/modules/news/news.module.ts index 261a4b2b6ba..3766b26052e 100644 --- a/apps/server/src/modules/news/news.module.ts +++ b/apps/server/src/modules/news/news.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { NewsRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { AuthorizationModule } from '@modules/authorization'; import { NewsController } from './controller/news.controller'; import { TeamNewsController } from './controller/team-news.controller'; import { NewsUc } from './uc/news.uc'; diff --git a/apps/server/src/modules/news/uc/news.uc.spec.ts b/apps/server/src/modules/news/uc/news.uc.spec.ts index 39e4c9af9f7..d0671dd4259 100644 --- a/apps/server/src/modules/news/uc/news.uc.spec.ts +++ b/apps/server/src/modules/news/uc/news.uc.spec.ts @@ -6,7 +6,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ICreateNews, NewsTargetModel, Permission } from '@shared/domain'; import { NewsRepo } from '@shared/repo'; import { Logger } from '@src/core/logger'; -import { FeathersAuthorizationService } from '@src/modules/authorization'; +import { FeathersAuthorizationService } from '@modules/authorization'; import { NewsUc } from './news.uc'; describe('NewsUc', () => { diff --git a/apps/server/src/modules/news/uc/news.uc.ts b/apps/server/src/modules/news/uc/news.uc.ts index 39aeb05f03b..de0608b0234 100644 --- a/apps/server/src/modules/news/uc/news.uc.ts +++ b/apps/server/src/modules/news/uc/news.uc.ts @@ -14,7 +14,7 @@ import { import { NewsRepo, NewsTargetFilter } from '@shared/repo'; import { CrudOperation } from '@shared/types'; import { Logger } from '@src/core/logger'; -import { FeathersAuthorizationService } from '@src/modules/authorization'; +import { FeathersAuthorizationService } from '@modules/authorization'; import { NewsCrudOperationLoggable } from '../loggable/news-crud-operation.loggable'; type NewsPermission = Permission.NEWS_VIEW | Permission.NEWS_EDIT; diff --git a/apps/server/src/modules/oauth-provider/controller/dto/request/oauth-client.body.ts b/apps/server/src/modules/oauth-provider/controller/dto/request/oauth-client.body.ts index 735ebc02d5f..277922526f7 100644 --- a/apps/server/src/modules/oauth-provider/controller/dto/request/oauth-client.body.ts +++ b/apps/server/src/modules/oauth-provider/controller/dto/request/oauth-client.body.ts @@ -1,7 +1,7 @@ import { IsArray, IsEnum, IsOptional, IsString } from 'class-validator'; import { ApiProperty } from '@nestjs/swagger'; -import { SubjectTypeEnum } from '@src/modules/oauth-provider/interface/subject-type.enum'; -import { TokenAuthMethod } from '@src/modules/oauth-provider/interface/token-auth-method.enum'; +import { SubjectTypeEnum } from '@modules/oauth-provider/interface/subject-type.enum'; +import { TokenAuthMethod } from '@modules/oauth-provider/interface/token-auth-method.enum'; export class OauthClientBody { @IsString() diff --git a/apps/server/src/modules/oauth-provider/controller/dto/response/consent.response.ts b/apps/server/src/modules/oauth-provider/controller/dto/response/consent.response.ts index 3953273c2bf..a5c90fe11a2 100644 --- a/apps/server/src/modules/oauth-provider/controller/dto/response/consent.response.ts +++ b/apps/server/src/modules/oauth-provider/controller/dto/response/consent.response.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { IsArray, IsOptional, IsString } from 'class-validator'; -import { OidcContextResponse } from '@src/modules/oauth-provider/controller/dto/response/oidc-context.response'; -import { OauthClientResponse } from '@src/modules/oauth-provider/controller/dto/response/oauth-client.response'; +import { OidcContextResponse } from '@modules/oauth-provider/controller/dto/response/oidc-context.response'; +import { OauthClientResponse } from '@modules/oauth-provider/controller/dto/response/oauth-client.response'; export class ConsentResponse { constructor(consentResponse: ConsentResponse) { diff --git a/apps/server/src/modules/oauth-provider/controller/dto/response/login.response.ts b/apps/server/src/modules/oauth-provider/controller/dto/response/login.response.ts index ddde211e219..761276bdc5c 100644 --- a/apps/server/src/modules/oauth-provider/controller/dto/response/login.response.ts +++ b/apps/server/src/modules/oauth-provider/controller/dto/response/login.response.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; -import { OauthClientResponse } from '@src/modules/oauth-provider/controller/dto/response/oauth-client.response'; -import { OidcContextResponse } from '@src/modules/oauth-provider/controller/dto/response/oidc-context.response'; +import { OauthClientResponse } from '@modules/oauth-provider/controller/dto/response/oauth-client.response'; +import { OidcContextResponse } from '@modules/oauth-provider/controller/dto/response/oidc-context.response'; import { IsArray, IsOptional, IsString } from 'class-validator'; export class LoginResponse { diff --git a/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.spec.ts b/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.spec.ts index 7edec7ece8d..83a3e3ac47b 100644 --- a/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.spec.ts +++ b/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.spec.ts @@ -1,8 +1,8 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { OauthProviderLogoutFlowUc } from '@src/modules/oauth-provider/uc/oauth-provider.logout-flow.uc'; +import { OauthProviderLogoutFlowUc } from '@modules/oauth-provider/uc/oauth-provider.logout-flow.uc'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Configuration } from '@hpi-schul-cloud/commons/lib'; -import { OauthProviderResponseMapper } from '@src/modules/oauth-provider/mapper/oauth-provider-response.mapper'; +import { OauthProviderResponseMapper } from '@modules/oauth-provider/mapper/oauth-provider-response.mapper'; import { AcceptQuery, ChallengeParams, @@ -14,16 +14,16 @@ import { OauthClientBody, OauthClientResponse, RedirectResponse, -} from '@src/modules/oauth-provider/controller/dto'; +} from '@modules/oauth-provider/controller/dto'; import { ProviderConsentResponse, ProviderConsentSessionResponse, ProviderLoginResponse, ProviderRedirectResponse, } from '@shared/infra/oauth-provider/dto'; -import { OauthProviderConsentFlowUc } from '@src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc'; -import { ICurrentUser } from '@src/modules/authentication'; -import { OauthProviderUc } from '@src/modules/oauth-provider/uc/oauth-provider.uc'; +import { OauthProviderConsentFlowUc } from '@modules/oauth-provider/uc/oauth-provider.consent-flow.uc'; +import { ICurrentUser } from '@modules/authentication'; +import { OauthProviderUc } from '@modules/oauth-provider/uc/oauth-provider.uc'; import { OauthProviderController } from './oauth-provider.controller'; import { OauthProviderClientCrudUc } from '../uc/oauth-provider.client-crud.uc'; import { OauthProviderLoginFlowUc } from '../uc/oauth-provider.login-flow.uc'; diff --git a/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.ts b/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.ts index 5c1a0e8e1e8..054cca37ffa 100644 --- a/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.ts +++ b/apps/server/src/modules/oauth-provider/controller/oauth-provider.controller.ts @@ -1,6 +1,6 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { Body, Controller, Delete, Get, Param, Patch, Post, Put, Query } from '@nestjs/common'; -import { ICurrentUser, Authenticate, CurrentUser } from '@src/modules/authentication'; +import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; // import should be @shared/infra/oauth-provider import { ProviderConsentResponse, diff --git a/apps/server/src/modules/oauth-provider/mapper/oauth-provider-request.mapper.ts b/apps/server/src/modules/oauth-provider/mapper/oauth-provider-request.mapper.ts index c44bda25da6..e0d4c4aaef4 100644 --- a/apps/server/src/modules/oauth-provider/mapper/oauth-provider-request.mapper.ts +++ b/apps/server/src/modules/oauth-provider/mapper/oauth-provider-request.mapper.ts @@ -1,5 +1,5 @@ import { AcceptLoginRequestBody } from '@shared/infra/oauth-provider/dto'; -import { LoginRequestBody } from '@src/modules/oauth-provider/controller/dto'; +import { LoginRequestBody } from '@modules/oauth-provider/controller/dto'; export class OauthProviderRequestMapper { static mapCreateAcceptLoginRequestBody( diff --git a/apps/server/src/modules/oauth-provider/mapper/oauth-provider-response.mapper.spec.ts b/apps/server/src/modules/oauth-provider/mapper/oauth-provider-response.mapper.spec.ts index 6e62e1ce6c7..13119635f75 100644 --- a/apps/server/src/modules/oauth-provider/mapper/oauth-provider-response.mapper.spec.ts +++ b/apps/server/src/modules/oauth-provider/mapper/oauth-provider-response.mapper.spec.ts @@ -1,4 +1,4 @@ -import { OauthProviderResponseMapper } from '@src/modules/oauth-provider/mapper/oauth-provider-response.mapper'; +import { OauthProviderResponseMapper } from '@modules/oauth-provider/mapper/oauth-provider-response.mapper'; import { ProviderConsentResponse, ProviderConsentSessionResponse, @@ -12,7 +12,7 @@ import { LoginResponse, OauthClientResponse, RedirectResponse, -} from '@src/modules/oauth-provider/controller/dto/'; +} from '@modules/oauth-provider/controller/dto/'; describe('OauthProviderResponseMapper', () => { let mapper: OauthProviderResponseMapper; diff --git a/apps/server/src/modules/oauth-provider/mapper/oauth-provider-response.mapper.ts b/apps/server/src/modules/oauth-provider/mapper/oauth-provider-response.mapper.ts index 19fef36d0f5..01038c23526 100644 --- a/apps/server/src/modules/oauth-provider/mapper/oauth-provider-response.mapper.ts +++ b/apps/server/src/modules/oauth-provider/mapper/oauth-provider-response.mapper.ts @@ -12,7 +12,7 @@ import { LoginResponse, OauthClientResponse, RedirectResponse, -} from '@src/modules/oauth-provider/controller/dto'; +} from '@modules/oauth-provider/controller/dto'; @Injectable() export class OauthProviderResponseMapper { diff --git a/apps/server/src/modules/oauth-provider/oauth-provider-api.module.ts b/apps/server/src/modules/oauth-provider/oauth-provider-api.module.ts index 907a28c1fc7..ccbd1566cda 100644 --- a/apps/server/src/modules/oauth-provider/oauth-provider-api.module.ts +++ b/apps/server/src/modules/oauth-provider/oauth-provider-api.module.ts @@ -1,9 +1,9 @@ import { Module } from '@nestjs/common'; import { OauthProviderServiceModule } from '@shared/infra/oauth-provider'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { PseudonymModule } from '@src/modules/pseudonym'; -import { UserModule } from '@src/modules/user'; +import { AuthorizationModule } from '@modules/authorization'; +import { PseudonymModule } from '@modules/pseudonym'; +import { UserModule } from '@modules/user'; import { OauthProviderController } from './controller/oauth-provider.controller'; import { OauthProviderResponseMapper } from './mapper/oauth-provider-response.mapper'; import { OauthProviderModule } from './oauth-provider.module'; diff --git a/apps/server/src/modules/oauth-provider/oauth-provider.module.ts b/apps/server/src/modules/oauth-provider/oauth-provider.module.ts index d963d247a93..4289644d29e 100644 --- a/apps/server/src/modules/oauth-provider/oauth-provider.module.ts +++ b/apps/server/src/modules/oauth-provider/oauth-provider.module.ts @@ -2,11 +2,11 @@ import { Module } from '@nestjs/common'; import { OauthProviderServiceModule } from '@shared/infra/oauth-provider'; import { TeamsRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { LtiToolModule } from '@src/modules/lti-tool'; -import { PseudonymModule } from '@src/modules/pseudonym'; -import { ToolModule } from '@src/modules/tool'; -import { ToolConfigModule } from '@src/modules/tool/tool-config.module'; -import { UserModule } from '@src/modules/user'; +import { LtiToolModule } from '@modules/lti-tool'; +import { PseudonymModule } from '@modules/pseudonym'; +import { ToolModule } from '@modules/tool'; +import { ToolConfigModule } from '@modules/tool/tool-config.module'; +import { UserModule } from '@modules/user'; import { IdTokenService } from './service/id-token.service'; import { OauthProviderLoginFlowService } from './service/oauth-provider.login-flow.service'; diff --git a/apps/server/src/modules/oauth-provider/service/id-token.service.spec.ts b/apps/server/src/modules/oauth-provider/service/id-token.service.spec.ts index 48f21de4077..4f8eff19c80 100644 --- a/apps/server/src/modules/oauth-provider/service/id-token.service.spec.ts +++ b/apps/server/src/modules/oauth-provider/service/id-token.service.spec.ts @@ -4,12 +4,12 @@ import { Pseudonym, TeamEntity, UserDO } from '@shared/domain'; import { TeamsRepo } from '@shared/repo'; import { externalToolFactory, pseudonymFactory, setupEntities, userDoFactory } from '@shared/testing'; import { teamFactory } from '@shared/testing/factory/team.factory'; -import { IdToken } from '@src/modules/oauth-provider/interface/id-token'; -import { OauthScope } from '@src/modules/oauth-provider/interface/oauth-scope.enum'; -import { IdTokenService } from '@src/modules/oauth-provider/service/id-token.service'; -import { PseudonymService } from '@src/modules/pseudonym/service'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { UserService } from '@src/modules/user/service/user.service'; +import { IdToken } from '@modules/oauth-provider/interface/id-token'; +import { OauthScope } from '@modules/oauth-provider/interface/oauth-scope.enum'; +import { IdTokenService } from '@modules/oauth-provider/service/id-token.service'; +import { PseudonymService } from '@modules/pseudonym/service'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { UserService } from '@modules/user/service/user.service'; import { IdTokenCreationLoggableException } from '../error/id-token-creation-exception.loggable'; import { OauthProviderLoginFlowService } from './oauth-provider.login-flow.service'; import resetAllMocks = jest.resetAllMocks; diff --git a/apps/server/src/modules/oauth-provider/service/id-token.service.ts b/apps/server/src/modules/oauth-provider/service/id-token.service.ts index dbe1b2c54fc..998ff46f15f 100644 --- a/apps/server/src/modules/oauth-provider/service/id-token.service.ts +++ b/apps/server/src/modules/oauth-provider/service/id-token.service.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; import { LtiToolDO, Pseudonym, TeamEntity, UserDO } from '@shared/domain'; import { TeamsRepo } from '@shared/repo'; -import { PseudonymService } from '@src/modules/pseudonym'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { UserService } from '@src/modules/user'; +import { PseudonymService } from '@modules/pseudonym'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { UserService } from '@modules/user'; import { IdTokenCreationLoggableException } from '../error/id-token-creation-exception.loggable'; import { GroupNameIdTuple, IdToken, OauthScope } from '../interface'; import { OauthProviderLoginFlowService } from './oauth-provider.login-flow.service'; diff --git a/apps/server/src/modules/oauth-provider/service/oauth-provider.login-flow.service.spec.ts b/apps/server/src/modules/oauth-provider/service/oauth-provider.login-flow.service.spec.ts index 3d382fffe3b..3c85dde6c62 100644 --- a/apps/server/src/modules/oauth-provider/service/oauth-provider.login-flow.service.spec.ts +++ b/apps/server/src/modules/oauth-provider/service/oauth-provider.login-flow.service.spec.ts @@ -3,10 +3,10 @@ import { NotFoundException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { LtiToolDO } from '@shared/domain'; import { externalToolFactory, ltiToolDOFactory, setupEntities } from '@shared/testing'; -import { LtiToolService } from '@src/modules/lti-tool'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { ExternalToolService } from '@src/modules/tool/external-tool/service'; -import { IToolFeatures, ToolFeatures } from '@src/modules/tool/tool-config'; +import { LtiToolService } from '@modules/lti-tool'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { ExternalToolService } from '@modules/tool/external-tool/service'; +import { IToolFeatures, ToolFeatures } from '@modules/tool/tool-config'; import { OauthProviderLoginFlowService } from './oauth-provider.login-flow.service'; describe('OauthProviderLoginFlowService', () => { diff --git a/apps/server/src/modules/oauth-provider/service/oauth-provider.login-flow.service.ts b/apps/server/src/modules/oauth-provider/service/oauth-provider.login-flow.service.ts index 367efdcf6f7..0b1948baa28 100644 --- a/apps/server/src/modules/oauth-provider/service/oauth-provider.login-flow.service.ts +++ b/apps/server/src/modules/oauth-provider/service/oauth-provider.login-flow.service.ts @@ -2,10 +2,10 @@ import { Inject } from '@nestjs/common'; import { Injectable } from '@nestjs/common/decorators/core/injectable.decorator'; import { NotFoundException } from '@nestjs/common/exceptions/not-found.exception'; import { LtiToolDO } from '@shared/domain/domainobject/ltitool.do'; -import { LtiToolService } from '@src/modules/lti-tool/service'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { ExternalToolService } from '@src/modules/tool/external-tool/service'; -import { IToolFeatures, ToolFeatures } from '@src/modules/tool/tool-config'; +import { LtiToolService } from '@modules/lti-tool/service'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { ExternalToolService } from '@modules/tool/external-tool/service'; +import { IToolFeatures, ToolFeatures } from '@modules/tool/tool-config'; @Injectable() export class OauthProviderLoginFlowService { diff --git a/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.spec.ts b/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.spec.ts index b9d2abc6117..6ce203ab5b7 100644 --- a/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.spec.ts +++ b/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.spec.ts @@ -5,8 +5,8 @@ import { Permission, User } from '@shared/domain'; import { OauthProviderService } from '@shared/infra/oauth-provider'; import { ProviderOauthClient } from '@shared/infra/oauth-provider/dto'; import { setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationService } from '@src/modules'; -import { ICurrentUser } from '@src/modules/authentication'; +import { AuthorizationService } from '@modules/authorization'; +import { ICurrentUser } from '@modules/authentication'; import { OauthProviderClientCrudUc } from './oauth-provider.client-crud.uc'; import resetAllMocks = jest.resetAllMocks; diff --git a/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.ts b/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.ts index d0480398ec8..3595f00679b 100644 --- a/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.ts +++ b/apps/server/src/modules/oauth-provider/uc/oauth-provider.client-crud.uc.ts @@ -1,9 +1,9 @@ import { Injectable } from '@nestjs/common'; import { OauthProviderService } from '@shared/infra/oauth-provider/index'; import { Permission, User } from '@shared/domain/index'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationService } from '@modules/authorization'; import { ProviderOauthClient } from '@shared/infra/oauth-provider/dto'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; @Injectable() export class OauthProviderClientCrudUc { diff --git a/apps/server/src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc.spec.ts b/apps/server/src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc.spec.ts index 9abb5c8564c..b397b048dd4 100644 --- a/apps/server/src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc.spec.ts +++ b/apps/server/src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc.spec.ts @@ -1,17 +1,17 @@ import { Test, TestingModule } from '@nestjs/testing'; import { OauthProviderService } from '@shared/infra/oauth-provider/index'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { AcceptQuery, ConsentRequestBody } from '@src/modules/oauth-provider/controller/dto'; +import { AcceptQuery, ConsentRequestBody } from '@modules/oauth-provider/controller/dto'; import { AcceptConsentRequestBody, ProviderConsentResponse, ProviderRedirectResponse, } from '@shared/infra/oauth-provider/dto'; -import { OauthProviderConsentFlowUc } from '@src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc'; -import { ICurrentUser } from '@src/modules/authentication'; +import { OauthProviderConsentFlowUc } from '@modules/oauth-provider/uc/oauth-provider.consent-flow.uc'; +import { ICurrentUser } from '@modules/authentication'; import { ForbiddenException } from '@nestjs/common'; -import { IdTokenService } from '@src/modules/oauth-provider/service/id-token.service'; -import { IdToken } from '@src/modules/oauth-provider/interface/id-token'; +import { IdTokenService } from '@modules/oauth-provider/service/id-token.service'; +import { IdToken } from '@modules/oauth-provider/interface/id-token'; describe('OauthProviderConsentFlowUc', () => { let module: TestingModule; diff --git a/apps/server/src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc.ts b/apps/server/src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc.ts index 3fcf82687f6..eb91d8132fe 100644 --- a/apps/server/src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc.ts +++ b/apps/server/src/modules/oauth-provider/uc/oauth-provider.consent-flow.uc.ts @@ -4,12 +4,12 @@ import { ProviderRedirectResponse, RejectRequestBody, } from '@shared/infra/oauth-provider/dto'; -import { AcceptQuery, ConsentRequestBody } from '@src/modules/oauth-provider/controller/dto'; -import { ICurrentUser } from '@src/modules/authentication'; +import { AcceptQuery, ConsentRequestBody } from '@modules/oauth-provider/controller/dto'; +import { ICurrentUser } from '@modules/authentication'; import { ForbiddenException, Injectable } from '@nestjs/common'; -import { IdTokenService } from '@src/modules/oauth-provider/service/id-token.service'; +import { IdTokenService } from '@modules/oauth-provider/service/id-token.service'; import { OauthProviderService } from '@shared/infra/oauth-provider'; -import { IdToken } from '@src/modules/oauth-provider/interface/id-token'; +import { IdToken } from '@modules/oauth-provider/interface/id-token'; @Injectable() export class OauthProviderConsentFlowUc { diff --git a/apps/server/src/modules/oauth-provider/uc/oauth-provider.login-flow.uc.spec.ts b/apps/server/src/modules/oauth-provider/uc/oauth-provider.login-flow.uc.spec.ts index e95aea26594..a9225031d04 100644 --- a/apps/server/src/modules/oauth-provider/uc/oauth-provider.login-flow.uc.spec.ts +++ b/apps/server/src/modules/oauth-provider/uc/oauth-provider.login-flow.uc.spec.ts @@ -12,10 +12,10 @@ import { userDoFactory, userFactory, } from '@shared/testing'; -import { AuthorizationService } from '@src/modules/authorization'; -import { PseudonymService } from '@src/modules/pseudonym'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { UserService } from '@src/modules/user'; +import { AuthorizationService } from '@modules/authorization'; +import { PseudonymService } from '@modules/pseudonym'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { UserService } from '@modules/user'; import { AcceptQuery, LoginRequestBody, OAuthRejectableBody } from '../controller/dto'; import { OauthProviderLoginFlowService } from '../service/oauth-provider.login-flow.service'; import { OauthProviderLoginFlowUc } from './oauth-provider.login-flow.uc'; diff --git a/apps/server/src/modules/oauth-provider/uc/oauth-provider.login-flow.uc.ts b/apps/server/src/modules/oauth-provider/uc/oauth-provider.login-flow.uc.ts index 8901b506f99..dade1cb3f07 100644 --- a/apps/server/src/modules/oauth-provider/uc/oauth-provider.login-flow.uc.ts +++ b/apps/server/src/modules/oauth-provider/uc/oauth-provider.login-flow.uc.ts @@ -7,12 +7,12 @@ import { ProviderLoginResponse, ProviderRedirectResponse, } from '@shared/infra/oauth-provider/dto'; -import { AuthorizationService } from '@src/modules/authorization'; -import { AcceptQuery, LoginRequestBody, OAuthRejectableBody } from '@src/modules/oauth-provider/controller/dto'; -import { OauthProviderRequestMapper } from '@src/modules/oauth-provider/mapper/oauth-provider-request.mapper'; -import { PseudonymService } from '@src/modules/pseudonym/service'; -import { ExternalTool, Oauth2ToolConfig } from '@src/modules/tool/external-tool/domain'; -import { UserService } from '@src/modules/user'; +import { AuthorizationService } from '@modules/authorization'; +import { AcceptQuery, LoginRequestBody, OAuthRejectableBody } from '@modules/oauth-provider/controller/dto'; +import { OauthProviderRequestMapper } from '@modules/oauth-provider/mapper/oauth-provider-request.mapper'; +import { PseudonymService } from '@modules/pseudonym/service'; +import { ExternalTool, Oauth2ToolConfig } from '@modules/tool/external-tool/domain'; +import { UserService } from '@modules/user'; import { OauthProviderLoginFlowService } from '../service/oauth-provider.login-flow.service'; @Injectable() diff --git a/apps/server/src/modules/oauth-provider/uc/oauth-provider.logout-flow.uc.spec.ts b/apps/server/src/modules/oauth-provider/uc/oauth-provider.logout-flow.uc.spec.ts index 7814ee90a7a..778112840b2 100644 --- a/apps/server/src/modules/oauth-provider/uc/oauth-provider.logout-flow.uc.spec.ts +++ b/apps/server/src/modules/oauth-provider/uc/oauth-provider.logout-flow.uc.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { OauthProviderLogoutFlowUc } from '@src/modules/oauth-provider/uc/oauth-provider.logout-flow.uc'; +import { OauthProviderLogoutFlowUc } from '@modules/oauth-provider/uc/oauth-provider.logout-flow.uc'; import { OauthProviderService } from '@shared/infra/oauth-provider/index'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; diff --git a/apps/server/src/modules/oauth-provider/uc/oauth-provider.uc.spec.ts b/apps/server/src/modules/oauth-provider/uc/oauth-provider.uc.spec.ts index 90a1e5262d8..f1205db3d28 100644 --- a/apps/server/src/modules/oauth-provider/uc/oauth-provider.uc.spec.ts +++ b/apps/server/src/modules/oauth-provider/uc/oauth-provider.uc.spec.ts @@ -1,5 +1,5 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { OauthProviderUc } from '@src/modules/oauth-provider/uc/oauth-provider.uc'; +import { OauthProviderUc } from '@modules/oauth-provider/uc/oauth-provider.uc'; import { OauthProviderService } from '@shared/infra/oauth-provider/index'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ProviderConsentSessionResponse } from '@shared/infra/oauth-provider/dto'; diff --git a/apps/server/src/modules/oauth/controller/api-test/oauth-sso.api.spec.ts b/apps/server/src/modules/oauth/controller/api-test/oauth-sso.api.spec.ts index 7ddecbbaa54..eaaf07f4500 100644 --- a/apps/server/src/modules/oauth/controller/api-test/oauth-sso.api.spec.ts +++ b/apps/server/src/modules/oauth/controller/api-test/oauth-sso.api.spec.ts @@ -16,10 +16,10 @@ import { } from '@shared/testing'; import { JwtTestFactory } from '@shared/testing/factory/jwt.test.factory'; import { userLoginMigrationFactory } from '@shared/testing/factory/user-login-migration.factory'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { SanisResponse, SanisRole } from '@src/modules/provisioning/strategy/sanis/response'; -import { ServerTestModule } from '@src/modules/server'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { SanisResponse, SanisRole } from '@modules/provisioning/strategy/sanis/response'; +import { ServerTestModule } from '@modules/server'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { UUID } from 'bson'; diff --git a/apps/server/src/modules/oauth/controller/oauth-sso.controller.spec.ts b/apps/server/src/modules/oauth/controller/oauth-sso.controller.spec.ts index ee86f8e1b25..3d1a470e227 100644 --- a/apps/server/src/modules/oauth/controller/oauth-sso.controller.spec.ts +++ b/apps/server/src/modules/oauth/controller/oauth-sso.controller.spec.ts @@ -3,8 +3,8 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { UnauthorizedException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { LegacyLogger } from '@src/core/logger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { HydraOauthUc } from '@src/modules/oauth/uc/hydra-oauth.uc'; +import { ICurrentUser } from '@modules/authentication'; +import { HydraOauthUc } from '@modules/oauth/uc/hydra-oauth.uc'; import { Request } from 'express'; import { OauthSSOController } from './oauth-sso.controller'; import { StatelessAuthorizationParams } from './dto/stateless-authorization.params'; diff --git a/apps/server/src/modules/oauth/controller/oauth-sso.controller.ts b/apps/server/src/modules/oauth/controller/oauth-sso.controller.ts index a2c1b116d6d..5ff7e7cae02 100644 --- a/apps/server/src/modules/oauth/controller/oauth-sso.controller.ts +++ b/apps/server/src/modules/oauth/controller/oauth-sso.controller.ts @@ -14,9 +14,9 @@ import { import { ApiOkResponse, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ISession } from '@shared/domain/types/session'; import { LegacyLogger } from '@src/core/logger'; -import { ICurrentUser, Authenticate, CurrentUser, JWT } from '@src/modules/authentication'; -import { OAuthMigrationError } from '@src/modules/user-login-migration/error/oauth-migration.error'; -import { MigrationDto } from '@src/modules/user-login-migration/service/dto'; +import { ICurrentUser, Authenticate, CurrentUser, JWT } from '@modules/authentication'; +import { OAuthMigrationError } from '@modules/user-login-migration/error/oauth-migration.error'; +import { MigrationDto } from '@modules/user-login-migration/service/dto'; import { CookieOptions, Request, Response } from 'express'; import { HydraOauthUc } from '../uc/hydra-oauth.uc'; import { UserMigrationResponse } from './dto/user-migration.response'; diff --git a/apps/server/src/modules/oauth/mapper/user-migration.mapper.ts b/apps/server/src/modules/oauth/mapper/user-migration.mapper.ts index a6c7694aa60..42134d0b4d2 100644 --- a/apps/server/src/modules/oauth/mapper/user-migration.mapper.ts +++ b/apps/server/src/modules/oauth/mapper/user-migration.mapper.ts @@ -1,4 +1,4 @@ -import { MigrationDto } from '@src/modules/user-login-migration/service/dto'; +import { MigrationDto } from '@modules/user-login-migration/service/dto'; import { UserMigrationResponse } from '../controller/dto'; export class UserMigrationMapper { diff --git a/apps/server/src/modules/oauth/oauth-api.module.ts b/apps/server/src/modules/oauth/oauth-api.module.ts index 4bde06ce6a0..98e62d87eca 100644 --- a/apps/server/src/modules/oauth/oauth-api.module.ts +++ b/apps/server/src/modules/oauth/oauth-api.module.ts @@ -1,12 +1,12 @@ import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; -import { AuthenticationModule } from '@src/modules/authentication/authentication.module'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { ProvisioningModule } from '@src/modules/provisioning'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; -import { SystemModule } from '@src/modules/system'; -import { UserModule } from '@src/modules/user'; -import { UserLoginMigrationModule } from '@src/modules/user-login-migration'; +import { AuthenticationModule } from '@modules/authentication/authentication.module'; +import { AuthorizationModule } from '@modules/authorization'; +import { ProvisioningModule } from '@modules/provisioning'; +import { LegacySchoolModule } from '@modules/legacy-school'; +import { SystemModule } from '@modules/system'; +import { UserModule } from '@modules/user'; +import { UserLoginMigrationModule } from '@modules/user-login-migration'; import { OauthSSOController } from './controller/oauth-sso.controller'; import { OauthModule } from './oauth.module'; import { HydraOauthUc, OauthUc } from './uc'; diff --git a/apps/server/src/modules/oauth/oauth.module.ts b/apps/server/src/modules/oauth/oauth.module.ts index b8519e3eaa2..273a099159b 100644 --- a/apps/server/src/modules/oauth/oauth.module.ts +++ b/apps/server/src/modules/oauth/oauth.module.ts @@ -4,12 +4,12 @@ import { CacheWrapperModule } from '@shared/infra/cache'; import { EncryptionModule } from '@shared/infra/encryption'; import { LtiToolRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { ProvisioningModule } from '@src/modules/provisioning'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; -import { SystemModule } from '@src/modules/system'; -import { UserModule } from '@src/modules/user'; -import { UserLoginMigrationModule } from '@src/modules/user-login-migration'; +import { AuthorizationModule } from '@modules/authorization'; +import { ProvisioningModule } from '@modules/provisioning'; +import { LegacySchoolModule } from '@modules/legacy-school'; +import { SystemModule } from '@modules/system'; +import { UserModule } from '@modules/user'; +import { UserLoginMigrationModule } from '@modules/user-login-migration'; import { HydraSsoService } from './service/hydra.service'; import { OauthAdapterService } from './service/oauth-adapter.service'; import { OAuthService } from './service/oauth.service'; diff --git a/apps/server/src/modules/oauth/service/dto/hydra.redirect.dto.ts b/apps/server/src/modules/oauth/service/dto/hydra.redirect.dto.ts index fe4a244de4e..4a1ead7c157 100644 --- a/apps/server/src/modules/oauth/service/dto/hydra.redirect.dto.ts +++ b/apps/server/src/modules/oauth/service/dto/hydra.redirect.dto.ts @@ -1,4 +1,4 @@ -import { CookiesDto } from '@src/modules/oauth/service/dto/cookies.dto'; +import { CookiesDto } from '@modules/oauth/service/dto/cookies.dto'; import { AxiosRequestConfig, AxiosResponse } from 'axios'; export class HydraRedirectDto { diff --git a/apps/server/src/modules/oauth/service/hydra.service.spec.ts b/apps/server/src/modules/oauth/service/hydra.service.spec.ts index 4912bc039ca..3886aa40a58 100644 --- a/apps/server/src/modules/oauth/service/hydra.service.spec.ts +++ b/apps/server/src/modules/oauth/service/hydra.service.spec.ts @@ -10,9 +10,9 @@ import { DefaultEncryptionService, SymetricKeyEncryptionService } from '@shared/ import { LtiToolRepo } from '@shared/repo'; import { axiosResponseFactory } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { CookiesDto } from '@src/modules/oauth/service/dto/cookies.dto'; -import { HydraRedirectDto } from '@src/modules/oauth/service/dto/hydra.redirect.dto'; -import { HydraSsoService } from '@src/modules/oauth/service/hydra.service'; +import { CookiesDto } from '@modules/oauth/service/dto/cookies.dto'; +import { HydraRedirectDto } from '@modules/oauth/service/dto/hydra.redirect.dto'; +import { HydraSsoService } from '@modules/oauth/service/hydra.service'; import { AxiosResponse } from 'axios'; import { of } from 'rxjs'; import { StatelessAuthorizationParams } from '../controller/dto/stateless-authorization.params'; diff --git a/apps/server/src/modules/oauth/service/hydra.service.ts b/apps/server/src/modules/oauth/service/hydra.service.ts index 94ab8c66ff8..9b335604825 100644 --- a/apps/server/src/modules/oauth/service/hydra.service.ts +++ b/apps/server/src/modules/oauth/service/hydra.service.ts @@ -7,9 +7,9 @@ import { LtiToolDO } from '@shared/domain/domainobject/ltitool.do'; import { DefaultEncryptionService, IEncryptionService } from '@shared/infra/encryption'; import { LtiToolRepo } from '@shared/repo'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationParams } from '@src/modules/oauth/controller/dto/authorization.params'; -import { CookiesDto } from '@src/modules/oauth/service/dto/cookies.dto'; -import { HydraRedirectDto } from '@src/modules/oauth/service/dto/hydra.redirect.dto'; +import { AuthorizationParams } from '@modules/oauth/controller/dto/authorization.params'; +import { CookiesDto } from '@modules/oauth/service/dto/cookies.dto'; +import { HydraRedirectDto } from '@modules/oauth/service/dto/hydra.redirect.dto'; import { AxiosRequestConfig, AxiosResponse } from 'axios'; import { nanoid } from 'nanoid'; import QueryString from 'qs'; diff --git a/apps/server/src/modules/oauth/service/oauth.service.spec.ts b/apps/server/src/modules/oauth/service/oauth.service.spec.ts index 81bb7724dbd..2743037e214 100644 --- a/apps/server/src/modules/oauth/service/oauth.service.spec.ts +++ b/apps/server/src/modules/oauth/service/oauth.service.spec.ts @@ -7,14 +7,14 @@ import { SystemProvisioningStrategy } from '@shared/domain/interface/system-prov import { DefaultEncryptionService, IEncryptionService, SymetricKeyEncryptionService } from '@shared/infra/encryption'; import { legacySchoolDoFactory, setupEntities, systemFactory, userDoFactory } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { ProvisioningDto, ProvisioningService } from '@src/modules/provisioning'; -import { ExternalSchoolDto, ExternalUserDto, OauthDataDto, ProvisioningSystemDto } from '@src/modules/provisioning/dto'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { OauthConfigDto } from '@src/modules/system/service'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; -import { SystemService } from '@src/modules/system/service/system.service'; -import { UserService } from '@src/modules/user'; -import { MigrationCheckService, UserMigrationService } from '@src/modules/user-login-migration'; +import { ProvisioningDto, ProvisioningService } from '@modules/provisioning'; +import { ExternalSchoolDto, ExternalUserDto, OauthDataDto, ProvisioningSystemDto } from '@modules/provisioning/dto'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { OauthConfigDto } from '@modules/system/service'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; +import { SystemService } from '@modules/system/service/system.service'; +import { UserService } from '@modules/user'; +import { MigrationCheckService, UserMigrationService } from '@modules/user-login-migration'; import jwt, { JwtPayload } from 'jsonwebtoken'; import { OAuthSSOError, UserNotFoundAfterProvisioningLoggableException } from '../loggable'; import { OAuthTokenDto } from '../interface'; diff --git a/apps/server/src/modules/oauth/service/oauth.service.ts b/apps/server/src/modules/oauth/service/oauth.service.ts index 4445438b11f..28a24c0534a 100644 --- a/apps/server/src/modules/oauth/service/oauth.service.ts +++ b/apps/server/src/modules/oauth/service/oauth.service.ts @@ -4,13 +4,13 @@ import { Injectable } from '@nestjs/common/decorators/core/injectable.decorator' import { EntityId, LegacySchoolDo, OauthConfig, SchoolFeatures, UserDO } from '@shared/domain'; import { DefaultEncryptionService, IEncryptionService } from '@shared/infra/encryption'; import { LegacyLogger } from '@src/core/logger'; -import { ProvisioningService } from '@src/modules/provisioning'; -import { OauthDataDto } from '@src/modules/provisioning/dto'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { SystemService } from '@src/modules/system'; -import { SystemDto } from '@src/modules/system/service'; -import { UserService } from '@src/modules/user'; -import { MigrationCheckService, UserMigrationService } from '@src/modules/user-login-migration'; +import { ProvisioningService } from '@modules/provisioning'; +import { OauthDataDto } from '@modules/provisioning/dto'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { SystemService } from '@modules/system'; +import { SystemDto } from '@modules/system/service'; +import { UserService } from '@modules/user'; +import { MigrationCheckService, UserMigrationService } from '@modules/user-login-migration'; import jwt, { JwtPayload } from 'jsonwebtoken'; import { OAuthSSOError, SSOErrorCode, UserNotFoundAfterProvisioningLoggableException } from '../loggable'; import { OAuthTokenDto } from '../interface'; diff --git a/apps/server/src/modules/oauth/uc/hydra-oauth.uc.spec.ts b/apps/server/src/modules/oauth/uc/hydra-oauth.uc.spec.ts index 4736e1905d1..3d42b0e977f 100644 --- a/apps/server/src/modules/oauth/uc/hydra-oauth.uc.spec.ts +++ b/apps/server/src/modules/oauth/uc/hydra-oauth.uc.spec.ts @@ -6,9 +6,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { OauthConfig } from '@shared/domain'; import { axiosResponseFactory } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { HydraRedirectDto } from '@src/modules/oauth/service/dto/hydra.redirect.dto'; -import { HydraSsoService } from '@src/modules/oauth/service/hydra.service'; -import { OAuthService } from '@src/modules/oauth/service/oauth.service'; +import { HydraRedirectDto } from '@modules/oauth/service/dto/hydra.redirect.dto'; +import { HydraSsoService } from '@modules/oauth/service/hydra.service'; +import { OAuthService } from '@modules/oauth/service/oauth.service'; import { AxiosResponse } from 'axios'; import { HydraOauthUc } from '.'; import { AuthorizationParams } from '../controller/dto'; diff --git a/apps/server/src/modules/oauth/uc/hydra-oauth.uc.ts b/apps/server/src/modules/oauth/uc/hydra-oauth.uc.ts index 7889ad31def..905cd3c8802 100644 --- a/apps/server/src/modules/oauth/uc/hydra-oauth.uc.ts +++ b/apps/server/src/modules/oauth/uc/hydra-oauth.uc.ts @@ -2,7 +2,7 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { OauthConfig } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { HydraRedirectDto } from '@src/modules/oauth/service/dto/hydra.redirect.dto'; +import { HydraRedirectDto } from '@modules/oauth/service/dto/hydra.redirect.dto'; import { AxiosRequestConfig, AxiosResponse } from 'axios'; import { AuthorizationParams } from '../controller/dto'; import { OAuthSSOError } from '../loggable/oauth-sso.error'; diff --git a/apps/server/src/modules/oauth/uc/oauth.uc.spec.ts b/apps/server/src/modules/oauth/uc/oauth.uc.spec.ts index 15907d5dde2..4323cd5bc85 100644 --- a/apps/server/src/modules/oauth/uc/oauth.uc.spec.ts +++ b/apps/server/src/modules/oauth/uc/oauth.uc.spec.ts @@ -6,19 +6,19 @@ import { SystemProvisioningStrategy } from '@shared/domain/interface/system-prov import { ISession } from '@shared/domain/types/session'; import { legacySchoolDoFactory, setupEntities } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { AuthenticationService } from '@src/modules/authentication/services/authentication.service'; -import { OauthUc } from '@src/modules/oauth/uc/oauth.uc'; -import { ProvisioningService } from '@src/modules/provisioning'; -import { ExternalUserDto, OauthDataDto, ProvisioningSystemDto } from '@src/modules/provisioning/dto'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { SystemService } from '@src/modules/system'; -import { OauthConfigDto, SystemDto } from '@src/modules/system/service'; -import { UserService } from '@src/modules/user'; -import { UserMigrationService } from '@src/modules/user-login-migration'; -import { OAuthMigrationError } from '@src/modules/user-login-migration/error/oauth-migration.error'; -import { SchoolMigrationService } from '@src/modules/user-login-migration/service'; -import { MigrationDto } from '@src/modules/user-login-migration/service/dto'; +import { ICurrentUser } from '@modules/authentication'; +import { AuthenticationService } from '@modules/authentication/services/authentication.service'; +import { OauthUc } from '@modules/oauth/uc/oauth.uc'; +import { ProvisioningService } from '@modules/provisioning'; +import { ExternalUserDto, OauthDataDto, ProvisioningSystemDto } from '@modules/provisioning/dto'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { SystemService } from '@modules/system'; +import { OauthConfigDto, SystemDto } from '@modules/system/service'; +import { UserService } from '@modules/user'; +import { UserMigrationService } from '@modules/user-login-migration'; +import { OAuthMigrationError } from '@modules/user-login-migration/error/oauth-migration.error'; +import { SchoolMigrationService } from '@modules/user-login-migration/service'; +import { MigrationDto } from '@modules/user-login-migration/service/dto'; import { OAuthSSOError } from '../loggable/oauth-sso.error'; import { AuthorizationParams } from '../controller/dto'; import { OAuthTokenDto } from '../interface'; diff --git a/apps/server/src/modules/oauth/uc/oauth.uc.ts b/apps/server/src/modules/oauth/uc/oauth.uc.ts index e4dd7e68264..53d986bf029 100644 --- a/apps/server/src/modules/oauth/uc/oauth.uc.ts +++ b/apps/server/src/modules/oauth/uc/oauth.uc.ts @@ -2,16 +2,16 @@ import { Injectable, UnauthorizedException, UnprocessableEntityException } from import { EntityId, LegacySchoolDo, UserDO } from '@shared/domain'; import { ISession } from '@shared/domain/types/session'; import { LegacyLogger } from '@src/core/logger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { AuthenticationService } from '@src/modules/authentication/services/authentication.service'; -import { ProvisioningService } from '@src/modules/provisioning'; -import { OauthDataDto } from '@src/modules/provisioning/dto'; -import { SystemService } from '@src/modules/system'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; -import { UserService } from '@src/modules/user'; -import { UserMigrationService } from '@src/modules/user-login-migration'; -import { SchoolMigrationService } from '@src/modules/user-login-migration/service'; -import { MigrationDto } from '@src/modules/user-login-migration/service/dto'; +import { ICurrentUser } from '@modules/authentication'; +import { AuthenticationService } from '@modules/authentication/services/authentication.service'; +import { ProvisioningService } from '@modules/provisioning'; +import { OauthDataDto } from '@modules/provisioning/dto'; +import { SystemService } from '@modules/system'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; +import { UserService } from '@modules/user'; +import { UserMigrationService } from '@modules/user-login-migration'; +import { SchoolMigrationService } from '@modules/user-login-migration/service'; +import { MigrationDto } from '@modules/user-login-migration/service/dto'; import { nanoid } from 'nanoid'; import { AuthorizationParams } from '../controller/dto'; import { OAuthTokenDto } from '../interface'; diff --git a/apps/server/src/modules/provisioning/dto/external-group.dto.ts b/apps/server/src/modules/provisioning/dto/external-group.dto.ts index 57cdc78e44c..e01093fa4ea 100644 --- a/apps/server/src/modules/provisioning/dto/external-group.dto.ts +++ b/apps/server/src/modules/provisioning/dto/external-group.dto.ts @@ -1,4 +1,4 @@ -import { GroupTypes } from '@src/modules/group'; +import { GroupTypes } from '@modules/group'; import { ExternalGroupUserDto } from './external-group-user.dto'; export class ExternalGroupDto { diff --git a/apps/server/src/modules/provisioning/mapper/provisioning-system-input.mapper.spec.ts b/apps/server/src/modules/provisioning/mapper/provisioning-system-input.mapper.spec.ts index fe7d866e2c9..f25054ce1ac 100644 --- a/apps/server/src/modules/provisioning/mapper/provisioning-system-input.mapper.spec.ts +++ b/apps/server/src/modules/provisioning/mapper/provisioning-system-input.mapper.spec.ts @@ -1,5 +1,5 @@ import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; import { ProvisioningSystemDto } from '../dto'; import { ProvisioningSystemInputMapper } from './provisioning-system-input.mapper'; diff --git a/apps/server/src/modules/provisioning/mapper/provisioning-system-input.mapper.ts b/apps/server/src/modules/provisioning/mapper/provisioning-system-input.mapper.ts index b0b6a81a5af..9668bbfffea 100644 --- a/apps/server/src/modules/provisioning/mapper/provisioning-system-input.mapper.ts +++ b/apps/server/src/modules/provisioning/mapper/provisioning-system-input.mapper.ts @@ -1,5 +1,5 @@ import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; import { ProvisioningSystemDto } from '../dto'; export class ProvisioningSystemInputMapper { diff --git a/apps/server/src/modules/provisioning/provisioning.module.ts b/apps/server/src/modules/provisioning/provisioning.module.ts index 854d1834387..185516e3f38 100644 --- a/apps/server/src/modules/provisioning/provisioning.module.ts +++ b/apps/server/src/modules/provisioning/provisioning.module.ts @@ -1,12 +1,12 @@ import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; -import { AccountModule } from '@src/modules/account/account.module'; -import { RoleModule } from '@src/modules/role'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; -import { SystemModule } from '@src/modules/system/system.module'; -import { UserModule } from '@src/modules/user'; -import { GroupModule } from '@src/modules/group'; +import { AccountModule } from '@modules/account/account.module'; +import { RoleModule } from '@modules/role'; +import { LegacySchoolModule } from '@modules/legacy-school'; +import { SystemModule } from '@modules/system/system.module'; +import { UserModule } from '@modules/user'; +import { GroupModule } from '@modules/group'; import { ProvisioningService } from './service/provisioning.service'; import { IservProvisioningStrategy, OidcMockProvisioningStrategy, SanisProvisioningStrategy } from './strategy'; import { OidcProvisioningService } from './strategy/oidc/service/oidc-provisioning.service'; diff --git a/apps/server/src/modules/provisioning/service/provisioning.service.spec.ts b/apps/server/src/modules/provisioning/service/provisioning.service.spec.ts index fee48a9b457..1d80c6c7b90 100644 --- a/apps/server/src/modules/provisioning/service/provisioning.service.spec.ts +++ b/apps/server/src/modules/provisioning/service/provisioning.service.spec.ts @@ -2,8 +2,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; -import { SystemService } from '@src/modules/system/service/system.service'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; +import { SystemService } from '@modules/system/service/system.service'; import { ExternalUserDto, OauthDataDto, diff --git a/apps/server/src/modules/provisioning/service/provisioning.service.ts b/apps/server/src/modules/provisioning/service/provisioning.service.ts index 015c0d02fc9..50ee527001c 100644 --- a/apps/server/src/modules/provisioning/service/provisioning.service.ts +++ b/apps/server/src/modules/provisioning/service/provisioning.service.ts @@ -1,7 +1,7 @@ import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; -import { SystemService } from '@src/modules/system'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; +import { SystemService } from '@modules/system'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; import { OauthDataDto, OauthDataStrategyInputDto, ProvisioningDto, ProvisioningSystemDto } from '../dto'; import { ProvisioningSystemInputMapper } from '../mapper/provisioning-system-input.mapper'; import { diff --git a/apps/server/src/modules/provisioning/strategy/iserv/iserv.strategy.spec.ts b/apps/server/src/modules/provisioning/strategy/iserv/iserv.strategy.spec.ts index fad80012e06..e91ec8ca159 100644 --- a/apps/server/src/modules/provisioning/strategy/iserv/iserv.strategy.spec.ts +++ b/apps/server/src/modules/provisioning/strategy/iserv/iserv.strategy.spec.ts @@ -4,10 +4,10 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LegacySchoolDo, RoleName, User, UserDO } from '@shared/domain'; import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; import { legacySchoolDoFactory, schoolFactory, setupEntities, userDoFactory, userFactory } from '@shared/testing'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { UserService } from '@src/modules/user'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; import jwt from 'jsonwebtoken'; -import { OAuthSSOError } from '@src/modules/oauth/loggable'; +import { OAuthSSOError } from '@modules/oauth/loggable'; import { RoleDto } from '../../../role/service/dto/role.dto'; import { ExternalSchoolDto, diff --git a/apps/server/src/modules/provisioning/strategy/iserv/iserv.strategy.ts b/apps/server/src/modules/provisioning/strategy/iserv/iserv.strategy.ts index 4a449b9287f..8d5e1b5378f 100644 --- a/apps/server/src/modules/provisioning/strategy/iserv/iserv.strategy.ts +++ b/apps/server/src/modules/provisioning/strategy/iserv/iserv.strategy.ts @@ -1,10 +1,10 @@ import { Injectable } from '@nestjs/common'; import { LegacySchoolDo, RoleName, RoleReference, User, UserDO } from '@shared/domain'; import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { UserService } from '@src/modules/user'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; import jwt, { JwtPayload } from 'jsonwebtoken'; -import { OAuthSSOError } from '@src/modules/oauth/loggable'; +import { OAuthSSOError } from '@modules/oauth/loggable'; import { ExternalSchoolDto, ExternalUserDto, diff --git a/apps/server/src/modules/provisioning/strategy/oidc-mock/oidc-mock.strategy.spec.ts b/apps/server/src/modules/provisioning/strategy/oidc-mock/oidc-mock.strategy.spec.ts index c6cff205686..f9235522100 100644 --- a/apps/server/src/modules/provisioning/strategy/oidc-mock/oidc-mock.strategy.spec.ts +++ b/apps/server/src/modules/provisioning/strategy/oidc-mock/oidc-mock.strategy.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; import jwt from 'jsonwebtoken'; -import { OAuthSSOError } from '@src/modules/oauth/loggable'; +import { OAuthSSOError } from '@modules/oauth/loggable'; import { ExternalUserDto, OauthDataDto, diff --git a/apps/server/src/modules/provisioning/strategy/oidc-mock/oidc-mock.strategy.ts b/apps/server/src/modules/provisioning/strategy/oidc-mock/oidc-mock.strategy.ts index 5daa69ed7d2..e505386ca97 100644 --- a/apps/server/src/modules/provisioning/strategy/oidc-mock/oidc-mock.strategy.ts +++ b/apps/server/src/modules/provisioning/strategy/oidc-mock/oidc-mock.strategy.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; import jwt, { JwtPayload } from 'jsonwebtoken'; -import { OAuthSSOError } from '@src/modules/oauth/loggable'; +import { OAuthSSOError } from '@modules/oauth/loggable'; import { ExternalUserDto, OauthDataDto, OauthDataStrategyInputDto, ProvisioningDto } from '../../dto'; import { ProvisioningStrategy } from '../base.strategy'; diff --git a/apps/server/src/modules/provisioning/strategy/oidc/service/oidc-provisioning.service.spec.ts b/apps/server/src/modules/provisioning/strategy/oidc/service/oidc-provisioning.service.spec.ts index 7e6e05c7b2e..09e253dddbf 100644 --- a/apps/server/src/modules/provisioning/strategy/oidc/service/oidc-provisioning.service.spec.ts +++ b/apps/server/src/modules/provisioning/strategy/oidc/service/oidc-provisioning.service.spec.ts @@ -14,13 +14,13 @@ import { roleFactory, } from '@shared/testing'; import { Logger } from '@src/core/logger'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountSaveDto } from '@src/modules/account/services/dto'; -import { Group, GroupService } from '@src/modules/group'; -import { RoleService } from '@src/modules/role'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; -import { FederalStateService, LegacySchoolService, SchoolYearService } from '@src/modules/legacy-school'; -import { UserService } from '@src/modules/user'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountSaveDto } from '@modules/account/services/dto'; +import { Group, GroupService } from '@modules/group'; +import { RoleService } from '@modules/role'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; +import { FederalStateService, LegacySchoolService, SchoolYearService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; import CryptoJS from 'crypto-js'; import { NotFoundLoggableException } from '@shared/common/loggable-exception'; import { ExternalGroupDto, ExternalSchoolDto, ExternalUserDto } from '../../../dto'; diff --git a/apps/server/src/modules/provisioning/strategy/oidc/service/oidc-provisioning.service.ts b/apps/server/src/modules/provisioning/strategy/oidc/service/oidc-provisioning.service.ts index 31b2d1ab8f3..66c243e6457 100644 --- a/apps/server/src/modules/provisioning/strategy/oidc/service/oidc-provisioning.service.ts +++ b/apps/server/src/modules/provisioning/strategy/oidc/service/oidc-provisioning.service.ts @@ -2,14 +2,14 @@ import { Injectable, UnprocessableEntityException } from '@nestjs/common'; import { EntityId, ExternalSource, FederalStateEntity, SchoolFeatures, SchoolYearEntity } from '@shared/domain'; import { LegacySchoolDo, RoleReference, UserDO } from '@shared/domain/domainobject'; import { Logger } from '@src/core/logger'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountSaveDto } from '@src/modules/account/services/dto'; -import { Group, GroupService, GroupUser } from '@src/modules/group'; -import { FederalStateService, LegacySchoolService, SchoolYearService } from '@src/modules/legacy-school'; -import { FederalStateNames } from '@src/modules/legacy-school/types'; -import { RoleService } from '@src/modules/role'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; -import { UserService } from '@src/modules/user'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountSaveDto } from '@modules/account/services/dto'; +import { Group, GroupService, GroupUser } from '@modules/group'; +import { FederalStateService, LegacySchoolService, SchoolYearService } from '@modules/legacy-school'; +import { FederalStateNames } from '@modules/legacy-school/types'; +import { RoleService } from '@modules/role'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; +import { UserService } from '@modules/user'; import { ObjectId } from 'bson'; import CryptoJS from 'crypto-js'; import { NotFoundLoggableException } from '@shared/common/loggable-exception'; diff --git a/apps/server/src/modules/provisioning/strategy/sanis/sanis-response.mapper.spec.ts b/apps/server/src/modules/provisioning/strategy/sanis/sanis-response.mapper.spec.ts index c52e654155b..2fe68c0163b 100644 --- a/apps/server/src/modules/provisioning/strategy/sanis/sanis-response.mapper.spec.ts +++ b/apps/server/src/modules/provisioning/strategy/sanis/sanis-response.mapper.spec.ts @@ -2,7 +2,7 @@ import { createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { RoleName } from '@shared/domain'; import { Logger } from '@src/core/logger'; -import { GroupTypes } from '@src/modules/group'; +import { GroupTypes } from '@modules/group'; import { UUID } from 'bson'; import { ExternalGroupDto, ExternalSchoolDto, ExternalUserDto } from '../../dto'; import { diff --git a/apps/server/src/modules/provisioning/strategy/sanis/sanis-response.mapper.ts b/apps/server/src/modules/provisioning/strategy/sanis/sanis-response.mapper.ts index e11e57e9068..23d9b15fbdc 100644 --- a/apps/server/src/modules/provisioning/strategy/sanis/sanis-response.mapper.ts +++ b/apps/server/src/modules/provisioning/strategy/sanis/sanis-response.mapper.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { RoleName } from '@shared/domain'; import { Logger } from '@src/core/logger'; -import { GroupTypes } from '@src/modules/group'; +import { GroupTypes } from '@modules/group'; import { ExternalGroupDto, ExternalGroupUserDto, ExternalSchoolDto, ExternalUserDto } from '../../dto'; import { GroupRoleUnknownLoggable } from '../../loggable'; import { diff --git a/apps/server/src/modules/provisioning/strategy/sanis/sanis.strategy.spec.ts b/apps/server/src/modules/provisioning/strategy/sanis/sanis.strategy.spec.ts index b1324cbf74b..f0ea97f89fd 100644 --- a/apps/server/src/modules/provisioning/strategy/sanis/sanis.strategy.spec.ts +++ b/apps/server/src/modules/provisioning/strategy/sanis/sanis.strategy.spec.ts @@ -6,7 +6,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { RoleName } from '@shared/domain'; import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; import { axiosResponseFactory, setupEntities } from '@shared/testing'; -import { GroupTypes } from '@src/modules/group'; +import { GroupTypes } from '@modules/group'; import { UUID } from 'bson'; import { of } from 'rxjs'; import { diff --git a/apps/server/src/modules/pseudonym/controller/api-test/pseudonym.api.spec.ts b/apps/server/src/modules/pseudonym/controller/api-test/pseudonym.api.spec.ts index 05bd5d4f2f5..fa72cf82075 100644 --- a/apps/server/src/modules/pseudonym/controller/api-test/pseudonym.api.spec.ts +++ b/apps/server/src/modules/pseudonym/controller/api-test/pseudonym.api.spec.ts @@ -11,8 +11,8 @@ import { import { Test, TestingModule } from '@nestjs/testing'; import { Response } from 'supertest'; import { SchoolEntity } from '@shared/domain'; -import { ServerTestModule } from '@src/modules/server'; -import { ExternalToolEntity } from '@src/modules/tool/external-tool/entity'; +import { ServerTestModule } from '@modules/server'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; import { UUID } from 'bson'; import { ExternalToolPseudonymEntity } from '../../entity'; import { PseudonymResponse } from '../dto'; diff --git a/apps/server/src/modules/pseudonym/controller/pseudonym.controller.ts b/apps/server/src/modules/pseudonym/controller/pseudonym.controller.ts index 16d55fba9db..f7e378e050a 100644 --- a/apps/server/src/modules/pseudonym/controller/pseudonym.controller.ts +++ b/apps/server/src/modules/pseudonym/controller/pseudonym.controller.ts @@ -7,7 +7,7 @@ import { ApiUnauthorizedResponse, } from '@nestjs/swagger'; import { Pseudonym } from '@shared/domain'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { PseudonymMapper } from '../mapper/pseudonym.mapper'; import { PseudonymUc } from '../uc'; import { PseudonymResponse } from './dto'; diff --git a/apps/server/src/modules/pseudonym/pseudonym-api.module.ts b/apps/server/src/modules/pseudonym/pseudonym-api.module.ts index 8ba18f1cb72..2b9fdd3afed 100644 --- a/apps/server/src/modules/pseudonym/pseudonym-api.module.ts +++ b/apps/server/src/modules/pseudonym/pseudonym-api.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; +import { AuthorizationModule } from '@modules/authorization'; +import { LegacySchoolModule } from '@modules/legacy-school'; import { PseudonymModule } from './pseudonym.module'; import { PseudonymController } from './controller/pseudonym.controller'; import { PseudonymUc } from './uc'; diff --git a/apps/server/src/modules/pseudonym/pseudonym.module.ts b/apps/server/src/modules/pseudonym/pseudonym.module.ts index 21da4ef3c59..d282c5dd9fe 100644 --- a/apps/server/src/modules/pseudonym/pseudonym.module.ts +++ b/apps/server/src/modules/pseudonym/pseudonym.module.ts @@ -1,9 +1,9 @@ import { forwardRef, Module } from '@nestjs/common'; import { LegacyLogger } from '@src/core/logger'; -import { LearnroomModule } from '@src/modules/learnroom'; -import { UserModule } from '@src/modules/user'; -import { ToolModule } from '@src/modules/tool'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { LearnroomModule } from '@modules/learnroom'; +import { UserModule } from '@modules/user'; +import { ToolModule } from '@modules/tool'; +import { AuthorizationModule } from '@modules/authorization'; import { ExternalToolPseudonymRepo, PseudonymsRepo } from './repo'; import { FeathersRosterService, PseudonymService } from './service'; diff --git a/apps/server/src/modules/pseudonym/service/feathers-roster.service.spec.ts b/apps/server/src/modules/pseudonym/service/feathers-roster.service.spec.ts index e2a8484d630..59de8663ea6 100644 --- a/apps/server/src/modules/pseudonym/service/feathers-roster.service.spec.ts +++ b/apps/server/src/modules/pseudonym/service/feathers-roster.service.spec.ts @@ -15,15 +15,15 @@ import { UserAndAccountTestFactory, userDoFactory, } from '@shared/testing'; -import { CourseService } from '@src/modules/learnroom/service/course.service'; -import { ToolContextType } from '@src/modules/tool/common/enum'; -import { ContextExternalTool, ContextRef } from '@src/modules/tool/context-external-tool/domain'; -import { ContextExternalToolService } from '@src/modules/tool/context-external-tool/service'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { ExternalToolService } from '@src/modules/tool/external-tool/service'; -import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; -import { SchoolExternalToolService } from '@src/modules/tool/school-external-tool/service'; -import { UserService } from '@src/modules/user'; +import { CourseService } from '@modules/learnroom/service/course.service'; +import { ToolContextType } from '@modules/tool/common/enum'; +import { ContextExternalTool, ContextRef } from '@modules/tool/context-external-tool/domain'; +import { ContextExternalToolService } from '@modules/tool/context-external-tool/service'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { ExternalToolService } from '@modules/tool/external-tool/service'; +import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; +import { SchoolExternalToolService } from '@modules/tool/school-external-tool/service'; +import { UserService } from '@modules/user'; import { ObjectId } from 'bson'; import { FeathersRosterService } from './feathers-roster.service'; import { PseudonymService } from './pseudonym.service'; diff --git a/apps/server/src/modules/pseudonym/service/feathers-roster.service.ts b/apps/server/src/modules/pseudonym/service/feathers-roster.service.ts index e808a2fc59f..a6b512d13c2 100644 --- a/apps/server/src/modules/pseudonym/service/feathers-roster.service.ts +++ b/apps/server/src/modules/pseudonym/service/feathers-roster.service.ts @@ -1,15 +1,15 @@ import { Injectable } from '@nestjs/common'; import { NotFoundLoggableException } from '@shared/common/loggable-exception'; import { Course, EntityId, Pseudonym, RoleName, RoleReference, UserDO } from '@shared/domain'; -import { CourseService } from '@src/modules/learnroom/service'; -import { ToolContextType } from '@src/modules/tool/common/enum'; -import { ContextExternalTool, ContextRef } from '@src/modules/tool/context-external-tool/domain'; -import { ContextExternalToolService } from '@src/modules/tool/context-external-tool/service'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { ExternalToolService } from '@src/modules/tool/external-tool/service'; -import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; -import { SchoolExternalToolService } from '@src/modules/tool/school-external-tool/service'; -import { UserService } from '@src/modules/user'; +import { CourseService } from '@modules/learnroom/service'; +import { ToolContextType } from '@modules/tool/common/enum'; +import { ContextExternalTool, ContextRef } from '@modules/tool/context-external-tool/domain'; +import { ContextExternalToolService } from '@modules/tool/context-external-tool/service'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { ExternalToolService } from '@modules/tool/external-tool/service'; +import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; +import { SchoolExternalToolService } from '@modules/tool/school-external-tool/service'; +import { UserService } from '@modules/user'; import { PseudonymService } from './pseudonym.service'; interface UserMetdata { diff --git a/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts b/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts index e2fbb6e1b1f..026d28d039f 100644 --- a/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts +++ b/apps/server/src/modules/pseudonym/service/pseudonym.service.spec.ts @@ -4,7 +4,7 @@ import { InternalServerErrorException, NotFoundException } from '@nestjs/common' import { Test, TestingModule } from '@nestjs/testing'; import { IFindOptions, LtiToolDO, Page, Pseudonym, UserDO } from '@shared/domain'; import { externalToolFactory, ltiToolDOFactory, pseudonymFactory, userDoFactory } from '@shared/testing/factory'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; import { PseudonymSearchQuery } from '../domain'; import { ExternalToolPseudonymRepo, PseudonymsRepo } from '../repo'; import { PseudonymService } from './pseudonym.service'; diff --git a/apps/server/src/modules/pseudonym/service/pseudonym.service.ts b/apps/server/src/modules/pseudonym/service/pseudonym.service.ts index 23819f2fd3b..6d15d6a1ec9 100644 --- a/apps/server/src/modules/pseudonym/service/pseudonym.service.ts +++ b/apps/server/src/modules/pseudonym/service/pseudonym.service.ts @@ -2,7 +2,7 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { ObjectId } from '@mikro-orm/mongodb'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { IFindOptions, LtiToolDO, Page, Pseudonym, UserDO } from '@shared/domain'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; import { v4 as uuidv4 } from 'uuid'; import { PseudonymSearchQuery } from '../domain'; import { ExternalToolPseudonymRepo, PseudonymsRepo } from '../repo'; diff --git a/apps/server/src/modules/pseudonym/uc/pseudonym.uc.spec.ts b/apps/server/src/modules/pseudonym/uc/pseudonym.uc.spec.ts index 87fbfcbb526..26140da8a28 100644 --- a/apps/server/src/modules/pseudonym/uc/pseudonym.uc.spec.ts +++ b/apps/server/src/modules/pseudonym/uc/pseudonym.uc.spec.ts @@ -3,8 +3,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LegacySchoolDo, Pseudonym, SchoolEntity, User } from '@shared/domain'; import { legacySchoolDoFactory, pseudonymFactory, schoolFactory, setupEntities, userFactory } from '@shared/testing'; import { ForbiddenException } from '@nestjs/common'; -import { Action, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { Action, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { PseudonymService } from '../service'; import { PseudonymUc } from './pseudonym.uc'; diff --git a/apps/server/src/modules/pseudonym/uc/pseudonym.uc.ts b/apps/server/src/modules/pseudonym/uc/pseudonym.uc.ts index a960a33bc3c..4c0ecb19a36 100644 --- a/apps/server/src/modules/pseudonym/uc/pseudonym.uc.ts +++ b/apps/server/src/modules/pseudonym/uc/pseudonym.uc.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { EntityId, LegacySchoolDo, Pseudonym, User } from '@shared/domain'; import { NotFoundLoggableException } from '@shared/common/loggable-exception'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { PseudonymService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/role/mapper/role.mapper.spec.ts b/apps/server/src/modules/role/mapper/role.mapper.spec.ts index 806d21d01e8..c7152cfd949 100644 --- a/apps/server/src/modules/role/mapper/role.mapper.spec.ts +++ b/apps/server/src/modules/role/mapper/role.mapper.spec.ts @@ -1,7 +1,7 @@ import { Permission, Role } from '@shared/domain'; import { roleFactory, setupEntities } from '@shared/testing'; -import { RoleMapper } from '@src/modules/role/mapper/role.mapper'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; +import { RoleMapper } from '@modules/role/mapper/role.mapper'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; describe('RoleMapper', () => { beforeAll(async () => { diff --git a/apps/server/src/modules/role/mapper/role.mapper.ts b/apps/server/src/modules/role/mapper/role.mapper.ts index a805098544b..7b427bb809d 100644 --- a/apps/server/src/modules/role/mapper/role.mapper.ts +++ b/apps/server/src/modules/role/mapper/role.mapper.ts @@ -1,5 +1,5 @@ import { Role } from '@shared/domain'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; export class RoleMapper { static mapFromEntityToDto(entity: Role): RoleDto { diff --git a/apps/server/src/modules/role/role.module.ts b/apps/server/src/modules/role/role.module.ts index 9189d3ab191..42063b400e3 100644 --- a/apps/server/src/modules/role/role.module.ts +++ b/apps/server/src/modules/role/role.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { RoleRepo } from '@shared/repo'; -import { RoleService } from '@src/modules/role/service/role.service'; -import { RoleUc } from '@src/modules/role/uc/role.uc'; +import { RoleService } from '@modules/role/service/role.service'; +import { RoleUc } from '@modules/role/uc/role.uc'; @Module({ providers: [RoleRepo, RoleService, RoleUc], diff --git a/apps/server/src/modules/role/uc/role.uc.spec.ts b/apps/server/src/modules/role/uc/role.uc.spec.ts index 8be660bcffb..b026687af3f 100644 --- a/apps/server/src/modules/role/uc/role.uc.spec.ts +++ b/apps/server/src/modules/role/uc/role.uc.spec.ts @@ -1,9 +1,9 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { RoleName } from '@shared/domain'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; -import { RoleService } from '@src/modules/role/service/role.service'; -import { RoleUc } from '@src/modules/role/uc/role.uc'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; +import { RoleService } from '@modules/role/service/role.service'; +import { RoleUc } from '@modules/role/uc/role.uc'; describe('RoleUc', () => { let module: TestingModule; diff --git a/apps/server/src/modules/role/uc/role.uc.ts b/apps/server/src/modules/role/uc/role.uc.ts index faa29b7df9b..a1b5ba0b649 100644 --- a/apps/server/src/modules/role/uc/role.uc.ts +++ b/apps/server/src/modules/role/uc/role.uc.ts @@ -1,5 +1,5 @@ -import { RoleService } from '@src/modules/role/service/role.service'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; +import { RoleService } from '@modules/role/service/role.service'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; import { Injectable } from '@nestjs/common'; import { RoleName } from '@shared/domain'; diff --git a/apps/server/src/modules/server/controller/api-test/server.api.spec.ts b/apps/server/src/modules/server/controller/api-test/server.api.spec.ts index e730fe016d6..a6e42141c6a 100644 --- a/apps/server/src/modules/server/controller/api-test/server.api.spec.ts +++ b/apps/server/src/modules/server/controller/api-test/server.api.spec.ts @@ -1,6 +1,6 @@ import { INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import request from 'supertest'; describe('Server Controller (API)', () => { diff --git a/apps/server/src/modules/server/server.config.ts b/apps/server/src/modules/server/server.config.ts index 1f3222b0250..09dd2210928 100644 --- a/apps/server/src/modules/server/server.config.ts +++ b/apps/server/src/modules/server/server.config.ts @@ -1,8 +1,10 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import type { IIdentityManagementConfig } from '@shared/infra/identity-management'; import type { ICoreModuleConfig } from '@src/core'; -import type { IAccountConfig, IFilesStorageClientConfig, IUserConfig } from '@src/modules/'; -import type { ICommonCartridgeConfig } from '@src/modules/learnroom/common-cartridge'; +import type { IAccountConfig } from '@modules/account'; +import type { IFilesStorageClientConfig } from '@modules/files-storage-client'; +import type { IUserConfig } from '@modules/user'; +import type { ICommonCartridgeConfig } from '@modules/learnroom/common-cartridge'; export enum NodeEnvType { TEST = 'test', diff --git a/apps/server/src/modules/server/server.module.ts b/apps/server/src/modules/server/server.module.ts index 20b346929cd..084ca72fca3 100644 --- a/apps/server/src/modules/server/server.module.ts +++ b/apps/server/src/modules/server/server.module.ts @@ -11,32 +11,32 @@ import { REDIS_CLIENT, RedisModule } from '@shared/infra/redis'; import { createConfigModuleOptions, DB_PASSWORD, DB_URL, DB_USERNAME } from '@src/config'; import { CoreModule } from '@src/core'; import { LegacyLogger, LoggerModule } from '@src/core/logger'; -import { AccountApiModule } from '@src/modules/account/account-api.module'; -import { AuthenticationApiModule } from '@src/modules/authentication/authentication-api.module'; -import { BoardApiModule } from '@src/modules/board/board-api.module'; -import { CollaborativeStorageModule } from '@src/modules/collaborative-storage'; -import { FilesStorageClientModule } from '@src/modules/files-storage-client'; -import { GroupApiModule } from '@src/modules/group/group-api.module'; -import { LearnroomApiModule } from '@src/modules/learnroom/learnroom-api.module'; -import { LessonApiModule } from '@src/modules/lesson/lesson-api.module'; -import { NewsModule } from '@src/modules/news'; -import { OauthProviderApiModule } from '@src/modules/oauth-provider'; -import { OauthApiModule } from '@src/modules/oauth/oauth-api.module'; -import { RocketChatModule } from '@src/modules/rocketchat'; -import { LegacySchoolApiModule } from '@src/modules/legacy-school/legacy-school-api.module'; -import { SharingApiModule } from '@src/modules/sharing/sharing.module'; -import { SystemApiModule } from '@src/modules/system/system-api.module'; -import { TaskApiModule } from '@src/modules/task/task-api.module'; -import { ToolApiModule } from '@src/modules/tool/tool-api.module'; -import { ImportUserModule } from '@src/modules/user-import'; -import { UserLoginMigrationApiModule } from '@src/modules/user-login-migration/user-login-migration-api.module'; -import { UserApiModule } from '@src/modules/user/user-api.module'; -import { VideoConferenceApiModule } from '@src/modules/video-conference/video-conference-api.module'; +import { AccountApiModule } from '@modules/account/account-api.module'; +import { AuthenticationApiModule } from '@modules/authentication/authentication-api.module'; +import { BoardApiModule } from '@modules/board/board-api.module'; +import { CollaborativeStorageModule } from '@modules/collaborative-storage'; +import { FilesStorageClientModule } from '@modules/files-storage-client'; +import { GroupApiModule } from '@modules/group/group-api.module'; +import { LearnroomApiModule } from '@modules/learnroom/learnroom-api.module'; +import { LessonApiModule } from '@modules/lesson/lesson-api.module'; +import { NewsModule } from '@modules/news'; +import { OauthProviderApiModule } from '@modules/oauth-provider'; +import { OauthApiModule } from '@modules/oauth/oauth-api.module'; +import { RocketChatModule } from '@modules/rocketchat'; +import { LegacySchoolApiModule } from '@modules/legacy-school/legacy-school-api.module'; +import { SharingApiModule } from '@modules/sharing/sharing.module'; +import { SystemApiModule } from '@modules/system/system-api.module'; +import { TaskApiModule } from '@modules/task/task-api.module'; +import { ToolApiModule } from '@modules/tool/tool-api.module'; +import { ImportUserModule } from '@modules/user-import'; +import { UserLoginMigrationApiModule } from '@modules/user-login-migration/user-login-migration-api.module'; +import { UserApiModule } from '@modules/user/user-api.module'; +import { VideoConferenceApiModule } from '@modules/video-conference/video-conference-api.module'; import connectRedis from 'connect-redis'; import session from 'express-session'; import { RedisClient } from 'redis'; -import { TeamsApiModule } from '@src/modules/teams/teams-api.module'; -import { PseudonymApiModule } from '@src/modules/pseudonym/pseudonym-api.module'; +import { TeamsApiModule } from '@modules/teams/teams-api.module'; +import { PseudonymApiModule } from '@modules/pseudonym/pseudonym-api.module'; import { ServerController } from './controller/server.controller'; import { serverConfig } from './server.config'; diff --git a/apps/server/src/modules/sharing/controller/api-test/sharing-create-token.api.spec.ts b/apps/server/src/modules/sharing/controller/api-test/sharing-create-token.api.spec.ts index 2abd2019fbf..7890f778f86 100644 --- a/apps/server/src/modules/sharing/controller/api-test/sharing-create-token.api.spec.ts +++ b/apps/server/src/modules/sharing/controller/api-test/sharing-create-token.api.spec.ts @@ -6,7 +6,7 @@ import { ExecutionContext, HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common'; import { Permission } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, courseFactory, @@ -15,8 +15,8 @@ import { schoolFactory, userFactory, } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { ShareTokenBodyParams, ShareTokenResponse } from '../dto'; import { ShareTokenParentType } from '../../domainobject/share-token.do'; diff --git a/apps/server/src/modules/sharing/controller/api-test/sharing-import-token.api.spec.ts b/apps/server/src/modules/sharing/controller/api-test/sharing-import-token.api.spec.ts index 737a24b022e..f19dea681f2 100644 --- a/apps/server/src/modules/sharing/controller/api-test/sharing-import-token.api.spec.ts +++ b/apps/server/src/modules/sharing/controller/api-test/sharing-import-token.api.spec.ts @@ -4,7 +4,7 @@ import { ExecutionContext, HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common'; import { Permission } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, courseFactory, @@ -13,9 +13,9 @@ import { schoolFactory, userFactory, } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { CopyApiResponse, CopyElementType, CopyStatusEnum } from '@src/modules/copy-helper'; -import { ServerTestModule } from '@src/modules/server'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { CopyApiResponse, CopyElementType, CopyStatusEnum } from '@modules/copy-helper'; +import { ServerTestModule } from '@modules/server'; import { Request } from 'express'; import request from 'supertest'; import { ShareTokenContext, ShareTokenContextType, ShareTokenParentType } from '../../domainobject/share-token.do'; diff --git a/apps/server/src/modules/sharing/controller/api-test/sharing-lookup-token.api.spec.ts b/apps/server/src/modules/sharing/controller/api-test/sharing-lookup-token.api.spec.ts index 7065c06a026..88f23408be4 100644 --- a/apps/server/src/modules/sharing/controller/api-test/sharing-lookup-token.api.spec.ts +++ b/apps/server/src/modules/sharing/controller/api-test/sharing-lookup-token.api.spec.ts @@ -4,7 +4,7 @@ import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; import { TestApiClient, UserAndAccountTestFactory, courseFactory, schoolFactory } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import { ShareTokenService } from '../../service'; import { ShareTokenInfoResponse } from '../dto'; import { ShareTokenContextType, ShareTokenParentType } from '../../domainobject/share-token.do'; diff --git a/apps/server/src/modules/sharing/controller/share-token.controller.spec.ts b/apps/server/src/modules/sharing/controller/share-token.controller.spec.ts index b694c60731a..7d6f51fa471 100644 --- a/apps/server/src/modules/sharing/controller/share-token.controller.spec.ts +++ b/apps/server/src/modules/sharing/controller/share-token.controller.spec.ts @@ -1,8 +1,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { courseFactory, setupEntities, shareTokenFactory } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { CopyElementType, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; +import { ICurrentUser } from '@modules/authentication'; +import { CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; import { ShareTokenParentType } from '../domainobject/share-token.do'; import { ShareTokenUC } from '../uc'; import { ShareTokenInfoDto } from '../uc/dto'; diff --git a/apps/server/src/modules/sharing/controller/share-token.controller.ts b/apps/server/src/modules/sharing/controller/share-token.controller.ts index b4120dc02ae..373e1169600 100644 --- a/apps/server/src/modules/sharing/controller/share-token.controller.ts +++ b/apps/server/src/modules/sharing/controller/share-token.controller.ts @@ -11,10 +11,10 @@ import { } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { ApiValidationError, RequestTimeout } from '@shared/common'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; -import { CopyApiResponse, CopyMapper } from '@src/modules/copy-helper'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; +import { CopyApiResponse, CopyMapper } from '@modules/copy-helper'; // invalid import can produce dependency cycles -import { serverConfig } from '@src/modules/server/server.config'; +import { serverConfig } from '@modules/server/server.config'; import { ShareTokenInfoResponseMapper, ShareTokenResponseMapper } from '../mapper'; import { ShareTokenUC } from '../uc'; import { diff --git a/apps/server/src/modules/sharing/mapper/context-type.mapper.spec.ts b/apps/server/src/modules/sharing/mapper/context-type.mapper.spec.ts index 68c0ccbd972..6f106c3e90a 100644 --- a/apps/server/src/modules/sharing/mapper/context-type.mapper.spec.ts +++ b/apps/server/src/modules/sharing/mapper/context-type.mapper.spec.ts @@ -1,5 +1,5 @@ import { NotImplementedException } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; import { ShareTokenContextType } from '../domainobject/share-token.do'; import { ShareTokenContextTypeMapper } from './context-type.mapper'; diff --git a/apps/server/src/modules/sharing/mapper/context-type.mapper.ts b/apps/server/src/modules/sharing/mapper/context-type.mapper.ts index 05ed42843c7..fbb6526eac9 100644 --- a/apps/server/src/modules/sharing/mapper/context-type.mapper.ts +++ b/apps/server/src/modules/sharing/mapper/context-type.mapper.ts @@ -1,5 +1,5 @@ import { NotImplementedException } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; import { ShareTokenContextType } from '../domainobject/share-token.do'; export class ShareTokenContextTypeMapper { diff --git a/apps/server/src/modules/sharing/mapper/parent-type.mapper.spec.ts b/apps/server/src/modules/sharing/mapper/parent-type.mapper.spec.ts index 4f8750245b4..99a9ea19453 100644 --- a/apps/server/src/modules/sharing/mapper/parent-type.mapper.spec.ts +++ b/apps/server/src/modules/sharing/mapper/parent-type.mapper.spec.ts @@ -1,5 +1,5 @@ import { NotImplementedException } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; import { ShareTokenParentType } from '../domainobject/share-token.do'; import { ShareTokenParentTypeMapper } from './parent-type.mapper'; diff --git a/apps/server/src/modules/sharing/mapper/parent-type.mapper.ts b/apps/server/src/modules/sharing/mapper/parent-type.mapper.ts index 2ea01ea39f9..7f2f0fdea60 100644 --- a/apps/server/src/modules/sharing/mapper/parent-type.mapper.ts +++ b/apps/server/src/modules/sharing/mapper/parent-type.mapper.ts @@ -1,5 +1,5 @@ import { NotImplementedException } from '@nestjs/common'; -import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; import { ShareTokenParentType } from '../domainobject/share-token.do'; export class ShareTokenParentTypeMapper { diff --git a/apps/server/src/modules/sharing/service/share-token.service.spec.ts b/apps/server/src/modules/sharing/service/share-token.service.spec.ts index c3436fb54ce..08680427058 100644 --- a/apps/server/src/modules/sharing/service/share-token.service.spec.ts +++ b/apps/server/src/modules/sharing/service/share-token.service.spec.ts @@ -2,9 +2,9 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { NotFoundException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { courseFactory, lessonFactory, setupEntities, shareTokenFactory, taskFactory } from '@shared/testing'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LessonService } from '@src/modules/lesson/service'; -import { TaskService } from '@src/modules/task/service'; +import { CourseService } from '@modules/learnroom/service'; +import { LessonService } from '@modules/lesson/service'; +import { TaskService } from '@modules/task/service'; import { ObjectId } from 'bson'; import { ShareTokenContextType, ShareTokenParentType } from '../domainobject/share-token.do'; import { ShareTokenRepo } from '../repo/share-token.repo'; diff --git a/apps/server/src/modules/sharing/service/share-token.service.ts b/apps/server/src/modules/sharing/service/share-token.service.ts index e6431d64ed5..befdc580b2b 100644 --- a/apps/server/src/modules/sharing/service/share-token.service.ts +++ b/apps/server/src/modules/sharing/service/share-token.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LessonService } from '@src/modules/lesson/service'; -import { TaskService } from '@src/modules/task/service'; +import { CourseService } from '@modules/learnroom/service'; +import { LessonService } from '@modules/lesson/service'; +import { TaskService } from '@modules/task/service'; import { ShareTokenContext, ShareTokenDO, diff --git a/apps/server/src/modules/sharing/sharing.module.ts b/apps/server/src/modules/sharing/sharing.module.ts index 519033065b5..183141e19a7 100644 --- a/apps/server/src/modules/sharing/sharing.module.ts +++ b/apps/server/src/modules/sharing/sharing.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { AuthorizationReferenceModule } from '@src/modules/authorization/authorization-reference.module'; +import { AuthorizationModule } from '@modules/authorization'; +import { AuthorizationReferenceModule } from '@modules/authorization/authorization-reference.module'; import { ShareTokenController } from './controller/share-token.controller'; import { ShareTokenUC } from './uc'; import { ShareTokenService, TokenGenerator } from './service'; diff --git a/apps/server/src/modules/sharing/uc/share-token.uc.spec.ts b/apps/server/src/modules/sharing/uc/share-token.uc.spec.ts index 8f33076cff2..72d2a824327 100644 --- a/apps/server/src/modules/sharing/uc/share-token.uc.spec.ts +++ b/apps/server/src/modules/sharing/uc/share-token.uc.spec.ts @@ -15,13 +15,13 @@ import { userFactory, } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { Action, AuthorizationService } from '@src/modules/authorization'; -import { AuthorizableReferenceType, AuthorizationReferenceService } from '@src/modules/authorization/domain'; -import { CopyElementType, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; -import { CourseCopyService } from '@src/modules/learnroom'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LessonCopyService } from '@src/modules/lesson/service'; -import { TaskCopyService } from '@src/modules/task/service'; +import { Action, AuthorizationService } from '@modules/authorization'; +import { AuthorizableReferenceType, AuthorizationReferenceService } from '@modules/authorization/domain'; +import { CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { CourseCopyService } from '@modules/learnroom'; +import { CourseService } from '@modules/learnroom/service'; +import { LessonCopyService } from '@modules/lesson/service'; +import { TaskCopyService } from '@modules/task/service'; import { ShareTokenContextType, ShareTokenParentType, ShareTokenPayload } from '../domainobject/share-token.do'; import { ShareTokenService } from '../service'; import { ShareTokenUC } from './share-token.uc'; diff --git a/apps/server/src/modules/sharing/uc/share-token.uc.ts b/apps/server/src/modules/sharing/uc/share-token.uc.ts index b2bbc635403..c2a12054cb7 100644 --- a/apps/server/src/modules/sharing/uc/share-token.uc.ts +++ b/apps/server/src/modules/sharing/uc/share-token.uc.ts @@ -2,13 +2,13 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { BadRequestException, Injectable, InternalServerErrorException, NotImplementedException } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; -import { CopyStatus } from '@src/modules/copy-helper'; -import { CourseCopyService } from '@src/modules/learnroom'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LessonCopyService } from '@src/modules/lesson/service'; -import { TaskCopyService } from '@src/modules/task/service'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; +import { CopyStatus } from '@modules/copy-helper'; +import { CourseCopyService } from '@modules/learnroom'; +import { CourseService } from '@modules/learnroom/service'; +import { LessonCopyService } from '@modules/lesson/service'; +import { TaskCopyService } from '@modules/task/service'; import { ShareTokenContext, ShareTokenContextType, diff --git a/apps/server/src/modules/system/controller/api-test/system.api.spec.ts b/apps/server/src/modules/system/controller/api-test/system.api.spec.ts index 823b0d82abf..d067abf1889 100644 --- a/apps/server/src/modules/system/controller/api-test/system.api.spec.ts +++ b/apps/server/src/modules/system/controller/api-test/system.api.spec.ts @@ -3,9 +3,9 @@ import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { OauthConfig, SystemEntity } from '@shared/domain'; import { cleanupCollections, systemFactory } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server'; import { Request } from 'express'; import request, { Response } from 'supertest'; import { PublicSystemListResponse } from '../dto/public-system-list.response'; diff --git a/apps/server/src/modules/system/controller/dto/public-system-response.ts b/apps/server/src/modules/system/controller/dto/public-system-response.ts index 2d37338273c..b00055a1849 100644 --- a/apps/server/src/modules/system/controller/dto/public-system-response.ts +++ b/apps/server/src/modules/system/controller/dto/public-system-response.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { OauthConfigResponse } from '@src/modules/system/controller/dto/oauth-config.response'; +import { OauthConfigResponse } from '@modules/system/controller/dto/oauth-config.response'; export class PublicSystemResponse { @ApiProperty({ diff --git a/apps/server/src/modules/system/controller/mapper/system-response.mapper.ts b/apps/server/src/modules/system/controller/mapper/system-response.mapper.ts index f996a59af99..20a215a3dbe 100644 --- a/apps/server/src/modules/system/controller/mapper/system-response.mapper.ts +++ b/apps/server/src/modules/system/controller/mapper/system-response.mapper.ts @@ -1,6 +1,6 @@ -import { OauthConfigResponse } from '@src/modules/system/controller/dto/oauth-config.response'; -import { OauthConfigDto } from '@src/modules/system/service/dto/oauth-config.dto'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; +import { OauthConfigResponse } from '@modules/system/controller/dto/oauth-config.response'; +import { OauthConfigDto } from '@modules/system/service/dto/oauth-config.dto'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; import { PublicSystemListResponse } from '../dto/public-system-list.response'; import { PublicSystemResponse } from '../dto/public-system-response'; diff --git a/apps/server/src/modules/system/controller/system.controller.ts b/apps/server/src/modules/system/controller/system.controller.ts index 95881cad907..bbeca71b83c 100644 --- a/apps/server/src/modules/system/controller/system.controller.ts +++ b/apps/server/src/modules/system/controller/system.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, Param, Query } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; -import { SystemFilterParams } from '@src/modules/system/controller/dto/system.filter.params'; +import { SystemFilterParams } from '@modules/system/controller/dto/system.filter.params'; import { SystemDto } from '../service'; import { SystemUc } from '../uc/system.uc'; import { PublicSystemListResponse } from './dto/public-system-list.response'; diff --git a/apps/server/src/modules/system/mapper/system-oidc.mapper.spec.ts b/apps/server/src/modules/system/mapper/system-oidc.mapper.spec.ts index 5cc88f020ad..2486d4a1872 100644 --- a/apps/server/src/modules/system/mapper/system-oidc.mapper.spec.ts +++ b/apps/server/src/modules/system/mapper/system-oidc.mapper.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SystemEntity } from '@shared/domain'; import { systemFactory } from '@shared/testing'; -import { SystemOidcMapper } from '@src/modules/system/mapper/system-oidc.mapper'; +import { SystemOidcMapper } from '@modules/system/mapper/system-oidc.mapper'; describe('SystemOidcMapper', () => { let module: TestingModule; diff --git a/apps/server/src/modules/system/mapper/system-oidc.mapper.ts b/apps/server/src/modules/system/mapper/system-oidc.mapper.ts index f62a2e022c9..8726ce09a78 100644 --- a/apps/server/src/modules/system/mapper/system-oidc.mapper.ts +++ b/apps/server/src/modules/system/mapper/system-oidc.mapper.ts @@ -1,5 +1,5 @@ import { OidcConfig, SystemEntity } from '@shared/domain'; -import { OidcConfigDto } from '@src/modules/system/service/dto/oidc-config.dto'; +import { OidcConfigDto } from '@modules/system/service/dto/oidc-config.dto'; export class SystemOidcMapper { static mapFromEntityToDto(entity: SystemEntity): OidcConfigDto | undefined { diff --git a/apps/server/src/modules/system/mapper/system.mapper.spec.ts b/apps/server/src/modules/system/mapper/system.mapper.spec.ts index 21e9cf5f9b3..54c20cc0cff 100644 --- a/apps/server/src/modules/system/mapper/system.mapper.spec.ts +++ b/apps/server/src/modules/system/mapper/system.mapper.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SystemEntity } from '@shared/domain'; import { systemFactory } from '@shared/testing'; -import { SystemMapper } from '@src/modules/system/mapper/system.mapper'; +import { SystemMapper } from '@modules/system/mapper/system.mapper'; describe('SystemMapper', () => { let module: TestingModule; diff --git a/apps/server/src/modules/system/mapper/system.mapper.ts b/apps/server/src/modules/system/mapper/system.mapper.ts index b464e54f263..ae29fea67c8 100644 --- a/apps/server/src/modules/system/mapper/system.mapper.ts +++ b/apps/server/src/modules/system/mapper/system.mapper.ts @@ -1,6 +1,6 @@ import { OauthConfig, SystemEntity } from '@shared/domain'; -import { OauthConfigDto } from '@src/modules/system/service/dto/oauth-config.dto'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; +import { OauthConfigDto } from '@modules/system/service/dto/oauth-config.dto'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; export class SystemMapper { static mapFromEntityToDto(entity: SystemEntity): SystemDto { diff --git a/apps/server/src/modules/system/service/dto/system.dto.ts b/apps/server/src/modules/system/service/dto/system.dto.ts index dbb5b7f7315..1ea7e4a84ee 100644 --- a/apps/server/src/modules/system/service/dto/system.dto.ts +++ b/apps/server/src/modules/system/service/dto/system.dto.ts @@ -1,6 +1,6 @@ import { EntityId } from '@shared/domain'; import { SystemProvisioningStrategy } from '@shared/domain/interface/system-provisioning.strategy'; -import { OauthConfigDto } from '@src/modules/system/service/dto/oauth-config.dto'; +import { OauthConfigDto } from '@modules/system/service/dto/oauth-config.dto'; export class SystemDto { id?: EntityId; diff --git a/apps/server/src/modules/system/service/system-oidc.service.ts b/apps/server/src/modules/system/service/system-oidc.service.ts index a4987ff47d8..c1f1cf0a4c1 100644 --- a/apps/server/src/modules/system/service/system-oidc.service.ts +++ b/apps/server/src/modules/system/service/system-oidc.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { EntityNotFoundError } from '@shared/common'; import { EntityId, SystemEntity, SystemTypeEnum } from '@shared/domain'; import { SystemRepo } from '@shared/repo'; -import { SystemOidcMapper } from '@src/modules/system/mapper/system-oidc.mapper'; +import { SystemOidcMapper } from '@modules/system/mapper/system-oidc.mapper'; import { OidcConfigDto } from './dto'; @Injectable() diff --git a/apps/server/src/modules/system/service/system.service.ts b/apps/server/src/modules/system/service/system.service.ts index ec15c7d8bb3..960c15f7945 100644 --- a/apps/server/src/modules/system/service/system.service.ts +++ b/apps/server/src/modules/system/service/system.service.ts @@ -3,8 +3,8 @@ import { EntityNotFoundError } from '@shared/common'; import { EntityId, SystemEntity, SystemTypeEnum } from '@shared/domain'; import { IdentityManagementOauthService } from '@shared/infra/identity-management/identity-management-oauth.service'; import { SystemRepo } from '@shared/repo'; -import { SystemMapper } from '@src/modules/system/mapper/system.mapper'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; +import { SystemMapper } from '@modules/system/mapper/system.mapper'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; @Injectable() export class SystemService { diff --git a/apps/server/src/modules/system/system-api.module.ts b/apps/server/src/modules/system/system-api.module.ts index 27c14327e1f..e7213c1fac7 100644 --- a/apps/server/src/modules/system/system-api.module.ts +++ b/apps/server/src/modules/system/system-api.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { SystemController } from '@src/modules/system/controller/system.controller'; -import { SystemUc } from '@src/modules/system/uc/system.uc'; +import { SystemController } from '@modules/system/controller/system.controller'; +import { SystemUc } from '@modules/system/uc/system.uc'; import { SystemModule } from './system.module'; @Module({ diff --git a/apps/server/src/modules/system/system.module.ts b/apps/server/src/modules/system/system.module.ts index f72c73855a0..64caef0df61 100644 --- a/apps/server/src/modules/system/system.module.ts +++ b/apps/server/src/modules/system/system.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { IdentityManagementModule } from '@shared/infra/identity-management/identity-management.module'; import { SystemRepo } from '@shared/repo'; -import { SystemService } from '@src/modules/system/service/system.service'; +import { SystemService } from '@modules/system/service/system.service'; import { SystemOidcService } from './service/system-oidc.service'; @Module({ diff --git a/apps/server/src/modules/system/uc/system.uc.spec.ts b/apps/server/src/modules/system/uc/system.uc.spec.ts index 21fc452c134..45bd65694d9 100644 --- a/apps/server/src/modules/system/uc/system.uc.spec.ts +++ b/apps/server/src/modules/system/uc/system.uc.spec.ts @@ -3,10 +3,10 @@ import { Test, TestingModule } from '@nestjs/testing'; import { EntityNotFoundError } from '@shared/common'; import { EntityId, SystemEntity, SystemTypeEnum } from '@shared/domain'; import { systemFactory } from '@shared/testing'; -import { SystemMapper } from '@src/modules/system/mapper/system.mapper'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; -import { SystemService } from '@src/modules/system/service/system.service'; -import { SystemUc } from '@src/modules/system/uc/system.uc'; +import { SystemMapper } from '@modules/system/mapper/system.mapper'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; +import { SystemService } from '@modules/system/service/system.service'; +import { SystemUc } from '@modules/system/uc/system.uc'; describe('SystemUc', () => { let module: TestingModule; diff --git a/apps/server/src/modules/system/uc/system.uc.ts b/apps/server/src/modules/system/uc/system.uc.ts index 89953cd29dc..00665191da7 100644 --- a/apps/server/src/modules/system/uc/system.uc.ts +++ b/apps/server/src/modules/system/uc/system.uc.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { EntityNotFoundError } from '@shared/common'; import { EntityId, SystemEntity, SystemType, SystemTypeEnum } from '@shared/domain'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; -import { SystemService } from '@src/modules/system/service/system.service'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; +import { SystemService } from '@modules/system/service/system.service'; @Injectable() export class SystemUc { diff --git a/apps/server/src/modules/task/controller/api-test/submission.api.spec.ts b/apps/server/src/modules/task/controller/api-test/submission.api.spec.ts index f01b3f5baf0..6aea7ac2521 100644 --- a/apps/server/src/modules/task/controller/api-test/submission.api.spec.ts +++ b/apps/server/src/modules/task/controller/api-test/submission.api.spec.ts @@ -4,7 +4,7 @@ import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common'; import { Permission, Submission } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, courseGroupFactory, @@ -14,10 +14,10 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { FilesStorageClientAdapterService } from '@src/modules'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; -import { SubmissionStatusListResponse } from '@src/modules/task/controller/dto/submission.response'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; +import { SubmissionStatusListResponse } from '@modules/task/controller/dto/submission.response'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/task/controller/api-test/task-copy-timeout.api.spec.ts b/apps/server/src/modules/task/controller/api-test/task-copy-timeout.api.spec.ts index f8e431b4d09..446a0fab032 100644 --- a/apps/server/src/modules/task/controller/api-test/task-copy-timeout.api.spec.ts +++ b/apps/server/src/modules/task/controller/api-test/task-copy-timeout.api.spec.ts @@ -3,7 +3,7 @@ import { IConfig } from '@hpi-schul-cloud/commons/lib/interfaces/IConfig'; import { EntityManager } from '@mikro-orm/mongodb'; import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, courseFactory, @@ -12,14 +12,14 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; import { Request } from 'express'; import request from 'supertest'; Configuration.set('FEATURE_COPY_SERVICE_ENABLED', true); Configuration.set('INCOMING_REQUEST_TIMEOUT_COPY_API', 1); // eslint-disable-next-line import/first -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ServerTestModule } from '@modules/server/server.module'; // This needs to be in a separate test file because of the above configuration. // When we find a way to mock the config, it should be moved alongside the other API tests. diff --git a/apps/server/src/modules/task/controller/api-test/task-delete.api.spec.ts b/apps/server/src/modules/task/controller/api-test/task-delete.api.spec.ts index a85474c7862..efdc78d53b2 100644 --- a/apps/server/src/modules/task/controller/api-test/task-delete.api.spec.ts +++ b/apps/server/src/modules/task/controller/api-test/task-delete.api.spec.ts @@ -10,8 +10,8 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; -import { ServerTestModule } from '@src/modules/server'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; +import { ServerTestModule } from '@modules/server'; const createStudent = () => { const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent({}, [ diff --git a/apps/server/src/modules/task/controller/api-test/task-finish.api.spec.ts b/apps/server/src/modules/task/controller/api-test/task-finish.api.spec.ts index 43597913adb..acf66c36a1d 100644 --- a/apps/server/src/modules/task/controller/api-test/task-finish.api.spec.ts +++ b/apps/server/src/modules/task/controller/api-test/task-finish.api.spec.ts @@ -9,7 +9,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; const createStudent = () => { const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent({}, [ diff --git a/apps/server/src/modules/task/controller/api-test/task-finished.api.spec.ts b/apps/server/src/modules/task/controller/api-test/task-finished.api.spec.ts index 9d48217bcde..93c79e4015d 100644 --- a/apps/server/src/modules/task/controller/api-test/task-finished.api.spec.ts +++ b/apps/server/src/modules/task/controller/api-test/task-finished.api.spec.ts @@ -2,7 +2,7 @@ import { EntityManager } from '@mikro-orm/mongodb'; import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, courseFactory, @@ -12,9 +12,9 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; -import { TaskListResponse } from '@src/modules/task/controller/dto'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; +import { TaskListResponse } from '@modules/task/controller/dto'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/task/controller/api-test/task-restore.api.spec.ts b/apps/server/src/modules/task/controller/api-test/task-restore.api.spec.ts index 221d53f0f75..9d8f50e0950 100644 --- a/apps/server/src/modules/task/controller/api-test/task-restore.api.spec.ts +++ b/apps/server/src/modules/task/controller/api-test/task-restore.api.spec.ts @@ -9,7 +9,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; const createStudent = () => { const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent({}, [ diff --git a/apps/server/src/modules/task/controller/api-test/task-revert-published.api.spec.ts b/apps/server/src/modules/task/controller/api-test/task-revert-published.api.spec.ts index b26555c7c7c..d4e6d4dee64 100644 --- a/apps/server/src/modules/task/controller/api-test/task-revert-published.api.spec.ts +++ b/apps/server/src/modules/task/controller/api-test/task-revert-published.api.spec.ts @@ -9,7 +9,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; const createStudent = () => { const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent({}, [ diff --git a/apps/server/src/modules/task/controller/api-test/task.api.spec.ts b/apps/server/src/modules/task/controller/api-test/task.api.spec.ts index 369216b0d50..b7afee9c257 100644 --- a/apps/server/src/modules/task/controller/api-test/task.api.spec.ts +++ b/apps/server/src/modules/task/controller/api-test/task.api.spec.ts @@ -10,8 +10,8 @@ import { submissionFactory, taskFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server/server.module'; -import { TaskListResponse } from '@src/modules/task/controller/dto'; +import { ServerTestModule } from '@modules/server/server.module'; +import { TaskListResponse } from '@modules/task/controller/dto'; const tomorrow = new Date(Date.now() + 86400000); diff --git a/apps/server/src/modules/task/controller/submission.controller.ts b/apps/server/src/modules/task/controller/submission.controller.ts index 66d65261ded..64d3a23296f 100644 --- a/apps/server/src/modules/task/controller/submission.controller.ts +++ b/apps/server/src/modules/task/controller/submission.controller.ts @@ -1,6 +1,6 @@ import { Controller, Delete, Get, Param } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { SubmissionMapper } from '../mapper'; import { SubmissionUc } from '../uc'; import { SubmissionStatusListResponse, SubmissionUrlParams, TaskUrlParams } from './dto'; diff --git a/apps/server/src/modules/task/controller/task.controller.spec.ts b/apps/server/src/modules/task/controller/task.controller.spec.ts index 652f4797fa8..e44deee06c3 100644 --- a/apps/server/src/modules/task/controller/task.controller.spec.ts +++ b/apps/server/src/modules/task/controller/task.controller.spec.ts @@ -1,8 +1,8 @@ import { createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { CopyElementType, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; -import { CopyApiResponse } from '@src/modules/copy-helper/dto/copy.response'; +import { ICurrentUser } from '@modules/authentication'; +import { CopyElementType, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { CopyApiResponse } from '@modules/copy-helper/dto/copy.response'; import { TaskCopyUC, TaskUC } from '../uc'; import { TaskController } from './task.controller'; diff --git a/apps/server/src/modules/task/controller/task.controller.ts b/apps/server/src/modules/task/controller/task.controller.ts index c438526ede1..44911973ffd 100644 --- a/apps/server/src/modules/task/controller/task.controller.ts +++ b/apps/server/src/modules/task/controller/task.controller.ts @@ -2,10 +2,10 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestj import { ApiTags } from '@nestjs/swagger'; import { RequestTimeout } from '@shared/common'; import { PaginationParams } from '@shared/controller/'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; -import { CopyApiResponse, CopyMapper } from '@src/modules/copy-helper'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; +import { CopyApiResponse, CopyMapper } from '@modules/copy-helper'; // invalid import can produce dependency cycles -import { serverConfig } from '@src/modules/server/server.config'; +import { serverConfig } from '@modules/server/server.config'; import { TaskMapper } from '../mapper'; import { TaskCopyUC } from '../uc/task-copy.uc'; import { TaskUC } from '../uc/task.uc'; diff --git a/apps/server/src/modules/task/service/submission.service.spec.ts b/apps/server/src/modules/task/service/submission.service.spec.ts index 97267be74df..4d7373570cf 100644 --- a/apps/server/src/modules/task/service/submission.service.spec.ts +++ b/apps/server/src/modules/task/service/submission.service.spec.ts @@ -4,7 +4,7 @@ import { Counted, Submission } from '@shared/domain'; import { FileRecordParentType } from '@shared/infra/rabbitmq'; import { SubmissionRepo } from '@shared/repo'; import { setupEntities, submissionFactory, taskFactory } from '@shared/testing'; -import { FileDto, FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FileDto, FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { SubmissionService } from './submission.service'; describe('Submission Service', () => { diff --git a/apps/server/src/modules/task/service/submission.service.ts b/apps/server/src/modules/task/service/submission.service.ts index 378bd29c67e..c451dc8ad95 100644 --- a/apps/server/src/modules/task/service/submission.service.ts +++ b/apps/server/src/modules/task/service/submission.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Counted, EntityId, Submission } from '@shared/domain'; import { SubmissionRepo } from '@shared/repo'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; @Injectable() export class SubmissionService { diff --git a/apps/server/src/modules/task/service/task-copy.service.spec.ts b/apps/server/src/modules/task/service/task-copy.service.spec.ts index a54ac5ca002..ca320ac60d6 100644 --- a/apps/server/src/modules/task/service/task-copy.service.spec.ts +++ b/apps/server/src/modules/task/service/task-copy.service.spec.ts @@ -11,8 +11,8 @@ import { userFactory, legacyFileEntityMockFactory, } from '@shared/testing'; -import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@src/modules/copy-helper'; -import { CopyFilesService } from '@src/modules/files-storage-client'; +import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@modules/copy-helper'; +import { CopyFilesService } from '@modules/files-storage-client'; import { TaskCopyService } from './task-copy.service'; describe('task copy service', () => { diff --git a/apps/server/src/modules/task/service/task-copy.service.ts b/apps/server/src/modules/task/service/task-copy.service.ts index 024ed123f4b..f7faa684e8d 100644 --- a/apps/server/src/modules/task/service/task-copy.service.ts +++ b/apps/server/src/modules/task/service/task-copy.service.ts @@ -2,9 +2,9 @@ import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain'; import { Course, LessonEntity, Task, User } from '@shared/domain/entity'; import { TaskRepo } from '@shared/repo'; -import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@src/modules/copy-helper'; -import { CopyFilesService } from '@src/modules/files-storage-client'; -import { FileUrlReplacement } from '@src/modules/files-storage-client/service/copy-files.service'; +import { CopyElementType, CopyHelperService, CopyStatus, CopyStatusEnum } from '@modules/copy-helper'; +import { CopyFilesService } from '@modules/files-storage-client'; +import { FileUrlReplacement } from '@modules/files-storage-client/service/copy-files.service'; type TaskCopyParams = { originalTaskId: EntityId; diff --git a/apps/server/src/modules/task/service/task.service.spec.ts b/apps/server/src/modules/task/service/task.service.spec.ts index 3183337a540..4cf950964bd 100644 --- a/apps/server/src/modules/task/service/task.service.spec.ts +++ b/apps/server/src/modules/task/service/task.service.spec.ts @@ -2,7 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { TaskRepo } from '@shared/repo'; import { setupEntities, submissionFactory, taskFactory } from '@shared/testing'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { SubmissionService } from './submission.service'; import { TaskService } from './task.service'; diff --git a/apps/server/src/modules/task/service/task.service.ts b/apps/server/src/modules/task/service/task.service.ts index c7e433ae73b..78f9dca9a51 100644 --- a/apps/server/src/modules/task/service/task.service.ts +++ b/apps/server/src/modules/task/service/task.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Counted, EntityId, IFindOptions, Task } from '@shared/domain'; import { TaskRepo } from '@shared/repo'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { SubmissionService } from './submission.service'; @Injectable() diff --git a/apps/server/src/modules/task/task-api.module.ts b/apps/server/src/modules/task/task-api.module.ts index 442b5d2d111..cdb998eab4a 100644 --- a/apps/server/src/modules/task/task-api.module.ts +++ b/apps/server/src/modules/task/task-api.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { CourseRepo, LessonRepo, TaskRepo } from '@shared/repo'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { CopyHelperModule } from '@src/modules/copy-helper/copy-helper.module'; +import { AuthorizationModule } from '@modules/authorization'; +import { CopyHelperModule } from '@modules/copy-helper/copy-helper.module'; import { SubmissionController, TaskController } from './controller'; import { TaskModule } from './task.module'; import { SubmissionUc, TaskCopyUC, TaskUC } from './uc'; diff --git a/apps/server/src/modules/task/task.module.ts b/apps/server/src/modules/task/task.module.ts index 0d95bcc19b0..696d608d0a3 100644 --- a/apps/server/src/modules/task/task.module.ts +++ b/apps/server/src/modules/task/task.module.ts @@ -1,8 +1,8 @@ import { forwardRef, Module } from '@nestjs/common'; import { CourseRepo, LessonRepo, SubmissionRepo, TaskRepo } from '@shared/repo'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { CopyHelperModule } from '@src/modules/copy-helper'; -import { FilesStorageClientModule } from '@src/modules/files-storage-client'; +import { AuthorizationModule } from '@modules/authorization'; +import { CopyHelperModule } from '@modules/copy-helper'; +import { FilesStorageClientModule } from '@modules/files-storage-client'; import { SubmissionService, TaskCopyService, TaskService } from './service'; @Module({ diff --git a/apps/server/src/modules/task/uc/submission.uc.spec.ts b/apps/server/src/modules/task/uc/submission.uc.spec.ts index 5747e0c6cc8..63bac0f52f1 100644 --- a/apps/server/src/modules/task/uc/submission.uc.spec.ts +++ b/apps/server/src/modules/task/uc/submission.uc.spec.ts @@ -2,7 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { Counted, Permission, Submission } from '@shared/domain'; import { setupEntities, submissionFactory, taskFactory, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { SubmissionService } from '../service/submission.service'; import { SubmissionUc } from './submission.uc'; diff --git a/apps/server/src/modules/task/uc/submission.uc.ts b/apps/server/src/modules/task/uc/submission.uc.ts index 2edc26a97a3..50c2430e8de 100644 --- a/apps/server/src/modules/task/uc/submission.uc.ts +++ b/apps/server/src/modules/task/uc/submission.uc.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission, Submission, User } from '@shared/domain'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { SubmissionService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/task/uc/task-copy.uc.spec.ts b/apps/server/src/modules/task/uc/task-copy.uc.spec.ts index 2ad21c68dc2..dc381cda22e 100644 --- a/apps/server/src/modules/task/uc/task-copy.uc.spec.ts +++ b/apps/server/src/modules/task/uc/task-copy.uc.spec.ts @@ -5,9 +5,9 @@ import { ForbiddenException, InternalServerErrorException, NotFoundException } f import { Test, TestingModule } from '@nestjs/testing'; import { CourseRepo, LessonRepo, TaskRepo, UserRepo } from '@shared/repo'; import { courseFactory, lessonFactory, setupEntities, taskFactory, userFactory } from '@shared/testing'; -import { Action, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@src/modules/copy-helper'; -import { FilesStorageClientAdapterService } from '@src/modules/files-storage-client'; +import { Action, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { CopyElementType, CopyHelperService, CopyStatusEnum } from '@modules/copy-helper'; +import { FilesStorageClientAdapterService } from '@modules/files-storage-client'; import { TaskCopyService } from '../service'; import { TaskCopyUC } from './task-copy.uc'; import { TaskCopyParentParams } from '../types'; diff --git a/apps/server/src/modules/task/uc/task-copy.uc.ts b/apps/server/src/modules/task/uc/task-copy.uc.ts index 94b0a5a3acb..69fd99e224f 100644 --- a/apps/server/src/modules/task/uc/task-copy.uc.ts +++ b/apps/server/src/modules/task/uc/task-copy.uc.ts @@ -2,8 +2,8 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { ForbiddenException, Injectable, InternalServerErrorException, NotFoundException } from '@nestjs/common'; import { Course, EntityId, Task, LessonEntity, User } from '@shared/domain'; import { CourseRepo, LessonRepo, TaskRepo } from '@shared/repo'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { CopyHelperService, CopyStatus } from '@src/modules/copy-helper'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { CopyHelperService, CopyStatus } from '@modules/copy-helper'; import { TaskCopyService } from '../service'; import { TaskCopyParentParams } from '../types'; diff --git a/apps/server/src/modules/task/uc/task.uc.spec.ts b/apps/server/src/modules/task/uc/task.uc.spec.ts index e190db57d74..90bb29db444 100644 --- a/apps/server/src/modules/task/uc/task.uc.spec.ts +++ b/apps/server/src/modules/task/uc/task.uc.spec.ts @@ -13,7 +13,7 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { Action, AuthorizationService } from '@src/modules/authorization'; +import { Action, AuthorizationService } from '@modules/authorization'; import { TaskService } from '../service'; import { TaskUC } from './task.uc'; diff --git a/apps/server/src/modules/task/uc/task.uc.ts b/apps/server/src/modules/task/uc/task.uc.ts index 47186c304fc..a6e40dd3b6d 100644 --- a/apps/server/src/modules/task/uc/task.uc.ts +++ b/apps/server/src/modules/task/uc/task.uc.ts @@ -12,7 +12,7 @@ import { User, } from '@shared/domain'; import { CourseRepo, LessonRepo, TaskRepo } from '@shared/repo'; -import { Action, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { Action, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { TaskService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/teams/teams-api.module.ts b/apps/server/src/modules/teams/teams-api.module.ts index 5fc620fe76a..e5df732a951 100644 --- a/apps/server/src/modules/teams/teams-api.module.ts +++ b/apps/server/src/modules/teams/teams-api.module.ts @@ -1,5 +1,5 @@ import { Module } from '@nestjs/common'; -import { TeamsModule } from '@src/modules/teams/teams.module'; +import { TeamsModule } from '@modules/teams/teams.module'; @Module({ imports: [TeamsModule], diff --git a/apps/server/src/modules/tool/common/common-tool.module.ts b/apps/server/src/modules/tool/common/common-tool.module.ts index 0bd9ba5385b..f56f595a99c 100644 --- a/apps/server/src/modules/tool/common/common-tool.module.ts +++ b/apps/server/src/modules/tool/common/common-tool.module.ts @@ -1,9 +1,9 @@ import { forwardRef, Module } from '@nestjs/common'; import { ContextExternalToolRepo, SchoolExternalToolRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; -import { LearnroomModule } from '@src/modules/learnroom'; +import { AuthorizationModule } from '@modules/authorization'; +import { LegacySchoolModule } from '@modules/legacy-school'; +import { LearnroomModule } from '@modules/learnroom'; import { CommonToolService, CommonToolValidationService } from './service'; import { ToolPermissionHelper } from './uc/tool-permission-helper'; diff --git a/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts b/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts index 78d79f45a1b..b05f50fc46c 100644 --- a/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts +++ b/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts @@ -1,4 +1,4 @@ -import { AuthorizableReferenceType } from '@src/modules/authorization/domain'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; import { ToolContextType } from '../enum'; import { ContextTypeMapper } from './context-type.mapper'; diff --git a/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts b/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts index 883400f4258..3ae6902c232 100644 --- a/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts +++ b/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts @@ -1,4 +1,4 @@ -import { AuthorizableReferenceType } from '@src/modules/authorization/domain/'; +import { AuthorizableReferenceType } from '@modules/authorization/domain/'; import { ToolContextType } from '../enum'; const typeMapping: Record = { diff --git a/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts b/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts index 7f0cb68ade8..542903bc4be 100644 --- a/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts +++ b/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts @@ -1,8 +1,8 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { Course, EntityId, LegacySchoolDo, User } from '@shared/domain'; -import { AuthorizationContext, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { CourseService } from '@src/modules/learnroom'; +import { AuthorizationContext, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { CourseService } from '@modules/learnroom'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; // import { ContextTypeMapper } from '../mapper'; diff --git a/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts b/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts index 0f2a1192d57..ad697a94694 100644 --- a/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts +++ b/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts @@ -9,10 +9,10 @@ import { userFactory, } from '@shared/testing'; import { Permission, LegacySchoolDo } from '@shared/domain'; -import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { ForbiddenException } from '@nestjs/common'; -import { CourseService } from '@src/modules/learnroom'; +import { CourseService } from '@modules/learnroom'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { ToolPermissionHelper } from './tool-permission-helper'; import { SchoolExternalTool } from '../../school-external-tool/domain'; diff --git a/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts index b5584426763..e2dbe6ec9ac 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts @@ -15,7 +15,7 @@ import { UserAndAccountTestFactory, userFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import { ObjectId } from 'bson'; import { CustomParameterScope, ToolContextType } from '../../../common/enum'; import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; diff --git a/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-reference.api.spec.ts b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-reference.api.spec.ts index f3072a95131..9eae3c7298e 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-reference.api.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-reference.api.spec.ts @@ -12,7 +12,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import { Response } from 'supertest'; import { ToolContextType } from '../../../common/enum'; import { ExternalToolEntity } from '../../../external-tool/entity'; diff --git a/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts b/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts index e42b9b59932..a80ad3f234f 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts @@ -12,7 +12,7 @@ import { } from '@nestjs/swagger'; import { ValidationError } from '@shared/common'; import { LegacyLogger } from '@src/core/logger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { ContextExternalTool } from '../domain'; import { ContextExternalToolRequestMapper, ContextExternalToolResponseMapper } from '../mapper'; import { ContextExternalToolUc } from '../uc'; diff --git a/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts b/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts index 0b9658b1182..3c00d30e6e2 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/tool-reference.controller.ts @@ -1,6 +1,6 @@ import { Controller, Get, Param } from '@nestjs/common'; import { ApiForbiddenResponse, ApiOkResponse, ApiOperation, ApiTags, ApiUnauthorizedResponse } from '@nestjs/swagger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { ToolReference } from '../domain'; import { ContextExternalToolResponseMapper } from '../mapper'; import { ToolReferenceUc } from '../uc'; diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.ts index 9f0606c6e14..20dbc16fff8 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.ts @@ -1,4 +1,4 @@ -import { AuthorizationLoaderService } from '@src/modules/authorization'; +import { AuthorizationLoaderService } from '@modules/authorization'; import { EntityId } from '@shared/domain'; import { ContextExternalToolRepo } from '@shared/repo'; import { Injectable } from '@nestjs/common'; diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.spec.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.spec.ts index 819e2895896..60275c71d0d 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool.service.spec.ts @@ -7,7 +7,7 @@ import { legacySchoolDoFactory, schoolExternalToolFactory, } from '@shared/testing/factory/domainobject'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationService } from '@modules/authorization'; import { ToolContextType } from '../../common/enum'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { ContextExternalTool, ContextRef } from '../domain'; diff --git a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts index 8be134b80f2..648ccb491fa 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts @@ -10,7 +10,7 @@ import { AuthorizationContextBuilder, AuthorizationService, ForbiddenLoggableException, -} from '@src/modules/authorization'; +} from '@modules/authorization'; import { ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../domain'; diff --git a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts index 9b6e3e3fe66..d29cc1454d4 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission, User } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool, ContextRef } from '../domain'; diff --git a/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.spec.ts b/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.spec.ts index e0fbdcf6a6a..9b18e7ffd3b 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.spec.ts @@ -3,7 +3,7 @@ import { ForbiddenException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain'; import { contextExternalToolFactory, externalToolFactory } from '@shared/testing'; -import { AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationContextBuilder } from '@modules/authorization'; import { ToolConfigurationStatus, ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ExternalTool } from '../../external-tool/domain'; diff --git a/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.ts b/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.ts index c044e01dfeb..ac6ccda016f 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/tool-reference.uc.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; -import { AuthorizationContext, AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationContext, AuthorizationContextBuilder } from '@modules/authorization'; import { ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool, ContextRef, ToolReference } from '../domain'; diff --git a/apps/server/src/modules/tool/external-tool/controller/api-test/tool-configuration.api.spec.ts b/apps/server/src/modules/tool/external-tool/controller/api-test/tool-configuration.api.spec.ts index d0030bf7b46..68543982174 100644 --- a/apps/server/src/modules/tool/external-tool/controller/api-test/tool-configuration.api.spec.ts +++ b/apps/server/src/modules/tool/external-tool/controller/api-test/tool-configuration.api.spec.ts @@ -15,8 +15,8 @@ import { UserAndAccountTestFactory, userFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; -import { CustomParameterTypeParams } from '@src/modules/tool/common/enum'; +import { ServerTestModule } from '@modules/server'; +import { CustomParameterTypeParams } from '@modules/tool/common/enum'; import { Response } from 'supertest'; import { CustomParameterLocationParams, CustomParameterScopeTypeParams } from '../../../common/enum'; import { ContextExternalToolEntity, ContextExternalToolType } from '../../../context-external-tool/entity'; diff --git a/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts b/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts index 0d0c3fb08dc..ebff659529d 100644 --- a/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts +++ b/apps/server/src/modules/tool/external-tool/controller/api-test/tool.api.spec.ts @@ -10,7 +10,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { Response } from 'supertest'; diff --git a/apps/server/src/modules/tool/external-tool/controller/tool-configuration.controller.ts b/apps/server/src/modules/tool/external-tool/controller/tool-configuration.controller.ts index a82d7353b1e..589a1e38e7c 100644 --- a/apps/server/src/modules/tool/external-tool/controller/tool-configuration.controller.ts +++ b/apps/server/src/modules/tool/external-tool/controller/tool-configuration.controller.ts @@ -7,7 +7,7 @@ import { ApiTags, ApiUnauthorizedResponse, } from '@nestjs/swagger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { ExternalTool } from '../domain'; import { ToolConfigurationMapper } from '../mapper/tool-configuration.mapper'; import { ContextExternalToolTemplateInfo, ExternalToolConfigurationUc } from '../uc'; diff --git a/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts b/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts index 1400da6490a..80139a586cb 100644 --- a/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts +++ b/apps/server/src/modules/tool/external-tool/controller/tool.controller.ts @@ -14,7 +14,7 @@ import { ValidationError } from '@shared/common'; import { PaginationParams } from '@shared/controller'; import { IFindOptions, Page } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { Response } from 'express'; import { ExternalToolSearchQuery } from '../../common/interface'; import { ExternalTool } from '../domain'; diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.spec.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.spec.ts index 0ed3a3317f8..5327c8d4f80 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.spec.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.spec.ts @@ -10,7 +10,7 @@ import { schoolExternalToolFactory, setupEntities, } from '@shared/testing'; -import { AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationContextBuilder } from '@modules/authorization'; import { CustomParameterScope, ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../../context-external-tool/domain'; diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts index 1e91214ccda..f0c1af811fb 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool-configuration.uc.ts @@ -2,7 +2,7 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common/exceptions/not-found.exception'; import { EntityId, Permission } from '@shared/domain'; import { Page } from '@shared/domain/domainobject/page'; -import { AuthorizationContext, AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationContext, AuthorizationContextBuilder } from '@modules/authorization'; import { CustomParameterScope, ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../../context-external-tool/domain'; diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts index f2baadd885c..2d371f47c9e 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.spec.ts @@ -8,8 +8,8 @@ import { externalToolFactory, oauth2ToolConfigFactory, } from '@shared/testing/factory/domainobject/tool/external-tool.factory'; -import { ICurrentUser } from '@src/modules/authentication'; -import { AuthorizationService } from '@src/modules/authorization'; +import { ICurrentUser } from '@modules/authentication'; +import { AuthorizationService } from '@modules/authorization'; import { ExternalToolSearchQuery } from '../../common/interface'; import { ExternalTool, Oauth2ToolConfig } from '../domain'; import { ExternalToolLogoService, ExternalToolService, ExternalToolValidationService } from '../service'; diff --git a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts index 3fb81e4f74a..2cf49867103 100644 --- a/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts +++ b/apps/server/src/modules/tool/external-tool/uc/external-tool.uc.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { EntityId, IFindOptions, Page, Permission, User } from '@shared/domain'; -import { AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationService } from '@modules/authorization'; import { ExternalToolSearchQuery } from '../../common/interface'; import { ExternalTool, ExternalToolConfig } from '../domain'; import { ExternalToolLogoService, ExternalToolService, ExternalToolValidationService } from '../service'; diff --git a/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts b/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts index c2504b42de2..3512e66038e 100644 --- a/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/controller/api-test/tool-school.api.spec.ts @@ -11,7 +11,7 @@ import { UserAndAccountTestFactory, userFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import { ToolConfigurationStatusResponse } from '../../../context-external-tool/controller/dto/tool-configuration-status.response'; import { ExternalToolEntity } from '../../../external-tool/entity'; import { SchoolExternalToolEntity } from '../../entity'; diff --git a/apps/server/src/modules/tool/school-external-tool/controller/tool-school.controller.ts b/apps/server/src/modules/tool/school-external-tool/controller/tool-school.controller.ts index 0977cdf478f..79d6f789443 100644 --- a/apps/server/src/modules/tool/school-external-tool/controller/tool-school.controller.ts +++ b/apps/server/src/modules/tool/school-external-tool/controller/tool-school.controller.ts @@ -13,7 +13,7 @@ import { import { Body, Controller, Delete, Get, Param, Post, Query, Put, HttpCode, HttpStatus } from '@nestjs/common'; import { ValidationError } from '@shared/common'; import { LegacyLogger } from '@src/core/logger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { SchoolExternalToolRequestMapper, SchoolExternalToolResponseMapper } from '../mapper'; import { ExternalToolSearchListResponse } from '../../external-tool/controller/dto'; import { diff --git a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts index 85f26f83679..377a5d24a38 100644 --- a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.spec.ts @@ -2,7 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityId, Permission, User } from '@shared/domain'; import { schoolExternalToolFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationContextBuilder } from '@modules/authorization'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalToolService } from '../../context-external-tool/service'; import { SchoolExternalTool } from '../domain'; diff --git a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts index 2640def12d5..d7adf3f4937 100644 --- a/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts +++ b/apps/server/src/modules/tool/school-external-tool/uc/school-external-tool.uc.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; -import { AuthorizationContext, AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationContext, AuthorizationContextBuilder } from '@modules/authorization'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalToolService } from '../../context-external-tool/service'; import { SchoolExternalTool } from '../domain'; diff --git a/apps/server/src/modules/tool/tool-api.module.ts b/apps/server/src/modules/tool/tool-api.module.ts index dea3405801d..8513eaed3fc 100644 --- a/apps/server/src/modules/tool/tool-api.module.ts +++ b/apps/server/src/modules/tool/tool-api.module.ts @@ -1,9 +1,9 @@ import { Module } from '@nestjs/common'; import { LtiToolRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; -import { UserModule } from '@src/modules/user'; +import { AuthorizationModule } from '@modules/authorization'; +import { LegacySchoolModule } from '@modules/legacy-school'; +import { UserModule } from '@modules/user'; import { CommonToolModule } from './common'; import { ToolContextController } from './context-external-tool/controller'; import { ToolReferenceController } from './context-external-tool/controller/tool-reference.controller'; diff --git a/apps/server/src/modules/tool/tool-launch/controller/api-test/tool-launch.controller.api.spec.ts b/apps/server/src/modules/tool/tool-launch/controller/api-test/tool-launch.controller.api.spec.ts index 33512062711..04defeeb0dc 100644 --- a/apps/server/src/modules/tool/tool-launch/controller/api-test/tool-launch.controller.api.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/controller/api-test/tool-launch.controller.api.spec.ts @@ -13,7 +13,7 @@ import { TestApiClient, UserAndAccountTestFactory, } from '@shared/testing'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import { Response } from 'supertest'; import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; import { LaunchRequestMethod } from '../../types'; diff --git a/apps/server/src/modules/tool/tool-launch/controller/tool-launch.controller.ts b/apps/server/src/modules/tool/tool-launch/controller/tool-launch.controller.ts index 73ce027b654..12424b294ed 100644 --- a/apps/server/src/modules/tool/tool-launch/controller/tool-launch.controller.ts +++ b/apps/server/src/modules/tool/tool-launch/controller/tool-launch.controller.ts @@ -7,7 +7,7 @@ import { ApiTags, ApiUnauthorizedResponse, } from '@nestjs/swagger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { ToolLaunchUc } from '../uc'; import { ToolLaunchParams, ToolLaunchRequestResponse } from './dto'; import { ToolLaunchMapper } from '../mapper'; diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.spec.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.spec.ts index 12f8716b5b3..7ee237b3e93 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.spec.ts @@ -12,8 +12,8 @@ import { schoolExternalToolFactory, setupEntities, } from '@shared/testing'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { CourseService } from '@modules/learnroom/service'; +import { LegacySchoolService } from '@modules/legacy-school'; import { CustomParameterEntry } from '../../../common/domain'; import { CustomParameterLocation, diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.ts index 644105a3c2b..63ba0680734 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/abstract-launch.strategy.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { Course, EntityId, LegacySchoolDo } from '@shared/domain'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { CourseService } from '@modules/learnroom/service'; +import { LegacySchoolService } from '@modules/legacy-school'; import { URLSearchParams } from 'url'; import { CustomParameter, CustomParameterEntry } from '../../../common/domain'; import { diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/basic-tool-launch.strategy.spec.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/basic-tool-launch.strategy.spec.ts index a18a760b0b3..3bb95b97755 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/basic-tool-launch.strategy.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/basic-tool-launch.strategy.spec.ts @@ -1,8 +1,8 @@ import { createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { contextExternalToolFactory, externalToolFactory, schoolExternalToolFactory } from '@shared/testing'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { CourseService } from '@modules/learnroom/service'; +import { LegacySchoolService } from '@modules/legacy-school'; import { ContextExternalTool } from '../../../context-external-tool/domain'; import { ExternalTool } from '../../../external-tool/domain'; import { SchoolExternalTool } from '../../../school-external-tool/domain'; diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.spec.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.spec.ts index 1c31aac36b6..5113ff3cc76 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.spec.ts @@ -9,10 +9,10 @@ import { userDoFactory, } from '@shared/testing'; import { pseudonymFactory } from '@shared/testing/factory/domainobject/pseudonym.factory'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { PseudonymService } from '@src/modules/pseudonym/service'; -import { UserService } from '@src/modules/user'; +import { CourseService } from '@modules/learnroom/service'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { PseudonymService } from '@modules/pseudonym/service'; +import { UserService } from '@modules/user'; import { ObjectId } from 'bson'; import { Authorization } from 'oauth-1.0a'; import { LtiMessageType, LtiPrivacyPermission, LtiRole, ToolContextType } from '../../../common/enum'; diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.ts index 654e14b45d9..09d04e388f3 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/lti11-tool-launch.strategy.ts @@ -1,10 +1,10 @@ import { Injectable, InternalServerErrorException, UnprocessableEntityException } from '@nestjs/common'; import { EntityId, LtiPrivacyPermission, Pseudonym, RoleName, UserDO } from '@shared/domain'; import { RoleReference } from '@shared/domain/domainobject'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { PseudonymService } from '@src/modules/pseudonym/service'; -import { UserService } from '@src/modules/user'; +import { CourseService } from '@modules/learnroom/service'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { PseudonymService } from '@modules/pseudonym/service'; +import { UserService } from '@modules/user'; import { Authorization } from 'oauth-1.0a'; import { LtiRole } from '../../../common/enum'; import { ExternalTool } from '../../../external-tool/domain'; diff --git a/apps/server/src/modules/tool/tool-launch/service/strategy/oauth2-tool-launch.strategy.spec.ts b/apps/server/src/modules/tool/tool-launch/service/strategy/oauth2-tool-launch.strategy.spec.ts index f1e3388ed15..bd97fafde71 100644 --- a/apps/server/src/modules/tool/tool-launch/service/strategy/oauth2-tool-launch.strategy.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/service/strategy/oauth2-tool-launch.strategy.spec.ts @@ -1,8 +1,8 @@ import { createMock } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { contextExternalToolFactory, externalToolFactory, schoolExternalToolFactory } from '@shared/testing'; -import { CourseService } from '@src/modules/learnroom/service'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { CourseService } from '@modules/learnroom/service'; +import { LegacySchoolService } from '@modules/legacy-school'; import { ContextExternalTool } from '../../../context-external-tool/domain'; import { ExternalTool } from '../../../external-tool/domain'; import { SchoolExternalTool } from '../../../school-external-tool/domain'; diff --git a/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts b/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts index 95d8a42ce1f..4ae6a3a38a5 100644 --- a/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts +++ b/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts @@ -1,8 +1,8 @@ import { Module, forwardRef } from '@nestjs/common'; -import { LearnroomModule } from '@src/modules/learnroom'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; -import { PseudonymModule } from '@src/modules/pseudonym'; -import { UserModule } from '@src/modules/user'; +import { LearnroomModule } from '@modules/learnroom'; +import { LegacySchoolModule } from '@modules/legacy-school'; +import { PseudonymModule } from '@modules/pseudonym'; +import { UserModule } from '@modules/user'; import { CommonToolModule } from '../common'; import { ContextExternalToolModule } from '../context-external-tool'; import { ExternalToolModule } from '../external-tool'; diff --git a/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.ts b/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.ts index fed27fa9aad..272d8dfd376 100644 --- a/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.ts +++ b/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { EntityId, Permission } from '@shared/domain'; -import { AuthorizationContext, AuthorizationContextBuilder } from '@src/modules/authorization'; +import { AuthorizationContext, AuthorizationContextBuilder } from '@modules/authorization'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { ContextExternalToolService } from '../../context-external-tool/service'; diff --git a/apps/server/src/modules/user-import/controller/api-test/import-user.api.spec.ts b/apps/server/src/modules/user-import/controller/api-test/import-user.api.spec.ts index 0315d40ee26..29b7796b40d 100644 --- a/apps/server/src/modules/user-import/controller/api-test/import-user.api.spec.ts +++ b/apps/server/src/modules/user-import/controller/api-test/import-user.api.spec.ts @@ -14,7 +14,7 @@ import { SystemEntity, User, } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, importUserFactory, @@ -24,8 +24,8 @@ import { systemFactory, userFactory, } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { FilterImportUserParams, FilterMatchType, @@ -41,7 +41,7 @@ import { UserMatchListResponse, UserMatchResponse, UserRole, -} from '@src/modules/user-import/controller/dto'; +} from '@modules/user-import/controller/dto'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/user-import/controller/import-user.controller.spec.ts b/apps/server/src/modules/user-import/controller/import-user.controller.spec.ts index 91b1ebc6ec7..c93c7bc58a7 100644 --- a/apps/server/src/modules/user-import/controller/import-user.controller.spec.ts +++ b/apps/server/src/modules/user-import/controller/import-user.controller.spec.ts @@ -1,8 +1,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ImportUserRepo, SystemRepo, UserRepo } from '@shared/repo'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AccountService } from '@modules/account/services/account.service'; +import { AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { LoggerModule } from '@src/core/logger'; import { ConfigModule } from '@nestjs/config'; import { UserImportUc } from '../uc/user-import.uc'; diff --git a/apps/server/src/modules/user-import/controller/import-user.controller.ts b/apps/server/src/modules/user-import/controller/import-user.controller.ts index 03aa2741fc2..c4368b5ea3f 100644 --- a/apps/server/src/modules/user-import/controller/import-user.controller.ts +++ b/apps/server/src/modules/user-import/controller/import-user.controller.ts @@ -2,7 +2,7 @@ import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestj import { ApiTags } from '@nestjs/swagger'; import { PaginationParams } from '@shared/controller'; import { IFindOptions, ImportUser, User } from '@shared/domain'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { ImportUserMapper } from '../mapper/import-user.mapper'; import { UserMatchMapper } from '../mapper/user-match.mapper'; import { UserImportUc } from '../uc/user-import.uc'; diff --git a/apps/server/src/modules/user-import/uc/user-import.uc.spec.ts b/apps/server/src/modules/user-import/uc/user-import.uc.spec.ts index 8dcccaf84e2..3c3baa56475 100644 --- a/apps/server/src/modules/user-import/uc/user-import.uc.spec.ts +++ b/apps/server/src/modules/user-import/uc/user-import.uc.spec.ts @@ -21,9 +21,9 @@ import { ImportUserRepo, SystemRepo, UserRepo } from '@shared/repo'; import { federalStateFactory, importUserFactory, schoolFactory, userFactory } from '@shared/testing'; import { systemFactory } from '@shared/testing/factory/system.factory'; import { LoggerModule } from '@src/core/logger'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AccountService } from '@modules/account/services/account.service'; +import { AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { LdapAlreadyPersistedException, MigrationAlreadyActivatedException, diff --git a/apps/server/src/modules/user-import/uc/user-import.uc.ts b/apps/server/src/modules/user-import/uc/user-import.uc.ts index ecef86207a0..2dda936b1fb 100644 --- a/apps/server/src/modules/user-import/uc/user-import.uc.ts +++ b/apps/server/src/modules/user-import/uc/user-import.uc.ts @@ -19,10 +19,10 @@ import { } from '@shared/domain'; import { ImportUserRepo, SystemRepo, UserRepo } from '@shared/repo'; import { Logger } from '@src/core/logger'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto } from '@src/modules/account/services/dto/account.dto'; -import { AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto } from '@modules/account/services/dto/account.dto'; +import { AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { AccountSaveDto } from '../../account/services/dto'; import { MigrationMayBeCompleted, diff --git a/apps/server/src/modules/user-import/user-import.module.ts b/apps/server/src/modules/user-import/user-import.module.ts index 6e881ff5b2f..2cf4d94704e 100644 --- a/apps/server/src/modules/user-import/user-import.module.ts +++ b/apps/server/src/modules/user-import/user-import.module.ts @@ -1,7 +1,7 @@ import { Module } from '@nestjs/common'; import { ImportUserRepo, LegacySchoolRepo, SystemRepo, UserRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; +import { LegacySchoolModule } from '@modules/legacy-school'; import { AccountModule } from '../account'; import { AuthorizationModule } from '../authorization'; import { ImportUserController } from './controller/import-user.controller'; diff --git a/apps/server/src/modules/user-login-migration/controller/api-test/user-login-migration.api.spec.ts b/apps/server/src/modules/user-login-migration/controller/api-test/user-login-migration.api.spec.ts index 486c814ccd7..b2de2ac9fc0 100644 --- a/apps/server/src/modules/user-login-migration/controller/api-test/user-login-migration.api.spec.ts +++ b/apps/server/src/modules/user-login-migration/controller/api-test/user-login-migration.api.spec.ts @@ -15,13 +15,13 @@ import { userLoginMigrationFactory, } from '@shared/testing'; import { JwtTestFactory } from '@shared/testing/factory/jwt.test.factory'; -import { OauthTokenResponse } from '@src/modules/oauth/service/dto'; -import { ServerTestModule } from '@src/modules/server'; +import { OauthTokenResponse } from '@modules/oauth/service/dto'; +import { ServerTestModule } from '@modules/server'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { UUID } from 'bson'; import { Response } from 'supertest'; -import { SanisResponse, SanisRole } from '@src/modules/provisioning/strategy/sanis/response'; +import { SanisResponse, SanisRole } from '@modules/provisioning/strategy/sanis/response'; import { UserLoginMigrationResponse } from '../dto'; import { Oauth2MigrationParams } from '../dto/oauth2-migration.params'; diff --git a/apps/server/src/modules/user-login-migration/controller/user-login-migration.controller.ts b/apps/server/src/modules/user-login-migration/controller/user-login-migration.controller.ts index d94a79b5b94..3e788a54725 100644 --- a/apps/server/src/modules/user-login-migration/controller/user-login-migration.controller.ts +++ b/apps/server/src/modules/user-login-migration/controller/user-login-migration.controller.ts @@ -11,7 +11,7 @@ import { ApiUnprocessableEntityResponse, } from '@nestjs/swagger'; import { Page, UserLoginMigrationDO } from '@shared/domain'; -import { Authenticate, CurrentUser, ICurrentUser, JWT } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser, JWT } from '@modules/authentication'; import { SchoolNumberMissingLoggableException, UserLoginMigrationAlreadyClosedLoggableException, diff --git a/apps/server/src/modules/user-login-migration/error/oauth-migration.error.ts b/apps/server/src/modules/user-login-migration/error/oauth-migration.error.ts index 06f0c0c235e..c21185f1c93 100644 --- a/apps/server/src/modules/user-login-migration/error/oauth-migration.error.ts +++ b/apps/server/src/modules/user-login-migration/error/oauth-migration.error.ts @@ -1,4 +1,4 @@ -import { OAuthSSOError } from '@src/modules/oauth/loggable'; +import { OAuthSSOError } from '@modules/oauth/loggable'; export class OAuthMigrationError extends OAuthSSOError { readonly message: string; diff --git a/apps/server/src/modules/user-login-migration/service/migration-check.service.spec.ts b/apps/server/src/modules/user-login-migration/service/migration-check.service.spec.ts index c61382b9f63..7afc017cf98 100644 --- a/apps/server/src/modules/user-login-migration/service/migration-check.service.spec.ts +++ b/apps/server/src/modules/user-login-migration/service/migration-check.service.spec.ts @@ -3,8 +3,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LegacySchoolDo, UserDO, UserLoginMigrationDO } from '@shared/domain'; import { UserLoginMigrationRepo } from '@shared/repo'; import { legacySchoolDoFactory, userDoFactory } from '@shared/testing'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { UserService } from '@src/modules/user'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; import { MigrationCheckService } from './migration-check.service'; describe('MigrationCheckService', () => { diff --git a/apps/server/src/modules/user-login-migration/service/migration-check.service.ts b/apps/server/src/modules/user-login-migration/service/migration-check.service.ts index 7a0596e3599..70d0ab94066 100644 --- a/apps/server/src/modules/user-login-migration/service/migration-check.service.ts +++ b/apps/server/src/modules/user-login-migration/service/migration-check.service.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { EntityId, LegacySchoolDo, UserDO, UserLoginMigrationDO } from '@shared/domain'; import { UserLoginMigrationRepo } from '@shared/repo'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { UserService } from '@src/modules/user'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; @Injectable() export class MigrationCheckService { diff --git a/apps/server/src/modules/user-login-migration/service/school-migration.service.spec.ts b/apps/server/src/modules/user-login-migration/service/school-migration.service.spec.ts index 19841ffb6de..8addd2afae6 100644 --- a/apps/server/src/modules/user-login-migration/service/school-migration.service.spec.ts +++ b/apps/server/src/modules/user-login-migration/service/school-migration.service.spec.ts @@ -7,10 +7,10 @@ import { LegacySchoolDo, Page, UserDO, UserLoginMigrationDO } from '@shared/doma import { UserLoginMigrationRepo } from '@shared/repo/userloginmigration/user-login-migration.repo'; import { legacySchoolDoFactory, setupEntities, userDoFactory, userLoginMigrationDOFactory } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { ICurrentUser } from '@src/modules/authentication'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { UserService } from '@src/modules/user'; -import { OAuthMigrationError } from '@src/modules/user-login-migration/error/oauth-migration.error'; +import { ICurrentUser } from '@modules/authentication'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; +import { OAuthMigrationError } from '@modules/user-login-migration/error/oauth-migration.error'; import { SchoolMigrationService } from './school-migration.service'; describe('SchoolMigrationService', () => { diff --git a/apps/server/src/modules/user-login-migration/service/school-migration.service.ts b/apps/server/src/modules/user-login-migration/service/school-migration.service.ts index 04f3e44b265..147d9ec112b 100644 --- a/apps/server/src/modules/user-login-migration/service/school-migration.service.ts +++ b/apps/server/src/modules/user-login-migration/service/school-migration.service.ts @@ -3,8 +3,8 @@ import { ValidationError } from '@shared/common'; import { Page, LegacySchoolDo, UserDO, UserLoginMigrationDO } from '@shared/domain'; import { UserLoginMigrationRepo } from '@shared/repo'; import { LegacyLogger } from '@src/core/logger'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { UserService } from '@src/modules/user'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; import { performance } from 'perf_hooks'; import { OAuthMigrationError } from '../error'; diff --git a/apps/server/src/modules/user-login-migration/service/user-login-migration-revert.service.spec.ts b/apps/server/src/modules/user-login-migration/service/user-login-migration-revert.service.spec.ts index 0b677fd8cf6..36f2176e00f 100644 --- a/apps/server/src/modules/user-login-migration/service/user-login-migration-revert.service.spec.ts +++ b/apps/server/src/modules/user-login-migration/service/user-login-migration-revert.service.spec.ts @@ -1,7 +1,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { SchoolFeatures } from '@shared/domain'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { LegacySchoolService } from '@modules/legacy-school'; import { setupEntities, userLoginMigrationDOFactory } from '@shared/testing'; import { UserLoginMigrationRevertService } from './user-login-migration-revert.service'; import { UserLoginMigrationService } from './user-login-migration.service'; diff --git a/apps/server/src/modules/user-login-migration/service/user-login-migration-revert.service.ts b/apps/server/src/modules/user-login-migration/service/user-login-migration-revert.service.ts index 88397e0b179..6d6512bc700 100644 --- a/apps/server/src/modules/user-login-migration/service/user-login-migration-revert.service.ts +++ b/apps/server/src/modules/user-login-migration/service/user-login-migration-revert.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { SchoolFeatures, UserLoginMigrationDO } from '@shared/domain'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { LegacySchoolService } from '@modules/legacy-school'; import { UserLoginMigrationService } from './user-login-migration.service'; @Injectable() diff --git a/apps/server/src/modules/user-login-migration/service/user-login-migration.service.spec.ts b/apps/server/src/modules/user-login-migration/service/user-login-migration.service.spec.ts index 0b755b68415..01e12e0df19 100644 --- a/apps/server/src/modules/user-login-migration/service/user-login-migration.service.spec.ts +++ b/apps/server/src/modules/user-login-migration/service/user-login-migration.service.spec.ts @@ -6,10 +6,10 @@ import { Test, TestingModule } from '@nestjs/testing'; import { EntityId, LegacySchoolDo, SchoolFeatures, UserDO, UserLoginMigrationDO } from '@shared/domain'; import { UserLoginMigrationRepo } from '@shared/repo'; import { legacySchoolDoFactory, userDoFactory, userLoginMigrationDOFactory } from '@shared/testing'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { SystemService } from '@src/modules/system'; -import { SystemDto } from '@src/modules/system/service'; -import { UserService } from '@src/modules/user'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { SystemService } from '@modules/system'; +import { SystemDto } from '@modules/system/service'; +import { UserService } from '@modules/user'; import { UserLoginMigrationNotFoundLoggableException } from '../error'; import { SchoolMigrationService } from './school-migration.service'; import { UserLoginMigrationService } from './user-login-migration.service'; diff --git a/apps/server/src/modules/user-login-migration/service/user-login-migration.service.ts b/apps/server/src/modules/user-login-migration/service/user-login-migration.service.ts index 9f3d6c59e84..534bb71e104 100644 --- a/apps/server/src/modules/user-login-migration/service/user-login-migration.service.ts +++ b/apps/server/src/modules/user-login-migration/service/user-login-migration.service.ts @@ -2,9 +2,9 @@ import { Configuration } from '@hpi-schul-cloud/commons/lib'; import { Injectable, InternalServerErrorException, UnprocessableEntityException } from '@nestjs/common'; import { EntityId, LegacySchoolDo, SchoolFeatures, SystemTypeEnum, UserDO, UserLoginMigrationDO } from '@shared/domain'; import { UserLoginMigrationRepo } from '@shared/repo'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { SystemDto, SystemService } from '@src/modules/system'; -import { UserService } from '@src/modules/user'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { SystemDto, SystemService } from '@modules/system'; +import { UserService } from '@modules/user'; import { UserLoginMigrationNotFoundLoggableException } from '../error'; import { SchoolMigrationService } from './school-migration.service'; diff --git a/apps/server/src/modules/user-login-migration/service/user-migration.service.spec.ts b/apps/server/src/modules/user-login-migration/service/user-migration.service.spec.ts index 24cb59030a5..c098066664d 100644 --- a/apps/server/src/modules/user-login-migration/service/user-migration.service.spec.ts +++ b/apps/server/src/modules/user-login-migration/service/user-migration.service.spec.ts @@ -7,13 +7,13 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LegacySchoolDo, RoleName, UserDO } from '@shared/domain'; import { legacySchoolDoFactory, setupEntities, userDoFactory } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto, AccountSaveDto } from '@src/modules/account/services/dto'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { SystemService } from '@src/modules/system'; -import { OauthConfigDto } from '@src/modules/system/service/dto/oauth-config.dto'; -import { SystemDto } from '@src/modules/system/service/dto/system.dto'; -import { UserService } from '@src/modules/user'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto, AccountSaveDto } from '@modules/account/services/dto'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { SystemService } from '@modules/system'; +import { OauthConfigDto } from '@modules/system/service/dto/oauth-config.dto'; +import { SystemDto } from '@modules/system/service/dto/system.dto'; +import { UserService } from '@modules/user'; import { PageTypes } from '../interface/page-types.enum'; import { PageContentDto } from './dto'; import { UserMigrationService } from './user-migration.service'; diff --git a/apps/server/src/modules/user-login-migration/service/user-migration.service.ts b/apps/server/src/modules/user-login-migration/service/user-migration.service.ts index a2e999994f5..c9a6e8648c8 100644 --- a/apps/server/src/modules/user-login-migration/service/user-migration.service.ts +++ b/apps/server/src/modules/user-login-migration/service/user-migration.service.ts @@ -3,11 +3,11 @@ import { BadRequestException, Injectable, NotFoundException, UnprocessableEntity import { LegacySchoolDo } from '@shared/domain'; import { UserDO } from '@shared/domain/domainobject/user.do'; import { LegacyLogger } from '@src/core/logger'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto } from '@src/modules/account/services/dto'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { SystemDto, SystemService } from '@src/modules/system/service'; -import { UserService } from '@src/modules/user'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto } from '@modules/account/services/dto'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { SystemDto, SystemService } from '@modules/system/service'; +import { UserService } from '@modules/user'; import { EntityId } from '@src/shared/domain/types'; import { PageTypes } from '../interface/page-types.enum'; import { MigrationDto } from './dto/migration.dto'; diff --git a/apps/server/src/modules/user-login-migration/uc/close-user-login-migration.uc.spec.ts b/apps/server/src/modules/user-login-migration/uc/close-user-login-migration.uc.spec.ts index 6b796e69bba..b14ab751d40 100644 --- a/apps/server/src/modules/user-login-migration/uc/close-user-login-migration.uc.spec.ts +++ b/apps/server/src/modules/user-login-migration/uc/close-user-login-migration.uc.spec.ts @@ -2,7 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission, UserLoginMigrationDO } from '@shared/domain'; import { setupEntities, userFactory, userLoginMigrationDOFactory } from '@shared/testing'; -import { Action, AuthorizationService } from '@src/modules/authorization'; +import { Action, AuthorizationService } from '@modules/authorization'; import { UserLoginMigrationNotFoundLoggableException } from '../error'; import { SchoolMigrationService, UserLoginMigrationRevertService, UserLoginMigrationService } from '../service'; import { CloseUserLoginMigrationUc } from './close-user-login-migration.uc'; diff --git a/apps/server/src/modules/user-login-migration/uc/close-user-login-migration.uc.ts b/apps/server/src/modules/user-login-migration/uc/close-user-login-migration.uc.ts index 13058e64c49..65bdad24782 100644 --- a/apps/server/src/modules/user-login-migration/uc/close-user-login-migration.uc.ts +++ b/apps/server/src/modules/user-login-migration/uc/close-user-login-migration.uc.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common/decorators/core/injectable.decorator'; import { EntityId, Permission, User, UserLoginMigrationDO } from '@shared/domain'; -import { Action, AuthorizationService } from '@src/modules/authorization'; +import { Action, AuthorizationService } from '@modules/authorization'; import { UserLoginMigrationGracePeriodExpiredLoggableException, UserLoginMigrationNotFoundLoggableException, diff --git a/apps/server/src/modules/user-login-migration/uc/restart-user-login-migration.uc.spec.ts b/apps/server/src/modules/user-login-migration/uc/restart-user-login-migration.uc.spec.ts index c6e53ab483d..dd4ac4f835b 100644 --- a/apps/server/src/modules/user-login-migration/uc/restart-user-login-migration.uc.spec.ts +++ b/apps/server/src/modules/user-login-migration/uc/restart-user-login-migration.uc.spec.ts @@ -4,8 +4,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Permission, LegacySchoolDo, User, UserLoginMigrationDO } from '@shared/domain'; import { legacySchoolDoFactory, setupEntities, userFactory, userLoginMigrationDOFactory } from '@shared/testing'; import { Logger } from '@src/core/logger'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { UserLoginMigrationGracePeriodExpiredLoggableException, UserLoginMigrationNotFoundLoggableException, diff --git a/apps/server/src/modules/user-login-migration/uc/restart-user-login-migration.uc.ts b/apps/server/src/modules/user-login-migration/uc/restart-user-login-migration.uc.ts index 42412e50fe3..997276c3661 100644 --- a/apps/server/src/modules/user-login-migration/uc/restart-user-login-migration.uc.ts +++ b/apps/server/src/modules/user-login-migration/uc/restart-user-login-migration.uc.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common/decorators/core/injectable.decorator'; import { Permission, LegacySchoolDo, User, UserLoginMigrationDO } from '@shared/domain'; import { Logger } from '@src/core/logger'; -import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { UserLoginMigrationGracePeriodExpiredLoggableException, UserLoginMigrationNotFoundLoggableException, diff --git a/apps/server/src/modules/user-login-migration/uc/start-user-login-migration.uc.spec.ts b/apps/server/src/modules/user-login-migration/uc/start-user-login-migration.uc.spec.ts index e4c7510e684..3f0c03c07ff 100644 --- a/apps/server/src/modules/user-login-migration/uc/start-user-login-migration.uc.spec.ts +++ b/apps/server/src/modules/user-login-migration/uc/start-user-login-migration.uc.spec.ts @@ -4,8 +4,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Permission, LegacySchoolDo, User, UserLoginMigrationDO } from '@shared/domain'; import { legacySchoolDoFactory, setupEntities, userFactory, userLoginMigrationDOFactory } from '@shared/testing'; import { Logger } from '@src/core/logger'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { SchoolNumberMissingLoggableException, UserLoginMigrationAlreadyClosedLoggableException } from '../error'; import { UserLoginMigrationService } from '../service'; import { StartUserLoginMigrationUc } from './start-user-login-migration.uc'; diff --git a/apps/server/src/modules/user-login-migration/uc/start-user-login-migration.uc.ts b/apps/server/src/modules/user-login-migration/uc/start-user-login-migration.uc.ts index 9e670e7fdf3..3dd84cc6ef5 100644 --- a/apps/server/src/modules/user-login-migration/uc/start-user-login-migration.uc.ts +++ b/apps/server/src/modules/user-login-migration/uc/start-user-login-migration.uc.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common/decorators/core/injectable.decorator'; import { Permission, LegacySchoolDo, User, UserLoginMigrationDO } from '@shared/domain'; import { Logger } from '@src/core/logger'; -import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { SchoolNumberMissingLoggableException, UserLoginMigrationAlreadyClosedLoggableException } from '../error'; import { UserLoginMigrationStartLoggable } from '../loggable'; import { UserLoginMigrationService } from '../service'; diff --git a/apps/server/src/modules/user-login-migration/uc/toggle-user-login-migration.uc.spec.ts b/apps/server/src/modules/user-login-migration/uc/toggle-user-login-migration.uc.spec.ts index cd97ad1e4ef..b0ff1f67e54 100644 --- a/apps/server/src/modules/user-login-migration/uc/toggle-user-login-migration.uc.spec.ts +++ b/apps/server/src/modules/user-login-migration/uc/toggle-user-login-migration.uc.spec.ts @@ -4,8 +4,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Permission, LegacySchoolDo, User, UserLoginMigrationDO } from '@shared/domain'; import { legacySchoolDoFactory, setupEntities, userFactory, userLoginMigrationDOFactory } from '@shared/testing'; import { Logger } from '@src/core/logger'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { UserLoginMigrationAlreadyClosedLoggableException, UserLoginMigrationGracePeriodExpiredLoggableException, diff --git a/apps/server/src/modules/user-login-migration/uc/toggle-user-login-migration.uc.ts b/apps/server/src/modules/user-login-migration/uc/toggle-user-login-migration.uc.ts index d07578e7b23..45de7b6e1e3 100644 --- a/apps/server/src/modules/user-login-migration/uc/toggle-user-login-migration.uc.ts +++ b/apps/server/src/modules/user-login-migration/uc/toggle-user-login-migration.uc.ts @@ -1,8 +1,8 @@ import { Injectable } from '@nestjs/common'; import { Permission, LegacySchoolDo, User, UserLoginMigrationDO } from '@shared/domain'; import { Logger } from '@src/core/logger'; -import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; import { UserLoginMigrationAlreadyClosedLoggableException, UserLoginMigrationGracePeriodExpiredLoggableException, diff --git a/apps/server/src/modules/user-login-migration/uc/user-login-migration.uc.spec.ts b/apps/server/src/modules/user-login-migration/uc/user-login-migration.uc.spec.ts index e9fc294f2ce..f5f710ae990 100644 --- a/apps/server/src/modules/user-login-migration/uc/user-login-migration.uc.spec.ts +++ b/apps/server/src/modules/user-login-migration/uc/user-login-migration.uc.spec.ts @@ -12,13 +12,13 @@ import { userLoginMigrationDOFactory, } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AuthenticationService } from '@src/modules/authentication/services/authentication.service'; -import { Action, AuthorizationService } from '@src/modules/authorization'; -import { OAuthTokenDto } from '@src/modules/oauth'; -import { OAuthService } from '@src/modules/oauth/service/oauth.service'; -import { ProvisioningService } from '@src/modules/provisioning'; -import { ExternalSchoolDto, ExternalUserDto, OauthDataDto, ProvisioningSystemDto } from '@src/modules/provisioning/dto'; -import { LegacySchoolService } from '@src/modules/legacy-school'; +import { AuthenticationService } from '@modules/authentication/services/authentication.service'; +import { Action, AuthorizationService } from '@modules/authorization'; +import { OAuthTokenDto } from '@modules/oauth'; +import { OAuthService } from '@modules/oauth/service/oauth.service'; +import { ProvisioningService } from '@modules/provisioning'; +import { ExternalSchoolDto, ExternalUserDto, OauthDataDto, ProvisioningSystemDto } from '@modules/provisioning/dto'; +import { LegacySchoolService } from '@modules/legacy-school'; import { Oauth2MigrationParams } from '../controller/dto/oauth2-migration.params'; import { OAuthMigrationError, SchoolMigrationError, UserLoginMigrationError } from '../error'; import { PageTypes } from '../interface/page-types.enum'; diff --git a/apps/server/src/modules/user-login-migration/uc/user-login-migration.uc.ts b/apps/server/src/modules/user-login-migration/uc/user-login-migration.uc.ts index bfbce03c9ca..a637afe01f6 100644 --- a/apps/server/src/modules/user-login-migration/uc/user-login-migration.uc.ts +++ b/apps/server/src/modules/user-login-migration/uc/user-login-migration.uc.ts @@ -2,12 +2,12 @@ import { ForbiddenException, Injectable } from '@nestjs/common'; import { NotFoundLoggableException } from '@shared/common/loggable-exception'; import { EntityId, Page, Permission, LegacySchoolDo, User, UserLoginMigrationDO } from '@shared/domain'; import { LegacyLogger } from '@src/core/logger'; -import { AuthenticationService } from '@src/modules/authentication/services/authentication.service'; -import { Action, AuthorizationService } from '@src/modules/authorization'; -import { OAuthTokenDto } from '@src/modules/oauth'; -import { OAuthService } from '@src/modules/oauth/service/oauth.service'; -import { ProvisioningService } from '@src/modules/provisioning'; -import { OauthDataDto } from '@src/modules/provisioning/dto'; +import { AuthenticationService } from '@modules/authentication/services/authentication.service'; +import { Action, AuthorizationService } from '@modules/authorization'; +import { OAuthTokenDto } from '@modules/oauth'; +import { OAuthService } from '@modules/oauth/service/oauth.service'; +import { ProvisioningService } from '@modules/provisioning'; +import { OauthDataDto } from '@modules/provisioning/dto'; import { OAuthMigrationError, SchoolMigrationError, UserLoginMigrationError } from '../error'; import { PageTypes } from '../interface/page-types.enum'; import { SchoolMigrationService, UserLoginMigrationService, UserMigrationService } from '../service'; diff --git a/apps/server/src/modules/user-login-migration/user-login-migration-api.module.ts b/apps/server/src/modules/user-login-migration/user-login-migration-api.module.ts index 4556103658d..b30a3f40f4d 100644 --- a/apps/server/src/modules/user-login-migration/user-login-migration-api.module.ts +++ b/apps/server/src/modules/user-login-migration/user-login-migration-api.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; -import { AuthenticationModule } from '@src/modules/authentication/authentication.module'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { OauthModule } from '@src/modules/oauth'; -import { ProvisioningModule } from '@src/modules/provisioning'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; +import { AuthenticationModule } from '@modules/authentication/authentication.module'; +import { AuthorizationModule } from '@modules/authorization'; +import { OauthModule } from '@modules/oauth'; +import { ProvisioningModule } from '@modules/provisioning'; +import { LegacySchoolModule } from '@modules/legacy-school'; import { UserLoginMigrationController } from './controller/user-login-migration.controller'; import { UserMigrationController } from './controller/user-migration.controller'; import { PageContentMapper } from './mapper'; diff --git a/apps/server/src/modules/user-login-migration/user-login-migration.module.ts b/apps/server/src/modules/user-login-migration/user-login-migration.module.ts index 40e965a4df3..705e5cb4094 100644 --- a/apps/server/src/modules/user-login-migration/user-login-migration.module.ts +++ b/apps/server/src/modules/user-login-migration/user-login-migration.module.ts @@ -1,10 +1,10 @@ import { Module } from '@nestjs/common'; import { UserLoginMigrationRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { AccountModule } from '@src/modules/account'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; -import { SystemModule } from '@src/modules/system'; -import { UserModule } from '@src/modules/user'; +import { AccountModule } from '@modules/account'; +import { LegacySchoolModule } from '@modules/legacy-school'; +import { SystemModule } from '@modules/system'; +import { UserModule } from '@modules/user'; import { MigrationCheckService, SchoolMigrationService, diff --git a/apps/server/src/modules/user/controller/api-test/user-language.api.spec.ts b/apps/server/src/modules/user/controller/api-test/user-language.api.spec.ts index f7929db5d37..28bc7c5d14e 100644 --- a/apps/server/src/modules/user/controller/api-test/user-language.api.spec.ts +++ b/apps/server/src/modules/user/controller/api-test/user-language.api.spec.ts @@ -5,9 +5,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ApiValidationError } from '@shared/common'; import { LanguageType, User } from '@shared/domain'; import { cleanupCollections, mapUserToCurrentUser, roleFactory, userFactory } from '@shared/testing'; -import { ICurrentUser } from '@src/modules/authentication'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; +import { ICurrentUser } from '@modules/authentication'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; import { Request } from 'express'; import request from 'supertest'; diff --git a/apps/server/src/modules/user/controller/api-test/user-me.api.spec.ts b/apps/server/src/modules/user/controller/api-test/user-me.api.spec.ts index 4fbf963266a..370de7e4fa6 100644 --- a/apps/server/src/modules/user/controller/api-test/user-me.api.spec.ts +++ b/apps/server/src/modules/user/controller/api-test/user-me.api.spec.ts @@ -7,11 +7,11 @@ import request from 'supertest'; import { ApiValidationError } from '@shared/common'; import { LanguageType } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { cleanupCollections, mapUserToCurrentUser, roleFactory, userFactory } from '@shared/testing'; -import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; -import { ServerTestModule } from '@src/modules/server/server.module'; -import { ResolvedUserResponse } from '@src/modules/user/controller/dto'; +import { JwtAuthGuard } from '@modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@modules/server/server.module'; +import { ResolvedUserResponse } from '@modules/user/controller/dto'; const baseRouteName = '/user/me'; diff --git a/apps/server/src/modules/user/controller/user.controller.ts b/apps/server/src/modules/user/controller/user.controller.ts index ffe5115bc8f..16729c2667b 100644 --- a/apps/server/src/modules/user/controller/user.controller.ts +++ b/apps/server/src/modules/user/controller/user.controller.ts @@ -1,6 +1,6 @@ import { Body, Controller, Get, Patch } from '@nestjs/common'; import { ApiTags } from '@nestjs/swagger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { ResolvedUserMapper } from '../mapper'; import { UserUc } from '../uc'; import { ChangeLanguageParams, ResolvedUserResponse, SuccessfulResponse } from './dto'; diff --git a/apps/server/src/modules/user/mapper/user.mapper.spec.ts b/apps/server/src/modules/user/mapper/user.mapper.spec.ts index fd97d8e9c9b..dbb5d475f05 100644 --- a/apps/server/src/modules/user/mapper/user.mapper.spec.ts +++ b/apps/server/src/modules/user/mapper/user.mapper.spec.ts @@ -1,7 +1,7 @@ import { User } from '@shared/domain'; import { roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { UserMapper } from '@src/modules/user/mapper/user.mapper'; -import { UserDto } from '@src/modules/user/uc/dto/user.dto'; +import { UserMapper } from '@modules/user/mapper/user.mapper'; +import { UserDto } from '@modules/user/uc/dto/user.dto'; describe('UserMapper', () => { let userEntity: User; diff --git a/apps/server/src/modules/user/mapper/user.mapper.ts b/apps/server/src/modules/user/mapper/user.mapper.ts index 72dd3e626d7..2238f0307b7 100644 --- a/apps/server/src/modules/user/mapper/user.mapper.ts +++ b/apps/server/src/modules/user/mapper/user.mapper.ts @@ -1,5 +1,5 @@ import { Role, User } from '@shared/domain'; -import { UserDto } from '@src/modules/user/uc/dto/user.dto'; +import { UserDto } from '@modules/user/uc/dto/user.dto'; export class UserMapper { static mapFromEntityToDto(entity: User): UserDto { diff --git a/apps/server/src/modules/user/service/user.service.spec.ts b/apps/server/src/modules/user/service/user.service.spec.ts index a24704014f5..1b208764c8f 100644 --- a/apps/server/src/modules/user/service/user.service.spec.ts +++ b/apps/server/src/modules/user/service/user.service.spec.ts @@ -7,12 +7,12 @@ import { UserDO } from '@shared/domain/domainobject/user.do'; import { UserRepo } from '@shared/repo'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; import { roleFactory, setupEntities, userDoFactory, userFactory } from '@shared/testing'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto } from '@src/modules/account/services/dto'; -import { ICurrentUser } from '@src/modules/authentication'; -import { RoleService } from '@src/modules/role/service/role.service'; -import { UserService } from '@src/modules/user/service/user.service'; -import { UserDto } from '@src/modules/user/uc/dto/user.dto'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto } from '@modules/account/services/dto'; +import { ICurrentUser } from '@modules/authentication'; +import { RoleService } from '@modules/role/service/role.service'; +import { UserService } from '@modules/user/service/user.service'; +import { UserDto } from '@modules/user/uc/dto/user.dto'; import { UserQuery } from './user-query.type'; describe('UserService', () => { diff --git a/apps/server/src/modules/user/service/user.service.ts b/apps/server/src/modules/user/service/user.service.ts index 199e8966a61..cc15404fc63 100644 --- a/apps/server/src/modules/user/service/user.service.ts +++ b/apps/server/src/modules/user/service/user.service.ts @@ -3,13 +3,13 @@ import { EntityId, IFindOptions, LanguageType, User } from '@shared/domain'; import { RoleReference, Page, UserDO } from '@shared/domain/domainobject'; import { UserRepo } from '@shared/repo'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; -import { AccountService } from '@src/modules/account'; -import { AccountDto } from '@src/modules/account/services/dto'; -import { ICurrentUser } from '@src/modules/authentication'; +import { AccountService } from '@modules/account'; +import { AccountDto } from '@modules/account/services/dto'; +import { ICurrentUser } from '@modules/authentication'; // invalid import -import { CurrentUserMapper } from '@src/modules/authentication/mapper'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; -import { RoleService } from '@src/modules/role/service/role.service'; +import { CurrentUserMapper } from '@modules/authentication/mapper'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; +import { RoleService } from '@modules/role/service/role.service'; import { BadRequestException, Injectable } from '@nestjs/common'; import { IUserConfig } from '../interfaces'; import { UserMapper } from '../mapper/user.mapper'; diff --git a/apps/server/src/modules/user/uc/user.uc.spec.ts b/apps/server/src/modules/user/uc/user.uc.spec.ts index 2988ff38671..3781a8c914a 100644 --- a/apps/server/src/modules/user/uc/user.uc.spec.ts +++ b/apps/server/src/modules/user/uc/user.uc.spec.ts @@ -5,7 +5,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { LanguageType, Permission, User } from '@shared/domain'; import { UserRepo } from '@shared/repo'; import { roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { UserService } from '@src/modules/user/service/user.service'; +import { UserService } from '@modules/user/service/user.service'; import { UserUc } from './user.uc'; describe('UserUc', () => { diff --git a/apps/server/src/modules/user/user.module.ts b/apps/server/src/modules/user/user.module.ts index 8b462c8ca20..d58c24546c6 100644 --- a/apps/server/src/modules/user/user.module.ts +++ b/apps/server/src/modules/user/user.module.ts @@ -2,9 +2,9 @@ import { Module } from '@nestjs/common'; import { UserRepo } from '@shared/repo'; import { UserDORepo } from '@shared/repo/user/user-do.repo'; import { LoggerModule } from '@src/core/logger'; -import { AccountModule } from '@src/modules/account'; -import { RoleModule } from '@src/modules/role/role.module'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; +import { AccountModule } from '@modules/account'; +import { RoleModule } from '@modules/role/role.module'; +import { LegacySchoolModule } from '@modules/legacy-school'; import { UserService } from './service/user.service'; @Module({ diff --git a/apps/server/src/modules/video-conference/controller/api-test/video-conference.api.spec.ts b/apps/server/src/modules/video-conference/controller/api-test/video-conference.api.spec.ts index fad7295bee3..d8b81c0c698 100644 --- a/apps/server/src/modules/video-conference/controller/api-test/video-conference.api.spec.ts +++ b/apps/server/src/modules/video-conference/controller/api-test/video-conference.api.spec.ts @@ -25,7 +25,7 @@ import { userFactory, } from '@shared/testing'; import { videoConferenceFactory } from '@shared/testing/factory/video-conference.factory'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import axios from 'axios'; import MockAdapter from 'axios-mock-adapter'; import { Response } from 'supertest'; diff --git a/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.spec.ts b/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.spec.ts index 84e78921410..6d78b513d75 100644 --- a/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.spec.ts +++ b/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.spec.ts @@ -2,7 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { VideoConferenceScope } from '@shared/domain/interface'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; import { BBBBaseResponse, BBBCreateResponse } from '../bbb'; import { defaultVideoConferenceOptions } from '../interface'; import { VideoConferenceDeprecatedUc } from '../uc'; diff --git a/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.ts b/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.ts index 1f6657c28b2..8dbec66972e 100644 --- a/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.ts +++ b/apps/server/src/modules/video-conference/controller/video-conference-deprecated.controller.ts @@ -11,7 +11,7 @@ import { } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { VideoConferenceScope } from '@shared/domain/interface'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { BBBBaseResponse } from '../bbb'; import { defaultVideoConferenceOptions } from '../interface'; import { VideoConferenceResponseDeprecatedMapper } from '../mapper/vc-deprecated-response.mapper'; diff --git a/apps/server/src/modules/video-conference/controller/video-conference.controller.ts b/apps/server/src/modules/video-conference/controller/video-conference.controller.ts index 76b8a5c53ad..442e9132758 100644 --- a/apps/server/src/modules/video-conference/controller/video-conference.controller.ts +++ b/apps/server/src/modules/video-conference/controller/video-conference.controller.ts @@ -1,6 +1,6 @@ import { Body, Controller, Get, HttpStatus, Param, Put, Req } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@src/modules/authentication'; +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { Request } from 'express'; import { InvalidOriginForLogoutUrlLoggableException } from '../error'; import { VideoConferenceOptions } from '../interface'; diff --git a/apps/server/src/modules/video-conference/service/video-conference.service.spec.ts b/apps/server/src/modules/video-conference/service/video-conference.service.spec.ts index 280a11976d7..c7a1ed30668 100644 --- a/apps/server/src/modules/video-conference/service/video-conference.service.spec.ts +++ b/apps/server/src/modules/video-conference/service/video-conference.service.spec.ts @@ -14,16 +14,16 @@ import { } from '@shared/domain'; import { CalendarEventDto, CalendarService } from '@shared/infra/calendar'; import { TeamsRepo, VideoConferenceRepo } from '@shared/repo'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { UserService } from '@src/modules/user'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; import { courseFactory, roleFactory, setupEntities, userDoFactory, userFactory } from '@shared/testing'; import { videoConferenceDOFactory } from '@shared/testing/factory/video-conference.do.factory'; import { ObjectId } from 'bson'; import { teamFactory } from '@shared/testing/factory/team.factory'; import { NotFoundException } from '@nestjs/common/exceptions/not-found.exception'; import { teamUserFactory } from '@shared/testing/factory/teamuser.factory'; -import { CourseService } from '@src/modules/learnroom/service'; +import { CourseService } from '@modules/learnroom/service'; import { VideoConferenceService } from './video-conference.service'; import { ErrorStatus } from '../error'; import { BBBRole } from '../bbb'; diff --git a/apps/server/src/modules/video-conference/service/video-conference.service.ts b/apps/server/src/modules/video-conference/service/video-conference.service.ts index 69a1a7fd74f..22e7a7462f1 100644 --- a/apps/server/src/modules/video-conference/service/video-conference.service.ts +++ b/apps/server/src/modules/video-conference/service/video-conference.service.ts @@ -16,10 +16,10 @@ import { } from '@shared/domain'; import { CalendarEventDto, CalendarService } from '@shared/infra/calendar'; import { TeamsRepo, VideoConferenceRepo } from '@shared/repo'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; -import { CourseService } from '@src/modules/learnroom'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { UserService } from '@src/modules/user'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { CourseService } from '@modules/learnroom'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; import { BBBRole } from '../bbb'; import { ErrorStatus } from '../error'; import { IVideoConferenceSettings, VideoConferenceOptions, VideoConferenceSettings } from '../interface'; diff --git a/apps/server/src/modules/video-conference/uc/video-conference-create.uc.spec.ts b/apps/server/src/modules/video-conference/uc/video-conference-create.uc.spec.ts index 7570d6d38ff..4c38bf18467 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-create.uc.spec.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-create.uc.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { UserService } from '@src/modules/user'; +import { UserService } from '@modules/user'; import { userDoFactory } from '@shared/testing'; import { UserDO, VideoConferenceScope } from '@shared/domain'; import { ObjectId } from 'bson'; diff --git a/apps/server/src/modules/video-conference/uc/video-conference-create.uc.ts b/apps/server/src/modules/video-conference/uc/video-conference-create.uc.ts index 853bf0f13bc..940113276a2 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-create.uc.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-create.uc.ts @@ -1,6 +1,6 @@ import { ForbiddenException, Injectable } from '@nestjs/common'; import { EntityId, UserDO } from '@shared/domain'; -import { UserService } from '@src/modules/user'; +import { UserService } from '@modules/user'; import { BBBBaseMeetingConfig, BBBCreateConfigBuilder, diff --git a/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.spec.ts b/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.spec.ts index 4ff494a4cc0..4d15397548b 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.spec.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.spec.ts @@ -19,10 +19,11 @@ import { CalendarEventDto } from '@shared/infra/calendar/dto/calendar-event.dto' import { TeamsRepo, VideoConferenceRepo } from '@shared/repo'; import { roleFactory, setupEntities, userDoFactory } from '@shared/testing'; import { teamFactory } from '@shared/testing/factory/team.factory'; -import { LegacySchoolService, UserService } from '@src/modules'; -import { AuthorizationReferenceService } from '@src/modules/authorization/domain'; -import { ICurrentUser } from '@src/modules/authentication'; -import { CourseService } from '@src/modules/learnroom'; +import { AuthorizationReferenceService } from '@modules/authorization/domain'; +import { ICurrentUser } from '@modules/authentication'; +import { CourseService } from '@modules/learnroom/service'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { UserService } from '@modules/user'; import { IScopeInfo, VideoConference, VideoConferenceJoin, VideoConferenceState } from './dto'; import { VideoConferenceDeprecatedUc } from './video-conference-deprecated.uc'; import { diff --git a/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.ts b/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.ts index e6b1f11ed50..41e011c4acd 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-deprecated.uc.ts @@ -17,12 +17,12 @@ import { CalendarService } from '@shared/infra/calendar'; import { CalendarEventDto } from '@shared/infra/calendar/dto/calendar-event.dto'; import { TeamsRepo } from '@shared/repo'; import { VideoConferenceRepo } from '@shared/repo/videoconference/video-conference.repo'; -import { ICurrentUser } from '@src/modules/authentication'; -import { Action, AuthorizationContextBuilder } from '@src/modules/authorization'; -import { AuthorizationReferenceService, AuthorizableReferenceType } from '@src/modules/authorization/domain'; -import { LegacySchoolService } from '@src/modules/legacy-school'; -import { CourseService } from '@src/modules/learnroom'; -import { UserService } from '@src/modules/user'; +import { ICurrentUser } from '@modules/authentication'; +import { Action, AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationReferenceService, AuthorizableReferenceType } from '@modules/authorization/domain'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { CourseService } from '@modules/learnroom'; +import { UserService } from '@modules/user'; import { BBBBaseMeetingConfig, BBBBaseResponse, diff --git a/apps/server/src/modules/video-conference/uc/video-conference-end.uc.spec.ts b/apps/server/src/modules/video-conference/uc/video-conference-end.uc.spec.ts index 2b5744b4d79..d1dbfc18b9d 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-end.uc.spec.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-end.uc.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { UserService } from '@src/modules/user'; +import { UserService } from '@modules/user'; import { userDoFactory } from '@shared/testing'; import { UserDO, VideoConferenceScope } from '@shared/domain'; import { ObjectId } from 'bson'; diff --git a/apps/server/src/modules/video-conference/uc/video-conference-end.uc.ts b/apps/server/src/modules/video-conference/uc/video-conference-end.uc.ts index 50318c001c0..6cb70edc176 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-end.uc.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-end.uc.ts @@ -1,7 +1,7 @@ import { ForbiddenException, Injectable } from '@nestjs/common'; import { EntityId, UserDO } from '@shared/domain'; -import { ErrorStatus } from '@src/modules/video-conference/error/error-status.enum'; -import { UserService } from '@src/modules/user'; +import { ErrorStatus } from '@modules/video-conference/error/error-status.enum'; +import { UserService } from '@modules/user'; import { BBBBaseMeetingConfig, BBBBaseResponse, BBBResponse, BBBRole, BBBService } from '../bbb'; import { IScopeInfo, ScopeRef, VideoConference, VideoConferenceState } from './dto'; import { VideoConferenceService } from '../service'; diff --git a/apps/server/src/modules/video-conference/uc/video-conference-info.uc.spec.ts b/apps/server/src/modules/video-conference/uc/video-conference-info.uc.spec.ts index 1d044c7b9db..ebc8daa4084 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-info.uc.spec.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-info.uc.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { UserService } from '@src/modules/user'; +import { UserService } from '@modules/user'; import { userDoFactory } from '@shared/testing'; import { Permission, UserDO, VideoConferenceDO, VideoConferenceScope } from '@shared/domain'; import { ObjectId } from 'bson'; diff --git a/apps/server/src/modules/video-conference/uc/video-conference-info.uc.ts b/apps/server/src/modules/video-conference/uc/video-conference-info.uc.ts index 79a1f95b8d1..1c3736d1d01 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-info.uc.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-info.uc.ts @@ -1,7 +1,7 @@ import { ForbiddenException, Injectable } from '@nestjs/common'; import { EntityId, UserDO, VideoConferenceDO, VideoConferenceOptionsDO } from '@shared/domain'; -import { ErrorStatus } from '@src/modules/video-conference/error/error-status.enum'; -import { UserService } from '@src/modules/user'; +import { ErrorStatus } from '@modules/video-conference/error/error-status.enum'; +import { UserService } from '@modules/user'; import { BBBBaseMeetingConfig, BBBMeetingInfoResponse, BBBResponse, BBBRole, BBBService } from '../bbb'; import { IScopeInfo, ScopeRef, VideoConferenceInfo, VideoConferenceState } from './dto'; import { VideoConferenceService } from '../service'; diff --git a/apps/server/src/modules/video-conference/uc/video-conference-join.uc.spec.ts b/apps/server/src/modules/video-conference/uc/video-conference-join.uc.spec.ts index 0f46fcde450..525a43b4c69 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-join.uc.spec.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-join.uc.spec.ts @@ -1,6 +1,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { UserService } from '@src/modules/user'; +import { UserService } from '@modules/user'; import { userDoFactory } from '@shared/testing'; import { Permission, UserDO, VideoConferenceDO, VideoConferenceScope } from '@shared/domain'; import { ObjectId } from 'bson'; diff --git a/apps/server/src/modules/video-conference/uc/video-conference-join.uc.ts b/apps/server/src/modules/video-conference/uc/video-conference-join.uc.ts index 5728a4b0da2..2015c20d381 100644 --- a/apps/server/src/modules/video-conference/uc/video-conference-join.uc.ts +++ b/apps/server/src/modules/video-conference/uc/video-conference-join.uc.ts @@ -1,7 +1,7 @@ import { ForbiddenException, Injectable } from '@nestjs/common'; import { EntityId, UserDO, VideoConferenceDO } from '@shared/domain'; -import { ErrorStatus } from '@src/modules/video-conference/error/error-status.enum'; -import { UserService } from '@src/modules/user'; +import { ErrorStatus } from '@modules/video-conference/error/error-status.enum'; +import { UserService } from '@modules/user'; import { BBBJoinConfigBuilder, BBBRole, BBBService } from '../bbb'; import { ScopeRef, VideoConferenceJoin, VideoConferenceState } from './dto'; import { VideoConferenceService } from '../service'; diff --git a/apps/server/src/modules/video-conference/video-conference-api.module.ts b/apps/server/src/modules/video-conference/video-conference-api.module.ts index 4c2448d55d2..d4594a10a34 100644 --- a/apps/server/src/modules/video-conference/video-conference-api.module.ts +++ b/apps/server/src/modules/video-conference/video-conference-api.module.ts @@ -1,6 +1,6 @@ import { Module } from '@nestjs/common'; -import { UserModule } from '@src/modules/user'; -import { AuthorizationModule } from '@src/modules/authorization'; +import { UserModule } from '@modules/user'; +import { AuthorizationModule } from '@modules/authorization'; import { VideoConferenceController } from './controller'; import { VideoConferenceCreateUc, VideoConferenceJoinUc, VideoConferenceEndUc, VideoConferenceInfoUc } from './uc'; import { VideoConferenceModule } from './video-conference.module'; diff --git a/apps/server/src/modules/video-conference/video-conference.module.ts b/apps/server/src/modules/video-conference/video-conference.module.ts index 6277f0dde0a..c9708b16dc9 100644 --- a/apps/server/src/modules/video-conference/video-conference.module.ts +++ b/apps/server/src/modules/video-conference/video-conference.module.ts @@ -2,13 +2,13 @@ import { Module } from '@nestjs/common'; import { HttpModule } from '@nestjs/axios'; import { CalendarModule } from '@shared/infra/calendar'; import { VideoConferenceRepo } from '@shared/repo/videoconference/video-conference.repo'; -import { AuthorizationModule } from '@src/modules/authorization'; -import { AuthorizationReferenceModule } from '@src/modules/authorization/authorization-reference.module'; +import { AuthorizationModule } from '@modules/authorization'; +import { AuthorizationReferenceModule } from '@modules/authorization/authorization-reference.module'; import { TeamsRepo } from '@shared/repo'; -import { LegacySchoolModule } from '@src/modules/legacy-school'; +import { LegacySchoolModule } from '@modules/legacy-school'; import { LoggerModule } from '@src/core/logger'; import { ConverterUtil } from '@shared/common'; -import { UserModule } from '@src/modules/user'; +import { UserModule } from '@modules/user'; import { BBBService, BbbSettings } from './bbb'; import { VideoConferenceService } from './service'; import { VideoConferenceDeprecatedUc } from './uc'; diff --git a/apps/server/src/shared/common/interceptor/request-logging.interceptor.ts b/apps/server/src/shared/common/interceptor/request-logging.interceptor.ts index 4c9a7bf7135..838ae020039 100644 --- a/apps/server/src/shared/common/interceptor/request-logging.interceptor.ts +++ b/apps/server/src/shared/common/interceptor/request-logging.interceptor.ts @@ -3,7 +3,7 @@ import { LegacyLogger, RequestLoggingBody } from '@src/core/logger'; import { Request } from 'express'; import { Observable, throwError } from 'rxjs'; import { catchError, tap } from 'rxjs/operators'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication/interface/user'; @Injectable() export class RequestLoggingInterceptor implements NestInterceptor { diff --git a/apps/server/src/shared/controller/swagger.spec.ts b/apps/server/src/shared/controller/swagger.spec.ts index 74236b00811..413a3fa3a9c 100644 --- a/apps/server/src/shared/controller/swagger.spec.ts +++ b/apps/server/src/shared/controller/swagger.spec.ts @@ -1,6 +1,6 @@ import { INestApplication } from '@nestjs/common'; import { NestFactory } from '@nestjs/core'; -import { ServerTestModule } from '@src/modules/server'; +import { ServerTestModule } from '@modules/server'; import request from 'supertest'; import { enableOpenApiDocs } from './swagger'; diff --git a/apps/server/src/shared/domain/entity/all-entities.ts b/apps/server/src/shared/domain/entity/all-entities.ts index 6cbb3bf9810..9dc33c55b78 100644 --- a/apps/server/src/shared/domain/entity/all-entities.ts +++ b/apps/server/src/shared/domain/entity/all-entities.ts @@ -1,10 +1,10 @@ -import { ClassEntity } from '@src/modules/class/entity'; -import { GroupEntity } from '@src/modules/group/entity'; -import { ExternalToolPseudonymEntity, PseudonymEntity } from '@src/modules/pseudonym/entity'; -import { ShareToken } from '@src/modules/sharing/entity/share-token.entity'; -import { ContextExternalToolEntity } from '@src/modules/tool/context-external-tool/entity'; -import { ExternalToolEntity } from '@src/modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; +import { ClassEntity } from '@modules/class/entity'; +import { GroupEntity } from '@modules/group/entity'; +import { ExternalToolPseudonymEntity, PseudonymEntity } from '@modules/pseudonym/entity'; +import { ShareToken } from '@modules/sharing/entity/share-token.entity'; +import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { Account } from './account.entity'; import { BoardNode, diff --git a/apps/server/src/shared/domain/entity/boardnode/external-tool-element-node.entity.ts b/apps/server/src/shared/domain/entity/boardnode/external-tool-element-node.entity.ts index 68df0b696b5..ffe2ef83bec 100644 --- a/apps/server/src/shared/domain/entity/boardnode/external-tool-element-node.entity.ts +++ b/apps/server/src/shared/domain/entity/boardnode/external-tool-element-node.entity.ts @@ -1,6 +1,6 @@ import { Entity, ManyToOne } from '@mikro-orm/core'; import { AnyBoardDo } from '@shared/domain/domainobject'; -import { ContextExternalToolEntity } from '@src/modules/tool/context-external-tool/entity/context-external-tool.entity'; +import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity/context-external-tool.entity'; import { BoardNode, BoardNodeProps } from './boardnode.entity'; import { BoardDoBuilder, BoardNodeType } from './types'; diff --git a/apps/server/src/shared/domain/entity/course.entity.ts b/apps/server/src/shared/domain/entity/course.entity.ts index e873ed05300..81fdcb741d9 100644 --- a/apps/server/src/shared/domain/entity/course.entity.ts +++ b/apps/server/src/shared/domain/entity/course.entity.ts @@ -1,8 +1,8 @@ import { Collection, Entity, Enum, Index, ManyToMany, ManyToOne, OneToMany, Property, Unique } from '@mikro-orm/core'; import { InternalServerErrorException } from '@nestjs/common/exceptions/internal-server-error.exception'; import { IEntityWithSchool, ILearnroom } from '@shared/domain/interface'; -import { ClassEntity } from '@src/modules/class/entity/class.entity'; -import { GroupEntity } from '@src/modules/group/entity/group.entity'; +import { ClassEntity } from '@modules/class/entity/class.entity'; +import { GroupEntity } from '@modules/group/entity/group.entity'; import { EntityId, LearnroomMetadata, LearnroomTypes } from '../types'; import { BaseEntityWithTimestamps } from './base.entity'; import { CourseGroup } from './coursegroup.entity'; diff --git a/apps/server/src/shared/infra/antivirus/antivirus.service.ts b/apps/server/src/shared/infra/antivirus/antivirus.service.ts index 1f49b9907fb..15fbcbf4c1e 100644 --- a/apps/server/src/shared/infra/antivirus/antivirus.service.ts +++ b/apps/server/src/shared/infra/antivirus/antivirus.service.ts @@ -1,7 +1,7 @@ import { AmqpConnection } from '@golevelup/nestjs-rabbitmq'; import { Inject, Injectable, InternalServerErrorException } from '@nestjs/common'; import { ErrorUtils } from '@src/core/error/utils'; -import { API_VERSION_PATH, FilesStorageInternalActions } from '@src/modules/files-storage/files-storage.const'; +import { API_VERSION_PATH, FilesStorageInternalActions } from '@modules/files-storage/files-storage.const'; import NodeClam from 'clamscan'; import { Readable } from 'stream'; import { AntivirusServiceOptions, ScanResult } from './interfaces'; diff --git a/apps/server/src/shared/infra/collaborative-storage/collaborative-storage-adapter.module.ts b/apps/server/src/shared/infra/collaborative-storage/collaborative-storage-adapter.module.ts index 0580777a666..84e4f4596d6 100644 --- a/apps/server/src/shared/infra/collaborative-storage/collaborative-storage-adapter.module.ts +++ b/apps/server/src/shared/infra/collaborative-storage/collaborative-storage-adapter.module.ts @@ -6,9 +6,9 @@ import { NextcloudClient } from '@shared/infra/collaborative-storage/strategy/ne import { NextcloudStrategy } from '@shared/infra/collaborative-storage/strategy/nextcloud/nextcloud.strategy'; import { LtiToolRepo } from '@shared/repo/ltitool/'; import { LoggerModule } from '@src/core/logger'; -import { ToolModule } from '@src/modules/tool'; -import { PseudonymModule } from '@src/modules/pseudonym'; -import { UserModule } from '@src/modules/user'; +import { ToolModule } from '@modules/tool'; +import { PseudonymModule } from '@modules/pseudonym'; +import { UserModule } from '@modules/user'; import { CollaborativeStorageAdapter } from './collaborative-storage.adapter'; const storageStrategy: Provider = { diff --git a/apps/server/src/shared/infra/collaborative-storage/collaborative-storage.adapter.spec.ts b/apps/server/src/shared/infra/collaborative-storage/collaborative-storage.adapter.spec.ts index db2a05e26b5..c33f9be282a 100644 --- a/apps/server/src/shared/infra/collaborative-storage/collaborative-storage.adapter.spec.ts +++ b/apps/server/src/shared/infra/collaborative-storage/collaborative-storage.adapter.spec.ts @@ -6,7 +6,7 @@ import { CollaborativeStorageAdapter } from '@shared/infra/collaborative-storage import { CollaborativeStorageAdapterMapper } from '@shared/infra/collaborative-storage/mapper/collaborative-storage-adapter.mapper'; import { ICollaborativeStorageStrategy } from '@shared/infra/collaborative-storage/strategy/base.interface.strategy'; import { LegacyLogger } from '@src/core/logger'; -import { TeamDto } from '@src/modules/collaborative-storage/services/dto/team.dto'; +import { TeamDto } from '@modules/collaborative-storage/services/dto/team.dto'; class TestStrategy implements ICollaborativeStorageStrategy { baseURL: string; diff --git a/apps/server/src/shared/infra/collaborative-storage/collaborative-storage.adapter.ts b/apps/server/src/shared/infra/collaborative-storage/collaborative-storage.adapter.ts index c6ef05c1e5b..9edcafbdc12 100644 --- a/apps/server/src/shared/infra/collaborative-storage/collaborative-storage.adapter.ts +++ b/apps/server/src/shared/infra/collaborative-storage/collaborative-storage.adapter.ts @@ -1,10 +1,10 @@ -import { TeamPermissionsDto } from '@src/modules/collaborative-storage/services/dto/team-permissions.dto'; -import { TeamDto } from '@src/modules/collaborative-storage/services/dto/team.dto'; +import { TeamPermissionsDto } from '@modules/collaborative-storage/services/dto/team-permissions.dto'; +import { TeamDto } from '@modules/collaborative-storage/services/dto/team.dto'; import { ICollaborativeStorageStrategy } from '@shared/infra/collaborative-storage/strategy/base.interface.strategy'; import { Inject, Injectable } from '@nestjs/common'; import { CollaborativeStorageAdapterMapper } from '@shared/infra/collaborative-storage/mapper/collaborative-storage-adapter.mapper'; import { LegacyLogger } from '@src/core/logger'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; /** * Provides an Adapter to an external collaborative storage. diff --git a/apps/server/src/shared/infra/collaborative-storage/mapper/collaborative-storage-adapter.mapper.ts b/apps/server/src/shared/infra/collaborative-storage/mapper/collaborative-storage-adapter.mapper.ts index e3595440f9f..a04ace04490 100644 --- a/apps/server/src/shared/infra/collaborative-storage/mapper/collaborative-storage-adapter.mapper.ts +++ b/apps/server/src/shared/infra/collaborative-storage/mapper/collaborative-storage-adapter.mapper.ts @@ -1,7 +1,7 @@ -import { TeamPermissionsDto } from '@src/modules/collaborative-storage/services/dto/team-permissions.dto'; -import { TeamDto } from '@src/modules/collaborative-storage/services/dto/team.dto'; +import { TeamPermissionsDto } from '@modules/collaborative-storage/services/dto/team-permissions.dto'; +import { TeamDto } from '@modules/collaborative-storage/services/dto/team.dto'; import { Injectable } from '@nestjs/common'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; import { TeamRolePermissionsDto } from '../dto/team-role-permissions.dto'; @Injectable() diff --git a/apps/server/src/shared/infra/collaborative-storage/strategy/base.interface.strategy.ts b/apps/server/src/shared/infra/collaborative-storage/strategy/base.interface.strategy.ts index 51d741105f0..f960452df5f 100644 --- a/apps/server/src/shared/infra/collaborative-storage/strategy/base.interface.strategy.ts +++ b/apps/server/src/shared/infra/collaborative-storage/strategy/base.interface.strategy.ts @@ -1,4 +1,4 @@ -import { TeamDto } from '@src/modules/collaborative-storage/services/dto/team.dto'; +import { TeamDto } from '@modules/collaborative-storage/services/dto/team.dto'; import { TeamRolePermissionsDto } from '../dto/team-role-permissions.dto'; /** diff --git a/apps/server/src/shared/infra/collaborative-storage/strategy/nextcloud/nextcloud.strategy.spec.ts b/apps/server/src/shared/infra/collaborative-storage/strategy/nextcloud/nextcloud.strategy.spec.ts index bc4b3878ea5..7684b14dbb0 100644 --- a/apps/server/src/shared/infra/collaborative-storage/strategy/nextcloud/nextcloud.strategy.spec.ts +++ b/apps/server/src/shared/infra/collaborative-storage/strategy/nextcloud/nextcloud.strategy.spec.ts @@ -9,10 +9,10 @@ import { NextcloudStrategy } from '@shared/infra/collaborative-storage/strategy/ import { LtiToolRepo } from '@shared/repo'; import { ltiToolDOFactory, pseudonymFactory, setupEntities, userDoFactory, userFactory } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { TeamDto, TeamUserDto } from '@src/modules/collaborative-storage/services/dto/team.dto'; -import { PseudonymService } from '@src/modules/pseudonym'; -import { ExternalToolService } from '@src/modules/tool/external-tool/service'; -import { UserService } from '@src/modules/user'; +import { TeamDto, TeamUserDto } from '@modules/collaborative-storage/services/dto/team.dto'; +import { PseudonymService } from '@modules/pseudonym'; +import { ExternalToolService } from '@modules/tool/external-tool/service'; +import { UserService } from '@modules/user'; class NextcloudStrategySpec extends NextcloudStrategy { static specGenerateGroupId(dto: TeamRolePermissionsDto): string { diff --git a/apps/server/src/shared/infra/collaborative-storage/strategy/nextcloud/nextcloud.strategy.ts b/apps/server/src/shared/infra/collaborative-storage/strategy/nextcloud/nextcloud.strategy.ts index 1292bff3a42..6b75d6ec76f 100644 --- a/apps/server/src/shared/infra/collaborative-storage/strategy/nextcloud/nextcloud.strategy.ts +++ b/apps/server/src/shared/infra/collaborative-storage/strategy/nextcloud/nextcloud.strategy.ts @@ -3,11 +3,11 @@ import { Pseudonym, UserDO } from '@shared/domain/'; import { LtiToolDO } from '@shared/domain/domainobject/ltitool.do'; import { LtiToolRepo } from '@shared/repo/ltitool/'; import { LegacyLogger } from '@src/core/logger'; -import { TeamDto, TeamUserDto } from '@src/modules/collaborative-storage'; -import { PseudonymService } from '@src/modules/pseudonym'; -import { UserService } from '@src/modules/user'; -import { ExternalToolService } from '@src/modules/tool/external-tool/service'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; +import { TeamDto, TeamUserDto } from '@modules/collaborative-storage'; +import { PseudonymService } from '@modules/pseudonym'; +import { UserService } from '@modules/user'; +import { ExternalToolService } from '@modules/tool/external-tool/service'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; import { TeamRolePermissionsDto } from '../../dto/team-role-permissions.dto'; import { ICollaborativeStorageStrategy } from '../base.interface.strategy'; import { NextcloudClient } from './nextcloud.client'; diff --git a/apps/server/src/shared/infra/identity-management/identity-management-oauth.service.ts b/apps/server/src/shared/infra/identity-management/identity-management-oauth.service.ts index 5003da4c62e..2a486e87c32 100644 --- a/apps/server/src/shared/infra/identity-management/identity-management-oauth.service.ts +++ b/apps/server/src/shared/infra/identity-management/identity-management-oauth.service.ts @@ -1,4 +1,4 @@ -import { OauthConfigDto } from '@src/modules/system/service/dto'; +import { OauthConfigDto } from '@modules/system/service/dto'; export abstract class IdentityManagementOauthService { /** diff --git a/apps/server/src/shared/infra/identity-management/keycloak-configuration/controller/keycloak-configuration.controller.spec.ts b/apps/server/src/shared/infra/identity-management/keycloak-configuration/controller/keycloak-configuration.controller.spec.ts index d1155cc3f88..734e8f628b2 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak-configuration/controller/keycloak-configuration.controller.spec.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak-configuration/controller/keycloak-configuration.controller.spec.ts @@ -3,7 +3,7 @@ import { ServiceUnavailableException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { LegacyLogger } from '@src/core/logger'; -import { NodeEnvType } from '@src/modules/server/server.config'; +import { NodeEnvType } from '@modules/server/server.config'; import { KeycloakConfigurationUc } from '../uc/keycloak-configuration.uc'; import { KeycloakManagementController } from './keycloak-configuration.controller'; diff --git a/apps/server/src/shared/infra/identity-management/keycloak-configuration/keycloak-configuration.module.ts b/apps/server/src/shared/infra/identity-management/keycloak-configuration/keycloak-configuration.module.ts index 8c51103495d..2012dad00a5 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak-configuration/keycloak-configuration.module.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak-configuration/keycloak-configuration.module.ts @@ -2,7 +2,8 @@ import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; import { EncryptionModule } from '@shared/infra/encryption'; import { ConsoleWriterModule } from '@shared/infra/console'; -import { AccountModule, SystemModule } from '@src/modules'; +import { AccountModule } from '@modules/account'; +import { SystemModule } from '@modules/system'; import { KeycloakAdministrationModule } from '../keycloak-administration/keycloak-administration.module'; import { KeycloakConsole } from './console/keycloak-configuration.console'; import { KeycloakConfigurationInputFiles } from './interface/keycloak-configuration-input-files.interface'; diff --git a/apps/server/src/shared/infra/identity-management/keycloak-configuration/mapper/identity-provider.mapper.spec.ts b/apps/server/src/shared/infra/identity-management/keycloak-configuration/mapper/identity-provider.mapper.spec.ts index 2b565bc77e8..b28d74ca3d5 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak-configuration/mapper/identity-provider.mapper.spec.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak-configuration/mapper/identity-provider.mapper.spec.ts @@ -3,7 +3,7 @@ import IdentityProviderRepresentation from '@keycloak/keycloak-admin-client/lib/ import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { DefaultEncryptionService, SymetricKeyEncryptionService } from '@shared/infra/encryption'; -import { OidcConfigDto } from '@src/modules/system/service'; +import { OidcConfigDto } from '@modules/system/service'; import { OidcIdentityProviderMapper } from './identity-provider.mapper'; describe('OidcIdentityProviderMapper', () => { diff --git a/apps/server/src/shared/infra/identity-management/keycloak-configuration/mapper/identity-provider.mapper.ts b/apps/server/src/shared/infra/identity-management/keycloak-configuration/mapper/identity-provider.mapper.ts index c7f6ec65af7..75737263cac 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak-configuration/mapper/identity-provider.mapper.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak-configuration/mapper/identity-provider.mapper.ts @@ -1,7 +1,7 @@ import IdentityProviderRepresentation from '@keycloak/keycloak-admin-client/lib/defs/identityProviderRepresentation'; import { Inject } from '@nestjs/common'; import { DefaultEncryptionService, IEncryptionService } from '@shared/infra/encryption'; -import { OidcConfigDto } from '@src/modules/system/service'; +import { OidcConfigDto } from '@modules/system/service'; export class OidcIdentityProviderMapper { constructor(@Inject(DefaultEncryptionService) private readonly defaultEncryptionService: IEncryptionService) {} diff --git a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts index a18a07e9fe7..ad5af6a1d75 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.integration.spec.ts @@ -8,7 +8,7 @@ import { MongoMemoryDatabaseModule } from '@shared/infra/database'; import { SystemRepo } from '@shared/repo/system/system.repo'; import { systemFactory } from '@shared/testing/factory'; import { LoggerModule } from '@src/core/logger'; -import { SystemService } from '@src/modules/system/service/system.service'; +import { SystemService } from '@modules/system/service/system.service'; import { v1 } from 'uuid'; import { KeycloakAdministrationService } from '../../keycloak-administration/service/keycloak-administration.service'; import { KeycloakConfigurationModule } from '../keycloak-configuration.module'; diff --git a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.spec.ts b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.spec.ts index 012d63d18c2..1388392995e 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.spec.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.spec.ts @@ -12,8 +12,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SystemEntity, SystemTypeEnum } from '@shared/domain'; import { SymetricKeyEncryptionService } from '@shared/infra/encryption'; import { systemFactory } from '@shared/testing'; -import { SystemOidcMapper } from '@src/modules/system/mapper/system-oidc.mapper'; -import { SystemOidcService } from '@src/modules/system/service/system-oidc.service'; +import { SystemOidcMapper } from '@modules/system/mapper/system-oidc.mapper'; +import { SystemOidcService } from '@modules/system/service/system-oidc.service'; import { AxiosResponse } from 'axios'; import { of } from 'rxjs'; import { v1 } from 'uuid'; diff --git a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.ts b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.ts index 02f27db9eb2..ae7f2631bce 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-configuration.service.ts @@ -6,9 +6,9 @@ import IdentityProviderRepresentation from '@keycloak/keycloak-admin-client/lib/ import ProtocolMapperRepresentation from '@keycloak/keycloak-admin-client/lib/defs/protocolMapperRepresentation'; import { Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; -import { IServerConfig } from '@src/modules/server/server.config'; -import { OidcConfigDto } from '@src/modules/system/service'; -import { SystemOidcService } from '@src/modules/system/service/system-oidc.service'; +import { IServerConfig } from '@modules/server/server.config'; +import { OidcConfigDto } from '@modules/system/service'; +import { SystemOidcService } from '@modules/system/service/system-oidc.service'; import { KeycloakAdministrationService } from '../../keycloak-administration/service/keycloak-administration.service'; import { OidcIdentityProviderMapper } from '../mapper/identity-provider.mapper'; diff --git a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.spec.ts b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.spec.ts index c8a1011e2c1..e02ade6a26b 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.spec.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.spec.ts @@ -1,8 +1,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { LegacyLogger } from '@src/core/logger'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto } from '@src/modules/account/services/dto/account.dto'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto } from '@modules/account/services/dto/account.dto'; import KeycloakAdminClient from '@keycloak/keycloak-admin-client-cjs/keycloak-admin-client-cjs-index'; import { Users } from '@keycloak/keycloak-admin-client/lib/resources/users'; import UserRepresentation from '@keycloak/keycloak-admin-client/lib/defs/userRepresentation'; diff --git a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.ts b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.ts index b64f69d5043..ce87478f637 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak-configuration/service/keycloak-migration.service.ts @@ -1,8 +1,8 @@ import UserRepresentation from '@keycloak/keycloak-admin-client/lib/defs/userRepresentation'; import { Injectable } from '@nestjs/common'; import { LegacyLogger } from '@src/core/logger'; -import { AccountService } from '@src/modules/account/services/account.service'; -import { AccountDto } from '@src/modules/account/services/dto'; +import { AccountService } from '@modules/account/services/account.service'; +import { AccountDto } from '@modules/account/services/dto'; import { KeycloakAdministrationService } from '../../keycloak-administration/service/keycloak-administration.service'; @Injectable() diff --git a/apps/server/src/shared/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.ts b/apps/server/src/shared/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.ts index e6526a34c22..7e10179b2cd 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak/service/keycloak-identity-management-oauth.service.ts @@ -2,7 +2,7 @@ import { HttpService } from '@nestjs/axios'; import { Inject, Injectable } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { DefaultEncryptionService, IEncryptionService } from '@shared/infra/encryption'; -import { OauthConfigDto } from '@src/modules/system/service'; +import { OauthConfigDto } from '@modules/system/service'; import qs from 'qs'; import { lastValueFrom } from 'rxjs'; import { IdentityManagementOauthService } from '../../identity-management-oauth.service'; diff --git a/apps/server/src/shared/infra/identity-management/keycloak/service/keycloak-identity-management.service.integration.spec.ts b/apps/server/src/shared/infra/identity-management/keycloak/service/keycloak-identity-management.service.integration.spec.ts index b4e6535011d..fd66603f730 100644 --- a/apps/server/src/shared/infra/identity-management/keycloak/service/keycloak-identity-management.service.integration.spec.ts +++ b/apps/server/src/shared/infra/identity-management/keycloak/service/keycloak-identity-management.service.integration.spec.ts @@ -6,7 +6,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { IdmAccount, IdmAccountUpdate } from '@shared/domain'; import { KeycloakAdministrationService } from '@shared/infra/identity-management/keycloak-administration/service/keycloak-administration.service'; import { KeycloakModule } from '@shared/infra/identity-management/keycloak/keycloak.module'; -import { ServerModule } from '@src/modules/server'; +import { ServerModule } from '@modules/server'; import { v1 } from 'uuid'; import { IdentityManagementService } from '../../identity-management.service'; import { KeycloakIdentityManagementService } from './keycloak-identity-management.service'; diff --git a/apps/server/src/shared/infra/oauth-provider/dto/request/accept-consent-request.body.ts b/apps/server/src/shared/infra/oauth-provider/dto/request/accept-consent-request.body.ts index 6c553afdd50..235cb9191ea 100644 --- a/apps/server/src/shared/infra/oauth-provider/dto/request/accept-consent-request.body.ts +++ b/apps/server/src/shared/infra/oauth-provider/dto/request/accept-consent-request.body.ts @@ -1,4 +1,4 @@ -import { IdToken } from '@src/modules/oauth-provider/interface/id-token'; +import { IdToken } from '@modules/oauth-provider/interface/id-token'; export interface AcceptConsentRequestBody { grant_access_token_audience?: string[]; diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts index 6806aeb3f71..0a9151d8c9c 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.integration.spec.ts @@ -12,12 +12,12 @@ import { schoolFactory, } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { CustomParameterEntry } from '@src/modules/tool/common/domain'; -import { ToolContextType } from '@src/modules/tool/common/enum'; -import { ContextExternalTool, ContextExternalToolProps } from '@src/modules/tool/context-external-tool/domain'; -import { ContextExternalToolEntity, ContextExternalToolType } from '@src/modules/tool/context-external-tool/entity'; -import { ContextExternalToolQuery } from '@src/modules/tool/context-external-tool/uc/dto/context-external-tool.types'; -import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; +import { CustomParameterEntry } from '@modules/tool/common/domain'; +import { ToolContextType } from '@modules/tool/common/enum'; +import { ContextExternalTool, ContextExternalToolProps } from '@modules/tool/context-external-tool/domain'; +import { ContextExternalToolEntity, ContextExternalToolType } from '@modules/tool/context-external-tool/entity'; +import { ContextExternalToolQuery } from '@modules/tool/context-external-tool/uc/dto/context-external-tool.types'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { ContextExternalToolRepo } from './context-external-tool.repo'; describe('ContextExternalToolRepo', () => { diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts index 084adb4b727..5ad1629f0c2 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.repo.ts @@ -3,16 +3,16 @@ import { EntityManager } from '@mikro-orm/mongodb'; import { Injectable } from '@nestjs/common'; import { BaseDORepo } from '@shared/repo'; import { LegacyLogger } from '@src/core/logger'; -import { ToolContextType } from '@src/modules/tool/common/enum/tool-context-type.enum'; -import { ContextExternalTool, ContextRef } from '@src/modules/tool/context-external-tool/domain'; +import { ToolContextType } from '@modules/tool/common/enum/tool-context-type.enum'; +import { ContextExternalTool, ContextRef } from '@modules/tool/context-external-tool/domain'; import { ContextExternalToolEntity, ContextExternalToolType, IContextExternalToolProperties, -} from '@src/modules/tool/context-external-tool/entity'; -import { ContextExternalToolQuery } from '@src/modules/tool/context-external-tool/uc/dto/context-external-tool.types'; -import { SchoolExternalToolRefDO } from '@src/modules/tool/school-external-tool/domain'; -import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; +} from '@modules/tool/context-external-tool/entity'; +import { ContextExternalToolQuery } from '@modules/tool/context-external-tool/uc/dto/context-external-tool.types'; +import { SchoolExternalToolRefDO } from '@modules/tool/school-external-tool/domain'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { EntityId } from '../../domain'; import { ExternalToolRepoMapper } from '../externaltool'; import { ContextExternalToolScope } from './context-external-tool.scope'; diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.scope.spec.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.scope.spec.ts index b6896ae497f..25c77bf5beb 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.scope.spec.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.scope.spec.ts @@ -1,6 +1,6 @@ import { schoolExternalToolEntityFactory } from '@shared/testing'; -import { ToolContextType } from '@src/modules/tool/common/enum'; -import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; +import { ToolContextType } from '@modules/tool/common/enum'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { ContextExternalToolScope } from './context-external-tool.scope'; describe('CourseExternalToolScope', () => { diff --git a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.scope.ts b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.scope.ts index 51540ed17ba..b2382c71c2c 100644 --- a/apps/server/src/shared/repo/contextexternaltool/context-external-tool.scope.ts +++ b/apps/server/src/shared/repo/contextexternaltool/context-external-tool.scope.ts @@ -1,7 +1,7 @@ import { Scope } from '@shared/repo'; import { EntityId } from '@shared/domain'; -import { ToolContextType } from '@src/modules/tool/common/enum'; -import { ContextExternalToolEntity } from '@src/modules/tool/context-external-tool/entity'; +import { ToolContextType } from '@modules/tool/common/enum'; +import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; export class ContextExternalToolScope extends Scope { byId(id: EntityId | undefined): ContextExternalToolScope { diff --git a/apps/server/src/shared/repo/externaltool/external-tool-sorting.mapper.spec.ts b/apps/server/src/shared/repo/externaltool/external-tool-sorting.mapper.spec.ts index d40c8654878..d3fe01c1b07 100644 --- a/apps/server/src/shared/repo/externaltool/external-tool-sorting.mapper.spec.ts +++ b/apps/server/src/shared/repo/externaltool/external-tool-sorting.mapper.spec.ts @@ -1,8 +1,8 @@ import { QueryOrderMap } from '@mikro-orm/core'; import { LtiTool, SortOrder, SortOrderMap } from '@shared/domain'; import { ExternalToolSortingMapper } from '@shared/repo'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { ExternalToolEntity } from '@src/modules/tool/external-tool/entity'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; describe('ExternalToolSortingMapper', () => { describe('mapDOSortOrderToQueryOrder', () => { diff --git a/apps/server/src/shared/repo/externaltool/external-tool-sorting.mapper.ts b/apps/server/src/shared/repo/externaltool/external-tool-sorting.mapper.ts index 751130ad784..893f5cbc923 100644 --- a/apps/server/src/shared/repo/externaltool/external-tool-sorting.mapper.ts +++ b/apps/server/src/shared/repo/externaltool/external-tool-sorting.mapper.ts @@ -1,7 +1,7 @@ import { QueryOrderMap } from '@mikro-orm/core'; import { SortOrderMap } from '@shared/domain'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { ExternalToolEntity } from '@src/modules/tool/external-tool/entity'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; export class ExternalToolSortingMapper { static mapDOSortOrderToQueryOrder(sort: SortOrderMap): QueryOrderMap { diff --git a/apps/server/src/shared/repo/externaltool/external-tool.repo.integration.spec.ts b/apps/server/src/shared/repo/externaltool/external-tool.repo.integration.spec.ts index 1ba9fa09aea..56cba80ec69 100644 --- a/apps/server/src/shared/repo/externaltool/external-tool.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/externaltool/external-tool.repo.integration.spec.ts @@ -6,8 +6,8 @@ import { MongoMemoryDatabaseModule } from '@shared/infra/database'; import { ExternalToolRepo, ExternalToolRepoMapper } from '@shared/repo'; import { cleanupCollections, externalToolEntityFactory } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { ExternalToolSearchQuery } from '@src/modules/tool'; -import { CustomParameter } from '@src/modules/tool/common/domain'; +import { ExternalToolSearchQuery } from '@modules/tool'; +import { CustomParameter } from '@modules/tool/common/domain'; import { CustomParameterLocation, CustomParameterScope, @@ -15,14 +15,9 @@ import { LtiMessageType, LtiPrivacyPermission, ToolConfigType, -} from '@src/modules/tool/common/enum'; -import { - BasicToolConfig, - ExternalTool, - Lti11ToolConfig, - Oauth2ToolConfig, -} from '@src/modules/tool/external-tool/domain'; -import { ExternalToolEntity } from '@src/modules/tool/external-tool/entity'; +} from '@modules/tool/common/enum'; +import { BasicToolConfig, ExternalTool, Lti11ToolConfig, Oauth2ToolConfig } from '@modules/tool/external-tool/domain'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; describe('ExternalToolRepo', () => { let module: TestingModule; diff --git a/apps/server/src/shared/repo/externaltool/external-tool.repo.mapper.ts b/apps/server/src/shared/repo/externaltool/external-tool.repo.mapper.ts index bcd319de14d..ab35c7b5fda 100644 --- a/apps/server/src/shared/repo/externaltool/external-tool.repo.mapper.ts +++ b/apps/server/src/shared/repo/externaltool/external-tool.repo.mapper.ts @@ -1,13 +1,8 @@ import { UnprocessableEntityException } from '@nestjs/common'; -import { CustomParameter, CustomParameterEntry } from '@src/modules/tool/common/domain'; -import { CustomParameterEntryEntity } from '@src/modules/tool/common/entity'; -import { ToolConfigType } from '@src/modules/tool/common/enum'; -import { - BasicToolConfig, - ExternalTool, - Lti11ToolConfig, - Oauth2ToolConfig, -} from '@src/modules/tool/external-tool/domain'; +import { CustomParameter, CustomParameterEntry } from '@modules/tool/common/domain'; +import { CustomParameterEntryEntity } from '@modules/tool/common/entity'; +import { ToolConfigType } from '@modules/tool/common/enum'; +import { BasicToolConfig, ExternalTool, Lti11ToolConfig, Oauth2ToolConfig } from '@modules/tool/external-tool/domain'; import { BasicToolConfigEntity, CustomParameterEntity, @@ -15,7 +10,7 @@ import { IExternalToolProperties, Lti11ToolConfigEntity, Oauth2ToolConfigEntity, -} from '@src/modules/tool/external-tool/entity'; +} from '@modules/tool/external-tool/entity'; // TODO: maybe rename because of usage in external tool repo and school external tool repo export class ExternalToolRepoMapper { diff --git a/apps/server/src/shared/repo/externaltool/external-tool.repo.ts b/apps/server/src/shared/repo/externaltool/external-tool.repo.ts index 05f3a2d47d1..4ea69a54855 100644 --- a/apps/server/src/shared/repo/externaltool/external-tool.repo.ts +++ b/apps/server/src/shared/repo/externaltool/external-tool.repo.ts @@ -4,10 +4,10 @@ import { Injectable } from '@nestjs/common/decorators/core/injectable.decorator' import { IFindOptions, IPagination, Page, SortOrder } from '@shared/domain'; import { BaseDORepo, ExternalToolRepoMapper, ExternalToolSortingMapper, Scope } from '@shared/repo'; import { LegacyLogger } from '@src/core/logger'; -import { ToolConfigType } from '@src/modules/tool/common/enum'; -import { ExternalToolSearchQuery } from '@src/modules/tool/common/interface'; -import { ExternalTool } from '@src/modules/tool/external-tool/domain'; -import { ExternalToolEntity, IExternalToolProperties } from '@src/modules/tool/external-tool/entity'; +import { ToolConfigType } from '@modules/tool/common/enum'; +import { ExternalToolSearchQuery } from '@modules/tool/common/interface'; +import { ExternalTool } from '@modules/tool/external-tool/domain'; +import { ExternalToolEntity, IExternalToolProperties } from '@modules/tool/external-tool/entity'; import { ExternalToolScope } from './external-tool.scope'; @Injectable() diff --git a/apps/server/src/shared/repo/externaltool/external-tool.scope.ts b/apps/server/src/shared/repo/externaltool/external-tool.scope.ts index 38e7fbf7754..5065c84413c 100644 --- a/apps/server/src/shared/repo/externaltool/external-tool.scope.ts +++ b/apps/server/src/shared/repo/externaltool/external-tool.scope.ts @@ -1,5 +1,5 @@ import { Scope } from '@shared/repo/scope'; -import { ExternalToolEntity } from '@src/modules/tool/external-tool/entity'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; export class ExternalToolScope extends Scope { byName(name: string | undefined): this { diff --git a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.integration.spec.ts b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.integration.spec.ts index 98fceceee5f..a2844e8e426 100644 --- a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.integration.spec.ts @@ -11,11 +11,11 @@ import { } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; import { createMock } from '@golevelup/ts-jest'; -import { SchoolExternalToolQuery } from '@src/modules/tool/school-external-tool/uc/dto/school-external-tool.types'; -import { ExternalToolEntity } from '@src/modules/tool/external-tool/entity'; -import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; -import { CustomParameterEntry } from '@src/modules/tool/common/domain'; -import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; +import { SchoolExternalToolQuery } from '@modules/tool/school-external-tool/uc/dto/school-external-tool.types'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { CustomParameterEntry } from '@modules/tool/common/domain'; +import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; import { SchoolExternalToolRepo } from './school-external-tool.repo'; describe('SchoolExternalToolRepo', () => { diff --git a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.ts b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.ts index bc9ab0cc868..64f55c715e8 100644 --- a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.ts +++ b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.repo.ts @@ -4,10 +4,10 @@ import { Injectable } from '@nestjs/common/decorators/core/injectable.decorator' import { SchoolEntity } from '@shared/domain'; import { BaseDORepo } from '@shared/repo/base.do.repo'; import { LegacyLogger } from '@src/core/logger'; -import { SchoolExternalToolQuery } from '@src/modules/tool/school-external-tool/uc/dto/school-external-tool.types'; -import { ISchoolExternalToolProperties, SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; -import { SchoolExternalTool } from '@src/modules/tool/school-external-tool/domain'; -import { ExternalToolEntity } from '@src/modules/tool/external-tool/entity'; +import { SchoolExternalToolQuery } from '@modules/tool/school-external-tool/uc/dto/school-external-tool.types'; +import { ISchoolExternalToolProperties, SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; +import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; +import { ExternalToolEntity } from '@modules/tool/external-tool/entity'; import { SchoolExternalToolScope } from './school-external-tool.scope'; import { ExternalToolRepoMapper } from '../externaltool'; diff --git a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.scope.ts b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.scope.ts index 89ca466b72a..eed938cd7f3 100644 --- a/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.scope.ts +++ b/apps/server/src/shared/repo/schoolexternaltool/school-external-tool.scope.ts @@ -1,6 +1,6 @@ import { Scope } from '@shared/repo/scope'; import { EntityId } from '@shared/domain'; -import { SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; +import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; export class SchoolExternalToolScope extends Scope { bySchoolId(schoolId: EntityId | undefined): this { diff --git a/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts b/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts index 0d273283ec2..ddcfb55b520 100644 --- a/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts +++ b/apps/server/src/shared/repo/user/user-do.repo.integration.spec.ts @@ -27,7 +27,7 @@ import { userFactory, } from '@shared/testing'; import { LegacyLogger } from '@src/core/logger'; -import { UserQuery } from '@src/modules/user/service/user-query.type'; +import { UserQuery } from '@modules/user/service/user-query.type'; describe('UserRepo', () => { let module: TestingModule; diff --git a/apps/server/src/shared/repo/user/user-do.repo.ts b/apps/server/src/shared/repo/user/user-do.repo.ts index 1856befc18b..e9eae128a1f 100644 --- a/apps/server/src/shared/repo/user/user-do.repo.ts +++ b/apps/server/src/shared/repo/user/user-do.repo.ts @@ -17,7 +17,7 @@ import { RoleReference } from '@shared/domain/domainobject'; import { Page } from '@shared/domain/domainobject/page'; import { UserDO } from '@shared/domain/domainobject/user.do'; import { BaseDORepo, Scope } from '@shared/repo'; -import { UserQuery } from '@src/modules/user/service/user-query.type'; +import { UserQuery } from '@modules/user/service/user-query.type'; import { UserScope } from './user.scope'; @Injectable() diff --git a/apps/server/src/shared/testing/factory/account-dto.factory.ts b/apps/server/src/shared/testing/factory/account-dto.factory.ts index c8c5b07e183..126d469e644 100644 --- a/apps/server/src/shared/testing/factory/account-dto.factory.ts +++ b/apps/server/src/shared/testing/factory/account-dto.factory.ts @@ -1,4 +1,4 @@ -import { AccountDto } from '@src/modules/account/services/dto'; +import { AccountDto } from '@modules/account/services/dto'; import { ObjectId } from 'bson'; import { defaultTestPasswordHash } from './account.factory'; import { BaseFactory } from './base.factory'; diff --git a/apps/server/src/shared/testing/factory/context-external-tool-entity.factory.ts b/apps/server/src/shared/testing/factory/context-external-tool-entity.factory.ts index 1299340095e..fb545c1e2e7 100644 --- a/apps/server/src/shared/testing/factory/context-external-tool-entity.factory.ts +++ b/apps/server/src/shared/testing/factory/context-external-tool-entity.factory.ts @@ -1,10 +1,10 @@ import { BaseFactory } from '@shared/testing/factory/base.factory'; -import { CustomParameterEntryEntity } from '@src/modules/tool/common/entity'; +import { CustomParameterEntryEntity } from '@modules/tool/common/entity'; import { ContextExternalToolEntity, ContextExternalToolType, IContextExternalToolProperties, -} from '@src/modules/tool/context-external-tool/entity'; +} from '@modules/tool/context-external-tool/entity'; import { courseFactory } from './course.factory'; import { schoolExternalToolEntityFactory } from './school-external-tool-entity.factory'; diff --git a/apps/server/src/shared/testing/factory/domainobject/groups/group.factory.ts b/apps/server/src/shared/testing/factory/domainobject/groups/group.factory.ts index a65d5141b61..ba0f8899249 100644 --- a/apps/server/src/shared/testing/factory/domainobject/groups/group.factory.ts +++ b/apps/server/src/shared/testing/factory/domainobject/groups/group.factory.ts @@ -1,5 +1,5 @@ import { ExternalSource } from '@shared/domain'; -import { Group, GroupProps, GroupTypes } from '@src/modules/group/domain'; +import { Group, GroupProps, GroupTypes } from '@modules/group/domain'; import { ObjectId } from 'bson'; import { DomainObjectFactory } from '../domain-object.factory'; diff --git a/apps/server/src/shared/testing/factory/domainobject/tool/context-external-tool.factory.ts b/apps/server/src/shared/testing/factory/domainobject/tool/context-external-tool.factory.ts index 015713dd997..8bb20364db1 100644 --- a/apps/server/src/shared/testing/factory/domainobject/tool/context-external-tool.factory.ts +++ b/apps/server/src/shared/testing/factory/domainobject/tool/context-external-tool.factory.ts @@ -1,7 +1,7 @@ import { ObjectId } from '@mikro-orm/mongodb'; -import { CustomParameterEntry } from '@src/modules/tool/common/domain'; -import { ToolContextType } from '@src/modules/tool/common/enum'; -import { ContextExternalTool, ContextExternalToolProps } from '@src/modules/tool/context-external-tool/domain'; +import { CustomParameterEntry } from '@modules/tool/common/domain'; +import { ToolContextType } from '@modules/tool/common/enum'; +import { ContextExternalTool, ContextExternalToolProps } from '@modules/tool/context-external-tool/domain'; import { DeepPartial } from 'fishery'; import { DoBaseFactory } from '../do-base.factory'; diff --git a/apps/server/src/shared/testing/factory/domainobject/tool/external-tool.factory.ts b/apps/server/src/shared/testing/factory/domainobject/tool/external-tool.factory.ts index cc33b3d63b9..7815d863a69 100644 --- a/apps/server/src/shared/testing/factory/domainobject/tool/external-tool.factory.ts +++ b/apps/server/src/shared/testing/factory/domainobject/tool/external-tool.factory.ts @@ -1,4 +1,4 @@ -import { CustomParameter } from '@src/modules/tool/common/domain'; +import { CustomParameter } from '@modules/tool/common/domain'; import { CustomParameterLocation, CustomParameterScope, @@ -7,14 +7,14 @@ import { LtiPrivacyPermission, TokenEndpointAuthMethod, ToolConfigType, -} from '@src/modules/tool/common/enum'; +} from '@modules/tool/common/enum'; import { BasicToolConfig, ExternalTool, ExternalToolProps, Lti11ToolConfig, Oauth2ToolConfig, -} from '@src/modules/tool/external-tool/domain'; +} from '@modules/tool/external-tool/domain'; import { DeepPartial } from 'fishery'; import { DoBaseFactory } from '../do-base.factory'; diff --git a/apps/server/src/shared/testing/factory/domainobject/tool/school-external-tool.factory.ts b/apps/server/src/shared/testing/factory/domainobject/tool/school-external-tool.factory.ts index 08c52e487cc..a10ae7797fe 100644 --- a/apps/server/src/shared/testing/factory/domainobject/tool/school-external-tool.factory.ts +++ b/apps/server/src/shared/testing/factory/domainobject/tool/school-external-tool.factory.ts @@ -1,5 +1,5 @@ -import { CustomParameterEntry, ToolConfigurationStatus } from '@src/modules/tool/common/domain'; -import { SchoolExternalTool, SchoolExternalToolProps } from '@src/modules/tool/school-external-tool/domain'; +import { CustomParameterEntry, ToolConfigurationStatus } from '@modules/tool/common/domain'; +import { SchoolExternalTool, SchoolExternalToolProps } from '@modules/tool/school-external-tool/domain'; import { DeepPartial } from 'fishery'; import { DoBaseFactory } from '../do-base.factory'; diff --git a/apps/server/src/shared/testing/factory/external-group-dto.factory.ts b/apps/server/src/shared/testing/factory/external-group-dto.factory.ts index 241b1fb45bd..562b68f8767 100644 --- a/apps/server/src/shared/testing/factory/external-group-dto.factory.ts +++ b/apps/server/src/shared/testing/factory/external-group-dto.factory.ts @@ -1,7 +1,7 @@ import { RoleName } from '@shared/domain'; import { ObjectId } from 'bson'; -import { ExternalGroupDto } from '@src/modules/provisioning/dto'; -import { GroupTypes } from '@src/modules/group'; +import { ExternalGroupDto } from '@modules/provisioning/dto'; +import { GroupTypes } from '@modules/group'; import { BaseFactory } from './base.factory'; export const externalGroupDtoFactory = BaseFactory.define( diff --git a/apps/server/src/shared/testing/factory/external-tool-entity.factory.ts b/apps/server/src/shared/testing/factory/external-tool-entity.factory.ts index ed47f93b28b..32c077e2529 100644 --- a/apps/server/src/shared/testing/factory/external-tool-entity.factory.ts +++ b/apps/server/src/shared/testing/factory/external-tool-entity.factory.ts @@ -5,7 +5,7 @@ import { LtiMessageType, LtiPrivacyPermission, ToolConfigType, -} from '@src/modules/tool/common/enum'; +} from '@modules/tool/common/enum'; import { BasicToolConfigEntity, CustomParameterEntity, @@ -13,7 +13,7 @@ import { IExternalToolProperties, Lti11ToolConfigEntity, Oauth2ToolConfigEntity, -} from '@src/modules/tool/external-tool/entity'; +} from '@modules/tool/external-tool/entity'; import { DeepPartial } from 'fishery'; import { BaseFactory } from './base.factory'; diff --git a/apps/server/src/shared/testing/factory/external-tool-pseudonym.factory.ts b/apps/server/src/shared/testing/factory/external-tool-pseudonym.factory.ts index 3e263dc9ae5..b3ee2595412 100644 --- a/apps/server/src/shared/testing/factory/external-tool-pseudonym.factory.ts +++ b/apps/server/src/shared/testing/factory/external-tool-pseudonym.factory.ts @@ -1,6 +1,6 @@ import { BaseFactory } from '@shared/testing/factory/base.factory'; import { ObjectId } from '@mikro-orm/mongodb'; -import { ExternalToolPseudonymEntity, IExternalToolPseudonymEntityProps } from '@src/modules/pseudonym/entity'; +import { ExternalToolPseudonymEntity, IExternalToolPseudonymEntityProps } from '@modules/pseudonym/entity'; export const externalToolPseudonymEntityFactory = BaseFactory.define< ExternalToolPseudonymEntity, diff --git a/apps/server/src/shared/testing/factory/filerecord.factory.ts b/apps/server/src/shared/testing/factory/filerecord.factory.ts index 4a0c73966fd..36811ed9752 100644 --- a/apps/server/src/shared/testing/factory/filerecord.factory.ts +++ b/apps/server/src/shared/testing/factory/filerecord.factory.ts @@ -1,5 +1,5 @@ import { FileRecordParentType } from '@shared/infra/rabbitmq'; -import { FileRecord, FileRecordSecurityCheck, IFileRecordProperties } from '@src/modules/files-storage/entity'; +import { FileRecord, FileRecordSecurityCheck, IFileRecordProperties } from '@modules/files-storage/entity'; import { ObjectId } from 'bson'; import { DeepPartial } from 'fishery'; import { BaseFactory } from './base.factory'; diff --git a/apps/server/src/shared/testing/factory/group-entity.factory.ts b/apps/server/src/shared/testing/factory/group-entity.factory.ts index 591b7c37d41..482aca971cf 100644 --- a/apps/server/src/shared/testing/factory/group-entity.factory.ts +++ b/apps/server/src/shared/testing/factory/group-entity.factory.ts @@ -1,5 +1,5 @@ import { ExternalSourceEntity, RoleName } from '@shared/domain'; -import { GroupEntity, GroupEntityProps, GroupEntityTypes, GroupValidPeriodEntity } from '@src/modules/group/entity'; +import { GroupEntity, GroupEntityProps, GroupEntityTypes, GroupValidPeriodEntity } from '@modules/group/entity'; import { BaseFactory } from './base.factory'; import { roleFactory } from './role.factory'; import { schoolFactory } from './school.factory'; diff --git a/apps/server/src/shared/testing/factory/pseudonym.factory.ts b/apps/server/src/shared/testing/factory/pseudonym.factory.ts index 6f2f60e371c..96be2d1c3c0 100644 --- a/apps/server/src/shared/testing/factory/pseudonym.factory.ts +++ b/apps/server/src/shared/testing/factory/pseudonym.factory.ts @@ -1,6 +1,6 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { BaseFactory } from '@shared/testing/factory/base.factory'; -import { PseudonymEntity, PseudonymEntityProps } from '@src/modules/pseudonym/entity'; +import { PseudonymEntity, PseudonymEntityProps } from '@modules/pseudonym/entity'; export const pseudonymEntityFactory = BaseFactory.define( PseudonymEntity, diff --git a/apps/server/src/shared/testing/factory/role-dto.factory.ts b/apps/server/src/shared/testing/factory/role-dto.factory.ts index 03d14965d41..2158580753d 100644 --- a/apps/server/src/shared/testing/factory/role-dto.factory.ts +++ b/apps/server/src/shared/testing/factory/role-dto.factory.ts @@ -1,6 +1,6 @@ import { RoleName } from '@shared/domain'; import { ObjectId } from 'bson'; -import { RoleDto } from '@src/modules/role/service/dto/role.dto'; +import { RoleDto } from '@modules/role/service/dto/role.dto'; import { BaseFactory } from './base.factory'; import { userPermissions } from '../user-role-permissions'; diff --git a/apps/server/src/shared/testing/factory/school-external-tool-entity.factory.ts b/apps/server/src/shared/testing/factory/school-external-tool-entity.factory.ts index c991d5abc75..456356e6e23 100644 --- a/apps/server/src/shared/testing/factory/school-external-tool-entity.factory.ts +++ b/apps/server/src/shared/testing/factory/school-external-tool-entity.factory.ts @@ -1,5 +1,5 @@ import { BaseFactory } from '@shared/testing/factory/base.factory'; -import { ISchoolExternalToolProperties, SchoolExternalToolEntity } from '@src/modules/tool/school-external-tool/entity'; +import { ISchoolExternalToolProperties, SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { externalToolEntityFactory } from './external-tool-entity.factory'; import { schoolFactory } from './school.factory'; diff --git a/apps/server/src/shared/testing/factory/share-token.do.factory.ts b/apps/server/src/shared/testing/factory/share-token.do.factory.ts index 02262f4d175..2c23ca904be 100644 --- a/apps/server/src/shared/testing/factory/share-token.do.factory.ts +++ b/apps/server/src/shared/testing/factory/share-token.do.factory.ts @@ -1,6 +1,6 @@ /* istanbul ignore file */ import { EntityId } from '@shared/domain'; -import { ShareTokenDO, ShareTokenParentType } from '@src/modules/sharing/domainobject/share-token.do'; +import { ShareTokenDO, ShareTokenParentType } from '@modules/sharing/domainobject/share-token.do'; import { ObjectId } from 'bson'; import { Factory } from 'fishery'; diff --git a/apps/server/src/shared/testing/map-user-to-current-user.ts b/apps/server/src/shared/testing/map-user-to-current-user.ts index 1d77d8d2bb3..d835c822066 100644 --- a/apps/server/src/shared/testing/map-user-to-current-user.ts +++ b/apps/server/src/shared/testing/map-user-to-current-user.ts @@ -1,6 +1,6 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { Account, EntityId, User } from '@shared/domain'; -import { ICurrentUser } from '@src/modules/authentication'; +import { ICurrentUser } from '@modules/authentication'; export const mapUserToCurrentUser = ( user: User, diff --git a/jest.config.ts b/jest.config.ts index dde0a796711..032f4828dde 100644 --- a/jest.config.ts +++ b/jest.config.ts @@ -25,6 +25,7 @@ let config: Config.InitialOptions = { // add ts-config path's here as regex '^@shared/(.*)$': '/apps/server/src/shared/$1', '^@src/(.*)$': '/apps/server/src/$1', + '^@modules/(.*)$': '/apps/server/src/modules/$1', }, maxWorkers: 2, // limited for not taking all workers within of a single github action }; diff --git a/tsconfig.json b/tsconfig.json index 912ea1b5a78..9bb7c6f72f0 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -19,7 +19,8 @@ "incremental": true, "paths": { "@shared/*": ["apps/server/src/shared/*"], - "@src/*": ["apps/server/src/*"] + "@src/*": ["apps/server/src/*"], + "@modules/*": ["apps/server/src/modules/*"], }, } } From e70a7b7a804f44640a60542859600ce9a262d285 Mon Sep 17 00:00:00 2001 From: Igor Richter <93926487+IgorCapCoder@users.noreply.github.com> Date: Fri, 20 Oct 2023 14:55:37 +0200 Subject: [PATCH 08/14] N21 1264 new class page extension (#4474) * add boolean to class --- .../controller/api-test/group.api.spec.ts | 1 + .../dto/response/class-info.response.ts | 4 ++ .../mapper/group-response.mapper.ts | 1 + .../modules/group/uc/dto/class-info.dto.ts | 3 ++ .../src/modules/group/uc/group.uc.spec.ts | 2 + .../group/uc/mapper/group-uc.mapper.ts | 2 + backup/setup/groups.json | 37 +++++++++++++++++++ 7 files changed, 50 insertions(+) create mode 100644 backup/setup/groups.json diff --git a/apps/server/src/modules/group/controller/api-test/group.api.spec.ts b/apps/server/src/modules/group/controller/api-test/group.api.spec.ts index 471d8b348af..3ace7386565 100644 --- a/apps/server/src/modules/group/controller/api-test/group.api.spec.ts +++ b/apps/server/src/modules/group/controller/api-test/group.api.spec.ts @@ -126,6 +126,7 @@ describe('Group (API)', () => { name: clazz.gradeLevel ? `${clazz.gradeLevel}${clazz.name}` : clazz.name, teachers: [teacherUser.lastName], schoolYear: schoolYear.name, + isUpgradable: false, }, ], skip: 0, diff --git a/apps/server/src/modules/group/controller/dto/response/class-info.response.ts b/apps/server/src/modules/group/controller/dto/response/class-info.response.ts index 62c52501b95..a62b8134158 100644 --- a/apps/server/src/modules/group/controller/dto/response/class-info.response.ts +++ b/apps/server/src/modules/group/controller/dto/response/class-info.response.ts @@ -20,6 +20,9 @@ export class ClassInfoResponse { @ApiPropertyOptional() schoolYear?: string; + @ApiPropertyOptional() + isUpgradable?: boolean; + constructor(props: ClassInfoResponse) { this.id = props.id; this.type = props.type; @@ -27,5 +30,6 @@ export class ClassInfoResponse { this.externalSourceName = props.externalSourceName; this.teachers = props.teachers; this.schoolYear = props.schoolYear; + this.isUpgradable = props.isUpgradable; } } diff --git a/apps/server/src/modules/group/controller/mapper/group-response.mapper.ts b/apps/server/src/modules/group/controller/mapper/group-response.mapper.ts index 958aeee2c6b..5f337eb204c 100644 --- a/apps/server/src/modules/group/controller/mapper/group-response.mapper.ts +++ b/apps/server/src/modules/group/controller/mapper/group-response.mapper.ts @@ -30,6 +30,7 @@ export class GroupResponseMapper { externalSourceName: classInfo.externalSourceName, teachers: classInfo.teachers, schoolYear: classInfo.schoolYear, + isUpgradable: classInfo.isUpgradable, }); return mapped; diff --git a/apps/server/src/modules/group/uc/dto/class-info.dto.ts b/apps/server/src/modules/group/uc/dto/class-info.dto.ts index d17c0169c93..8c564d9e106 100644 --- a/apps/server/src/modules/group/uc/dto/class-info.dto.ts +++ b/apps/server/src/modules/group/uc/dto/class-info.dto.ts @@ -13,6 +13,8 @@ export class ClassInfoDto { schoolYear?: string; + isUpgradable?: boolean; + constructor(props: ClassInfoDto) { this.id = props.id; this.type = props.type; @@ -20,5 +22,6 @@ export class ClassInfoDto { this.externalSourceName = props.externalSourceName; this.teachers = props.teachers; this.schoolYear = props.schoolYear; + this.isUpgradable = props.isUpgradable; } } diff --git a/apps/server/src/modules/group/uc/group.uc.spec.ts b/apps/server/src/modules/group/uc/group.uc.spec.ts index 3de4d262679..8e882c052d3 100644 --- a/apps/server/src/modules/group/uc/group.uc.spec.ts +++ b/apps/server/src/modules/group/uc/group.uc.spec.ts @@ -248,6 +248,7 @@ describe('GroupUc', () => { externalSourceName: clazz.source, teachers: [teacherUser.lastName], schoolYear: schoolYear.name, + isUpgradable: false, }, { id: group.id, @@ -290,6 +291,7 @@ describe('GroupUc', () => { externalSourceName: clazz.source, teachers: [teacherUser.lastName], schoolYear: schoolYear.name, + isUpgradable: false, }, { id: groupWithSystem.id, diff --git a/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts b/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts index d5a415498db..ebeba60117e 100644 --- a/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts +++ b/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts @@ -26,6 +26,7 @@ export class GroupUcMapper { public static mapClassToClassInfoDto(clazz: Class, teachers: UserDO[], schoolYear?: SchoolYearEntity): ClassInfoDto { const name = clazz.gradeLevel ? `${clazz.gradeLevel}${clazz.name}` : clazz.name; + const isUpgradable = clazz.gradeLevel !== 13 && !clazz.successor; const mapped: ClassInfoDto = new ClassInfoDto({ id: clazz.id, @@ -34,6 +35,7 @@ export class GroupUcMapper { externalSourceName: clazz.source, teachers: teachers.map((user: UserDO) => user.lastName), schoolYear: schoolYear?.name, + isUpgradable, }); return mapped; diff --git a/backup/setup/groups.json b/backup/setup/groups.json new file mode 100644 index 00000000000..a682580f824 --- /dev/null +++ b/backup/setup/groups.json @@ -0,0 +1,37 @@ +[ + { + "createdAt": { + "$date": "2023-10-17T12:15:26.458Z" + }, + "updatedAt": { + "$date": "2023-10-17T12:15:26.461Z" + }, + "name": "Cypress-Test-Group", + "type": "class", + "externalSource_externalId": "fd84869b-56e8-41d2-a3dd-6c7239068ed5", + "externalSource_system": { + "$oid": "0000d186816abba584714c93" + }, + "users": [ + { + "user": { + "$oid": "5fa2c71bb229544f2c6966d9" + }, + "role": { + "$oid": "0000d186816abba584714c98" + } + }, + { + "user": { + "$oid": "5fa2cccab229544f2c696917" + }, + "role": { + "$oid": "0000d186816abba584714c99" + } + } + ], + "organization": { + "$oid": "5fa2c5ccb229544f2c69666c" + } + } +] From d570dd6a0cabfb4efcbf18f13df20d3dbb8fe168 Mon Sep 17 00:00:00 2001 From: agnisa-cap Date: Fri, 20 Oct 2023 16:08:46 +0200 Subject: [PATCH 09/14] N21-1207 show group members (#4479) * adds get /groups/:id * adds script for adding permission GROUP_LIST and GROUP_VIEW --- .../authorization/authorization.module.ts | 2 + .../domain/rules/group.rule.spec.ts | 210 ++++++++++++++++++ .../authorization/domain/rules/group.rule.ts | 24 ++ .../authorization/domain/rules/index.ts | 1 + .../domain/service/rule-manager.spec.ts | 8 + .../domain/service/rule-manager.ts | 5 +- .../controller/api-test/group.api.spec.ts | 118 +++++++++- .../controller/dto/request/group-id-params.ts | 8 + .../group/controller/dto/request/index.ts | 1 + .../dto/response/external-source.response.ts | 14 ++ .../dto/response/group-type.response.ts | 3 + .../dto/response/group-user.response.ts | 23 ++ .../controller/dto/response/group.response.ts | 33 +++ .../group/controller/dto/response/index.ts | 4 + .../group/controller/group.controller.ts | 22 +- .../mapper/group-response.mapper.ts | 42 +++- apps/server/src/modules/group/domain/group.ts | 4 + apps/server/src/modules/group/uc/dto/index.ts | 1 + .../group/uc/dto/resolved-group.dto.ts | 26 +++ .../src/modules/group/uc/group.uc.spec.ts | 149 ++++++++++++- apps/server/src/modules/group/uc/group.uc.ts | 28 ++- .../group/uc/mapper/group-uc.mapper.ts | 14 +- .../domain/interface/permission.enum.ts | 2 + apps/server/src/shared/domain/rules/index.ts | 0 .../shared/testing/user-role-permissions.ts | 2 + backup/setup/migrations.json | 11 + backup/setup/roles.json | 20 +- ...4886-add-group-view-and-list-permission.js | 100 +++++++++ 28 files changed, 853 insertions(+), 22 deletions(-) create mode 100644 apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts create mode 100644 apps/server/src/modules/authorization/domain/rules/group.rule.ts create mode 100644 apps/server/src/modules/group/controller/dto/request/group-id-params.ts create mode 100644 apps/server/src/modules/group/controller/dto/response/external-source.response.ts create mode 100644 apps/server/src/modules/group/controller/dto/response/group-type.response.ts create mode 100644 apps/server/src/modules/group/controller/dto/response/group-user.response.ts create mode 100644 apps/server/src/modules/group/controller/dto/response/group.response.ts create mode 100644 apps/server/src/modules/group/uc/dto/resolved-group.dto.ts create mode 100644 apps/server/src/shared/domain/rules/index.ts create mode 100644 migrations/1697553524886-add-group-view-and-list-permission.js diff --git a/apps/server/src/modules/authorization/authorization.module.ts b/apps/server/src/modules/authorization/authorization.module.ts index 37ca0a2b229..c555f13dc7b 100644 --- a/apps/server/src/modules/authorization/authorization.module.ts +++ b/apps/server/src/modules/authorization/authorization.module.ts @@ -15,6 +15,7 @@ import { UserRule, UserLoginMigrationRule, LegacySchoolRule, + GroupRule, } from './domain/rules'; import { AuthorizationHelper, AuthorizationService, RuleManager } from './domain'; import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; @@ -33,6 +34,7 @@ import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; ContextExternalToolRule, CourseGroupRule, CourseRule, + GroupRule, LessonRule, SchoolExternalToolRule, SubmissionRule, diff --git a/apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts new file mode 100644 index 00000000000..bb2bc2e48b3 --- /dev/null +++ b/apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts @@ -0,0 +1,210 @@ +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { Test, TestingModule } from '@nestjs/testing'; +import { Permission, Role, SchoolEntity, User } from '@shared/domain'; +import { groupFactory, roleFactory, schoolFactory, setupEntities, userFactory } from '@shared/testing'; +import { Action, AuthorizationContext, AuthorizationHelper } from '@src/modules/authorization'; +import { Group } from '@src/modules/group'; +import { ObjectId } from 'bson'; +import { GroupRule } from './group.rule'; + +describe('GroupRule', () => { + let module: TestingModule; + let rule: GroupRule; + + let authorizationHelper: DeepMocked; + + beforeAll(async () => { + await setupEntities(); + + module = await Test.createTestingModule({ + providers: [ + GroupRule, + { + provide: AuthorizationHelper, + useValue: createMock(), + }, + ], + }).compile(); + + rule = module.get(GroupRule); + authorizationHelper = module.get(AuthorizationHelper); + }); + + afterAll(async () => { + await module.close(); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('isApplicable', () => { + describe('when the entity is applicable', () => { + const setup = () => { + const role: Role = roleFactory.buildWithId(); + const user: User = userFactory.buildWithId({ roles: [role] }); + const group: Group = groupFactory.build({ + users: [ + { + userId: user.id, + roleId: user.roles[0].id, + }, + ], + }); + + return { + user, + group, + }; + }; + + it('should return true', () => { + const { user, group } = setup(); + + const result = rule.isApplicable(user, group); + + expect(result).toEqual(true); + }); + }); + + describe('when the entity is not applicable', () => { + const setup = () => { + const role: Role = roleFactory.buildWithId(); + const userNotInGroup: User = userFactory.buildWithId({ roles: [role] }); + + return { + userNotInGroup, + }; + }; + + it('should return false', () => { + const { userNotInGroup } = setup(); + + const result = rule.isApplicable(userNotInGroup, {} as unknown as Group); + + expect(result).toEqual(false); + }); + }); + }); + + describe('hasPermission', () => { + describe('when the user has all required permissions and is at the same school then the group', () => { + const setup = () => { + const role: Role = roleFactory.buildWithId(); + const school: SchoolEntity = schoolFactory.buildWithId(); + const user: User = userFactory.buildWithId({ school, roles: [role] }); + const group: Group = groupFactory.build({ + users: [ + { + userId: user.id, + roleId: user.roles[0].id, + }, + ], + organizationId: user.school.id, + }); + const context: AuthorizationContext = { + action: Action.write, + requiredPermissions: [Permission.GROUP_VIEW], + }; + + authorizationHelper.hasAllPermissions.mockReturnValue(true); + + return { + user, + group, + context, + }; + }; + + it('should check all permissions', () => { + const { user, group, context } = setup(); + + rule.hasPermission(user, group, context); + + expect(authorizationHelper.hasAllPermissions).toHaveBeenCalledWith(user, context.requiredPermissions); + }); + + it('should return true', () => { + const { user, group, context } = setup(); + + const result = rule.hasPermission(user, group, context); + + expect(result).toEqual(true); + }); + }); + + describe('when the user has not the required permission', () => { + const setup = () => { + const role: Role = roleFactory.buildWithId({ permissions: [] }); + const school: SchoolEntity = schoolFactory.buildWithId(); + const user: User = userFactory.buildWithId({ school, roles: [role] }); + const group: Group = groupFactory.build({ + users: [ + { + userId: user.id, + roleId: user.roles[0].id, + }, + ], + organizationId: user.school.id, + }); + const context: AuthorizationContext = { + action: Action.write, + requiredPermissions: [Permission.GROUP_VIEW], + }; + + authorizationHelper.hasAllPermissions.mockReturnValue(false); + + return { + user, + group, + context, + }; + }; + + it('should return false', () => { + const { user, group, context } = setup(); + + const result = rule.hasPermission(user, group, context); + + expect(result).toEqual(false); + }); + }); + + describe('when the user is at another school then the group', () => { + const setup = () => { + const role: Role = roleFactory.buildWithId({ permissions: [] }); + const school: SchoolEntity = schoolFactory.buildWithId(); + const user: User = userFactory.buildWithId({ school, roles: [role] }); + const group: Group = groupFactory.build({ + users: [ + { + userId: user.id, + roleId: user.roles[0].id, + }, + ], + organizationId: new ObjectId().toHexString(), + }); + const context: AuthorizationContext = { + action: Action.write, + requiredPermissions: [Permission.GROUP_VIEW], + }; + + authorizationHelper.hasAllPermissions.mockReturnValue(true); + + return { + user, + group, + context, + }; + }; + + it('should return false', () => { + const { user, group, context } = setup(); + + const result = rule.hasPermission(user, group, context); + + expect(result).toEqual(false); + }); + }); + }); +}); diff --git a/apps/server/src/modules/authorization/domain/rules/group.rule.ts b/apps/server/src/modules/authorization/domain/rules/group.rule.ts new file mode 100644 index 00000000000..e25e90230c8 --- /dev/null +++ b/apps/server/src/modules/authorization/domain/rules/group.rule.ts @@ -0,0 +1,24 @@ +import { Injectable } from '@nestjs/common'; +import { User } from '@shared/domain'; +import { Group } from '@src/modules/group'; +import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper } from '../service/authorization.helper'; + +@Injectable() +export class GroupRule implements Rule { + constructor(private readonly authorizationHelper: AuthorizationHelper) {} + + public isApplicable(user: User, domainObject: Group): boolean { + const isMatched: boolean = domainObject instanceof Group; + + return isMatched; + } + + public hasPermission(user: User, domainObject: Group, context: AuthorizationContext): boolean { + const hasPermission: boolean = + this.authorizationHelper.hasAllPermissions(user, context.requiredPermissions) && + (domainObject.organizationId ? user.school.id === domainObject.organizationId : true); + + return hasPermission; + } +} diff --git a/apps/server/src/modules/authorization/domain/rules/index.ts b/apps/server/src/modules/authorization/domain/rules/index.ts index bd4ffe27a59..b78f43051d0 100644 --- a/apps/server/src/modules/authorization/domain/rules/index.ts +++ b/apps/server/src/modules/authorization/domain/rules/index.ts @@ -14,3 +14,4 @@ export * from './task.rule'; export * from './team.rule'; export * from './user-login-migration.rule'; export * from './user.rule'; +export * from './group.rule'; diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts index 78ef313ade1..5b62f850416 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts @@ -16,6 +16,7 @@ import { TeamRule, UserRule, UserLoginMigrationRule, + GroupRule, } from '../rules'; import { RuleManager } from './rule-manager'; @@ -33,6 +34,7 @@ describe('RuleManager', () => { let boardDoRule: DeepMocked; let contextExternalToolRule: DeepMocked; let userLoginMigrationRule: DeepMocked; + let groupRule: DeepMocked; beforeAll(async () => { await setupEntities(); @@ -42,6 +44,7 @@ describe('RuleManager', () => { RuleManager, { provide: CourseRule, useValue: createMock() }, { provide: CourseGroupRule, useValue: createMock() }, + { provide: GroupRule, useValue: createMock() }, { provide: LessonRule, useValue: createMock() }, { provide: LegacySchoolRule, useValue: createMock() }, { provide: UserRule, useValue: createMock() }, @@ -68,6 +71,7 @@ describe('RuleManager', () => { boardDoRule = await module.get(BoardDoRule); contextExternalToolRule = await module.get(ContextExternalToolRule); userLoginMigrationRule = await module.get(UserLoginMigrationRule); + groupRule = await module.get(GroupRule); }); afterEach(() => { @@ -98,6 +102,7 @@ describe('RuleManager', () => { boardDoRule.isApplicable.mockReturnValueOnce(false); contextExternalToolRule.isApplicable.mockReturnValueOnce(false); userLoginMigrationRule.isApplicable.mockReturnValueOnce(false); + groupRule.isApplicable.mockReturnValueOnce(false); return { user, object, context }; }; @@ -119,6 +124,7 @@ describe('RuleManager', () => { expect(boardDoRule.isApplicable).toBeCalled(); expect(contextExternalToolRule.isApplicable).toBeCalled(); expect(userLoginMigrationRule.isApplicable).toBeCalled(); + expect(groupRule.isApplicable).toBeCalled(); }); it('should return CourseRule', () => { @@ -148,6 +154,7 @@ describe('RuleManager', () => { boardDoRule.isApplicable.mockReturnValueOnce(false); contextExternalToolRule.isApplicable.mockReturnValueOnce(false); userLoginMigrationRule.isApplicable.mockReturnValueOnce(false); + groupRule.isApplicable.mockReturnValueOnce(false); return { user, object, context }; }; @@ -177,6 +184,7 @@ describe('RuleManager', () => { boardDoRule.isApplicable.mockReturnValueOnce(false); contextExternalToolRule.isApplicable.mockReturnValueOnce(false); userLoginMigrationRule.isApplicable.mockReturnValueOnce(false); + groupRule.isApplicable.mockReturnValueOnce(false); return { user, object, context }; }; diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.ts index 77d09f284c2..6e6237d125f 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -15,6 +15,7 @@ import { TeamRule, UserLoginMigrationRule, UserRule, + GroupRule, } from '../rules'; @Injectable() @@ -33,7 +34,8 @@ export class RuleManager { private readonly schoolExternalToolRule: SchoolExternalToolRule, private readonly boardDoRule: BoardDoRule, private readonly contextExternalToolRule: ContextExternalToolRule, - private readonly userLoginMigrationRule: UserLoginMigrationRule + private readonly userLoginMigrationRule: UserLoginMigrationRule, + private readonly groupRule: GroupRule ) { this.rules = [ this.courseRule, @@ -48,6 +50,7 @@ export class RuleManager { this.boardDoRule, this.contextExternalToolRule, this.userLoginMigrationRule, + this.groupRule, ]; } diff --git a/apps/server/src/modules/group/controller/api-test/group.api.spec.ts b/apps/server/src/modules/group/controller/api-test/group.api.spec.ts index 3ace7386565..34a49c03a35 100644 --- a/apps/server/src/modules/group/controller/api-test/group.api.spec.ts +++ b/apps/server/src/modules/group/controller/api-test/group.api.spec.ts @@ -15,6 +15,7 @@ import { import { ClassEntity } from '@modules/class/entity'; import { classEntityFactory } from '@modules/class/entity/testing/factory/class.entity.factory'; import { ServerTestModule } from '@modules/server'; +import { ObjectId } from 'bson'; import { GroupEntity, GroupEntityTypes } from '../../entity'; import { ClassRootType } from '../../uc/dto/class-root-type'; import { ClassInfoSearchListResponse, ClassSortBy } from '../dto'; @@ -41,7 +42,7 @@ describe('Group (API)', () => { await app.close(); }); - describe('findClassesForSchool', () => { + describe('[GET] /groups/class', () => { describe('when an admin requests a list of classes', () => { const setup = async () => { const school: SchoolEntity = schoolFactory.buildWithId(); @@ -158,4 +159,119 @@ describe('Group (API)', () => { }); }); }); + + describe('[GET] /groups/:groupId', () => { + describe('when authorized user requests a group', () => { + describe('when group exists', () => { + const setup = async () => { + const school: SchoolEntity = schoolFactory.buildWithId(); + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher({ school }); + + const group: GroupEntity = groupEntityFactory.buildWithId({ + users: [ + { + user: teacherUser, + role: teacherUser.roles[0], + }, + ], + organization: school, + }); + + await em.persistAndFlush([teacherAccount, teacherUser, group]); + em.clear(); + + const loggedInClient = await testApiClient.login(teacherAccount); + + return { + loggedInClient, + group, + teacherUser, + }; + }; + + it('should return the group', async () => { + const { loggedInClient, group, teacherUser } = await setup(); + + const response = await loggedInClient.get(`${group.id}`); + + expect(response.status).toEqual(HttpStatus.OK); + expect(response.body).toEqual({ + id: group.id, + name: group.name, + type: group.type, + users: [ + { + id: teacherUser.id, + firstName: teacherUser.firstName, + lastName: teacherUser.lastName, + role: teacherUser.roles[0].name, + }, + ], + externalSource: { + externalId: group.externalSource?.externalId, + systemId: group.externalSource?.system.id, + }, + }); + }); + }); + + describe('when group does not exist', () => { + const setup = async () => { + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); + + await em.persistAndFlush([teacherAccount, teacherUser]); + em.clear(); + + const loggedInClient = await testApiClient.login(teacherAccount); + + return { + loggedInClient, + }; + }; + + it('should return not found', async () => { + const { loggedInClient } = await setup(); + + const response = await loggedInClient.get(`${new ObjectId().toHexString()}`); + + expect(response.status).toEqual(HttpStatus.NOT_FOUND); + expect(response.body).toEqual({ + code: HttpStatus.NOT_FOUND, + message: 'Not Found', + title: 'Not Found', + type: 'NOT_FOUND', + }); + }); + }); + }); + + describe('when unauthorized user requests a group', () => { + const setup = async () => { + const { studentAccount, studentUser } = UserAndAccountTestFactory.buildStudent(); + + const group: GroupEntity = groupEntityFactory.buildWithId(); + + await em.persistAndFlush([studentAccount, studentUser, group]); + em.clear(); + + return { + groupId: group.id, + }; + }; + + it('should return unauthorized', async () => { + const { groupId } = await setup(); + + const response = await testApiClient.get(`${groupId}`); + + expect(response.status).toEqual(HttpStatus.UNAUTHORIZED); + expect(response.body).toEqual({ + code: HttpStatus.UNAUTHORIZED, + message: 'Unauthorized', + title: 'Unauthorized', + type: 'UNAUTHORIZED', + }); + }); + }); + }); }); diff --git a/apps/server/src/modules/group/controller/dto/request/group-id-params.ts b/apps/server/src/modules/group/controller/dto/request/group-id-params.ts new file mode 100644 index 00000000000..9423966009f --- /dev/null +++ b/apps/server/src/modules/group/controller/dto/request/group-id-params.ts @@ -0,0 +1,8 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsMongoId } from 'class-validator'; + +export class GroupIdParams { + @IsMongoId() + @ApiProperty({ nullable: false, required: true }) + groupId!: string; +} diff --git a/apps/server/src/modules/group/controller/dto/request/index.ts b/apps/server/src/modules/group/controller/dto/request/index.ts index 2255e9aac09..17ecd658b7d 100644 --- a/apps/server/src/modules/group/controller/dto/request/index.ts +++ b/apps/server/src/modules/group/controller/dto/request/index.ts @@ -1 +1,2 @@ export * from './class-sort-params'; +export * from './group-id-params'; diff --git a/apps/server/src/modules/group/controller/dto/response/external-source.response.ts b/apps/server/src/modules/group/controller/dto/response/external-source.response.ts new file mode 100644 index 00000000000..f03327c8a8c --- /dev/null +++ b/apps/server/src/modules/group/controller/dto/response/external-source.response.ts @@ -0,0 +1,14 @@ +import { ApiProperty } from '@nestjs/swagger'; + +export class ExternalSourceResponse { + @ApiProperty() + externalId: string; + + @ApiProperty() + systemId: string; + + constructor(props: ExternalSourceResponse) { + this.externalId = props.externalId; + this.systemId = props.systemId; + } +} diff --git a/apps/server/src/modules/group/controller/dto/response/group-type.response.ts b/apps/server/src/modules/group/controller/dto/response/group-type.response.ts new file mode 100644 index 00000000000..54c32148ca1 --- /dev/null +++ b/apps/server/src/modules/group/controller/dto/response/group-type.response.ts @@ -0,0 +1,3 @@ +export enum GroupTypeResponse { + CLASS = 'class', +} diff --git a/apps/server/src/modules/group/controller/dto/response/group-user.response.ts b/apps/server/src/modules/group/controller/dto/response/group-user.response.ts new file mode 100644 index 00000000000..000958c96cf --- /dev/null +++ b/apps/server/src/modules/group/controller/dto/response/group-user.response.ts @@ -0,0 +1,23 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { RoleName } from '@shared/domain'; + +export class GroupUserResponse { + @ApiProperty() + id: string; + + @ApiProperty() + firstName: string; + + @ApiProperty() + lastName: string; + + @ApiProperty({ enum: RoleName }) + role: RoleName; + + constructor(user: GroupUserResponse) { + this.id = user.id; + this.firstName = user.firstName; + this.lastName = user.lastName; + this.role = user.role; + } +} diff --git a/apps/server/src/modules/group/controller/dto/response/group.response.ts b/apps/server/src/modules/group/controller/dto/response/group.response.ts new file mode 100644 index 00000000000..1abb28a8a30 --- /dev/null +++ b/apps/server/src/modules/group/controller/dto/response/group.response.ts @@ -0,0 +1,33 @@ +import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; +import { ExternalSourceResponse } from './external-source.response'; +import { GroupTypeResponse } from './group-type.response'; +import { GroupUserResponse } from './group-user.response'; + +export class GroupResponse { + @ApiProperty() + id: string; + + @ApiProperty() + name: string; + + @ApiProperty({ enum: GroupTypeResponse }) + type: GroupTypeResponse; + + @ApiProperty({ type: [GroupUserResponse] }) + users: GroupUserResponse[]; + + @ApiPropertyOptional() + externalSource?: ExternalSourceResponse; + + @ApiPropertyOptional() + organizationId?: string; + + constructor(group: GroupResponse) { + this.id = group.id; + this.name = group.name; + this.type = group.type; + this.users = group.users; + this.externalSource = group.externalSource; + this.organizationId = group.organizationId; + } +} diff --git a/apps/server/src/modules/group/controller/dto/response/index.ts b/apps/server/src/modules/group/controller/dto/response/index.ts index 1ec8a62f0d4..9593930f21e 100644 --- a/apps/server/src/modules/group/controller/dto/response/index.ts +++ b/apps/server/src/modules/group/controller/dto/response/index.ts @@ -1,2 +1,6 @@ export * from './class-info.response'; export * from './class-info-search-list.response'; +export * from './external-source.response'; +export * from './group.response'; +export * from './group-type.response'; +export * from './group-user.response'; diff --git a/apps/server/src/modules/group/controller/group.controller.ts b/apps/server/src/modules/group/controller/group.controller.ts index 553d07b0d5c..9e5f4b3b51a 100644 --- a/apps/server/src/modules/group/controller/group.controller.ts +++ b/apps/server/src/modules/group/controller/group.controller.ts @@ -1,12 +1,12 @@ -import { Controller, Get, HttpStatus, Query } from '@nestjs/common'; +import { Controller, Get, HttpStatus, Param, Query } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; import { PaginationParams } from '@shared/controller'; import { Page } from '@shared/domain'; import { ErrorResponse } from '@src/core/error/dto'; import { ICurrentUser, Authenticate, CurrentUser } from '@modules/authentication'; import { GroupUc } from '../uc'; -import { ClassInfoDto } from '../uc/dto'; -import { ClassInfoSearchListResponse, ClassSortParams } from './dto'; +import { ClassInfoDto, ResolvedGroupDto } from '../uc/dto'; +import { ClassInfoSearchListResponse, ClassSortParams, GroupIdParams, GroupResponse } from './dto'; import { GroupResponseMapper } from './mapper'; @ApiTags('Group') @@ -42,4 +42,20 @@ export class GroupController { return response; } + + @Get('/:groupId') + @ApiOperation({ summary: 'Get a group by id.' }) + @ApiResponse({ status: HttpStatus.OK, type: GroupResponse }) + @ApiResponse({ status: '4XX', type: ErrorResponse }) + @ApiResponse({ status: '5XX', type: ErrorResponse }) + public async getGroup( + @CurrentUser() currentUser: ICurrentUser, + @Param() params: GroupIdParams + ): Promise { + const group: ResolvedGroupDto = await this.groupUc.getGroup(currentUser.userId, params.groupId); + + const response: GroupResponse = GroupResponseMapper.mapToGroupResponse(group); + + return response; + } } diff --git a/apps/server/src/modules/group/controller/mapper/group-response.mapper.ts b/apps/server/src/modules/group/controller/mapper/group-response.mapper.ts index 5f337eb204c..6efd02d899d 100644 --- a/apps/server/src/modules/group/controller/mapper/group-response.mapper.ts +++ b/apps/server/src/modules/group/controller/mapper/group-response.mapper.ts @@ -1,6 +1,18 @@ import { Page } from '@shared/domain'; -import { ClassInfoDto } from '../../uc/dto'; -import { ClassInfoResponse, ClassInfoSearchListResponse } from '../dto'; +import { GroupTypes } from '../../domain'; +import { ClassInfoDto, ResolvedGroupDto } from '../../uc/dto'; +import { + ClassInfoResponse, + ClassInfoSearchListResponse, + ExternalSourceResponse, + GroupResponse, + GroupTypeResponse, + GroupUserResponse, +} from '../dto'; + +const typeMapping: Record = { + [GroupTypes.CLASS]: GroupTypeResponse.CLASS, +}; export class GroupResponseMapper { static mapToClassInfosToListResponse( @@ -35,4 +47,30 @@ export class GroupResponseMapper { return mapped; } + + static mapToGroupResponse(resolvedGroup: ResolvedGroupDto): GroupResponse { + const mapped: GroupResponse = new GroupResponse({ + id: resolvedGroup.id, + name: resolvedGroup.name, + type: typeMapping[resolvedGroup.type], + externalSource: resolvedGroup.externalSource + ? new ExternalSourceResponse({ + externalId: resolvedGroup.externalSource.externalId, + systemId: resolvedGroup.externalSource.systemId, + }) + : undefined, + users: resolvedGroup.users.map( + (user) => + new GroupUserResponse({ + id: user.user.id as string, + role: user.role.name, + firstName: user.user.firstName, + lastName: user.user.lastName, + }) + ), + organizationId: resolvedGroup.organizationId, + }); + + return mapped; + } } diff --git a/apps/server/src/modules/group/domain/group.ts b/apps/server/src/modules/group/domain/group.ts index 826bbd36b22..3d1f19bc312 100644 --- a/apps/server/src/modules/group/domain/group.ts +++ b/apps/server/src/modules/group/domain/group.ts @@ -38,6 +38,10 @@ export class Group extends DomainObject { return this.props.organizationId; } + get type(): GroupTypes { + return this.props.type; + } + removeUser(user: UserDO): void { this.props.users = this.props.users.filter((groupUser: GroupUser): boolean => groupUser.userId !== user.id); } diff --git a/apps/server/src/modules/group/uc/dto/index.ts b/apps/server/src/modules/group/uc/dto/index.ts index 389a31da162..d795f1c30d3 100644 --- a/apps/server/src/modules/group/uc/dto/index.ts +++ b/apps/server/src/modules/group/uc/dto/index.ts @@ -1,2 +1,3 @@ export * from './class-info.dto'; export * from './resolved-group-user'; +export * from './resolved-group.dto'; diff --git a/apps/server/src/modules/group/uc/dto/resolved-group.dto.ts b/apps/server/src/modules/group/uc/dto/resolved-group.dto.ts new file mode 100644 index 00000000000..4d288f936a0 --- /dev/null +++ b/apps/server/src/modules/group/uc/dto/resolved-group.dto.ts @@ -0,0 +1,26 @@ +import { ExternalSource } from '@shared/domain'; +import { GroupTypes } from '../../domain'; +import { ResolvedGroupUser } from './resolved-group-user'; + +export class ResolvedGroupDto { + id: string; + + name: string; + + type: GroupTypes; + + users: ResolvedGroupUser[]; + + externalSource?: ExternalSource; + + organizationId?: string; + + constructor(group: ResolvedGroupDto) { + this.id = group.id; + this.name = group.name; + this.type = group.type; + this.users = group.users; + this.externalSource = group.externalSource; + this.organizationId = group.organizationId; + } +} diff --git a/apps/server/src/modules/group/uc/group.uc.spec.ts b/apps/server/src/modules/group/uc/group.uc.spec.ts index 8e882c052d3..34cb55a1354 100644 --- a/apps/server/src/modules/group/uc/group.uc.spec.ts +++ b/apps/server/src/modules/group/uc/group.uc.spec.ts @@ -2,6 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { ForbiddenException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; +import { NotFoundLoggableException } from '@shared/common/loggable-exception'; import { LegacySchoolDo, Page, Permission, SchoolYearEntity, SortOrder, User, UserDO } from '@shared/domain'; import { groupFactory, @@ -22,9 +23,9 @@ import { RoleService } from '@modules/role'; import { RoleDto } from '@modules/role/service/dto/role.dto'; import { SystemDto, SystemService } from '@modules/system'; import { UserService } from '@modules/user'; -import { Group } from '../domain'; +import { Group, GroupTypes } from '../domain'; import { GroupService } from '../service'; -import { ClassInfoDto } from './dto'; +import { ClassInfoDto, ResolvedGroupDto } from './dto'; import { ClassRootType } from './dto/class-root-type'; import { GroupUc } from './group.uc'; @@ -218,7 +219,7 @@ describe('GroupUc', () => { }; }; - it('should check the CLASS_LIST permission', async () => { + it('should check the required permissions', async () => { const { teacherUser, school } = setup(); await uc.findAllClassesForSchool(teacherUser.id, teacherUser.school.id); @@ -228,7 +229,7 @@ describe('GroupUc', () => { school, { action: Action.read, - requiredPermissions: [Permission.CLASS_LIST], + requiredPermissions: [Permission.CLASS_LIST, Permission.GROUP_LIST], } ); }); @@ -340,4 +341,144 @@ describe('GroupUc', () => { }); }); }); + + describe('getGroup', () => { + describe('when the user has no permission', () => { + const setup = () => { + const user: User = userFactory.buildWithId(); + const error = new ForbiddenException(); + + authorizationService.getUserWithPermissions.mockResolvedValue(user); + authorizationService.checkPermission.mockImplementation(() => { + throw error; + }); + + return { + user, + error, + }; + }; + + it('should throw forbidden', async () => { + const { user, error } = setup(); + + const func = () => uc.getGroup(user.id, 'groupId'); + + await expect(func).rejects.toThrow(error); + }); + }); + + describe('when the group is not found', () => { + const setup = () => { + groupService.findById.mockRejectedValue(new NotFoundLoggableException(Group.name, 'id', 'groupId')); + const { teacherUser } = UserAndAccountTestFactory.buildTeacher(); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(teacherUser); + + return { + teacherId: teacherUser.id, + }; + }; + + it('should throw not found', async () => { + const { teacherId } = setup(); + + const func = () => uc.getGroup(teacherId, 'groupId'); + + await expect(func).rejects.toThrow(NotFoundLoggableException); + }); + }); + + describe('when the group is found', () => { + const setup = () => { + const { teacherUser } = UserAndAccountTestFactory.buildTeacher(); + const { studentUser } = UserAndAccountTestFactory.buildStudent(); + const group: Group = groupFactory.build({ + users: [ + { userId: teacherUser.id, roleId: teacherUser.roles[0].id }, + { userId: studentUser.id, roleId: studentUser.roles[0].id }, + ], + }); + const teacherRole: RoleDto = roleDtoFactory.build({ + id: teacherUser.roles[0].id, + name: teacherUser.roles[0].name, + }); + const studentRole: RoleDto = roleDtoFactory.build({ + id: studentUser.roles[0].id, + name: studentUser.roles[0].name, + }); + const teacherUserDo: UserDO = userDoFactory.build({ + id: teacherUser.id, + firstName: teacherUser.firstName, + lastName: teacherUser.lastName, + email: teacherUser.email, + roles: [{ id: teacherUser.roles[0].id, name: teacherUser.roles[0].name }], + }); + const studentUserDo: UserDO = userDoFactory.build({ + id: studentUser.id, + firstName: teacherUser.firstName, + lastName: studentUser.lastName, + email: studentUser.email, + roles: [{ id: studentUser.roles[0].id, name: studentUser.roles[0].name }], + }); + + groupService.findById.mockResolvedValueOnce(group); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(teacherUser); + userService.findById.mockResolvedValueOnce(teacherUserDo); + roleService.findById.mockResolvedValueOnce(teacherRole); + userService.findById.mockResolvedValueOnce(studentUserDo); + roleService.findById.mockResolvedValueOnce(studentRole); + + return { + teacherId: teacherUser.id, + teacherUser, + studentUser, + group, + expectedExternalId: group.externalSource?.externalId as string, + expectedSystemId: group.externalSource?.systemId as string, + }; + }; + + it('should return the resolved group', async () => { + const { teacherId, teacherUser, studentUser, group, expectedExternalId, expectedSystemId } = setup(); + + const result: ResolvedGroupDto = await uc.getGroup(teacherId, group.id); + + expect(result).toMatchObject({ + id: group.id, + name: group.name, + type: GroupTypes.CLASS, + externalSource: { + externalId: expectedExternalId, + systemId: expectedSystemId, + }, + users: [ + { + user: { + id: teacherUser.id, + firstName: teacherUser.firstName, + lastName: teacherUser.lastName, + email: teacherUser.email, + }, + role: { + id: teacherUser.roles[0].id, + name: teacherUser.roles[0].name, + }, + }, + { + user: { + id: studentUser.id, + firstName: studentUser.firstName, + lastName: studentUser.lastName, + email: studentUser.email, + }, + role: { + id: studentUser.roles[0].id, + name: studentUser.roles[0].name, + }, + }, + ], + }); + }); + }); + }); }); diff --git a/apps/server/src/modules/group/uc/group.uc.ts b/apps/server/src/modules/group/uc/group.uc.ts index 23cac984a6d..2421e444e73 100644 --- a/apps/server/src/modules/group/uc/group.uc.ts +++ b/apps/server/src/modules/group/uc/group.uc.ts @@ -11,7 +11,7 @@ import { UserService } from '@modules/user'; import { Group, GroupUser } from '../domain'; import { GroupService } from '../service'; import { SortHelper } from '../util'; -import { ClassInfoDto, ResolvedGroupUser } from './dto'; +import { ClassInfoDto, ResolvedGroupDto, ResolvedGroupUser } from './dto'; import { GroupUcMapper } from './mapper/group-uc.mapper'; @Injectable() @@ -38,7 +38,11 @@ export class GroupUc { const school: LegacySchoolDo = await this.schoolService.getSchoolById(schoolId); const user: User = await this.authorizationService.getUserWithPermissions(userId); - this.authorizationService.checkPermission(user, school, AuthorizationContextBuilder.read([Permission.CLASS_LIST])); + this.authorizationService.checkPermission( + user, + school, + AuthorizationContextBuilder.read([Permission.CLASS_LIST, Permission.GROUP_LIST]) + ); const combinedClassInfo: ClassInfoDto[] = await this.findCombinedClassListForSchool(schoolId); @@ -153,4 +157,24 @@ export class GroupUc { return page; } + + public async getGroup(userId: EntityId, groupId: EntityId): Promise { + const group: Group = await this.groupService.findById(groupId); + + await this.checkPermission(userId, group); + + const resolvedUsers: ResolvedGroupUser[] = await this.findUsersForGroup(group); + const resolvedGroup: ResolvedGroupDto = GroupUcMapper.mapToResolvedGroupDto(group, resolvedUsers); + + return resolvedGroup; + } + + private async checkPermission(userId: EntityId, group: Group): Promise { + const user: User = await this.authorizationService.getUserWithPermissions(userId); + return this.authorizationService.checkPermission( + user, + group, + AuthorizationContextBuilder.read([Permission.GROUP_VIEW]) + ); + } } diff --git a/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts b/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts index ebeba60117e..5ac11f0e0b6 100644 --- a/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts +++ b/apps/server/src/modules/group/uc/mapper/group-uc.mapper.ts @@ -2,7 +2,7 @@ import { RoleName, SchoolYearEntity, UserDO } from '@shared/domain'; import { Class } from '@modules/class/domain'; import { SystemDto } from '@modules/system'; import { Group } from '../../domain'; -import { ClassInfoDto, ResolvedGroupUser } from '../dto'; +import { ClassInfoDto, ResolvedGroupDto, ResolvedGroupUser } from '../dto'; import { ClassRootType } from '../dto/class-root-type'; export class GroupUcMapper { @@ -40,4 +40,16 @@ export class GroupUcMapper { return mapped; } + + public static mapToResolvedGroupDto(group: Group, resolvedGroupUsers: ResolvedGroupUser[]): ResolvedGroupDto { + const mapped: ResolvedGroupDto = new ResolvedGroupDto({ + id: group.id, + name: group.name, + type: group.type, + externalSource: group.externalSource, + users: resolvedGroupUsers, + }); + + return mapped; + } } diff --git a/apps/server/src/shared/domain/interface/permission.enum.ts b/apps/server/src/shared/domain/interface/permission.enum.ts index 3d00ef24be2..4512b95de7f 100644 --- a/apps/server/src/shared/domain/interface/permission.enum.ts +++ b/apps/server/src/shared/domain/interface/permission.enum.ts @@ -54,6 +54,8 @@ export enum Permission { FILE_MOVE = 'FILE_MOVE', FOLDER_CREATE = 'FOLDER_CREATE', FOLDER_DELETE = 'FOLDER_DELETE', + GROUP_LIST = 'GROUP_LIST', + GROUP_VIEW = 'GROUP_VIEW', HELPDESK_CREATE = 'HELPDESK_CREATE', HELPDESK_EDIT = 'HELPDESK_EDIT', HELPDESK_VIEW = 'HELPDESK_VIEW', diff --git a/apps/server/src/shared/domain/rules/index.ts b/apps/server/src/shared/domain/rules/index.ts new file mode 100644 index 00000000000..e69de29bb2d diff --git a/apps/server/src/shared/testing/user-role-permissions.ts b/apps/server/src/shared/testing/user-role-permissions.ts index c2a75fe478d..cfd38cea3a3 100644 --- a/apps/server/src/shared/testing/user-role-permissions.ts +++ b/apps/server/src/shared/testing/user-role-permissions.ts @@ -53,6 +53,7 @@ export const userPermissions = [ Permission.CLASS_VIEW, Permission.COURSE_VIEW, Permission.LERNSTORE_VIEW, + Permission.GROUP_VIEW, ] as Permission[]; export const studentPermissions = [ @@ -73,6 +74,7 @@ const sharedAdminPermissions = [ Permission.COURSE_CREATE, Permission.COURSE_EDIT, Permission.COURSE_REMOVE, + Permission.GROUP_LIST, Permission.NEWS_CREATE, Permission.NEWS_EDIT, Permission.STUDENT_SKIP_REGISTRATION, diff --git a/backup/setup/migrations.json b/backup/setup/migrations.json index 4cec0040475..8292d8fc62f 100644 --- a/backup/setup/migrations.json +++ b/backup/setup/migrations.json @@ -317,5 +317,16 @@ "$date": "2023-10-11T10:40:18.782Z" }, "__v": 0 + }, + { + "_id": { + "$oid": "652ea0196ddf74176cb57561" + }, + "state": "up", + "name": "add-group-view-and-list-permission", + "createdAt": { + "$date": "2023-10-17T14:38:44.886Z" + }, + "__v": 0 } ] diff --git a/backup/setup/roles.json b/backup/setup/roles.json index 3d7909a84da..0ad460fc526 100644 --- a/backup/setup/roles.json +++ b/backup/setup/roles.json @@ -6,7 +6,7 @@ "name": "user", "roles": [], "updatedAt": { - "$date": "2023-05-16T11:11:21.297Z" + "$date": "2023-10-18T05:58:52.716Z" }, "createdAt": { "$date": "2017-01-01T00:06:37.148Z" @@ -58,7 +58,8 @@ "TOOL_VIEW", "TOPIC_VIEW", "NEXTCLOUD_USER", - "CONTEXT_TOOL_USER" + "CONTEXT_TOOL_USER", + "GROUP_VIEW" ], "__v": 0 }, @@ -68,7 +69,7 @@ }, "name": "administrator", "updatedAt": { - "$date": "2023-09-04T12:13:44.069Z" + "$date": "2023-10-18T05:58:52.729Z" }, "createdAt": { "$date": "2017-01-01T00:06:37.148Z" @@ -132,7 +133,8 @@ "SCHOOL_TOOL_ADMIN", "USER_LOGIN_MIGRATION_ADMIN", "START_MEETING", - "JOIN_MEETING" + "JOIN_MEETING", + "GROUP_LIST" ], "__v": 2 }, @@ -142,7 +144,7 @@ }, "name": "superhero", "updatedAt": { - "$date": "2022-09-22T13:14:23.839Z" + "$date": "2023-10-18T05:58:52.729Z" }, "createdAt": { "$date": "2017-01-01T00:06:37.148Z" @@ -188,7 +190,8 @@ "TEAM_EDIT", "TOOL_CREATE", "TOOL_EDIT", - "YEARS_EDIT" + "YEARS_EDIT", + "GROUP_LIST" ], "__v": 2 }, @@ -198,7 +201,7 @@ }, "name": "teacher", "updatedAt": { - "$date": "2023-09-04T12:54:47.237Z" + "$date": "2023-10-18T05:59:16.621Z" }, "createdAt": { "$date": "2017-01-01T00:06:37.148Z" @@ -245,7 +248,8 @@ "HOMEWORK_CREATE", "HOMEWORK_EDIT", "CONTEXT_TOOL_ADMIN", - "JOIN_MEETING" + "JOIN_MEETING", + "GROUP_LIST" ], "__v": 2 }, diff --git a/migrations/1697553524886-add-group-view-and-list-permission.js b/migrations/1697553524886-add-group-view-and-list-permission.js new file mode 100644 index 00000000000..2bf46755aed --- /dev/null +++ b/migrations/1697553524886-add-group-view-and-list-permission.js @@ -0,0 +1,100 @@ +const mongoose = require('mongoose'); +// eslint-disable-next-line no-unused-vars +const { info } = require('winston'); +const { alert, error } = require('../src/logger'); + +const { connect, close } = require('../src/utils/database'); + +const Roles = mongoose.model( + 'roles2023101716394', + new mongoose.Schema( + { + name: { type: String, required: true }, + permissions: [{ type: String }], + }, + { + timestamps: true, + } + ), + 'roles' +); + +module.exports = { + up: async function up() { + // eslint-disable-next-line no-process-env + if (process.env.SC_THEME !== 'n21') { + info('Permissions GROUP_VIEW and GROUP_LIST will not be added for this instance.'); + return; + } + + await connect(); + + const groupViewPermission = await Roles.updateOne( + { name: 'user' }, + { + $addToSet: { + permissions: { + $each: ['GROUP_VIEW'], + }, + }, + } + ).exec(); + if (groupViewPermission) { + alert(`Permission GROUP_VIEW added to role user`); + } + + const groupListPermission = await Roles.updateMany( + { name: { $in: ['teacher', 'administrator', 'superhero'] } }, + { + $addToSet: { + permissions: { + $each: ['GROUP_LIST'], + }, + }, + } + ).exec(); + if (groupListPermission) { + alert(`Permission GROUP_LIST added to role user and administrator`); + } + + await close(); + }, + + down: async function down() { + // eslint-disable-next-line no-process-env + if (process.env.SC_THEME !== 'n21') { + info('Permissions GROUP_VIEW and GROUP_LIST will not be removed for this instance.'); + return; + } + + await connect(); + + const groupViewRollback = await Roles.updateOne( + { name: 'user' }, + { + $pull: { + permissions: 'GROUP_VIEW', + }, + } + ).exec(); + + if (groupViewRollback) { + alert(`Rollback: Removed permission GROUP_VIEW from role user`); + } + + const groupListRollback = await Roles.updateMany( + { name: { $in: ['teacher', 'administrator', 'superhero'] } }, + { + $pull: { + permissions: 'GROUP_LIST', + }, + } + ).exec(); + + if (groupListRollback) { + alert(`Rollback: Removed permission GROUP_LIST from roles teacher and administrator`); + } + + await close(); + }, +}; From bbcb506fc0e35755043502f48a61cb269c57a171 Mon Sep 17 00:00:00 2001 From: MBergCap <111343628+MBergCap@users.noreply.github.com> Date: Fri, 20 Oct 2023 16:30:46 +0200 Subject: [PATCH 10/14] N21-1293 remove groups from course (#4478) * add group removel * N21-1293 add group to seed data --- backup/setup/groups.json | 148 +++++++++++++++++------ src/services/user-group/hooks/courses.js | 30 ++++- 2 files changed, 141 insertions(+), 37 deletions(-) diff --git a/backup/setup/groups.json b/backup/setup/groups.json index a682580f824..36e5e226896 100644 --- a/backup/setup/groups.json +++ b/backup/setup/groups.json @@ -1,37 +1,115 @@ [ - { - "createdAt": { - "$date": "2023-10-17T12:15:26.458Z" - }, - "updatedAt": { - "$date": "2023-10-17T12:15:26.461Z" - }, - "name": "Cypress-Test-Group", - "type": "class", - "externalSource_externalId": "fd84869b-56e8-41d2-a3dd-6c7239068ed5", - "externalSource_system": { - "$oid": "0000d186816abba584714c93" - }, - "users": [ - { - "user": { - "$oid": "5fa2c71bb229544f2c6966d9" - }, - "role": { - "$oid": "0000d186816abba584714c98" - } - }, - { - "user": { - "$oid": "5fa2cccab229544f2c696917" - }, - "role": { - "$oid": "0000d186816abba584714c99" - } - } - ], - "organization": { - "$oid": "5fa2c5ccb229544f2c69666c" - } - } + { + "createdAt": { + "$date": "2023-10-17T12:15:26.458Z" + }, + "updatedAt": { + "$date": "2023-10-17T12:15:26.461Z" + }, + "name": "Cypress-Test-Group", + "type": "class", + "externalSource_externalId": "fd84869b-56e8-41d2-a3dd-6c7239068ed5", + "externalSource_system": { + "$oid": "0000d186816abba584714c93" + }, + "users": [ + { + "user": { + "$oid": "5fa2c71bb229544f2c6966d9" + }, + "role": { + "$oid": "0000d186816abba584714c98" + } + }, + { + "user": { + "$oid": "5fa2cccab229544f2c696917" + }, + "role": { + "$oid": "0000d186816abba584714c99" + } + } + ], + "organization": { + "$oid": "5fa2c5ccb229544f2c69666c" + } + }, + { + "createdAt": { + "$date": "2023-10-17T12:15:26.458Z" + }, + "updatedAt": { + "$date": "2023-10-17T12:15:26.461Z" + }, + "name": "Cypress-Test-Group1", + "type": "class", + "externalSource_externalId": "fd84869b-56e8-41d2-a3dd-6c7239068ed5", + "externalSource_system": { + "$oid": "0000d186816abba584714c93" + }, + "users": [ + { + "user": { + "$oid": "5fa2c71bb229544f2c6966d9" + }, + "role": { + "$oid": "0000d186816abba584714c98" + } + }, + { + "user": { + "$oid": "5fa2cccab229544f2c696917" + }, + "role": { + "$oid": "0000d186816abba584714c99" + } + } + ], + "organization": { + "$oid": "5fa2c5ccb229544f2c69666c" + } + }, + { + "createdAt": { + "$date": "2023-10-17T12:15:26.458Z" + }, + "updatedAt": { + "$date": "2023-10-17T12:15:26.461Z" + }, + "name": "Cypress-Test-Group2", + "type": "class", + "externalSource_externalId": "fd84869b-56e8-41d2-a3dd-6c7239068ed5", + "externalSource_system": { + "$oid": "0000d186816abba584714c93" + }, + "users": [ + { + "user": { + "$oid": "5fa2c71bb229544f2c6966d9" + }, + "role": { + "$oid": "0000d186816abba584714c98" + } + }, + { + "user": { + "$oid": "5fa2cccab229544f2c696917" + }, + "role": { + "$oid": "0000d186816abba584714c99" + } + }, + { + "user": { + "$oid": "5fa30079b229544f2c6969ff" + }, + "role": { + "$oid": "0000d186816abba584714c99" + } + } + ], + "organization": { + "$oid": "5fa2c5ccb229544f2c69666c" + } + } ] diff --git a/src/services/user-group/hooks/courses.js b/src/services/user-group/hooks/courses.js index 41e2014feb0..92a5b987a7c 100644 --- a/src/services/user-group/hooks/courses.js +++ b/src/services/user-group/hooks/courses.js @@ -67,16 +67,42 @@ const addWholeClassToCourse = async (hook) => { * @param hook - contains and request body */ const deleteWholeClassFromCourse = (hook) => { + const { app } = hook; const requestBody = hook.data; const courseId = hook.id; - if (requestBody.classIds === undefined && requestBody.user === undefined) { + if (requestBody.classIds === undefined && requestBody.user === undefined && requestBody.groupIds === undefined) { return hook; } return CourseModel.findById(courseId) .exec() - .then((course) => { + .then(async (course) => { if (!course) return hook; + const removedGroups = _.differenceBy(course.groupIds, requestBody.groupIds, (v) => JSON.stringify(v)); + if (Configuration.get('FEATURE_GROUPS_IN_COURSE_ENABLED') && removedGroups.length > 0) { + await Promise.all( + removedGroups.map((groupId) => + app + .service('nest-group-service') + .findById(groupId) + .then((group) => group.users) + ) + ).then(async (groupUsers) => { + // flatten deep arrays and remove duplicates + const userIds = _.flattenDeep(groupUsers).map((groupUser) => groupUser.userId); + const uniqueUserIds = _.uniqWith(userIds, (a, b) => a === b); + + await CourseModel.update( + { _id: course._id }, + { $pull: { userIds: { $in: uniqueUserIds } } }, + { multi: true } + ).exec(); + hook.data.userIds = hook.data.userIds.filter((value) => !uniqueUserIds.some((id) => equalIds(id, value))); + + return undefined; + }); + } + const removedClasses = _.differenceBy(course.classIds, requestBody.classIds, (v) => JSON.stringify(v)); if (removedClasses.length < 1) return hook; return Promise.all( From 504e8d19c9da28341b1c59ad0974769db1896515 Mon Sep 17 00:00:00 2001 From: WahlMartin <132356096+WahlMartin@users.noreply.github.com> Date: Mon, 23 Oct 2023 14:16:20 +0200 Subject: [PATCH 11/14] Revert "EW-619 DataPort review testing part (#4442)" (#4490) This reverts commit a4364ecd01056abea5cdae1832f4293f32fa285c. --- .../controller/account.controller.spec.ts | 24 + .../account/controller/account.controller.ts | 6 +- .../controller/api-test/account.api.spec.ts | 779 +--- .../controller/dto/password-pattern.ts | 1 + .../account-entity-to-dto.mapper.spec.ts | 139 +- .../mapper/account-entity-to-dto.mapper.ts | 2 - .../account-idm-to-dto.mapper.db.spec.ts | 62 +- .../account-idm-to-dto.mapper.idm.spec.ts | 75 +- .../mapper/account-response.mapper.spec.ts | 81 +- .../account/mapper/account-response.mapper.ts | 1 - .../repo/account.repo.integration.spec.ts | 412 +- .../src/modules/account/repo/account.repo.ts | 5 - .../src/modules/account/review-comments.md | 12 - .../services/account-db.service.spec.ts | 995 ++-- .../account/services/account-db.service.ts | 21 +- .../account-idm.service.integration.spec.ts | 172 +- .../services/account-idm.service.spec.ts | 315 +- .../account/services/account-idm.service.ts | 9 +- .../services/account.service.abstract.ts | 3 - .../account.service.integration.spec.ts | 201 +- .../account/services/account.service.spec.ts | 710 ++- .../account/services/account.service.ts | 10 +- .../account.validation.service.spec.ts | 586 +-- .../services/account.validation.service.ts | 6 +- .../account/services/dto/account.dto.ts | 1 - .../src/modules/account/uc/account.uc.spec.ts | 4004 +++++------------ .../src/modules/account/uc/account.uc.ts | 18 +- .../shared/testing/factory/account.factory.ts | 32 +- 28 files changed, 2739 insertions(+), 5943 deletions(-) create mode 100644 apps/server/src/modules/account/controller/account.controller.spec.ts delete mode 100644 apps/server/src/modules/account/review-comments.md diff --git a/apps/server/src/modules/account/controller/account.controller.spec.ts b/apps/server/src/modules/account/controller/account.controller.spec.ts new file mode 100644 index 00000000000..ef15672edc5 --- /dev/null +++ b/apps/server/src/modules/account/controller/account.controller.spec.ts @@ -0,0 +1,24 @@ +import { Test, TestingModule } from '@nestjs/testing'; +import { AccountController } from './account.controller'; +import { AccountUc } from '../uc/account.uc'; + +describe('account.controller', () => { + let module: TestingModule; + let controller: AccountController; + beforeAll(async () => { + module = await Test.createTestingModule({ + providers: [ + AccountController, + { + provide: AccountUc, + useValue: {}, + }, + ], + }).compile(); + controller = module.get(AccountController); + }); + + it('should be defined', () => { + expect(controller).toBeDefined(); + }); +}); diff --git a/apps/server/src/modules/account/controller/account.controller.ts b/apps/server/src/modules/account/controller/account.controller.ts index 2f45de2403e..2256cb9fc90 100644 --- a/apps/server/src/modules/account/controller/account.controller.ts +++ b/apps/server/src/modules/account/controller/account.controller.ts @@ -1,8 +1,8 @@ import { Body, Controller, Delete, Get, Param, Patch, Query } from '@nestjs/common'; import { ApiOperation, ApiResponse, ApiTags } from '@nestjs/swagger'; +import { Authenticate, CurrentUser } from '@src/modules/authentication/decorator/auth.decorator'; import { EntityNotFoundError, ForbiddenOperationError, ValidationError } from '@shared/common'; -import { ICurrentUser } from '@modules/authentication'; -import { Authenticate, CurrentUser } from '@modules/authentication/decorator/auth.decorator'; +import { ICurrentUser } from '@src/modules/authentication'; import { AccountUc } from '../uc/account.uc'; import { AccountByIdBodyParams, @@ -33,8 +33,6 @@ export class AccountController { @Query() query: AccountSearchQueryParams ): Promise { return this.accountUc.searchAccounts(currentUser, query); - - // TODO: mapping from domain to api dto should be a responsability of the controller (also every other function here) } @Get(':id') diff --git a/apps/server/src/modules/account/controller/api-test/account.api.spec.ts b/apps/server/src/modules/account/controller/api-test/account.api.spec.ts index 44d47a3c3d2..fefdb006bf8 100644 --- a/apps/server/src/modules/account/controller/api-test/account.api.spec.ts +++ b/apps/server/src/modules/account/controller/api-test/account.api.spec.ts @@ -1,642 +1,315 @@ -import { EntityManager } from '@mikro-orm/mongodb'; -import { INestApplication } from '@nestjs/common'; +import { EntityManager } from '@mikro-orm/core'; +import { ExecutionContext, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Account, Permission, RoleName, User } from '@shared/domain'; -import { - accountFactory, - roleFactory, - schoolFactory, - userFactory, - TestApiClient, - cleanupCollections, -} from '@shared/testing'; +import { ICurrentUser } from '@src/modules/authentication'; +import { accountFactory, mapUserToCurrentUser, roleFactory, schoolFactory, userFactory } from '@shared/testing'; import { AccountByIdBodyParams, AccountSearchQueryParams, AccountSearchType, PatchMyAccountParams, PatchMyPasswordParams, -} from '@modules/account/controller/dto'; -import { ServerTestModule } from '@modules/server/server.module'; +} from '@src/modules/account/controller/dto'; +import { JwtAuthGuard } from '@src/modules/authentication/guard/jwt-auth.guard'; +import { ServerTestModule } from '@src/modules/server/server.module'; +import { Request } from 'express'; +import request from 'supertest'; describe('Account Controller (API)', () => { const basePath = '/account'; let app: INestApplication; let em: EntityManager; - let testApiClient: TestApiClient; + + let adminAccount: Account; + let teacherAccount: Account; + let studentAccount: Account; + let superheroAccount: Account; + + let adminUser: User; + let teacherUser: User; + let studentUser: User; + let superheroUser: User; + + let currentUser: ICurrentUser; const defaultPassword = 'DummyPasswd!1'; const defaultPasswordHash = '$2a$10$/DsztV5o6P5piW2eWJsxw.4nHovmJGBA.QNwiTmuZ/uvUc40b.Uhu'; - const mapUserToAccount = (user: User): Account => - accountFactory.buildWithId({ - userId: user.id, - username: user.email, - password: defaultPasswordHash, + const setup = async () => { + const school = schoolFactory.buildWithId(); + + const adminRoles = roleFactory.build({ + name: RoleName.ADMINISTRATOR, + permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], }); + const teacherRoles = roleFactory.build({ name: RoleName.TEACHER, permissions: [Permission.STUDENT_EDIT] }); + const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); + const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); + + adminUser = userFactory.buildWithId({ school, roles: [adminRoles] }); + teacherUser = userFactory.buildWithId({ school, roles: [teacherRoles] }); + studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); + superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); + + const mapUserToAccount = (user: User): Account => + accountFactory.buildWithId({ + userId: user.id, + username: user.email, + password: defaultPasswordHash, + }); + adminAccount = mapUserToAccount(adminUser); + teacherAccount = mapUserToAccount(teacherUser); + studentAccount = mapUserToAccount(studentUser); + superheroAccount = mapUserToAccount(superheroUser); + + em.persist(school); + em.persist([adminRoles, teacherRoles, studentRoles, superheroRoles]); + em.persist([adminUser, teacherUser, studentUser, superheroUser]); + em.persist([adminAccount, teacherAccount, studentAccount, superheroAccount]); + await em.flush(); + }; beforeAll(async () => { const moduleFixture: TestingModule = await Test.createTestingModule({ imports: [ServerTestModule], - }).compile(); + }) + .overrideGuard(JwtAuthGuard) + .useValue({ + canActivate(context: ExecutionContext) { + const req: Request = context.switchToHttp().getRequest(); + req.user = currentUser; + return true; + }, + }) + .compile(); app = moduleFixture.createNestApplication(); await app.init(); em = app.get(EntityManager); - testApiClient = new TestApiClient(app, basePath); }); beforeEach(async () => { - await cleanupCollections(em); + await setup(); }); afterAll(async () => { - await cleanupCollections(em); + // await cleanupCollections(em); await app.close(); }); describe('[PATCH] me/password', () => { - describe('When patching with a valid password', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const studentAccount = mapUserToAccount(studentUser); - - em.persist([school, studentRoles, studentUser, studentAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(studentAccount); - - const passwordPatchParams: PatchMyPasswordParams = { - password: 'Valid12$', - confirmPassword: 'Valid12$', - }; - - return { passwordPatchParams, loggedInClient, studentAccount }; + it(`should update the current user's (temporary) password`, async () => { + currentUser = mapUserToCurrentUser(studentUser, studentAccount); + const params: PatchMyPasswordParams = { + password: 'Valid12$', + confirmPassword: 'Valid12$', }; + await request(app.getHttpServer()) // + .patch(`${basePath}/me/password`) + .send(params) + .expect(200); - it(`should update the current user's (temporary) password`, async () => { - const { passwordPatchParams, loggedInClient, studentAccount } = await setup(); - - await loggedInClient.patch('/me/password', passwordPatchParams).expect(200); - - const updatedAccount = await em.findOneOrFail(Account, studentAccount.id); - expect(updatedAccount.password).not.toEqual(defaultPasswordHash); - }); + const updatedAccount = await em.findOneOrFail(Account, studentAccount.id); + expect(updatedAccount.password).not.toEqual(defaultPasswordHash); }); - - describe('When using a weak password', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const studentAccount = mapUserToAccount(studentUser); - - em.persist([school, studentRoles, studentUser, studentAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(studentAccount); - - const passwordPatchParams: PatchMyPasswordParams = { - password: 'weak', - confirmPassword: 'weak', - }; - - return { passwordPatchParams, loggedInClient }; + it('should reject if new password is weak', async () => { + currentUser = mapUserToCurrentUser(studentUser, studentAccount); + const params: PatchMyPasswordParams = { + password: 'weak', + confirmPassword: 'weak', }; - - it('should reject the password change', async () => { - const { passwordPatchParams, loggedInClient } = await setup(); - - await loggedInClient.patch('/me/password', passwordPatchParams).expect(400); - }); + await request(app.getHttpServer()) // + .patch(`${basePath}/me/password`) + .send(params) + .expect(400); }); }); describe('[PATCH] me', () => { - describe('When patching the account with account info', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const studentAccount = mapUserToAccount(studentUser); - - em.persist([school, studentRoles, studentUser, studentAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(studentAccount); - - const newEmailValue = 'new@mail.com'; - - const patchMyAccountParams: PatchMyAccountParams = { - passwordOld: defaultPassword, - email: newEmailValue, - }; - return { newEmailValue, patchMyAccountParams, loggedInClient, studentAccount }; + it(`should update a users account`, async () => { + const newEmailValue = 'new@mail.com'; + currentUser = mapUserToCurrentUser(studentUser, studentAccount); + const params: PatchMyAccountParams = { + passwordOld: defaultPassword, + email: newEmailValue, }; - it(`should update a users account`, async () => { - const { newEmailValue, patchMyAccountParams, loggedInClient, studentAccount } = await setup(); - - await loggedInClient.patch('/me', patchMyAccountParams).expect(200); - - const updatedAccount = await em.findOneOrFail(Account, studentAccount.id); - expect(updatedAccount.username).toEqual(newEmailValue); - }); + await request(app.getHttpServer()) // + .patch(`${basePath}/me`) + .send(params) + .expect(200); + const updatedAccount = await em.findOneOrFail(Account, studentAccount.id); + expect(updatedAccount.username).toEqual(newEmailValue); }); - describe('When patching with a not valid email', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const studentAccount = mapUserToAccount(studentUser); - - em.persist([school, studentRoles, studentUser, studentAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(studentAccount); - - const newEmailValue = 'new@mail.com'; - - const patchMyAccountParams: PatchMyAccountParams = { - passwordOld: defaultPassword, - email: 'invalid', - }; - return { newEmailValue, patchMyAccountParams, loggedInClient }; + it('should reject if new email is not valid', async () => { + currentUser = mapUserToCurrentUser(studentUser, studentAccount); + const params: PatchMyAccountParams = { + passwordOld: defaultPassword, + email: 'invalid', }; - - it('should reject patch request', async () => { - const { patchMyAccountParams, loggedInClient } = await setup(); - - await loggedInClient.patch('/me', patchMyAccountParams).expect(400); - }); + await request(app.getHttpServer()) // + .patch(`${basePath}/me`) + .send(params) + .expect(400); }); }); describe('[GET]', () => { - describe('When searching with a superhero user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - - const studentAccount = mapUserToAccount(studentUser); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist(school); - em.persist([studentRoles, superheroRoles]); - em.persist([studentUser, superheroUser]); - em.persist([studentAccount, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - const query: AccountSearchQueryParams = { - type: AccountSearchType.USER_ID, - value: studentUser.id, - skip: 5, - limit: 5, - }; - - return { query, loggedInClient }; + it('should search for user id', async () => { + currentUser = mapUserToCurrentUser(superheroUser, superheroAccount); + const query: AccountSearchQueryParams = { + type: AccountSearchType.USER_ID, + value: studentUser.id, + skip: 5, + limit: 5, }; - it('should successfully search for user id', async () => { - const { query, loggedInClient } = await setup(); - - await loggedInClient.get().query(query).send().expect(200); - }); + await request(app.getHttpServer()) // + .get(`${basePath}`) + .query(query) + .send() + .expect(200); }); - // If skip is too big, just return an empty list. // We testing it here, because we are mocking the database in the use case unit tests // and for realistic behavior we need database. - describe('When searching with a superhero user with large skip', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - - const studentAccount = mapUserToAccount(studentUser); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist(school); - em.persist([studentRoles, superheroRoles]); - em.persist([studentUser, superheroUser]); - em.persist([studentAccount, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - const query: AccountSearchQueryParams = { - type: AccountSearchType.USER_ID, - value: studentUser.id, - skip: 50000, - limit: 5, - }; - - return { query, loggedInClient }; + it('should search for user id with large skip', async () => { + currentUser = mapUserToCurrentUser(superheroUser); + const query: AccountSearchQueryParams = { + type: AccountSearchType.USER_ID, + value: studentUser.id, + skip: 50000, + limit: 5, }; - it('should search for user id', async () => { - const { query, loggedInClient } = await setup(); - - await loggedInClient.get().query(query).send().expect(200); - }); + await request(app.getHttpServer()) // + .get(`${basePath}`) + .query(query) + .send() + .expect(200); }); - - describe('When searching with a superhero user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - - const studentAccount = mapUserToAccount(studentUser); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist(school); - em.persist([studentRoles, superheroRoles]); - em.persist([studentUser, superheroUser]); - em.persist([studentAccount, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - const query: AccountSearchQueryParams = { - type: AccountSearchType.USERNAME, - value: '', - skip: 5, - limit: 5, - }; - - return { query, loggedInClient }; + it('should search for user name', async () => { + currentUser = mapUserToCurrentUser(superheroUser, superheroAccount); + const query: AccountSearchQueryParams = { + type: AccountSearchType.USERNAME, + value: '', + skip: 5, + limit: 5, }; - it('should search for username', async () => { - const { query, loggedInClient } = await setup(); - - await loggedInClient.get().query(query).send().expect(200); - }); + await request(app.getHttpServer()) // + .get(`${basePath}`) + .query(query) + .send() + .expect(200); }); - - describe('When searching with a superhero user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - - const studentAccount = mapUserToAccount(studentUser); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist(school); - em.persist([studentRoles, superheroRoles]); - em.persist([studentUser, superheroUser]); - em.persist([studentAccount, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - const query: AccountSearchQueryParams = { - type: '' as AccountSearchType, - value: '', - skip: 5, - limit: 5, - }; - - return { query, loggedInClient }; + it('should reject if type is unknown', async () => { + currentUser = mapUserToCurrentUser(superheroUser, superheroAccount); + const query: AccountSearchQueryParams = { + type: '' as AccountSearchType, + value: '', + skip: 5, + limit: 5, }; - - it('should reject if type is unknown', async () => { - const { query, loggedInClient } = await setup(); - - await loggedInClient.get().query(query).send().expect(400); - }); + await request(app.getHttpServer()) // + .get(`${basePath}`) + .query(query) + .send() + .expect(400); }); - describe('When searching with an admin user (not authorized)', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const adminRoles = roleFactory.build({ - name: RoleName.ADMINISTRATOR, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }); - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - - const adminUser = userFactory.buildWithId({ school, roles: [adminRoles] }); - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - - const adminAccount = mapUserToAccount(adminUser); - const studentAccount = mapUserToAccount(studentUser); - - em.persist(school); - em.persist([adminRoles, studentRoles]); - em.persist([adminUser, studentUser]); - em.persist([adminAccount, studentAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(adminAccount); - - const query: AccountSearchQueryParams = { - type: AccountSearchType.USERNAME, - value: '', - skip: 5, - limit: 5, - }; - - return { query, loggedInClient, studentAccount }; + it('should reject if user is not authorized', async () => { + currentUser = mapUserToCurrentUser(adminUser, adminAccount); + const query: AccountSearchQueryParams = { + type: AccountSearchType.USERNAME, + value: '', + skip: 5, + limit: 5, }; - - it('should reject search for user', async () => { - const { query, loggedInClient } = await setup(); - - await loggedInClient.get().query(query).send().expect(403); - }); + await request(app.getHttpServer()) // + .get(`${basePath}`) + .query(query) + .send() + .expect(403); }); }); describe('[GET] :id', () => { - describe('When searching with a superhero user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - - const studentAccount = mapUserToAccount(studentUser); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist(school); - em.persist([studentRoles, superheroRoles]); - em.persist([studentUser, superheroUser]); - em.persist([studentAccount, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - return { loggedInClient, studentAccount }; - }; - it('should return account for account id', async () => { - const { loggedInClient, studentAccount } = await setup(); - await loggedInClient.get(`/${studentAccount.id}`).expect(200); - }); + it('should return account for account id', async () => { + currentUser = mapUserToCurrentUser(superheroUser, superheroAccount); + await request(app.getHttpServer()) // + .get(`${basePath}/${studentAccount.id}`) + .expect(200); }); - - describe('When searching with a not authorized user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const adminRoles = roleFactory.build({ - name: RoleName.ADMINISTRATOR, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }); - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - - const adminUser = userFactory.buildWithId({ school, roles: [adminRoles] }); - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - - const adminAccount = mapUserToAccount(adminUser); - const studentAccount = mapUserToAccount(studentUser); - - em.persist(school); - em.persist([adminRoles, studentRoles]); - em.persist([adminUser, studentUser]); - em.persist([adminAccount, studentAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(adminAccount); - - return { loggedInClient, studentAccount }; - }; - it('should reject request', async () => { - const { loggedInClient, studentAccount } = await setup(); - await loggedInClient.get(`/${studentAccount.id}`).expect(403); - }); + it('should reject if user is not a authorized', async () => { + currentUser = mapUserToCurrentUser(adminUser, adminAccount); + await request(app.getHttpServer()) // + .get(`${basePath}/${studentAccount.id}`) + .expect(403); }); - - describe('When searching with a superhero user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist([school, superheroRoles, superheroUser, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - return { loggedInClient }; - }; - - it('should reject not existing account id', async () => { - const { loggedInClient } = await setup(); - await loggedInClient.get(`/000000000000000000000000`).expect(404); - }); + it('should reject not existing account id', async () => { + currentUser = mapUserToCurrentUser(superheroUser, superheroAccount); + await request(app.getHttpServer()) // + .get(`${basePath}/000000000000000000000000`) + .expect(404); }); }); describe('[PATCH] :id', () => { - describe('When using a superhero user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - - const studentAccount = mapUserToAccount(studentUser); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist(school); - em.persist([studentRoles, superheroRoles]); - em.persist([studentUser, superheroUser]); - em.persist([studentAccount, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - const body: AccountByIdBodyParams = { - password: defaultPassword, - username: studentAccount.username, - activated: true, - }; - - return { body, loggedInClient, studentAccount }; + it('should update account', async () => { + currentUser = mapUserToCurrentUser(superheroUser, superheroAccount); + const body: AccountByIdBodyParams = { + password: defaultPassword, + username: studentAccount.username, + activated: true, }; - - it('should update account', async () => { - const { body, loggedInClient, studentAccount } = await setup(); - - await loggedInClient.patch(`/${studentAccount.id}`, body).expect(200); - }); + await request(app.getHttpServer()) // + .patch(`${basePath}/${studentAccount.id}`) + .send(body) + .expect(200); }); - - describe('When the user is not authorized', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const studentAccount = mapUserToAccount(studentUser); - - em.persist([school, studentRoles, studentUser, studentAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(studentAccount); - - const body: AccountByIdBodyParams = { - password: defaultPassword, - username: studentAccount.username, - activated: true, - }; - - return { body, loggedInClient, studentAccount }; + it('should reject if user is not authorized', async () => { + currentUser = mapUserToCurrentUser(studentUser, studentAccount); + const body: AccountByIdBodyParams = { + password: defaultPassword, + username: studentAccount.username, + activated: true, }; - it('should reject update request', async () => { - const { body, loggedInClient, studentAccount } = await setup(); - - await loggedInClient.patch(`/${studentAccount.id}`, body).expect(403); - }); + await request(app.getHttpServer()) // + .patch(`${basePath}/${studentAccount.id}`) + .send(body) + .expect(403); }); - - describe('When updating with a superhero user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - - const studentAccount = mapUserToAccount(studentUser); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist(school); - em.persist([studentRoles, superheroRoles]); - em.persist([studentUser, superheroUser]); - em.persist([studentAccount, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - const body: AccountByIdBodyParams = { - password: defaultPassword, - username: studentAccount.username, - activated: true, - }; - - return { body, loggedInClient }; + it('should reject not existing account id', async () => { + currentUser = mapUserToCurrentUser(superheroUser, studentAccount); + const body: AccountByIdBodyParams = { + password: defaultPassword, + username: studentAccount.username, + activated: true, }; - it('should reject not existing account id', async () => { - const { body, loggedInClient } = await setup(); - await loggedInClient.patch('/000000000000000000000000', body).expect(404); - }); + await request(app.getHttpServer()) // + .patch(`${basePath}/000000000000000000000000`) + .send(body) + .expect(404); }); }); describe('[DELETE] :id', () => { - describe('When using a superhero user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - - const studentAccount = mapUserToAccount(studentUser); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist(school); - em.persist([studentRoles, superheroRoles]); - em.persist([studentUser, superheroUser]); - em.persist([studentAccount, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - return { loggedInClient, studentAccount }; - }; - it('should delete account', async () => { - const { loggedInClient, studentAccount } = await setup(); - await loggedInClient.delete(`/${studentAccount.id}`).expect(200); - }); + it('should delete account', async () => { + currentUser = mapUserToCurrentUser(superheroUser, studentAccount); + await request(app.getHttpServer()) // + .delete(`${basePath}/${studentAccount.id}`) + .expect(200); }); - - describe('When using a not authorized (admin) user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - - const adminRoles = roleFactory.build({ - name: RoleName.ADMINISTRATOR, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }); - const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] }); - - const adminUser = userFactory.buildWithId({ school, roles: [adminRoles] }); - const studentUser = userFactory.buildWithId({ school, roles: [studentRoles] }); - - const adminAccount = mapUserToAccount(adminUser); - const studentAccount = mapUserToAccount(studentUser); - - em.persist(school); - em.persist([adminRoles, studentRoles]); - em.persist([adminUser, studentUser]); - em.persist([adminAccount, studentAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(adminAccount); - - return { loggedInClient, studentAccount }; - }; - - it('should reject delete request', async () => { - const { loggedInClient, studentAccount } = await setup(); - await loggedInClient.delete(`/${studentAccount.id}`).expect(403); - }); + it('should reject if user is not a authorized', async () => { + currentUser = mapUserToCurrentUser(adminUser, adminAccount); + await request(app.getHttpServer()) // + .delete(`${basePath}/${studentAccount.id}`) + .expect(403); }); - - describe('When using a superhero user', () => { - const setup = async () => { - const school = schoolFactory.buildWithId(); - const superheroRoles = roleFactory.build({ name: RoleName.SUPERHERO, permissions: [] }); - const superheroUser = userFactory.buildWithId({ roles: [superheroRoles] }); - const superheroAccount = mapUserToAccount(superheroUser); - - em.persist([school, superheroRoles, superheroUser, superheroAccount]); - await em.flush(); - - const loggedInClient = await testApiClient.login(superheroAccount); - - return { loggedInClient }; - }; - - it('should reject not existing account id', async () => { - const { loggedInClient } = await setup(); - await loggedInClient.delete('/000000000000000000000000').expect(404); - }); + it('should reject not existing account id', async () => { + currentUser = mapUserToCurrentUser(superheroUser, studentAccount); + await request(app.getHttpServer()) // + .delete(`${basePath}/000000000000000000000000`) + .expect(404); }); }); }); diff --git a/apps/server/src/modules/account/controller/dto/password-pattern.ts b/apps/server/src/modules/account/controller/dto/password-pattern.ts index d849b8db235..6ca2fd9fab2 100644 --- a/apps/server/src/modules/account/controller/dto/password-pattern.ts +++ b/apps/server/src/modules/account/controller/dto/password-pattern.ts @@ -1 +1,2 @@ +// TODO Compare with client export const passwordPattern = /^(?=.*[A-Z])(?=.*[0-9])(?=.*[a-z])(?=.*[-_!<>§$%&/()=?\\;:,.#+*~'])\S.{6,253}\S$/; diff --git a/apps/server/src/modules/account/mapper/account-entity-to-dto.mapper.spec.ts b/apps/server/src/modules/account/mapper/account-entity-to-dto.mapper.spec.ts index 4de5040113f..8e9522434eb 100644 --- a/apps/server/src/modules/account/mapper/account-entity-to-dto.mapper.spec.ts +++ b/apps/server/src/modules/account/mapper/account-entity-to-dto.mapper.spec.ts @@ -1,5 +1,5 @@ import { Account } from '@shared/domain'; -import { accountFactory } from '@shared/testing'; +import { ObjectId } from 'bson'; import { AccountEntityToDtoMapper } from './account-entity-to-dto.mapper'; describe('AccountEntityToDtoMapper', () => { @@ -14,80 +14,101 @@ describe('AccountEntityToDtoMapper', () => { }); describe('mapToDto', () => { - describe('When mapping AccountEntity to AccountDto', () => { - const setup = () => { - const accountEntity = accountFactory.withAllProperties().buildWithId({}, '000000000000000000000001'); - - const missingSystemUserIdEntity: Account = accountFactory.withoutSystemAndUserId().build(); - - return { accountEntity, missingSystemUserIdEntity }; + it('should map all fields', () => { + const testEntity: Account = { + _id: new ObjectId(), + id: 'id', + createdAt: new Date(), + updatedAt: new Date(), + userId: new ObjectId(), + username: 'username', + activated: true, + credentialHash: 'credentialHash', + expiresAt: new Date(), + lasttriedFailedLogin: new Date(), + password: 'password', + systemId: new ObjectId(), + token: 'token', }; + const ret = AccountEntityToDtoMapper.mapToDto(testEntity); + + expect(ret.id).toBe(testEntity.id); + expect(ret.createdAt).toEqual(testEntity.createdAt); + expect(ret.updatedAt).toEqual(testEntity.createdAt); + expect(ret.userId).toBe(testEntity.userId?.toString()); + expect(ret.username).toBe(testEntity.username); + expect(ret.activated).toBe(testEntity.activated); + expect(ret.credentialHash).toBe(testEntity.credentialHash); + expect(ret.expiresAt).toBe(testEntity.expiresAt); + expect(ret.lasttriedFailedLogin).toBe(testEntity.lasttriedFailedLogin); + expect(ret.password).toBe(testEntity.password); + expect(ret.systemId).toBe(testEntity.systemId?.toString()); + expect(ret.token).toBe(testEntity.token); + }); - it('should map all fields', () => { - const { accountEntity } = setup(); - - const ret = AccountEntityToDtoMapper.mapToDto(accountEntity); - - expect({ ...ret, _id: accountEntity._id }).toMatchObject(accountEntity); - }); - - it('should ignore missing ids', () => { - const { missingSystemUserIdEntity } = setup(); - - const ret = AccountEntityToDtoMapper.mapToDto(missingSystemUserIdEntity); + it('should ignore missing ids', () => { + const testEntity: Account = { + _id: new ObjectId(), + id: 'id', + username: 'username', + createdAt: new Date(), + updatedAt: new Date(), + }; + const ret = AccountEntityToDtoMapper.mapToDto(testEntity); - expect(ret.userId).toBeUndefined(); - expect(ret.systemId).toBeUndefined(); - }); + expect(ret.userId).toBeUndefined(); + expect(ret.systemId).toBeUndefined(); }); }); describe('mapSearchResult', () => { - describe('When mapping multiple Account entities', () => { - const setup = () => { - const testEntity1: Account = accountFactory.buildWithId({}, '000000000000000000000001'); - const testEntity2: Account = accountFactory.buildWithId({}, '000000000000000000000002'); - - const testAmount = 10; - - const testEntities = [testEntity1, testEntity2]; - - return { testEntities, testAmount }; + it('should use actual date if date is', () => { + const testEntity1: Account = { + _id: new ObjectId(), + id: '1', + username: '1', + createdAt: new Date(), + updatedAt: new Date(), }; + const testEntity2: Account = { + _id: new ObjectId(), + id: '2', + username: '2', + createdAt: new Date(), + updatedAt: new Date(), + }; + const testAmount = 10; - it('should map exact same amount of entities', () => { - const { testEntities, testAmount } = setup(); - - const [accounts, total] = AccountEntityToDtoMapper.mapSearchResult([testEntities, testAmount]); + const [accounts, total] = AccountEntityToDtoMapper.mapSearchResult([[testEntity1, testEntity2], testAmount]); - expect(total).toBe(testAmount); - expect(accounts).toHaveLength(2); - expect(accounts).toContainEqual(expect.objectContaining({ id: '000000000000000000000001' })); - expect(accounts).toContainEqual(expect.objectContaining({ id: '000000000000000000000002' })); - }); + expect(total).toBe(testAmount); + expect(accounts).toHaveLength(2); + expect(accounts).toContainEqual(expect.objectContaining({ id: '1' })); + expect(accounts).toContainEqual(expect.objectContaining({ id: '2' })); }); }); describe('mapAccountsToDto', () => { - describe('When mapping multiple Account entities', () => { - const setup = () => { - const testEntity1: Account = accountFactory.buildWithId({}, '000000000000000000000001'); - const testEntity2: Account = accountFactory.buildWithId({}, '000000000000000000000002'); - - const testEntities = [testEntity1, testEntity2]; - - return testEntities; + it('should use actual date if date is', () => { + const testEntity1: Account = { + _id: new ObjectId(), + username: '1', + id: '1', + createdAt: new Date(), + updatedAt: new Date(), }; + const testEntity2: Account = { + _id: new ObjectId(), + username: '2', + id: '2', + createdAt: new Date(), + updatedAt: new Date(), + }; + const ret = AccountEntityToDtoMapper.mapAccountsToDto([testEntity1, testEntity2]); - it('should map all entities', () => { - const testEntities = setup(); - - const ret = AccountEntityToDtoMapper.mapAccountsToDto(testEntities); - - expect(ret).toHaveLength(2); - expect(ret).toContainEqual(expect.objectContaining({ id: '000000000000000000000001' })); - expect(ret).toContainEqual(expect.objectContaining({ id: '000000000000000000000002' })); - }); + expect(ret).toHaveLength(2); + expect(ret).toContainEqual(expect.objectContaining({ id: '1' })); + expect(ret).toContainEqual(expect.objectContaining({ id: '2' })); }); }); }); diff --git a/apps/server/src/modules/account/mapper/account-entity-to-dto.mapper.ts b/apps/server/src/modules/account/mapper/account-entity-to-dto.mapper.ts index d8af59e6716..417497b3218 100644 --- a/apps/server/src/modules/account/mapper/account-entity-to-dto.mapper.ts +++ b/apps/server/src/modules/account/mapper/account-entity-to-dto.mapper.ts @@ -19,8 +19,6 @@ export class AccountEntityToDtoMapper { }); } - // TODO: use Counted instead of [Account[], number] - // TODO: adjust naming of accountEntities static mapSearchResult(accountEntities: [Account[], number]): Counted { const foundAccounts = accountEntities[0]; const accountDtos: AccountDto[] = AccountEntityToDtoMapper.mapAccountsToDto(foundAccounts); diff --git a/apps/server/src/modules/account/mapper/account-idm-to-dto.mapper.db.spec.ts b/apps/server/src/modules/account/mapper/account-idm-to-dto.mapper.db.spec.ts index ee7d1644c94..2430afe6081 100644 --- a/apps/server/src/modules/account/mapper/account-idm-to-dto.mapper.db.spec.ts +++ b/apps/server/src/modules/account/mapper/account-idm-to-dto.mapper.db.spec.ts @@ -24,9 +24,9 @@ describe('AccountIdmToDtoMapperDb', () => { afterAll(async () => { await module.close(); }); - describe('mapToDto', () => { - describe('when mapping from entity to dto', () => { - const setup = () => { + describe('when mapping from entity to dto', () => { + describe('mapToDto', () => { + it('should map all fields', () => { const testIdmEntity: IdmAccount = { id: 'id', username: 'username', @@ -38,12 +38,6 @@ describe('AccountIdmToDtoMapperDb', () => { attDbcUserId: 'attDbcUserId', attDbcSystemId: 'attDbcSystemId', }; - return testIdmEntity; - }; - - it('should map all fields', () => { - const testIdmEntity = setup(); - const ret = mapper.mapToDto(testIdmEntity); expect(ret).toEqual( @@ -58,42 +52,30 @@ describe('AccountIdmToDtoMapperDb', () => { }) ); }); - }); - - describe('when date is undefined', () => { - const setup = () => { - const testIdmEntity: IdmAccount = { - id: 'id', - }; - return testIdmEntity; - }; - it('should use actual date', () => { - const testIdmEntity = setup(); + describe('when date is undefined', () => { + it('should use actual date', () => { + const testIdmEntity: IdmAccount = { + id: 'id', + }; + const ret = mapper.mapToDto(testIdmEntity); - const ret = mapper.mapToDto(testIdmEntity); - - const now = new Date(); - expect(ret.createdAt).toEqual(now); - expect(ret.updatedAt).toEqual(now); + const now = new Date(); + expect(ret.createdAt).toEqual(now); + expect(ret.updatedAt).toEqual(now); + }); }); - }); - describe('when a fields value is missing', () => { - const setup = () => { - const testIdmEntity: IdmAccount = { - id: 'id', - }; - return testIdmEntity; - }; - - it('should fill with empty string', () => { - const testIdmEntity = setup(); - - const ret = mapper.mapToDto(testIdmEntity); + describe('when a fields value is missing', () => { + it('should fill with empty string', () => { + const testIdmEntity: IdmAccount = { + id: 'id', + }; + const ret = mapper.mapToDto(testIdmEntity); - expect(ret.id).toBe(''); - expect(ret.username).toBe(''); + expect(ret.id).toBe(''); + expect(ret.username).toBe(''); + }); }); }); }); diff --git a/apps/server/src/modules/account/mapper/account-idm-to-dto.mapper.idm.spec.ts b/apps/server/src/modules/account/mapper/account-idm-to-dto.mapper.idm.spec.ts index 554e2d3025a..0d60a2cc57f 100644 --- a/apps/server/src/modules/account/mapper/account-idm-to-dto.mapper.idm.spec.ts +++ b/apps/server/src/modules/account/mapper/account-idm-to-dto.mapper.idm.spec.ts @@ -30,52 +30,39 @@ describe('AccountIdmToDtoMapperIdm', () => { await module.close(); }); - describe('mapToDto', () => { - describe('when mapping from entity to dto', () => { - const setup = () => { - const testIdmEntity: IdmAccount = { - id: 'id', - username: 'username', - email: 'email', - firstName: 'firstName', - lastName: 'lastName', - createdDate: new Date(), - attDbcAccountId: 'attDbcAccountId', - attDbcUserId: 'attDbcUserId', - attDbcSystemId: 'attDbcSystemId', - }; - return testIdmEntity; + describe('when mapping from entity to dto', () => { + it('should map all fields', () => { + const testIdmEntity: IdmAccount = { + id: 'id', + username: 'username', + email: 'email', + firstName: 'firstName', + lastName: 'lastName', + createdDate: new Date(), + attDbcAccountId: 'attDbcAccountId', + attDbcUserId: 'attDbcUserId', + attDbcSystemId: 'attDbcSystemId', }; - - it('should map all fields', () => { - const testIdmEntity = setup(); - - const ret = mapper.mapToDto(testIdmEntity); - - expect(ret).toEqual( - expect.objectContaining>({ - id: testIdmEntity.id, - idmReferenceId: undefined, - userId: testIdmEntity.attDbcUserId, - systemId: testIdmEntity.attDbcSystemId, - createdAt: testIdmEntity.createdDate, - updatedAt: testIdmEntity.createdDate, - username: testIdmEntity.username, - }) - ); - }); + const ret = mapper.mapToDto(testIdmEntity); + + expect(ret).toEqual( + expect.objectContaining>({ + id: testIdmEntity.id, + idmReferenceId: undefined, + userId: testIdmEntity.attDbcUserId, + systemId: testIdmEntity.attDbcSystemId, + createdAt: testIdmEntity.createdDate, + updatedAt: testIdmEntity.createdDate, + username: testIdmEntity.username, + }) + ); }); + describe('when date is undefined', () => { - const setup = () => { + it('should use actual date', () => { const testIdmEntity: IdmAccount = { id: 'id', }; - return testIdmEntity; - }; - - it('should use actual date', () => { - const testIdmEntity = setup(); - const ret = mapper.mapToDto(testIdmEntity); expect(ret.createdAt).toEqual(now); @@ -84,16 +71,10 @@ describe('AccountIdmToDtoMapperIdm', () => { }); describe('when a fields value is missing', () => { - const setup = () => { + it('should fill with empty string', () => { const testIdmEntity: IdmAccount = { id: 'id', }; - return testIdmEntity; - }; - - it('should fill with empty string', () => { - const testIdmEntity = setup(); - const ret = mapper.mapToDto(testIdmEntity); expect(ret.username).toBe(''); diff --git a/apps/server/src/modules/account/mapper/account-response.mapper.spec.ts b/apps/server/src/modules/account/mapper/account-response.mapper.spec.ts index 64858623c67..c4bd892faa0 100644 --- a/apps/server/src/modules/account/mapper/account-response.mapper.spec.ts +++ b/apps/server/src/modules/account/mapper/account-response.mapper.spec.ts @@ -1,57 +1,62 @@ import { Account } from '@shared/domain'; import { AccountDto } from '@modules/account/services/dto/account.dto'; -import { accountDtoFactory, accountFactory } from '@shared/testing'; +import { ObjectId } from '@mikro-orm/mongodb'; import { AccountResponseMapper } from '.'; describe('AccountResponseMapper', () => { describe('mapToResponseFromEntity', () => { - describe('When mapping AccountEntity to AccountResponse', () => { - const setup = () => { - const testEntityAllFields: Account = accountFactory.withAllProperties().buildWithId(); - - const testEntityMissingUserId: Account = accountFactory.withoutSystemAndUserId().build(); - - return { testEntityAllFields, testEntityMissingUserId }; + it('should map all fields', () => { + const testEntity: Account = { + _id: new ObjectId(), + id: new ObjectId().toString(), + userId: new ObjectId(), + activated: true, + username: 'username', + createdAt: new Date(), + updatedAt: new Date(), }; + const ret = AccountResponseMapper.mapToResponseFromEntity(testEntity); - it('should map all fields', () => { - const { testEntityAllFields } = setup(); - - const ret = AccountResponseMapper.mapToResponseFromEntity(testEntityAllFields); - - expect(ret.id).toBe(testEntityAllFields.id); - expect(ret.userId).toBe(testEntityAllFields.userId?.toString()); - expect(ret.activated).toBe(testEntityAllFields.activated); - expect(ret.username).toBe(testEntityAllFields.username); - }); - - it('should ignore missing userId', () => { - const { testEntityMissingUserId } = setup(); + expect(ret.id).toBe(testEntity.id); + expect(ret.userId).toBe(testEntity.userId?.toString()); + expect(ret.activated).toBe(testEntity.activated); + expect(ret.username).toBe(testEntity.username); + expect(ret.updatedAt).toBe(testEntity.updatedAt); + }); - const ret = AccountResponseMapper.mapToResponseFromEntity(testEntityMissingUserId); + it('should ignore missing userId', () => { + const testEntity: Account = { + _id: new ObjectId(), + id: new ObjectId().toString(), + userId: undefined, + activated: true, + username: 'username', + createdAt: new Date(), + updatedAt: new Date(), + }; + const ret = AccountResponseMapper.mapToResponseFromEntity(testEntity); - expect(ret.userId).toBeUndefined(); - }); + expect(ret.userId).toBeUndefined(); }); }); describe('mapToResponse', () => { - describe('When mapping AccountDto to AccountResponse', () => { - const setup = () => { - const testDto: AccountDto = accountDtoFactory.buildWithId(); - return testDto; + it('should map all fields', () => { + const testDto: AccountDto = { + id: new ObjectId().toString(), + userId: new ObjectId().toString(), + activated: true, + username: 'username', + createdAt: new Date(), + updatedAt: new Date(), }; + const ret = AccountResponseMapper.mapToResponse(testDto); - it('should map all fields', () => { - const testDto = setup(); - - const ret = AccountResponseMapper.mapToResponse(testDto); - - expect(ret.id).toBe(testDto.id); - expect(ret.userId).toBe(testDto.userId?.toString()); - expect(ret.activated).toBe(testDto.activated); - expect(ret.username).toBe(testDto.username); - }); + expect(ret.id).toBe(testDto.id); + expect(ret.userId).toBe(testDto.userId?.toString()); + expect(ret.activated).toBe(testDto.activated); + expect(ret.username).toBe(testDto.username); + expect(ret.updatedAt).toBe(testDto.updatedAt); }); }); }); diff --git a/apps/server/src/modules/account/mapper/account-response.mapper.ts b/apps/server/src/modules/account/mapper/account-response.mapper.ts index 84e20519bfe..dac10f98255 100644 --- a/apps/server/src/modules/account/mapper/account-response.mapper.ts +++ b/apps/server/src/modules/account/mapper/account-response.mapper.ts @@ -3,7 +3,6 @@ import { AccountDto } from '@modules/account/services/dto/account.dto'; import { AccountResponse } from '../controller/dto'; export class AccountResponseMapper { - // TODO: remove this one static mapToResponseFromEntity(account: Account): AccountResponse { return new AccountResponse({ id: account.id, diff --git a/apps/server/src/modules/account/repo/account.repo.integration.spec.ts b/apps/server/src/modules/account/repo/account.repo.integration.spec.ts index 1b1193cf8a4..bf4a44119fe 100644 --- a/apps/server/src/modules/account/repo/account.repo.integration.spec.ts +++ b/apps/server/src/modules/account/repo/account.repo.integration.spec.ts @@ -10,6 +10,7 @@ describe('account repo', () => { let module: TestingModule; let em: EntityManager; let repo: AccountRepo; + let mockAccounts: Account[]; beforeAll(async () => { module = await Test.createTestingModule({ @@ -24,6 +25,16 @@ describe('account repo', () => { await module.close(); }); + beforeEach(async () => { + mockAccounts = [ + accountFactory.build({ username: 'John Doe' }), + accountFactory.build({ username: 'Marry Doe' }), + accountFactory.build({ username: 'Susi Doe' }), + accountFactory.build({ username: 'Tim Doe' }), + ]; + await em.persistAndFlush(mockAccounts); + }); + afterEach(async () => { await cleanupCollections(em); }); @@ -33,340 +44,183 @@ describe('account repo', () => { }); describe('findByUserId', () => { - describe('When calling findByUserId with id', () => { - const setup = async () => { - const accountToFind = accountFactory.build(); - await em.persistAndFlush(accountToFind); - em.clear(); - return accountToFind; - }; - - it('should find user with id', async () => { - const accountToFind = await setup(); - const account = await repo.findByUserId(accountToFind.userId ?? ''); - expect(account?.id).toEqual(accountToFind.id); - }); + it('should findByUserId', async () => { + const accountToFind = accountFactory.build(); + await em.persistAndFlush(accountToFind); + em.clear(); + const account = await repo.findByUserId(accountToFind.userId ?? ''); + expect(account?.id).toEqual(accountToFind.id); }); }); describe('findByUsernameAndSystemId', () => { - describe('When username and systemId are given', () => { - const setup = async () => { - const accountToFind = accountFactory.withSystemId(new ObjectId(10)).build(); - await em.persistAndFlush(accountToFind); - em.clear(); - return accountToFind; - }; - - it('should return account', async () => { - const accountToFind = await setup(); - const account = await repo.findByUsernameAndSystemId( - accountToFind.username ?? '', - accountToFind.systemId ?? '' - ); - expect(account?.username).toEqual(accountToFind.username); - }); + it('should return account', async () => { + const accountToFind = accountFactory.withSystemId(new ObjectId(10)).build(); + await em.persistAndFlush(accountToFind); + em.clear(); + const account = await repo.findByUsernameAndSystemId(accountToFind.username ?? '', accountToFind.systemId ?? ''); + expect(account?.username).toEqual(accountToFind.username); }); - - describe('When username and systemId are not given', () => { - it('should return null', async () => { - const account = await repo.findByUsernameAndSystemId('', new ObjectId(undefined)); - expect(account).toBeNull(); - }); + it('should return null', async () => { + const account = await repo.findByUsernameAndSystemId('', new ObjectId(undefined)); + expect(account).toBeNull(); }); }); describe('findMultipleByUserId', () => { - describe('When multiple user ids are given', () => { - const setup = async () => { - const anAccountToFind = accountFactory.build(); - const anotherAccountToFind = accountFactory.build(); - await em.persistAndFlush(anAccountToFind); - await em.persistAndFlush(anotherAccountToFind); - em.clear(); - - return { anAccountToFind, anotherAccountToFind }; - }; - - it('should find multiple users', async () => { - const { anAccountToFind, anotherAccountToFind } = await setup(); - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - const accounts = await repo.findMultipleByUserId([anAccountToFind.userId!, anotherAccountToFind.userId!]); - expect(accounts).toContainEqual(anAccountToFind); - expect(accounts).toContainEqual(anotherAccountToFind); - expect(accounts).toHaveLength(2); - }); + it('should find multiple user by id', async () => { + const anAccountToFind = accountFactory.build(); + const anotherAccountToFind = accountFactory.build(); + await em.persistAndFlush(anAccountToFind); + await em.persistAndFlush(anotherAccountToFind); + em.clear(); + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + const accounts = await repo.findMultipleByUserId([anAccountToFind.userId!, anotherAccountToFind.userId!]); + expect(accounts).toContainEqual(anAccountToFind); + expect(accounts).toContainEqual(anotherAccountToFind); + expect(accounts).toHaveLength(2); }); - describe('When not existing user ids are given', () => { - it('should return empty list', async () => { - const accounts = await repo.findMultipleByUserId(['123456789012', '098765432101']); - expect(accounts).toHaveLength(0); - }); + it('should return empty list if no results', async () => { + const accountToFind = accountFactory.build(); + await em.persistAndFlush(accountToFind); + em.clear(); + const accounts = await repo.findMultipleByUserId(['123456789012', '098765432101']); + expect(accounts).toHaveLength(0); }); }); describe('findByUserIdOrFail', () => { - describe('When existing id is given', () => { - const setup = async () => { - const accountToFind = accountFactory.build(); - await em.persistAndFlush(accountToFind); - em.clear(); - return accountToFind; - }; - it('should find a user', async () => { - const accountToFind = await setup(); - const account = await repo.findByUserIdOrFail(accountToFind.userId ?? ''); - expect(account.id).toEqual(accountToFind.id); - }); + it('should find a user by id', async () => { + const accountToFind = accountFactory.build(); + await em.persistAndFlush(accountToFind); + em.clear(); + const account = await repo.findByUserIdOrFail(accountToFind.userId ?? ''); + expect(account.id).toEqual(accountToFind.id); }); - describe('When id does not exist', () => { - it('should throw not found error', async () => { - await expect(repo.findByUserIdOrFail('123456789012')).rejects.toThrow(NotFoundError); - }); + it('should throw if id does not exist', async () => { + const accountToFind = accountFactory.build(); + await em.persistAndFlush(accountToFind); + em.clear(); + await expect(repo.findByUserIdOrFail('123456789012')).rejects.toThrow(NotFoundError); }); }); describe('getObjectReference', () => { - describe('When a user id is given', () => { - const setup = async () => { - const user = userFactory.buildWithId(); - const account = accountFactory.build({ userId: user.id }); - await em.persistAndFlush([user, account]); - return { user, account }; - }; - it('should return a valid reference', async () => { - const { user, account } = await setup(); - - const reference = repo.getObjectReference(User, account.userId ?? ''); - - expect(reference).toBe(user); - }); + it('should return a valid reference', async () => { + const user = userFactory.buildWithId(); + const account = accountFactory.build({ userId: user.id }); + await em.persistAndFlush([user, account]); + + const reference = repo.getObjectReference(User, account.userId ?? ''); + + expect(reference).toBe(user); }); }); describe('saveWithoutFlush', () => { - describe('When calling saveWithoutFlush', () => { - const setup = () => { - const account = accountFactory.build(); - return account; - }; - it('should add an account to the persist stack', () => { - const account = setup(); - - repo.saveWithoutFlush(account); - expect(em.getUnitOfWork().getPersistStack().size).toBe(1); - }); + it('should add an account to the persist stack', () => { + const account = accountFactory.build(); + + repo.saveWithoutFlush(account); + expect(em.getUnitOfWork().getPersistStack().size).toBe(1); }); }); describe('flush', () => { - describe('When repo is flushed', () => { - const setup = () => { - const account = accountFactory.build(); - em.persist(account); - return account; - }; - - it('should save account', async () => { - const account = setup(); + it('should flush after save', async () => { + const account = accountFactory.build(); + em.persist(account); - expect(account.id).toBeNull(); + expect(account.id).toBeNull(); - await repo.flush(); + await repo.flush(); - expect(account.id).not.toBeNull(); - }); + expect(account.id).not.toBeNull(); }); }); - describe('searchByUsernamePartialMatch', () => { - describe('When searching with a partial user name', () => { - const setup = async () => { - const originalUsername = 'USER@EXAMPLE.COM'; - const partialUsername = 'user'; - const account = accountFactory.build({ username: originalUsername }); - await em.persistAndFlush([account]); - em.clear(); - return { originalUsername, partialUsername, account }; - }; - - it('should find exact one user', async () => { - const { originalUsername, partialUsername } = await setup(); - const [result] = await repo.searchByUsernamePartialMatch(partialUsername); - expect(result).toHaveLength(1); - expect(result[0]).toEqual(expect.objectContaining({ username: originalUsername })); - }); - }); - }); + describe('findByUsername', () => { + it('should find account by user name', async () => { + const originalUsername = 'USER@EXAMPLE.COM'; + const account = accountFactory.build({ username: originalUsername }); + await em.persistAndFlush([account]); + em.clear(); - describe('searchByUsernameExactMatch', () => { - describe('When searching for an exact match', () => { - const setup = async () => { - const originalUsername = 'USER@EXAMPLE.COM'; - const account = accountFactory.build({ username: originalUsername }); - await em.persistAndFlush([account]); - em.clear(); - return { originalUsername, account }; - }; - - it('should find exact one account', async () => { - const { originalUsername } = await setup(); - - const [result] = await repo.searchByUsernameExactMatch(originalUsername); - expect(result).toHaveLength(1); - expect(result[0]).toEqual(expect.objectContaining({ username: originalUsername })); - }); - }); + const [result] = await repo.searchByUsernameExactMatch('USER@EXAMPLE.COM'); + expect(result).toHaveLength(1); + expect(result[0]).toEqual(expect.objectContaining({ username: originalUsername })); - describe('When searching by username', () => { - const setup = async () => { - const originalUsername = 'USER@EXAMPLE.COM'; - const partialLowerCaseUsername = 'USER@example.COM'; - const lowercaseUsername = 'user@example.com'; - const account = accountFactory.build({ username: originalUsername }); - await em.persistAndFlush([account]); - em.clear(); - return { originalUsername, partialLowerCaseUsername, lowercaseUsername, account }; - }; - - it('should find account by user name, ignoring case', async () => { - const { originalUsername, partialLowerCaseUsername, lowercaseUsername } = await setup(); - - let [accounts] = await repo.searchByUsernameExactMatch(partialLowerCaseUsername); - expect(accounts).toHaveLength(1); - expect(accounts[0]).toEqual(expect.objectContaining({ username: originalUsername })); - - [accounts] = await repo.searchByUsernameExactMatch(lowercaseUsername); - expect(accounts).toHaveLength(1); - expect(accounts[0]).toEqual(expect.objectContaining({ username: originalUsername })); - }); + const [result2] = await repo.searchByUsernamePartialMatch('user'); + expect(result2).toHaveLength(1); + expect(result2[0]).toEqual(expect.objectContaining({ username: originalUsername })); }); + it('should find account by user name, ignoring case', async () => { + const originalUsername = 'USER@EXAMPLE.COM'; + const account = accountFactory.build({ username: originalUsername }); + await em.persistAndFlush([account]); + em.clear(); - describe('When using wildcard', () => { - const setup = async () => { - const originalUsername = 'USER@EXAMPLE.COM'; - const missingDotUserName = 'USER@EXAMPLECCOM'; - const wildcard = '.*'; - const account = accountFactory.build({ username: originalUsername }); - await em.persistAndFlush([account]); - em.clear(); - return { originalUsername, missingDotUserName, wildcard, account }; - }; - - it('should not find account', async () => { - const { missingDotUserName, wildcard } = await setup(); - - let [accounts] = await repo.searchByUsernameExactMatch(missingDotUserName); - expect(accounts).toHaveLength(0); - - [accounts] = await repo.searchByUsernameExactMatch(wildcard); - expect(accounts).toHaveLength(0); - }); + let [accounts] = await repo.searchByUsernameExactMatch('USER@example.COM'); + expect(accounts).toHaveLength(1); + expect(accounts[0]).toEqual(expect.objectContaining({ username: originalUsername })); + + [accounts] = await repo.searchByUsernameExactMatch('user@example.com'); + expect(accounts).toHaveLength(1); + expect(accounts[0]).toEqual(expect.objectContaining({ username: originalUsername })); }); - }); + it('should not find by wildcard', async () => { + const originalUsername = 'USER@EXAMPLE.COM'; + const account = accountFactory.build({ username: originalUsername }); + await em.persistAndFlush([account]); + em.clear(); - describe('deleteById', () => { - describe('When an id is given', () => { - const setup = async () => { - const account = accountFactory.buildWithId(); - await em.persistAndFlush([account]); + let [accounts] = await repo.searchByUsernameExactMatch('USER@EXAMPLECCOM'); + expect(accounts).toHaveLength(0); - return account; - }; + [accounts] = await repo.searchByUsernameExactMatch('.*'); + expect(accounts).toHaveLength(0); + }); + }); - it('should delete an account by id', async () => { - const account = await setup(); + describe('deleteId', () => { + it('should delete an account by id', async () => { + const account = accountFactory.buildWithId(); + await em.persistAndFlush([account]); - await expect(repo.deleteById(account.id)).resolves.not.toThrow(); + await expect(repo.deleteById(account.id)).resolves.not.toThrow(); - await expect(repo.findById(account.id)).rejects.toThrow(NotFoundError); - }); + await expect(repo.findById(account.id)).rejects.toThrow(NotFoundError); }); }); describe('deleteByUserId', () => { - describe('When an user id is given', () => { - const setup = async () => { - const user = userFactory.buildWithId(); - const account = accountFactory.build({ userId: user.id }); - await em.persistAndFlush([user, account]); - - return { user, account }; - }; + it('should delete an account by user id', async () => { + const user = userFactory.buildWithId(); + const account = accountFactory.build({ userId: user.id }); + await em.persistAndFlush([user, account]); - it('should delete an account by user id', async () => { - const { user, account } = await setup(); + await expect(repo.deleteByUserId(user.id)).resolves.not.toThrow(); - await expect(repo.deleteByUserId(user.id)).resolves.not.toThrow(); - - await expect(repo.findById(account.id)).rejects.toThrow(NotFoundError); - }); + await expect(repo.findById(account.id)).rejects.toThrow(NotFoundError); }); }); describe('findMany', () => { - describe('When no limit and offset are given', () => { - const setup = async () => { - const mockAccounts = [ - accountFactory.build({ username: 'John Doe' }), - accountFactory.build({ username: 'Marry Doe' }), - accountFactory.build({ username: 'Susi Doe' }), - accountFactory.build({ username: 'Tim Doe' }), - ]; - await em.persistAndFlush(mockAccounts); - return mockAccounts; - }; - - it('should find all accounts', async () => { - const mockAccounts = await setup(); - const foundAccounts = await repo.findMany(); - expect(foundAccounts).toEqual(mockAccounts); - }); - }); - - describe('When limit is given', () => { - const setup = async () => { - const limit = 1; - - const mockAccounts = [ - accountFactory.build({ username: 'John Doe' }), - accountFactory.build({ username: 'Marry Doe' }), - accountFactory.build({ username: 'Susi Doe' }), - accountFactory.build({ username: 'Tim Doe' }), - ]; - await em.persistAndFlush(mockAccounts); - return { limit, mockAccounts }; - }; - - it('should limit the result set', async () => { - const { limit } = await setup(); - const foundAccounts = await repo.findMany(0, limit); - expect(foundAccounts).toHaveLength(limit); - }); - }); - - describe('When offset is given', () => { - const setup = async () => { - const offset = 2; - - const mockAccounts = [ - accountFactory.build({ username: 'John Doe' }), - accountFactory.build({ username: 'Marry Doe' }), - accountFactory.build({ username: 'Susi Doe' }), - accountFactory.build({ username: 'Tim Doe' }), - ]; - await em.persistAndFlush(mockAccounts); - return { offset, mockAccounts }; - }; - - it('should skip n entries', async () => { - const { offset, mockAccounts } = await setup(); - - const foundAccounts = await repo.findMany(offset); - expect(foundAccounts).toHaveLength(mockAccounts.length - offset); - }); + it('should find all accounts', async () => { + const foundAccounts = await repo.findMany(); + expect(foundAccounts).toEqual(mockAccounts); + }); + it('limit the result set ', async () => { + const limit = 1; + const foundAccounts = await repo.findMany(0, limit); + expect(foundAccounts).toHaveLength(limit); + }); + it('skip n entries ', async () => { + const offset = 2; + const foundAccounts = await repo.findMany(offset); + expect(foundAccounts).toHaveLength(mockAccounts.length - offset); }); }); }); diff --git a/apps/server/src/modules/account/repo/account.repo.ts b/apps/server/src/modules/account/repo/account.repo.ts index e848973c5c6..fb68f0a759b 100644 --- a/apps/server/src/modules/account/repo/account.repo.ts +++ b/apps/server/src/modules/account/repo/account.repo.ts @@ -15,9 +15,7 @@ export class AccountRepo extends BaseRepo { * Finds an account by user id. * @param userId the user id */ - // TODO: here only EntityIds should arrive async findByUserId(userId: EntityId | ObjectId): Promise { - // TODO: you can use userId directly, without constructing an objectId return this._em.findOne(Account, { userId: new ObjectId(userId) }); } @@ -49,8 +47,6 @@ export class AccountRepo extends BaseRepo { await this._em.flush(); } - // TODO: the default values for skip and limit, are they required and/or correct here? - // TODO: use counted for the return type async searchByUsernameExactMatch(username: string, skip = 0, limit = 1): Promise<[Account[], number]> { return this.searchByUsername(username, skip, limit, true); } @@ -84,7 +80,6 @@ export class AccountRepo extends BaseRepo { limit: number, exactMatch: boolean ): Promise<[Account[], number]> { - // TODO: check that injections are not possible, eg make sure sanitizeHTML has been called at some point (for username) // escapes every character, that's not a unicode letter or number const escapedUsername = username.replace(/[^(\p{L}\p{N})]/gu, '\\$&'); const searchUsername = exactMatch ? `^${escapedUsername}$` : escapedUsername; diff --git a/apps/server/src/modules/account/review-comments.md b/apps/server/src/modules/account/review-comments.md deleted file mode 100644 index fc636019cdd..00000000000 --- a/apps/server/src/modules/account/review-comments.md +++ /dev/null @@ -1,12 +0,0 @@ -# Review Comments 14.7.23 - -- move mapper into repo folder -- write an md file or flow diagram describing how things work -- in what layer do the services belong? - -- naming of DO vs Entity (DO is the leading, "Account", entity is just the datalayer representation "AccountEntity") - -- new decisions for loggables - - -looked at this module only. \ No newline at end of file diff --git a/apps/server/src/modules/account/services/account-db.service.spec.ts b/apps/server/src/modules/account/services/account-db.service.spec.ts index 7fa4c4e44a8..64075bcb40c 100644 --- a/apps/server/src/modules/account/services/account-db.service.spec.ts +++ b/apps/server/src/modules/account/services/account-db.service.spec.ts @@ -3,9 +3,9 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityNotFoundError } from '@shared/common'; -import { Account, EntityId } from '@shared/domain'; +import { Account, EntityId, Permission, Role, RoleName, SchoolEntity, User } from '@shared/domain'; import { IdentityManagementService } from '@shared/infra/identity-management/identity-management.service'; -import { accountFactory, setupEntities, userFactory } from '@shared/testing'; +import { accountFactory, schoolFactory, setupEntities, userFactory } from '@shared/testing'; import { AccountEntityToDtoMapper } from '@modules/account/mapper'; import { AccountDto } from '@modules/account/services/dto'; import { IServerConfig } from '@modules/server'; @@ -19,11 +19,23 @@ import { AbstractAccountService } from './account.service.abstract'; describe('AccountDbService', () => { let module: TestingModule; let accountService: AbstractAccountService; - let accountRepo: DeepMocked; + let mockAccounts: Account[]; + let accountRepo: AccountRepo; let accountLookupServiceMock: DeepMocked; const defaultPassword = 'DummyPasswd!1'; + let mockSchool: SchoolEntity; + + let mockTeacherUser: User; + let mockStudentUser: User; + let mockUserWithoutAccount: User; + + let mockTeacherAccount: Account; + let mockStudentAccount: Account; + + let mockAccountWithSystemId: Account; + afterAll(async () => { await module.close(); }); @@ -35,7 +47,69 @@ describe('AccountDbService', () => { AccountLookupService, { provide: AccountRepo, - useValue: createMock(), + useValue: { + save: jest.fn().mockImplementation((account: Account): Promise => { + if (account.username === 'fail@to.update') { + return Promise.reject(); + } + const accountEntity = mockAccounts.find((tempAccount) => tempAccount.userId === account.userId); + if (accountEntity) { + Object.assign(accountEntity, account); + } + + return Promise.resolve(); + }), + deleteById: jest.fn().mockImplementation((): Promise => Promise.resolve()), + findMultipleByUserId: (userIds: EntityId[]): Promise => { + const accounts = mockAccounts.filter((tempAccount) => + userIds.find((userId) => tempAccount.userId?.toString() === userId) + ); + return Promise.resolve(accounts); + }, + findByUserId: (userId: EntityId): Promise => { + const account = mockAccounts.find((tempAccount) => tempAccount.userId?.toString() === userId); + if (account) { + return Promise.resolve(account); + } + return Promise.resolve(null); + }, + findByUserIdOrFail: (userId: EntityId): Promise => { + const account = mockAccounts.find((tempAccount) => tempAccount.userId?.toString() === userId); + + if (account) { + return Promise.resolve(account); + } + throw new EntityNotFoundError(Account.name); + }, + findByUsernameAndSystemId: (username: string, systemId: EntityId | ObjectId): Promise => { + const account = mockAccounts.find( + (tempAccount) => tempAccount.username === username && tempAccount.systemId === systemId + ); + if (account) { + return Promise.resolve(account); + } + return Promise.resolve(null); + }, + + findById: jest.fn().mockImplementation((accountId: EntityId | ObjectId): Promise => { + const account = mockAccounts.find((tempAccount) => tempAccount.id === accountId.toString()); + + if (account) { + return Promise.resolve(account); + } + throw new EntityNotFoundError(Account.name); + }), + searchByUsernameExactMatch: jest + .fn() + .mockImplementation((): Promise<[Account[], number]> => Promise.resolve([[mockTeacherAccount], 1])), + searchByUsernamePartialMatch: jest + .fn() + .mockImplementation( + (): Promise<[Account[], number]> => Promise.resolve([mockAccounts, mockAccounts.length]) + ), + deleteByUserId: jest.fn().mockImplementation((): Promise => Promise.resolve()), + findMany: jest.fn().mockImplementation((): Promise => Promise.resolve(mockAccounts)), + }, }, { provide: LegacyLogger, @@ -51,7 +125,14 @@ describe('AccountDbService', () => { }, { provide: AccountLookupService, - useValue: createMock(), + useValue: createMock({ + getInternalId: (id: EntityId | ObjectId): Promise => { + if (ObjectId.isValid(id)) { + return Promise.resolve(new ObjectId(id)); + } + return Promise.resolve(null); + }, + }), }, ], }).compile(); @@ -62,9 +143,28 @@ describe('AccountDbService', () => { }); beforeEach(() => { - jest.resetAllMocks(); jest.useFakeTimers(); jest.setSystemTime(new Date(2020, 1, 1)); + + mockSchool = schoolFactory.buildWithId(); + + mockTeacherUser = userFactory.buildWithId({ + school: mockSchool, + roles: [new Role({ name: RoleName.TEACHER, permissions: [Permission.STUDENT_EDIT] })], + }); + mockStudentUser = userFactory.buildWithId({ + school: mockSchool, + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], + }); + mockUserWithoutAccount = userFactory.buildWithId({ + school: mockSchool, + roles: [new Role({ name: RoleName.TEACHER, permissions: [Permission.STUDENT_EDIT] })], + }); + mockTeacherAccount = accountFactory.buildWithId({ userId: mockTeacherUser.id, password: defaultPassword }); + mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id, password: defaultPassword }); + + mockAccountWithSystemId = accountFactory.withSystemId(new ObjectId()).build(); + mockAccounts = [mockTeacherAccount, mockStudentAccount, mockAccountWithSystemId]; }); afterEach(() => { @@ -73,615 +173,294 @@ describe('AccountDbService', () => { }); describe('findById', () => { - describe('when searching by Id', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); - - mockTeacherAccountDto.username = 'changedUsername@example.org'; - mockTeacherAccountDto.activated = false; - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - - return { mockTeacherAccount }; - }; - it( - 'should return accountDto', - async () => { - const { mockTeacherAccount } = setup(); - - const resultAccount = await accountService.findById(mockTeacherAccount.id); - expect(resultAccount).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - }, - 10 * 60 * 1000 - ); - }); + it( + 'should return accountDto', + async () => { + const resultAccount = await accountService.findById(mockTeacherAccount.id); + expect(resultAccount).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); + }, + 10 * 60 * 1000 + ); }); describe('findByUserId', () => { - describe('when user id exists', () => { - const setup = () => { - const mockTeacherUser = userFactory.buildWithId(); - - const mockTeacherAccount = accountFactory.buildWithId(); - - accountRepo.findByUserId.mockImplementation((userId: EntityId | ObjectId): Promise => { - if (userId === mockTeacherUser.id) { - return Promise.resolve(mockTeacherAccount); - } - return Promise.resolve(null); - }); - - return { mockTeacherUser, mockTeacherAccount }; - }; - it('should return accountDto', async () => { - const { mockTeacherUser, mockTeacherAccount } = setup(); - const resultAccount = await accountService.findByUserId(mockTeacherUser.id); - expect(resultAccount).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - }); + it('should return accountDto', async () => { + const resultAccount = await accountService.findByUserId(mockTeacherUser.id); + expect(resultAccount).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); }); - - describe('when user id not exists', () => { - it('should return null', async () => { - const resultAccount = await accountService.findByUserId('nonExistentId'); - expect(resultAccount).toBeNull(); - }); + it('should return null', async () => { + const resultAccount = await accountService.findByUserId('nonExistentId'); + expect(resultAccount).toBeNull(); }); }); describe('findByUsernameAndSystemId', () => { - describe('when user name and system id exists', () => { - const setup = () => { - const mockAccountWithSystemId = accountFactory.withSystemId(new ObjectId()).build(); - accountRepo.findByUsernameAndSystemId.mockResolvedValue(mockAccountWithSystemId); - return { mockAccountWithSystemId }; - }; - it('should return accountDto', async () => { - const { mockAccountWithSystemId } = setup(); - const resultAccount = await accountService.findByUsernameAndSystemId( - mockAccountWithSystemId.username, - mockAccountWithSystemId.systemId ?? '' - ); - expect(resultAccount).not.toBe(undefined); - }); + it('should return accountDto', async () => { + const resultAccount = await accountService.findByUsernameAndSystemId( + mockAccountWithSystemId.username, + mockAccountWithSystemId.systemId ?? '' + ); + expect(resultAccount).not.toBe(undefined); }); - - describe('when only system id exists', () => { - const setup = () => { - const mockAccountWithSystemId = accountFactory.withSystemId(new ObjectId()).build(); - accountRepo.findByUsernameAndSystemId.mockImplementation( - (username: string, systemId: EntityId | ObjectId): Promise => { - if (mockAccountWithSystemId.username === username && mockAccountWithSystemId.systemId === systemId) { - return Promise.resolve(mockAccountWithSystemId); - } - return Promise.resolve(null); - } - ); - return { mockAccountWithSystemId }; - }; - it('should return null if username does not exist', async () => { - const { mockAccountWithSystemId } = setup(); - const resultAccount = await accountService.findByUsernameAndSystemId( - 'nonExistentUsername', - mockAccountWithSystemId.systemId ?? '' - ); - expect(resultAccount).toBeNull(); - }); + it('should return null if username does not exist', async () => { + const resultAccount = await accountService.findByUsernameAndSystemId( + 'nonExistentUsername', + mockAccountWithSystemId.systemId ?? '' + ); + expect(resultAccount).toBeNull(); }); - - describe('when only user name exists', () => { - const setup = () => { - const mockAccountWithSystemId = accountFactory.withSystemId(new ObjectId()).build(); - accountRepo.findByUsernameAndSystemId.mockImplementation( - (username: string, systemId: EntityId | ObjectId): Promise => { - if (mockAccountWithSystemId.username === username && mockAccountWithSystemId.systemId === systemId) { - return Promise.resolve(mockAccountWithSystemId); - } - return Promise.resolve(null); - } - ); - return { mockAccountWithSystemId }; - }; - it('should return null if system id does not exist', async () => { - const { mockAccountWithSystemId } = setup(); - const resultAccount = await accountService.findByUsernameAndSystemId( - mockAccountWithSystemId.username, - 'nonExistentSystemId' ?? '' - ); - expect(resultAccount).toBeNull(); - }); + it('should return null if system id does not exist', async () => { + const resultAccount = await accountService.findByUsernameAndSystemId( + mockAccountWithSystemId.username, + 'nonExistentSystemId' ?? '' + ); + expect(resultAccount).toBeNull(); }); }); describe('findMultipleByUserId', () => { - describe('when searching for multiple existing ids', () => { - const setup = () => { - const mockTeacherUser = userFactory.buildWithId(); - const mockStudentUser = userFactory.buildWithId(); - - const mockTeacherAccount = accountFactory.buildWithId({ - userId: mockTeacherUser.id, - password: defaultPassword, - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPassword, - }); - - accountRepo.findMultipleByUserId.mockImplementation((userIds: (EntityId | ObjectId)[]): Promise => { - const accounts = [mockStudentAccount, mockTeacherAccount].filter((tempAccount) => - userIds.find((userId) => tempAccount.userId?.toString() === userId) - ); - return Promise.resolve(accounts); - }); - return { mockStudentUser, mockStudentAccount, mockTeacherUser, mockTeacherAccount }; - }; - it('should return multiple accountDtos', async () => { - const { mockStudentUser, mockStudentAccount, mockTeacherUser, mockTeacherAccount } = setup(); - const resultAccounts = await accountService.findMultipleByUserId([mockTeacherUser.id, mockStudentUser.id]); - expect(resultAccounts).toContainEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - expect(resultAccounts).toContainEqual(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - expect(resultAccounts).toHaveLength(2); - }); + it('should return multiple accountDtos', async () => { + const resultAccounts = await accountService.findMultipleByUserId([mockTeacherUser.id, mockStudentUser.id]); + expect(resultAccounts).toContainEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); + expect(resultAccounts).toContainEqual(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); + expect(resultAccounts).toHaveLength(2); }); - - describe('when only user name exists', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - const mockStudentAccount = accountFactory.buildWithId(); - - accountRepo.findMultipleByUserId.mockImplementation((userIds: (EntityId | ObjectId)[]): Promise => { - const accounts = [mockStudentAccount, mockTeacherAccount].filter((tempAccount) => - userIds.find((userId) => tempAccount.userId?.toString() === userId) - ); - return Promise.resolve(accounts); - }); - return {}; - }; - it('should return empty array on mismatch', async () => { - setup(); - const resultAccount = await accountService.findMultipleByUserId(['nonExistentId1']); - expect(resultAccount).toHaveLength(0); - }); + it('should return empty array on mismatch', async () => { + const resultAccount = await accountService.findMultipleByUserId(['nonExistentId1']); + expect(resultAccount).toHaveLength(0); }); }); describe('findByUserIdOrFail', () => { - describe('when user exists', () => { - const setup = () => { - const mockTeacherUser = userFactory.buildWithId(); - const mockTeacherAccount = accountFactory.buildWithId({ - userId: mockTeacherUser.id, - password: defaultPassword, - }); - - accountRepo.findByUserIdOrFail.mockResolvedValue(mockTeacherAccount); - - return { mockTeacherUser, mockTeacherAccount }; - }; - - it('should return accountDto', async () => { - const { mockTeacherUser, mockTeacherAccount } = setup(); - const resultAccount = await accountService.findByUserIdOrFail(mockTeacherUser.id); - expect(resultAccount).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - }); + it('should return accountDto', async () => { + const resultAccount = await accountService.findByUserIdOrFail(mockTeacherUser.id); + expect(resultAccount).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); }); - - describe('when user does not exist', () => { - const setup = () => { - const mockTeacherUser = userFactory.buildWithId(); - const mockTeacherAccount = accountFactory.buildWithId({ - userId: mockTeacherUser.id, - password: defaultPassword, - }); - accountRepo.findByUserIdOrFail.mockImplementation((userId: EntityId | ObjectId): Promise => { - if (mockTeacherUser.id === userId) { - return Promise.resolve(mockTeacherAccount); - } - throw new EntityNotFoundError(Account.name); - }); - return {}; - }; - it('should throw EntityNotFoundError', async () => { - setup(); - await expect(accountService.findByUserIdOrFail('nonExistentId')).rejects.toThrow(EntityNotFoundError); - }); + it('should throw EntityNotFoundError', async () => { + await expect(accountService.findByUserIdOrFail('nonExistentId')).rejects.toThrow(EntityNotFoundError); }); }); describe('save', () => { - describe('when update an existing account', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); - - mockTeacherAccountDto.username = 'changedUsername@example.org'; - mockTeacherAccountDto.activated = false; - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - accountRepo.save.mockResolvedValue(); - - return { mockTeacherAccountDto, mockTeacherAccount }; - }; - - it('should update account', async () => { - const { mockTeacherAccountDto, mockTeacherAccount } = setup(); - const ret = await accountService.save(mockTeacherAccountDto); - - expect(accountRepo.save).toBeCalledTimes(1); - expect(ret).toBeDefined(); - expect(ret).toMatchObject({ - id: mockTeacherAccount.id, - username: mockTeacherAccountDto.username, - activated: mockTeacherAccountDto.activated, - systemId: mockTeacherAccount.systemId, - userId: mockTeacherAccount.userId, - }); + it('should update an existing account', async () => { + const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); + mockTeacherAccountDto.username = 'changedUsername@example.org'; + mockTeacherAccountDto.activated = false; + const ret = await accountService.save(mockTeacherAccountDto); + + expect(ret).toBeDefined(); + expect(ret).toMatchObject({ + id: mockTeacherAccount.id, + username: mockTeacherAccountDto.username, + activated: mockTeacherAccountDto.activated, + systemId: mockTeacherAccount.systemId, + userId: mockTeacherAccount.userId, }); }); - describe("when update an existing account's system", () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); - - mockTeacherAccountDto.username = 'changedUsername@example.org'; - mockTeacherAccountDto.systemId = '123456789012'; - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - accountRepo.save.mockResolvedValue(); - - return { mockTeacherAccountDto, mockTeacherAccount }; - }; - it("should update an existing account's system", async () => { - const { mockTeacherAccountDto, mockTeacherAccount } = setup(); - - const ret = await accountService.save(mockTeacherAccountDto); - expect(ret).toBeDefined(); - expect(ret).toMatchObject({ - id: mockTeacherAccount.id, - username: mockTeacherAccountDto.username, - activated: mockTeacherAccount.activated, - systemId: new ObjectId(mockTeacherAccountDto.systemId), - userId: mockTeacherAccount.userId, - }); + it("should update an existing account's system", async () => { + const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); + mockTeacherAccountDto.username = 'changedUsername@example.org'; + mockTeacherAccountDto.systemId = '123456789012'; + const ret = await accountService.save(mockTeacherAccountDto); + expect(ret).toBeDefined(); + expect(ret).toMatchObject({ + id: mockTeacherAccount.id, + username: mockTeacherAccountDto.username, + activated: mockTeacherAccount.activated, + systemId: new ObjectId(mockTeacherAccountDto.systemId), + userId: mockTeacherAccount.userId, }); }); - - describe("when update an existing account's user", () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - const mockStudentUser = accountFactory.buildWithId(); - const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); - - mockTeacherAccountDto.username = 'changedUsername@example.org'; - mockTeacherAccountDto.userId = mockStudentUser.id; - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - accountRepo.save.mockResolvedValue(); - - return { mockStudentUser, mockTeacherAccountDto, mockTeacherAccount }; - }; - it('should update account', async () => { - const { mockStudentUser, mockTeacherAccountDto, mockTeacherAccount } = setup(); - - const ret = await accountService.save(mockTeacherAccountDto); - expect(ret).toBeDefined(); - expect(ret).toMatchObject({ - id: mockTeacherAccount.id, - username: mockTeacherAccountDto.username, - activated: mockTeacherAccount.activated, - systemId: mockTeacherAccount.systemId, - userId: new ObjectId(mockStudentUser.id), - }); + it("should update an existing account's user", async () => { + const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); + mockTeacherAccountDto.username = 'changedUsername@example.org'; + mockTeacherAccountDto.userId = mockStudentUser.id; + const ret = await accountService.save(mockTeacherAccountDto); + expect(ret).toBeDefined(); + expect(ret).toMatchObject({ + id: mockTeacherAccount.id, + username: mockTeacherAccountDto.username, + activated: mockTeacherAccount.activated, + systemId: mockTeacherAccount.systemId, + userId: new ObjectId(mockStudentUser.id), }); }); - describe("when existing account's system is undefined", () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); - - mockTeacherAccountDto.username = 'changedUsername@example.org'; - mockTeacherAccountDto.systemId = undefined; - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - accountRepo.save.mockResolvedValue(); - - return { mockTeacherAccountDto, mockTeacherAccount }; - }; - it('should keep undefined on update', async () => { - const { mockTeacherAccountDto, mockTeacherAccount } = setup(); - - const ret = await accountService.save(mockTeacherAccountDto); - expect(ret).toBeDefined(); - expect(ret).toMatchObject({ - id: mockTeacherAccount.id, - username: mockTeacherAccountDto.username, - activated: mockTeacherAccount.activated, - systemId: mockTeacherAccountDto.systemId, - userId: mockTeacherAccount.userId, - }); - }); - }); - - describe('when account does not exists', () => { - const setup = () => { - const mockUserWithoutAccount = userFactory.buildWithId(); - - const accountToSave: AccountDto = { - createdAt: new Date(), - updatedAt: new Date(), - username: 'asdf@asdf.de', - userId: mockUserWithoutAccount.id, - systemId: '012345678912', - password: defaultPassword, - } as AccountDto; - (accountRepo.findById as jest.Mock).mockClear(); - (accountRepo.save as jest.Mock).mockClear(); - - return { accountToSave }; - }; - it('should save a new account', async () => { - const { accountToSave } = setup(); - - const ret = await accountService.save(accountToSave); - expect(ret).toBeDefined(); - expect(ret).toMatchObject({ - username: accountToSave.username, - userId: new ObjectId(accountToSave.userId), - systemId: new ObjectId(accountToSave.systemId), - createdAt: accountToSave.createdAt, - updatedAt: accountToSave.updatedAt, - }); + it("should keep existing account's system undefined on update", async () => { + const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); + mockTeacherAccountDto.username = 'changedUsername@example.org'; + mockTeacherAccountDto.systemId = undefined; + const ret = await accountService.save(mockTeacherAccountDto); + expect(ret).toBeDefined(); + expect(ret).toMatchObject({ + id: mockTeacherAccount.id, + username: mockTeacherAccountDto.username, + activated: mockTeacherAccount.activated, + systemId: mockTeacherAccountDto.systemId, + userId: mockTeacherAccount.userId, }); }); - - describe("when account's system undefined", () => { - const setup = () => { - const mockUserWithoutAccount = userFactory.buildWithId(); - - const accountToSave: AccountDto = { - createdAt: new Date(), - updatedAt: new Date(), - username: 'asdf@asdf.de', - userId: mockUserWithoutAccount.id, - password: defaultPassword, - } as AccountDto; - (accountRepo.findById as jest.Mock).mockClear(); - (accountRepo.save as jest.Mock).mockClear(); - - return { accountToSave }; - }; - it('should keep undefined on save', async () => { - const { accountToSave } = setup(); - - const ret = await accountService.save(accountToSave); - expect(ret).toBeDefined(); - expect(ret).toMatchObject({ - systemId: undefined, - }); + it('should save a new account', async () => { + const accountToSave: AccountDto = { + createdAt: new Date(), + updatedAt: new Date(), + username: 'asdf@asdf.de', + userId: mockUserWithoutAccount.id, + systemId: '012345678912', + password: defaultPassword, + } as AccountDto; + (accountRepo.findById as jest.Mock).mockClear(); + (accountRepo.save as jest.Mock).mockClear(); + const ret = await accountService.save(accountToSave); + expect(ret).toBeDefined(); + expect(ret).toMatchObject({ + username: accountToSave.username, + userId: new ObjectId(accountToSave.userId), + systemId: new ObjectId(accountToSave.systemId), + createdAt: accountToSave.createdAt, + updatedAt: accountToSave.updatedAt, }); }); - describe('when save account', () => { - const setup = () => { - const mockUserWithoutAccount = userFactory.buildWithId(); - - const accountToSave = { - createdAt: new Date(), - updatedAt: new Date(), - username: 'asdf@asdf.de', - userId: mockUserWithoutAccount.id, - systemId: '012345678912', - password: defaultPassword, - } as AccountDto; - (accountRepo.findById as jest.Mock).mockClear(); - (accountRepo.save as jest.Mock).mockClear(); - - return { accountToSave }; - }; - it('should encrypt password', async () => { - const { accountToSave } = setup(); - - await accountService.save(accountToSave); - const ret = await accountService.save(accountToSave); - expect(ret).toBeDefined(); - expect(ret).not.toMatchObject({ - password: defaultPassword, - }); + it("should keep account's system undefined on save", async () => { + const accountToSave: AccountDto = { + createdAt: new Date(), + updatedAt: new Date(), + username: 'asdf@asdf.de', + userId: mockUserWithoutAccount.id, + password: defaultPassword, + } as AccountDto; + (accountRepo.findById as jest.Mock).mockClear(); + (accountRepo.save as jest.Mock).mockClear(); + const ret = await accountService.save(accountToSave); + expect(ret).toBeDefined(); + expect(ret).toMatchObject({ + systemId: undefined, }); }); - describe('when creating a new account', () => { - const setup = () => { - const spy = jest.spyOn(accountRepo, 'save'); - const dto = { - username: 'john.doe@domain.tld', - password: '', - } as AccountDto; - (accountRepo.findById as jest.Mock).mockClear(); - (accountRepo.save as jest.Mock).mockClear(); - - return { spy, dto }; - }; - it('should set password to undefined if password is empty', async () => { - const { spy, dto } = setup(); - - await expect(accountService.save(dto)).resolves.not.toThrow(); - expect(accountRepo.findById).not.toHaveBeenCalled(); - expect(spy).toHaveBeenCalledWith( - expect.objectContaining({ - password: undefined, - }) - ); + it('should encrypt password', async () => { + const accountToSave = { + createdAt: new Date(), + updatedAt: new Date(), + username: 'asdf@asdf.de', + userId: mockUserWithoutAccount.id, + systemId: '012345678912', + password: defaultPassword, + } as AccountDto; + (accountRepo.findById as jest.Mock).mockClear(); + (accountRepo.save as jest.Mock).mockClear(); + await accountService.save(accountToSave); + const ret = await accountService.save(accountToSave); + expect(ret).toBeDefined(); + expect(ret).not.toMatchObject({ + password: defaultPassword, }); }); - describe('when password is empty while editing an existing account', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - - const spy = jest.spyOn(accountRepo, 'save'); - const dto = { - id: mockTeacherAccount.id, + it('should set password to undefined if password is empty while creating a new account', async () => { + const spy = jest.spyOn(accountRepo, 'save'); + const dto = { + username: 'john.doe@domain.tld', + password: '', + } as AccountDto; + (accountRepo.findById as jest.Mock).mockClear(); + (accountRepo.save as jest.Mock).mockClear(); + await expect(accountService.save(dto)).resolves.not.toThrow(); + expect(accountRepo.findById).not.toHaveBeenCalled(); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ password: undefined, - } as AccountDto; - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - accountRepo.save.mockResolvedValue(); + }) + ); + }); - return { mockTeacherAccount, spy, dto }; - }; - it('should not change password', async () => { - const { mockTeacherAccount, spy, dto } = setup(); - await expect(accountService.save(dto)).resolves.not.toThrow(); - expect(accountRepo.findById).toHaveBeenCalled(); - expect(spy).toHaveBeenCalledWith( - expect.objectContaining({ - password: mockTeacherAccount.password, - }) - ); - }); + it('should not change password if password is empty while editing an existing account', async () => { + const spy = jest.spyOn(accountRepo, 'save'); + const dto = { + id: mockTeacherAccount.id, + // username: 'john.doe@domain.tld', + password: undefined, + } as AccountDto; + (accountRepo.findById as jest.Mock).mockClear(); + (accountRepo.save as jest.Mock).mockClear(); + await expect(accountService.save(dto)).resolves.not.toThrow(); + expect(accountRepo.findById).toHaveBeenCalled(); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ + password: defaultPassword, + }) + ); }); }); describe('updateUsername', () => { - describe('when updating username', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); - const newUsername = 'newUsername'; - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - - return { mockTeacherAccount, mockTeacherAccountDto, newUsername }; - }; - it('should update only user name', async () => { - const { mockTeacherAccount, mockTeacherAccountDto, newUsername } = setup(); - const ret = await accountService.updateUsername(mockTeacherAccount.id, newUsername); - expect(ret).toBeDefined(); - expect(ret).toMatchObject({ - ...mockTeacherAccountDto, - username: newUsername, - }); + it('should update an existing account but no other information', async () => { + const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); + const newUsername = 'newUsername'; + const ret = await accountService.updateUsername(mockTeacherAccount.id, newUsername); + + expect(ret).toBeDefined(); + expect(ret).toMatchObject({ + ...mockTeacherAccountDto, + username: newUsername, }); }); }); describe('updateLastTriedFailedLogin', () => { - describe('when update last failed Login', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); - const theNewDate = new Date(); - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - - return { mockTeacherAccount, mockTeacherAccountDto, theNewDate }; - }; - it('should update last tried failed login', async () => { - const { mockTeacherAccount, mockTeacherAccountDto, theNewDate } = setup(); - const ret = await accountService.updateLastTriedFailedLogin(mockTeacherAccount.id, theNewDate); - - expect(ret).toBeDefined(); - expect(ret).toMatchObject({ - ...mockTeacherAccountDto, - lasttriedFailedLogin: theNewDate, - }); + it('should update last tried failed login', async () => { + const mockTeacherAccountDto = AccountEntityToDtoMapper.mapToDto(mockTeacherAccount); + const theNewDate = new Date(); + const ret = await accountService.updateLastTriedFailedLogin(mockTeacherAccount.id, theNewDate); + + expect(ret).toBeDefined(); + expect(ret).toMatchObject({ + ...mockTeacherAccountDto, + lasttriedFailedLogin: theNewDate, }); }); }); describe('validatePassword', () => { - describe('when accepted Password', () => { - const setup = async () => { - const ret = await accountService.validatePassword( - { password: await bcrypt.hash(defaultPassword, 10) } as unknown as AccountDto, - defaultPassword - ); - - return { ret }; - }; - it('should validate password', async () => { - const { ret } = await setup(); - - expect(ret).toBe(true); - }); + it('should validate password', async () => { + const ret = await accountService.validatePassword( + { password: await bcrypt.hash(defaultPassword, 10) } as unknown as AccountDto, + defaultPassword + ); + expect(ret).toBe(true); }); - - describe('when wrong Password', () => { - const setup = async () => { - const ret = await accountService.validatePassword( - { password: await bcrypt.hash(defaultPassword, 10) } as unknown as AccountDto, - 'incorrectPwd' - ); - - return { ret }; - }; - it('should report', async () => { - const { ret } = await setup(); - - expect(ret).toBe(false); - }); + it('should report wrong password', async () => { + const ret = await accountService.validatePassword( + { password: await bcrypt.hash(defaultPassword, 10) } as unknown as AccountDto, + 'incorrectPwd' + ); + expect(ret).toBe(false); }); - - describe('when missing account password', () => { - const setup = async () => { - const ret = await accountService.validatePassword({ password: undefined } as AccountDto, 'incorrectPwd'); - - return { ret }; - }; - it('should report', async () => { - const { ret } = await setup(); - - expect(ret).toBe(false); - }); + it('should report missing account password', async () => { + const ret = await accountService.validatePassword({ password: undefined } as AccountDto, 'incorrectPwd'); + expect(ret).toBe(false); }); }); describe('updatePassword', () => { - describe('when update Password', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - const newPassword = 'newPassword'; - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - - return { mockTeacherAccount, newPassword }; - }; - it('should update password', async () => { - const { mockTeacherAccount, newPassword } = setup(); + it('should update password', async () => { + const newPassword = 'newPassword'; + const ret = await accountService.updatePassword(mockTeacherAccount.id, newPassword); - const ret = await accountService.updatePassword(mockTeacherAccount.id, newPassword); - - expect(ret).toBeDefined(); - if (ret.password) { - await expect(bcrypt.compare(newPassword, ret.password)).resolves.toBe(true); - } else { - fail('return password is undefined'); - } - }); + expect(ret).toBeDefined(); + if (ret.password) { + await expect(bcrypt.compare(newPassword, ret.password)).resolves.toBe(true); + } else { + fail('return password is undefined'); + } }); }); describe('delete', () => { - describe('when delete an existing account', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - - return { mockTeacherAccount }; - }; + describe('when deleting existing account', () => { it('should delete account via repo', async () => { - const { mockTeacherAccount } = setup(); await accountService.delete(mockTeacherAccount.id); expect(accountRepo.deleteById).toHaveBeenCalledWith(new ObjectId(mockTeacherAccount.id)); }); @@ -689,125 +468,55 @@ describe('AccountDbService', () => { describe('when deleting non existing account', () => { const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); accountLookupServiceMock.getInternalId.mockResolvedValueOnce(null); - - return { mockTeacherAccount }; }; - it('should throw account not found', async () => { - const { mockTeacherAccount } = setup(); + it('should throw', async () => { + setup(); await expect(accountService.delete(mockTeacherAccount.id)).rejects.toThrow(); }); }); }); describe('deleteByUserId', () => { - describe('when delete account with given user id', () => { - const setup = () => { - const mockTeacherUser = userFactory.buildWithId(); - - const mockTeacherAccount = accountFactory.buildWithId({ - userId: mockTeacherUser.id, - password: defaultPassword, - }); - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - - return { mockTeacherUser, mockTeacherAccount }; - }; - it('should delete via repo', async () => { - const { mockTeacherUser, mockTeacherAccount } = setup(); - - await accountService.deleteByUserId(mockTeacherAccount.userId?.toString() ?? ''); - expect(accountRepo.deleteByUserId).toHaveBeenCalledWith(mockTeacherUser.id); - }); + it('should delete the account with given user id via repo', async () => { + await accountService.deleteByUserId(mockTeacherAccount.userId?.toString() ?? ''); + expect(accountRepo.deleteByUserId).toHaveBeenCalledWith(mockTeacherAccount.userId); }); }); describe('searchByUsernamePartialMatch', () => { - describe('when searching by part of username', () => { - const setup = () => { - const partialUserName = 'admin'; - const skip = 2; - const limit = 10; - const mockTeacherAccount = accountFactory.buildWithId(); - const mockStudentAccount = accountFactory.buildWithId(); - const mockAccountWithSystemId = accountFactory.withSystemId(new ObjectId()).build(); - const mockAccounts = [mockTeacherAccount, mockStudentAccount, mockAccountWithSystemId]; - - accountRepo.findById.mockResolvedValue(mockTeacherAccount); - accountRepo.searchByUsernamePartialMatch.mockResolvedValue([ - [mockTeacherAccount, mockStudentAccount, mockAccountWithSystemId], - 3, - ]); - accountLookupServiceMock.getInternalId.mockResolvedValue(mockTeacherAccount._id); - - return { partialUserName, skip, limit, mockTeacherAccount, mockAccounts }; - }; - it('should call repo', async () => { - const { partialUserName, skip, limit, mockTeacherAccount, mockAccounts } = setup(); - const [accounts, total] = await accountService.searchByUsernamePartialMatch(partialUserName, skip, limit); - expect(accountRepo.searchByUsernamePartialMatch).toHaveBeenCalledWith(partialUserName, skip, limit); - expect(total).toBe(mockAccounts.length); + it('should call repo', async () => { + const partialUserName = 'admin'; + const skip = 2; + const limit = 10; + const [accounts, total] = await accountService.searchByUsernamePartialMatch(partialUserName, skip, limit); + expect(accountRepo.searchByUsernamePartialMatch).toHaveBeenCalledWith(partialUserName, skip, limit); + expect(total).toBe(mockAccounts.length); - expect(accounts[0]).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - }); + expect(accounts[0]).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); }); }); - describe('searchByUsernameExactMatch', () => { - describe('when searching by username', () => { - const setup = () => { - const partialUserName = 'admin'; - const mockTeacherAccount = accountFactory.buildWithId(); - - accountRepo.searchByUsernameExactMatch.mockResolvedValue([[mockTeacherAccount], 1]); - - return { partialUserName, mockTeacherAccount }; - }; - it('should call repo', async () => { - const { partialUserName, mockTeacherAccount } = setup(); - const [accounts, total] = await accountService.searchByUsernameExactMatch(partialUserName); - expect(accountRepo.searchByUsernameExactMatch).toHaveBeenCalledWith(partialUserName); - expect(total).toBe(1); - expect(accounts[0]).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - }); + it('should call repo', async () => { + const partialUserName = 'admin'; + const [accounts, total] = await accountService.searchByUsernameExactMatch(partialUserName); + expect(accountRepo.searchByUsernameExactMatch).toHaveBeenCalledWith(partialUserName); + expect(total).toBe(1); + expect(accounts[0]).toEqual(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); }); }); - describe('findMany', () => { - describe('when find many one time', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - - accountRepo.findMany.mockResolvedValue([mockTeacherAccount]); - return {}; - }; - it('should call repo', async () => { - setup(); - const foundAccounts = await accountService.findMany(1, 1); - expect(accountRepo.findMany).toHaveBeenCalledWith(1, 1); - expect(foundAccounts).toBeDefined(); - }); - }); - describe('when call find many more than one time', () => { - const setup = () => { - const mockTeacherAccount = accountFactory.buildWithId(); - - accountRepo.findMany.mockResolvedValue([mockTeacherAccount]); - - return {}; - }; - it('should call repo each time', async () => { - setup(); - const foundAccounts = await accountService.findMany(); - expect(accountRepo.findMany).toHaveBeenCalledWith(0, 100); - expect(foundAccounts).toBeDefined(); - }); + describe('findMany', () => { + it('should call repo', async () => { + const foundAccounts = await accountService.findMany(1, 1); + expect(accountRepo.findMany).toHaveBeenCalledWith(1, 1); + expect(foundAccounts).toBeDefined(); + }); + it('should call repo', async () => { + const foundAccounts = await accountService.findMany(); + expect(accountRepo.findMany).toHaveBeenCalledWith(0, 100); + expect(foundAccounts).toBeDefined(); }); }); }); diff --git a/apps/server/src/modules/account/services/account-db.service.ts b/apps/server/src/modules/account/services/account-db.service.ts index 2ea02eeb3c4..1209ed86744 100644 --- a/apps/server/src/modules/account/services/account-db.service.ts +++ b/apps/server/src/modules/account/services/account-db.service.ts @@ -1,15 +1,13 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { Injectable } from '@nestjs/common'; +import bcrypt from 'bcryptjs'; import { EntityNotFoundError } from '@shared/common'; import { Account, Counted, EntityId } from '@shared/domain'; -import bcrypt from 'bcryptjs'; -import { AccountEntityToDtoMapper } from '../mapper'; import { AccountRepo } from '../repo/account.repo'; -import { AccountLookupService } from './account-lookup.service'; -import { AbstractAccountService } from './account.service.abstract'; +import { AccountEntityToDtoMapper } from '../mapper'; import { AccountDto, AccountSaveDto } from './dto'; - -// HINT: do more empty lines :) +import { AbstractAccountService } from './account.service.abstract'; +import { AccountLookupService } from './account-lookup.service'; @Injectable() export class AccountServiceDb extends AbstractAccountService { @@ -34,7 +32,10 @@ export class AccountServiceDb extends AbstractAccountService { } async findByUserIdOrFail(userId: EntityId): Promise { - const accountEntity = await this.accountRepo.findByUserIdOrFail(userId); + const accountEntity = await this.accountRepo.findByUserId(userId); + if (!accountEntity) { + throw new EntityNotFoundError('Account'); + } return AccountEntityToDtoMapper.mapToDto(accountEntity); } @@ -45,8 +46,6 @@ export class AccountServiceDb extends AbstractAccountService { async save(accountDto: AccountSaveDto): Promise { let account: Account; - // HINT: mapping could be done by a mapper (though this whole file is subject to be removed in the future) - // HINT: today we have logic to map back into unit work in the baseDO if (accountDto.id) { const internalId = await this.getInternalId(accountDto.id); account = await this.accountRepo.findById(internalId); @@ -76,7 +75,7 @@ export class AccountServiceDb extends AbstractAccountService { credentialHash: accountDto.credentialHash, }); - await this.accountRepo.save(account); // HINT: this can be done once in the end + await this.accountRepo.save(account); } return AccountEntityToDtoMapper.mapToDto(account); } @@ -129,7 +128,7 @@ export class AccountServiceDb extends AbstractAccountService { if (!account.password) { return Promise.resolve(false); } - return bcrypt.compare(comparePassword, account.password); // hint: first get result, then return seperately + return bcrypt.compare(comparePassword, account.password); } private async getInternalId(id: EntityId | ObjectId): Promise { diff --git a/apps/server/src/modules/account/services/account-idm.service.integration.spec.ts b/apps/server/src/modules/account/services/account-idm.service.integration.spec.ts index 2249a485f98..4761bbd80ca 100644 --- a/apps/server/src/modules/account/services/account-idm.service.integration.spec.ts +++ b/apps/server/src/modules/account/services/account-idm.service.integration.spec.ts @@ -94,125 +94,79 @@ describe('AccountIdmService Integration', () => { } }); - describe('save', () => { - describe('when account does not exists', () => { - it('should create a new account', async () => { - if (!isIdmReachable) return; - const createdAccount = await accountIdmService.save(testAccount); - const foundAccount = await identityManagementService.findAccountById(createdAccount.idmReferenceId ?? ''); - - expect(foundAccount).toEqual( - expect.objectContaining({ - id: createdAccount.idmReferenceId ?? '', - username: createdAccount.username, - attDbcAccountId: createdAccount.id, - attDbcUserId: createdAccount.userId, - attDbcSystemId: createdAccount.systemId, - }) - ); - }); - }); + it('save should create a new account', async () => { + if (!isIdmReachable) return; + const createdAccount = await accountIdmService.save(testAccount); + const foundAccount = await identityManagementService.findAccountById(createdAccount.idmReferenceId ?? ''); + + expect(foundAccount).toEqual( + expect.objectContaining({ + id: createdAccount.idmReferenceId ?? '', + username: createdAccount.username, + attDbcAccountId: testDbcAccountId, + attDbcUserId: createdAccount.userId, + attDbcSystemId: createdAccount.systemId, + }) + ); }); - describe('save', () => { - describe('when account exists', () => { - const setup = async () => { - const newUserName = 'jane.doe@mail.tld'; - const idmId = await createAccount(); - - return { idmId, newUserName }; - }; - it('should update account', async () => { - if (!isIdmReachable) return; - const { idmId, newUserName } = await setup(); - - await accountIdmService.save({ - id: testDbcAccountId, - username: newUserName, - }); - - const foundAccount = await identityManagementService.findAccountById(idmId); - - expect(foundAccount).toEqual( - expect.objectContaining({ - id: idmId, - username: newUserName, - }) - ); - }); + it('save should update existing account', async () => { + if (!isIdmReachable) return; + const newUsername = 'jane.doe@mail.tld'; + const idmId = await createAccount(); + + await accountIdmService.save({ + id: testDbcAccountId, + username: newUsername, }); + const foundAccount = await identityManagementService.findAccountById(idmId); + + expect(foundAccount).toEqual( + expect.objectContaining({ + id: idmId, + username: newUsername, + }) + ); }); - describe('updateUsername', () => { - describe('when updating username', () => { - const setup = async () => { - const newUserName = 'jane.doe@mail.tld'; - const idmId = await createAccount(); - - return { newUserName, idmId }; - }; - it('should update only username', async () => { - if (!isIdmReachable) return; - const { newUserName, idmId } = await setup(); - - await accountIdmService.updateUsername(testDbcAccountId, newUserName); - const foundAccount = await identityManagementService.findAccountById(idmId); - - expect(foundAccount).toEqual( - expect.objectContaining>({ - username: newUserName, - }) - ); - }); - }); + it('updateUsername should update username', async () => { + if (!isIdmReachable) return; + const newUserName = 'jane.doe@mail.tld'; + const idmId = await createAccount(); + await accountIdmService.updateUsername(testDbcAccountId, newUserName); + + const foundAccount = await identityManagementService.findAccountById(idmId); + + expect(foundAccount).toEqual( + expect.objectContaining>({ + username: newUserName, + }) + ); }); - describe('updatePassword', () => { - describe('when updating with permitted password', () => { - const setup = async () => { - await createAccount(); - }; - it('should update password', async () => { - if (!isIdmReachable) return; - await setup(); - await expect(accountIdmService.updatePassword(testDbcAccountId, 'newPassword')).resolves.not.toThrow(); - }); - }); + it('updatePassword should update password', async () => { + if (!isIdmReachable) return; + await createAccount(); + await expect(accountIdmService.updatePassword(testDbcAccountId, 'newPassword')).resolves.not.toThrow(); }); - describe('delete', () => { - describe('when delete account', () => { - const setup = async () => { - const idmId = await createAccount(); - const foundAccount = await identityManagementService.findAccountById(idmId); - return { idmId, foundAccount }; - }; - it('should remove account', async () => { - if (!isIdmReachable) return; - const { idmId, foundAccount } = await setup(); - expect(foundAccount).toBeDefined(); - - await accountIdmService.delete(testDbcAccountId); - await expect(identityManagementService.findAccountById(idmId)).rejects.toThrow(); - }); - }); + it('delete should remove account', async () => { + if (!isIdmReachable) return; + const idmId = await createAccount(); + const foundAccount = await identityManagementService.findAccountById(idmId); + expect(foundAccount).toBeDefined(); + + await accountIdmService.delete(testDbcAccountId); + await expect(identityManagementService.findAccountById(idmId)).rejects.toThrow(); }); - describe('deleteByUserId', () => { - describe('when deleting by UserId', () => { - const setup = async () => { - const idmId = await createAccount(); - const foundAccount = await identityManagementService.findAccountById(idmId); - return { idmId, foundAccount }; - }; - it('should remove account', async () => { - if (!isIdmReachable) return; - const { idmId, foundAccount } = await setup(); - expect(foundAccount).toBeDefined(); - - await accountIdmService.deleteByUserId(testAccount.userId ?? ''); - await expect(identityManagementService.findAccountById(idmId)).rejects.toThrow(); - }); - }); + it('deleteByUserId should remove account', async () => { + if (!isIdmReachable) return; + const idmId = await createAccount(); + const foundAccount = await identityManagementService.findAccountById(idmId); + expect(foundAccount).toBeDefined(); + + await accountIdmService.deleteByUserId(testAccount.userId ?? ''); + await expect(identityManagementService.findAccountById(idmId)).rejects.toThrow(); }); }); diff --git a/apps/server/src/modules/account/services/account-idm.service.spec.ts b/apps/server/src/modules/account/services/account-idm.service.spec.ts index 1669b4ca4c4..4b997d1b3fe 100644 --- a/apps/server/src/modules/account/services/account-idm.service.spec.ts +++ b/apps/server/src/modules/account/services/account-idm.service.spec.ts @@ -76,203 +76,155 @@ describe('AccountIdmService', () => { }); describe('save', () => { - describe('when save an existing account', () => { - const setup = () => { - idmServiceMock.createAccount.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.updateAccount.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.updateAccountPassword.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.findAccountById.mockResolvedValue(mockIdmAccount); - const updateSpy = jest.spyOn(idmServiceMock, 'updateAccount'); - const createSpy = jest.spyOn(idmServiceMock, 'createAccount'); - - const mockAccountDto = { - id: mockIdmAccountRefId, - username: 'testUserName', - userId: 'userId', - systemId: 'systemId', - }; - return { updateSpy, createSpy, mockAccountDto }; + const setup = () => { + idmServiceMock.createAccount.mockResolvedValue(mockIdmAccount.id); + idmServiceMock.updateAccount.mockResolvedValue(mockIdmAccount.id); + idmServiceMock.updateAccountPassword.mockResolvedValue(mockIdmAccount.id); + idmServiceMock.findAccountById.mockResolvedValue(mockIdmAccount); + }; + + it('should update an existing account', async () => { + setup(); + const updateSpy = jest.spyOn(idmServiceMock, 'updateAccount'); + const createSpy = jest.spyOn(idmServiceMock, 'createAccount'); + + const mockAccountDto = { + id: mockIdmAccountRefId, + username: 'testUserName', + userId: 'userId', + systemId: 'systemId', }; - - it('should update account information', async () => { - const { updateSpy, createSpy, mockAccountDto } = setup(); - - const ret = await accountIdmService.save(mockAccountDto); - - expect(updateSpy).toHaveBeenCalled(); - expect(createSpy).not.toHaveBeenCalled(); - - expect(ret).toBeDefined(); - expect(ret).toMatchObject>({ - id: mockIdmAccount.attDbcAccountId, - idmReferenceId: mockIdmAccount.id, - createdAt: mockIdmAccount.createdDate, - updatedAt: mockIdmAccount.createdDate, - username: mockIdmAccount.username, - }); + const ret = await accountIdmService.save(mockAccountDto); + + expect(updateSpy).toHaveBeenCalled(); + expect(createSpy).not.toHaveBeenCalled(); + + expect(ret).toBeDefined(); + expect(ret).toMatchObject>({ + id: mockIdmAccount.attDbcAccountId, + idmReferenceId: mockIdmAccount.id, + createdAt: mockIdmAccount.createdDate, + updatedAt: mockIdmAccount.createdDate, + username: mockIdmAccount.username, }); }); - describe('when save an existing account', () => { - const setup = () => { - idmServiceMock.createAccount.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.updateAccount.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.updateAccountPassword.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.findAccountById.mockResolvedValue(mockIdmAccount); - const updateSpy = jest.spyOn(idmServiceMock, 'updateAccount'); - const updatePasswordSpy = jest.spyOn(idmServiceMock, 'updateAccountPassword'); - - const mockAccountDto: AccountSaveDto = { - id: mockIdmAccountRefId, - username: 'testUserName', - userId: 'userId', - systemId: 'systemId', - password: 'password', - }; - return { updateSpy, updatePasswordSpy, mockAccountDto }; + it('should update an existing accounts password', async () => { + setup(); + const updateSpy = jest.spyOn(idmServiceMock, 'updateAccount'); + const updatePasswordSpy = jest.spyOn(idmServiceMock, 'updateAccountPassword'); + + const mockAccountDto: AccountSaveDto = { + id: mockIdmAccountRefId, + username: 'testUserName', + userId: 'userId', + systemId: 'systemId', + password: 'password', }; - it('should update account password', async () => { - const { updateSpy, updatePasswordSpy, mockAccountDto } = setup(); + const ret = await accountIdmService.save(mockAccountDto); - const ret = await accountIdmService.save(mockAccountDto); - - expect(updateSpy).toHaveBeenCalled(); - expect(updatePasswordSpy).toHaveBeenCalled(); - expect(ret).toBeDefined(); - }); + expect(updateSpy).toHaveBeenCalled(); + expect(updatePasswordSpy).toHaveBeenCalled(); + expect(ret).toBeDefined(); }); - describe('when save not existing account', () => { - const setup = () => { - idmServiceMock.createAccount.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.updateAccount.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.updateAccountPassword.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.findAccountById.mockResolvedValue(mockIdmAccount); - const updateSpy = jest.spyOn(idmServiceMock, 'updateAccount'); - const createSpy = jest.spyOn(idmServiceMock, 'createAccount'); - - const mockAccountDto = { username: 'testUserName', id: undefined, userId: 'userId', systemId: 'systemId' }; - - return { updateSpy, createSpy, mockAccountDto }; - }; - it('should create a new account', async () => { - const { updateSpy, createSpy, mockAccountDto } = setup(); + it('should create a new account', async () => { + setup(); + const updateSpy = jest.spyOn(idmServiceMock, 'updateAccount'); + const createSpy = jest.spyOn(idmServiceMock, 'createAccount'); - const ret = await accountIdmService.save(mockAccountDto); + const mockAccountDto = { username: 'testUserName', id: undefined, userId: 'userId', systemId: 'systemId' }; + const ret = await accountIdmService.save(mockAccountDto); - expect(updateSpy).not.toHaveBeenCalled(); - expect(createSpy).toHaveBeenCalled(); + expect(updateSpy).not.toHaveBeenCalled(); + expect(createSpy).toHaveBeenCalled(); - expect(ret).toBeDefined(); - expect(ret).toMatchObject>({ - id: mockIdmAccount.attDbcAccountId, - idmReferenceId: mockIdmAccount.id, - createdAt: mockIdmAccount.createdDate, - updatedAt: mockIdmAccount.createdDate, - username: mockIdmAccount.username, - }); + expect(ret).toBeDefined(); + expect(ret).toMatchObject>({ + id: mockIdmAccount.attDbcAccountId, + idmReferenceId: mockIdmAccount.id, + createdAt: mockIdmAccount.createdDate, + updatedAt: mockIdmAccount.createdDate, + username: mockIdmAccount.username, }); }); - - describe('when save not existing account', () => { - const setup = () => { - idmServiceMock.createAccount.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.updateAccount.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.updateAccountPassword.mockResolvedValue(mockIdmAccount.id); - idmServiceMock.findAccountById.mockResolvedValue(mockIdmAccount); - accountLookupServiceMock.getExternalId.mockResolvedValue(null); - const mockAccountDto = { - id: mockIdmAccountRefId, - username: 'testUserName', - userId: 'userId', - systemId: 'systemId', - }; - - return { mockAccountDto }; + it('should create a new account on update error', async () => { + setup(); + accountLookupServiceMock.getExternalId.mockResolvedValue(null); + const mockAccountDto = { + id: mockIdmAccountRefId, + username: 'testUserName', + userId: 'userId', + systemId: 'systemId', }; - it('should create a new account on update error', async () => { - const { mockAccountDto } = setup(); - - const ret = await accountIdmService.save(mockAccountDto); - - expect(idmServiceMock.createAccount).toHaveBeenCalled(); - expect(ret).toBeDefined(); - expect(ret).toMatchObject>({ - id: mockIdmAccount.attDbcAccountId, - idmReferenceId: mockIdmAccount.id, - createdAt: mockIdmAccount.createdDate, - updatedAt: mockIdmAccount.createdDate, - username: mockIdmAccount.username, - }); + + const ret = await accountIdmService.save(mockAccountDto); + + expect(idmServiceMock.createAccount).toHaveBeenCalled(); + expect(ret).toBeDefined(); + expect(ret).toMatchObject>({ + id: mockIdmAccount.attDbcAccountId, + idmReferenceId: mockIdmAccount.id, + createdAt: mockIdmAccount.createdDate, + updatedAt: mockIdmAccount.createdDate, + username: mockIdmAccount.username, }); }); }); describe('updateUsername', () => { - describe('when update Username', () => { - const setup = () => { - accountLookupServiceMock.getExternalId.mockResolvedValue(mockIdmAccount.id); - }; - it('should map result correctly', async () => { - setup(); - const ret = await accountIdmService.updateUsername(mockIdmAccountRefId, 'any'); - - expect(ret).toBeDefined(); - expect(ret).toMatchObject>({ - id: mockIdmAccount.attDbcAccountId, - idmReferenceId: mockIdmAccount.id, - createdAt: mockIdmAccount.createdDate, - updatedAt: mockIdmAccount.createdDate, - username: mockIdmAccount.username, - }); + it('should map result correctly', async () => { + accountLookupServiceMock.getExternalId.mockResolvedValue(mockIdmAccount.id); + const ret = await accountIdmService.updateUsername(mockIdmAccountRefId, 'any'); + + expect(ret).toBeDefined(); + expect(ret).toMatchObject>({ + id: mockIdmAccount.attDbcAccountId, + idmReferenceId: mockIdmAccount.id, + createdAt: mockIdmAccount.createdDate, + updatedAt: mockIdmAccount.createdDate, + username: mockIdmAccount.username, }); }); }); describe('updatePassword', () => { - describe('when update password', () => { - const setup = () => { - accountLookupServiceMock.getExternalId.mockResolvedValue(mockIdmAccount.id); - }; - it('should map result correctly', async () => { - setup(); - const ret = await accountIdmService.updatePassword(mockIdmAccountRefId, 'any'); - - expect(ret).toBeDefined(); - expect(ret).toMatchObject>({ - id: mockIdmAccount.attDbcAccountId, - idmReferenceId: mockIdmAccount.id, - createdAt: mockIdmAccount.createdDate, - updatedAt: mockIdmAccount.createdDate, - username: mockIdmAccount.username, - }); + it('should map result correctly', async () => { + accountLookupServiceMock.getExternalId.mockResolvedValue(mockIdmAccount.id); + const ret = await accountIdmService.updatePassword(mockIdmAccountRefId, 'any'); + + expect(ret).toBeDefined(); + expect(ret).toMatchObject>({ + id: mockIdmAccount.attDbcAccountId, + idmReferenceId: mockIdmAccount.id, + createdAt: mockIdmAccount.createdDate, + updatedAt: mockIdmAccount.createdDate, + username: mockIdmAccount.username, }); }); }); describe('validatePassword', () => { - describe('when validate password', () => { - const setup = (acceptPassword: boolean) => { - idmOauthServiceMock.resourceOwnerPasswordGrant.mockResolvedValue( - acceptPassword ? '{ "alg": "HS256", "typ": "JWT" }' : undefined - ); - }; - it('should validate password by checking JWT', async () => { - setup(true); - const ret = await accountIdmService.validatePassword( - { username: 'username' } as unknown as AccountDto, - 'password' - ); - expect(ret).toBe(true); - }); - it('should report wrong password, i. e. non successful JWT creation', async () => { - setup(false); - const ret = await accountIdmService.validatePassword( - { username: 'username' } as unknown as AccountDto, - 'password' - ); - expect(ret).toBe(false); - }); + const setup = (acceptPassword: boolean) => { + idmOauthServiceMock.resourceOwnerPasswordGrant.mockResolvedValue( + acceptPassword ? '{ "alg": "HS256", "typ": "JWT" }' : undefined + ); + }; + it('should validate password by checking JWT', async () => { + setup(true); + const ret = await accountIdmService.validatePassword( + { username: 'username' } as unknown as AccountDto, + 'password' + ); + expect(ret).toBe(true); + }); + it('should report wrong password, i. e. non successful JWT creation', async () => { + setup(false); + const ret = await accountIdmService.validatePassword( + { username: 'username' } as unknown as AccountDto, + 'password' + ); + expect(ret).toBe(false); }); }); @@ -296,7 +248,7 @@ describe('AccountIdmService', () => { accountLookupServiceMock.getExternalId.mockResolvedValue(null); }; - it('should throw account not found error', async () => { + it('should throw error', async () => { setup(); await expect(accountIdmService.delete(mockIdmAccountRefId)).rejects.toThrow(); }); @@ -304,19 +256,16 @@ describe('AccountIdmService', () => { }); describe('deleteByUserId', () => { - describe('when deleting an account by user id', () => { - const setup = () => { - idmServiceMock.findAccountByDbcUserId.mockResolvedValue(mockIdmAccount); - const deleteSpy = jest.spyOn(idmServiceMock, 'deleteAccountById'); - return { deleteSpy }; - }; + const setup = () => { + idmServiceMock.findAccountByDbcUserId.mockResolvedValue(mockIdmAccount); + }; - it('should delete the account with given user id via repo', async () => { - const { deleteSpy } = setup(); + it('should delete the account with given user id via repo', async () => { + setup(); + const deleteSpy = jest.spyOn(idmServiceMock, 'deleteAccountById'); - await accountIdmService.deleteByUserId(mockIdmAccount.attDbcUserId ?? ''); - expect(deleteSpy).toHaveBeenCalledWith(mockIdmAccount.id); - }); + await accountIdmService.deleteByUserId(mockIdmAccount.attDbcUserId ?? ''); + expect(deleteSpy).toHaveBeenCalledWith(mockIdmAccount.id); }); }); @@ -338,7 +287,7 @@ describe('AccountIdmService', () => { idmServiceMock.findAccountById.mockRejectedValue(new Error()); }; - it('should throw account not found', async () => { + it('should throw', async () => { setup(); await expect(accountIdmService.findById('notExistingId')).rejects.toThrow(); }); @@ -410,7 +359,7 @@ describe('AccountIdmService', () => { idmServiceMock.findAccountByDbcUserId.mockResolvedValue(undefined as unknown as IdmAccount); }; - it('should throw account not found', async () => { + it('should throw', async () => { setup(); await expect(accountIdmService.findByUserIdOrFail('notExistingId')).rejects.toThrow(EntityNotFoundError); }); @@ -516,7 +465,7 @@ describe('AccountIdmService', () => { }); }); - it('findMany should throw not implemented Exception', async () => { + it('findMany should throw', async () => { await expect(accountIdmService.findMany(0, 0)).rejects.toThrow(NotImplementedException); }); }); diff --git a/apps/server/src/modules/account/services/account-idm.service.ts b/apps/server/src/modules/account/services/account-idm.service.ts index 039db80eddf..68bcfb42bae 100644 --- a/apps/server/src/modules/account/services/account-idm.service.ts +++ b/apps/server/src/modules/account/services/account-idm.service.ts @@ -1,13 +1,13 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { Injectable, NotImplementedException } from '@nestjs/common'; import { EntityNotFoundError } from '@shared/common'; -import { IdentityManagementOauthService, IdentityManagementService } from '@shared/infra/identity-management'; import { Counted, EntityId, IdmAccount, IdmAccountUpdate } from '@shared/domain'; +import { IdentityManagementService, IdentityManagementOauthService } from '@shared/infra/identity-management'; import { LegacyLogger } from '@src/core/logger'; import { AccountIdmToDtoMapper } from '../mapper'; -import { AccountLookupService } from './account-lookup.service'; import { AbstractAccountService } from './account.service.abstract'; import { AccountDto, AccountSaveDto } from './dto'; +import { AccountLookupService } from './account-lookup.service'; @Injectable() export class AccountServiceIdm extends AbstractAccountService { @@ -27,7 +27,6 @@ export class AccountServiceIdm extends AbstractAccountService { return account; } - // TODO: this needs a better solution. probably needs followup meeting to come up with something async findMultipleByUserId(userIds: EntityId[]): Promise { const results = new Array(); for (const userId of userIds) { @@ -35,7 +34,6 @@ export class AccountServiceIdm extends AbstractAccountService { // eslint-disable-next-line no-await-in-loop results.push(await this.identityManager.findAccountByDbcUserId(userId)); } catch { - // TODO: dont simply forget errors. maybe use a filter instead? // ignore entry } } @@ -48,7 +46,6 @@ export class AccountServiceIdm extends AbstractAccountService { const result = await this.identityManager.findAccountByDbcUserId(userId); return this.accountIdmToDtoMapper.mapToDto(result); } catch { - // TODO: dont simply forget errors return null; } } @@ -96,10 +93,8 @@ export class AccountServiceIdm extends AbstractAccountService { attDbcUserId: accountDto.userId, attDbcSystemId: accountDto.systemId, }; - // TODO: probably do some method extraction here if (accountDto.id) { let idmId: string | undefined; - // TODO: extract into a method that hides the trycatch try { idmId = await this.getIdmAccountId(accountDto.id); } catch { diff --git a/apps/server/src/modules/account/services/account.service.abstract.ts b/apps/server/src/modules/account/services/account.service.abstract.ts index d25dbc0ac4a..b2e198f6a86 100644 --- a/apps/server/src/modules/account/services/account.service.abstract.ts +++ b/apps/server/src/modules/account/services/account.service.abstract.ts @@ -2,8 +2,6 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { Counted, EntityId } from '@shared/domain'; import { AccountDto, AccountSaveDto } from './dto'; -// TODO: split functions which are only needed for feathers - export abstract class AbstractAccountService { abstract findById(id: EntityId): Promise; @@ -13,7 +11,6 @@ export abstract class AbstractAccountService { abstract findByUserIdOrFail(userId: EntityId): Promise; - // HINT: it would be preferable to use entityId here. Needs to be checked if this is blocked by lecacy code abstract findByUsernameAndSystemId(username: string, systemId: EntityId | ObjectId): Promise; abstract save(accountDto: AccountSaveDto): Promise; diff --git a/apps/server/src/modules/account/services/account.service.integration.spec.ts b/apps/server/src/modules/account/services/account.service.integration.spec.ts index 5d5caa24263..d001925000b 100644 --- a/apps/server/src/modules/account/services/account.service.integration.spec.ts +++ b/apps/server/src/modules/account/services/account.service.integration.spec.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-unsafe-assignment */ import { createMock } from '@golevelup/ts-jest'; import KeycloakAdminClient from '@keycloak/keycloak-admin-client-cjs/keycloak-admin-client-cjs-index'; import { EntityManager } from '@mikro-orm/mongodb'; @@ -159,151 +158,95 @@ describe('AccountService Integration', () => { ); }; - describe('save', () => { - describe('when account not exists', () => { - it('should create a new account', async () => { - if (!isIdmReachable) return; - const account = await accountService.save(testAccount); - await compareDbAccount(account.id, account); - await compareIdmAccount(account.idmReferenceId ?? '', account); - }); - }); - - describe('when account exists', () => { - const setup = async () => { - const newUsername = 'jane.doe@mail.tld'; - const [dbId, idmId] = await createAccount(); - const originalAccount = await accountService.findById(dbId); - return { newUsername, dbId, idmId, originalAccount }; - }; - it('save should update existing account', async () => { - if (!isIdmReachable) return; - const { newUsername, dbId, idmId, originalAccount } = await setup(); - const updatedAccount = await accountService.save({ - ...originalAccount, - username: newUsername, - }); - await compareDbAccount(dbId, updatedAccount); - await compareIdmAccount(idmId, updatedAccount); - }); - }); - - describe('when only db account exists', () => { - const setup = async () => { - const newUsername = 'jane.doe@mail.tld'; - const dbId = await createDbAccount(); - const originalAccount = await accountService.findById(dbId); - return { newUsername, dbId, originalAccount }; - }; - it('should create idm account for existing db account', async () => { - if (!isIdmReachable) return; - const { newUsername, dbId, originalAccount } = await setup(); + it('save should create a new account', async () => { + if (!isIdmReachable) return; + const account = await accountService.save(testAccount); + await compareDbAccount(account.id, account); + await compareIdmAccount(account.idmReferenceId ?? '', account); + }); - const updatedAccount = await accountService.save({ - ...originalAccount, - username: newUsername, - }); - await compareDbAccount(dbId, updatedAccount); - await compareIdmAccount(updatedAccount.idmReferenceId ?? '', updatedAccount); - }); + it('save should update existing account', async () => { + if (!isIdmReachable) return; + const newUsername = 'jane.doe@mail.tld'; + const [dbId, idmId] = await createAccount(); + const originalAccount = await accountService.findById(dbId); + const updatedAccount = await accountService.save({ + ...originalAccount, + username: newUsername, }); + await compareDbAccount(dbId, updatedAccount); + await compareIdmAccount(idmId, updatedAccount); }); - describe('updateUsername', () => { - describe('when updating Username', () => { - const setup = async () => { - const newUsername = 'jane.doe@mail.tld'; - const [dbId, idmId] = await createAccount(); - - return { newUsername, dbId, idmId }; - }; - it('should update username', async () => { - if (!isIdmReachable) return; - const { newUsername, dbId, idmId } = await setup(); - - await accountService.updateUsername(dbId, newUsername); - const foundAccount = await identityManagementService.findAccountById(idmId); - const foundDbAccount = await accountRepo.findById(dbId); - - expect(foundAccount).toEqual( - expect.objectContaining>({ - username: newUsername, - }) - ); - expect(foundDbAccount).toEqual( - expect.objectContaining>({ - username: newUsername, - }) - ); - }); + it('save should create idm account for existing db account', async () => { + if (!isIdmReachable) return; + const newUsername = 'jane.doe@mail.tld'; + const dbId = await createDbAccount(); + const originalAccount = await accountService.findById(dbId); + const updatedAccount = await accountService.save({ + ...originalAccount, + username: newUsername, }); + await compareDbAccount(dbId, updatedAccount); + await compareIdmAccount(updatedAccount.idmReferenceId ?? '', updatedAccount); }); - describe('updatePassword', () => { - describe('when updating password', () => { - const setup = async () => { - const [dbId] = await createAccount(); - - const foundDbAccountBefore = await accountRepo.findById(dbId); - const previousPasswordHash = foundDbAccountBefore.password; - const foundDbAccountAfter = await accountRepo.findById(dbId); - - return { dbId, previousPasswordHash, foundDbAccountAfter }; - }; - it('should update password', async () => { - if (!isIdmReachable) return; - const { dbId, previousPasswordHash, foundDbAccountAfter } = await setup(); + it('updateUsername should update username', async () => { + if (!isIdmReachable) return; + const newUserName = 'jane.doe@mail.tld'; + const [dbId, idmId] = await createAccount(); + await accountService.updateUsername(dbId, newUserName); - await expect(accountService.updatePassword(dbId, 'newPassword')).resolves.not.toThrow(); - - expect(foundDbAccountAfter.password).not.toEqual(previousPasswordHash); - }); - }); + const foundAccount = await identityManagementService.findAccountById(idmId); + expect(foundAccount).toEqual( + expect.objectContaining>({ + username: newUserName, + }) + ); + const foundDbAccount = await accountRepo.findById(dbId); + expect(foundDbAccount).toEqual( + expect.objectContaining>({ + username: newUserName, + }) + ); }); - describe('delete', () => { - describe('when delete an account', () => { - const setup = async () => { - const [dbId, idmId] = await createAccount(); - const foundIdmAccount = await identityManagementService.findAccountById(idmId); - const foundDbAccount = await accountRepo.findById(dbId); + it('updatePassword should update password', async () => { + if (!isIdmReachable) return; + const [dbId] = await createAccount(); - return { dbId, idmId, foundIdmAccount, foundDbAccount }; - }; - it('should remove account', async () => { - if (!isIdmReachable) return; - const { dbId, idmId, foundIdmAccount, foundDbAccount } = await setup(); + const foundDbAccountBefore = await accountRepo.findById(dbId); + const previousPasswordHash = foundDbAccountBefore.password; - expect(foundIdmAccount).toBeDefined(); - expect(foundDbAccount).toBeDefined(); + await expect(accountService.updatePassword(dbId, 'newPassword')).resolves.not.toThrow(); - await accountService.delete(dbId); - await expect(identityManagementService.findAccountById(idmId)).rejects.toThrow(); - await expect(accountRepo.findById(dbId)).rejects.toThrow(); - }); - }); + const foundDbAccountAfter = await accountRepo.findById(dbId); + expect(foundDbAccountAfter.password).not.toEqual(previousPasswordHash); }); - describe('deleteByUserId', () => { - describe('when delete an account by User Id', () => { - const setup = async () => { - const [dbId, idmId] = await createAccount(); - const foundIdmAccount = await identityManagementService.findAccountById(idmId); - const foundDbAccount = await accountRepo.findById(dbId); + it('delete should remove account', async () => { + if (!isIdmReachable) return; + const [dbId, idmId] = await createAccount(); + const foundIdmAccount = await identityManagementService.findAccountById(idmId); + expect(foundIdmAccount).toBeDefined(); + const foundDbAccount = await accountRepo.findById(dbId); + expect(foundDbAccount).toBeDefined(); - return { dbId, idmId, foundIdmAccount, foundDbAccount }; - }; - it('should remove account', async () => { - if (!isIdmReachable) return; - const { dbId, idmId, foundIdmAccount, foundDbAccount } = await setup(); + await accountService.delete(dbId); + await expect(identityManagementService.findAccountById(idmId)).rejects.toThrow(); + await expect(accountRepo.findById(dbId)).rejects.toThrow(); + }); - expect(foundIdmAccount).toBeDefined(); - expect(foundDbAccount).toBeDefined(); + it('deleteByUserId should remove account', async () => { + if (!isIdmReachable) return; + const [dbId, idmId] = await createAccount(); + const foundAccount = await identityManagementService.findAccountById(idmId); + expect(foundAccount).toBeDefined(); + const foundDbAccount = await accountRepo.findById(dbId); + expect(foundDbAccount).toBeDefined(); - await accountService.deleteByUserId(testAccount.userId ?? ''); - await expect(identityManagementService.findAccountById(idmId)).rejects.toThrow(); - await expect(accountRepo.findById(dbId)).rejects.toThrow(); - }); - }); + await accountService.deleteByUserId(testAccount.userId ?? ''); + await expect(identityManagementService.findAccountById(idmId)).rejects.toThrow(); + await expect(accountRepo.findById(dbId)).rejects.toThrow(); }); }); diff --git a/apps/server/src/modules/account/services/account.service.spec.ts b/apps/server/src/modules/account/services/account.service.spec.ts index 67851d0a6b0..4cb95d96a36 100644 --- a/apps/server/src/modules/account/services/account.service.spec.ts +++ b/apps/server/src/modules/account/services/account.service.spec.ts @@ -63,568 +63,402 @@ describe('AccountService', () => { }); describe('findById', () => { - describe('When calling findById in accountService', () => { - it('should call findById in accountServiceDb', async () => { - await expect(accountService.findById('id')).resolves.not.toThrow(); - expect(accountServiceDb.findById).toHaveBeenCalledTimes(1); - }); - }); - - describe('When identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; - - it('should call idm implementation', async () => { - const service = setup(); - await expect(service.findById('accountId')).resolves.not.toThrow(); - expect(accountServiceIdm.findById).toHaveBeenCalledTimes(1); - }); + it('should call findById in accountServiceDb', async () => { + await expect(accountService.findById('id')).resolves.not.toThrow(); + expect(accountServiceDb.findById).toHaveBeenCalledTimes(1); }); }); describe('findByUserId', () => { - describe('When calling findByUserId in accountService', () => { - it('should call findByUserId in accountServiceDb', async () => { - await expect(accountService.findByUserId('userId')).resolves.not.toThrow(); - expect(accountServiceDb.findByUserId).toHaveBeenCalledTimes(1); - }); - }); - describe('When identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; - - it('should call idm implementation', async () => { - const service = setup(); - await expect(service.findByUserId('userId')).resolves.not.toThrow(); - expect(accountServiceIdm.findByUserId).toHaveBeenCalledTimes(1); - }); + it('should call findByUserId in accountServiceDb', async () => { + await expect(accountService.findByUserId('userId')).resolves.not.toThrow(); + expect(accountServiceDb.findByUserId).toHaveBeenCalledTimes(1); }); }); describe('findByUsernameAndSystemId', () => { - describe('When calling findByUsernameAndSystemId in accountService', () => { - it('should call findByUsernameAndSystemId in accountServiceDb', async () => { - await expect(accountService.findByUsernameAndSystemId('username', 'systemId')).resolves.not.toThrow(); - expect(accountServiceDb.findByUsernameAndSystemId).toHaveBeenCalledTimes(1); - }); - }); - describe('when identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; - - it('should call idm implementation', async () => { - const service = setup(); - await expect(service.findByUsernameAndSystemId('username', 'systemId')).resolves.not.toThrow(); - expect(accountServiceIdm.findByUsernameAndSystemId).toHaveBeenCalledTimes(1); - }); + it('should call findByUsernameAndSystemId in accountServiceDb', async () => { + await expect(accountService.findByUsernameAndSystemId('username', 'systemId')).resolves.not.toThrow(); + expect(accountServiceDb.findByUsernameAndSystemId).toHaveBeenCalledTimes(1); }); }); describe('findMultipleByUserId', () => { - describe('When calling findMultipleByUserId in accountService', () => { - it('should call findMultipleByUserId in accountServiceDb', async () => { - await expect(accountService.findMultipleByUserId(['userId1, userId2'])).resolves.not.toThrow(); - expect(accountServiceDb.findMultipleByUserId).toHaveBeenCalledTimes(1); - }); + it('should call findMultipleByUserId in accountServiceDb', async () => { + await expect(accountService.findMultipleByUserId(['userId1, userId2'])).resolves.not.toThrow(); + expect(accountServiceDb.findMultipleByUserId).toHaveBeenCalledTimes(1); }); - describe('When identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; + }); - it('should call idm implementation', async () => { - const service = setup(); - await expect(service.findMultipleByUserId(['userId'])).resolves.not.toThrow(); - expect(accountServiceIdm.findMultipleByUserId).toHaveBeenCalledTimes(1); - }); + describe('findByUserIdOrFail', () => { + it('should call findByUserIdOrFail in accountServiceDb', async () => { + await expect(accountService.findByUserIdOrFail('userId')).resolves.not.toThrow(); + expect(accountServiceDb.findByUserIdOrFail).toHaveBeenCalledTimes(1); }); }); - describe('findByUserIdOrFail', () => { - describe('When calling findByUserIdOrFail in accountService', () => { - it('should call findByUserIdOrFail in accountServiceDb', async () => { - await expect(accountService.findByUserIdOrFail('userId')).resolves.not.toThrow(); - expect(accountServiceDb.findByUserIdOrFail).toHaveBeenCalledTimes(1); - }); + describe('save', () => { + it('should call save in accountServiceDb', async () => { + await expect(accountService.save({} as AccountSaveDto)).resolves.not.toThrow(); + expect(accountServiceDb.save).toHaveBeenCalledTimes(1); }); - describe('When identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; + it('should call save in accountServiceIdm if feature is enabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(true); - it('should call idm implementation', async () => { - const service = setup(); - await expect(service.findByUserIdOrFail('userId')).resolves.not.toThrow(); - expect(accountServiceIdm.findByUserIdOrFail).toHaveBeenCalledTimes(1); - }); + await expect(accountService.save({} as AccountSaveDto)).resolves.not.toThrow(); + expect(accountServiceIdm.save).toHaveBeenCalledTimes(1); + }); + it('should not call save in accountServiceIdm if feature is disabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(false); + + await expect(accountService.save({} as AccountSaveDto)).resolves.not.toThrow(); + expect(accountServiceIdm.save).not.toHaveBeenCalled(); }); }); - describe('save', () => { - describe('When calling save in accountService', () => { - it('should call save in accountServiceDb', async () => { - await expect(accountService.save({} as AccountSaveDto)).resolves.not.toThrow(); - expect(accountServiceDb.save).toHaveBeenCalledTimes(1); - }); + describe('saveWithValidation', () => { + it('should not sanitize username for external user', async () => { + const spy = jest.spyOn(accountService, 'save'); + const params: AccountSaveDto = { + username: ' John.Doe@domain.tld ', + systemId: 'ABC123', + }; + await accountService.saveWithValidation(params); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ + username: ' John.Doe@domain.tld ', + }) + ); + spy.mockRestore(); + }); + it('should throw if username for a local user is not an email', async () => { + const params: AccountSaveDto = { + username: 'John Doe', + password: 'JohnsPassword', + }; + await expect(accountService.saveWithValidation(params)).rejects.toThrow('Username is not an email'); }); - describe('When calling save in accountService if feature is enabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(true); + it('should not throw if username for an external user is not an email', async () => { + const params: AccountSaveDto = { + username: 'John Doe', + systemId: 'ABC123', }; - it('should call save in accountServiceIdm', async () => { - setup(); - - await expect(accountService.save({} as AccountSaveDto)).resolves.not.toThrow(); - expect(accountServiceIdm.save).toHaveBeenCalledTimes(1); - }); + await expect(accountService.saveWithValidation(params)).resolves.not.toThrow(); }); - describe('When calling save in accountService if feature is disabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(false); + it('should not throw if username for an external user is a ldap search string', async () => { + const params: AccountSaveDto = { + username: 'dc=schul-cloud,dc=org/fake.ldap', + systemId: 'ABC123', }; - it('should not call save in accountServiceIdm', async () => { - setup(); - - await expect(accountService.save({} as AccountSaveDto)).resolves.not.toThrow(); - expect(accountServiceIdm.save).not.toHaveBeenCalled(); - }); + await expect(accountService.saveWithValidation(params)).resolves.not.toThrow(); }); - describe('when identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); + it('should throw if no password is provided for an internal user', async () => { + const params: AccountSaveDto = { + username: 'john.doe@mail.tld', }; - it('should call idm implementation', async () => { - setup(); - await expect(accountService.save({ username: 'username' })).resolves.not.toThrow(); - expect(accountServiceIdm.save).toHaveBeenCalledTimes(1); - }); + await expect(accountService.saveWithValidation(params)).rejects.toThrow('No password provided'); }); - }); - - describe('saveWithValidation', () => { - describe('When calling saveWithValidation on accountService', () => { - const setup = () => { - const spy = jest.spyOn(accountService, 'save'); - return spy; + it('should throw if account already exists', async () => { + const params: AccountSaveDto = { + username: 'john.doe@mail.tld', + password: 'JohnsPassword', + userId: 'userId123', + }; + accountServiceDb.findByUserId.mockResolvedValueOnce({ id: 'foundAccount123' } as AccountDto); + await expect(accountService.saveWithValidation(params)).rejects.toThrow('Account already exists'); + }); + it('should throw if username already exists', async () => { + const accountIsUniqueEmailSpy = jest.spyOn(accountValidationService, 'isUniqueEmail'); + accountIsUniqueEmailSpy.mockResolvedValueOnce(false); + const params: AccountSaveDto = { + username: 'john.doe@mail.tld', + password: 'JohnsPassword', }; - it('should not sanitize username for external user', async () => { - const spy = setup(); + await expect(accountService.saveWithValidation(params)).rejects.toThrow('Username already exists'); + }); + }); - const params: AccountSaveDto = { - username: ' John.Doe@domain.tld ', - systemId: 'ABC123', - }; - await accountService.saveWithValidation(params); - expect(spy).toHaveBeenCalledWith( - expect.objectContaining({ - username: ' John.Doe@domain.tld ', - }) - ); - spy.mockRestore(); - }); + describe('updateUsername', () => { + it('should call updateUsername in accountServiceDb', async () => { + await expect(accountService.updateUsername('accountId', 'username')).resolves.not.toThrow(); + expect(accountServiceDb.updateUsername).toHaveBeenCalledTimes(1); }); + it('should call updateUsername in accountServiceIdm if feature is enabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(true); - describe('When username for a local user is not an email', () => { - it('should throw username is not an email error', async () => { - const params: AccountSaveDto = { - username: 'John Doe', - password: 'JohnsPassword', - }; - await expect(accountService.saveWithValidation(params)).rejects.toThrow('Username is not an email'); - }); + await expect(accountService.updateUsername('accountId', 'username')).resolves.not.toThrow(); + expect(accountServiceIdm.updateUsername).toHaveBeenCalledTimes(1); }); + it('should not call updateUsername in accountServiceIdm if feature is disabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(false); - describe('When username for an external user is not an email', () => { - it('should not throw an error', async () => { - const params: AccountSaveDto = { - username: 'John Doe', - systemId: 'ABC123', - }; - await expect(accountService.saveWithValidation(params)).resolves.not.toThrow(); - }); + await expect(accountService.updateUsername('accountId', 'username')).resolves.not.toThrow(); + expect(accountServiceIdm.updateUsername).not.toHaveBeenCalled(); }); + }); - describe('When username for an external user is a ldap search string', () => { - it('should not throw an error', async () => { - const params: AccountSaveDto = { - username: 'dc=schul-cloud,dc=org/fake.ldap', - systemId: 'ABC123', - }; - await expect(accountService.saveWithValidation(params)).resolves.not.toThrow(); - }); + describe('updateLastTriedFailedLogin', () => { + it('should call updateLastTriedFailedLogin in accountServiceDb', async () => { + await expect(accountService.updateLastTriedFailedLogin('accountId', {} as Date)).resolves.not.toThrow(); + expect(accountServiceDb.updateLastTriedFailedLogin).toHaveBeenCalledTimes(1); }); + }); - describe('When no password is provided for an internal user', () => { - it('should throw no password provided error', async () => { - const params: AccountSaveDto = { - username: 'john.doe@mail.tld', - }; - await expect(accountService.saveWithValidation(params)).rejects.toThrow('No password provided'); - }); + describe('updatePassword', () => { + it('should call updatePassword in accountServiceDb', async () => { + await expect(accountService.updatePassword('accountId', 'password')).resolves.not.toThrow(); + expect(accountServiceDb.updatePassword).toHaveBeenCalledTimes(1); }); + it('should call updatePassword in accountServiceIdm if feature is enabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(true); - describe('When account already exists', () => { - it('should throw account already exists', async () => { - const params: AccountSaveDto = { - username: 'john.doe@mail.tld', - password: 'JohnsPassword', - userId: 'userId123', - }; - accountServiceDb.findByUserId.mockResolvedValueOnce({ id: 'foundAccount123' } as AccountDto); - await expect(accountService.saveWithValidation(params)).rejects.toThrow('Account already exists'); - }); + await expect(accountService.updatePassword('accountId', 'password')).resolves.not.toThrow(); + expect(accountServiceIdm.updatePassword).toHaveBeenCalledTimes(1); }); + it('should not call updatePassword in accountServiceIdm if feature is disabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(false); - describe('When username already exists', () => { - const setup = () => { - accountValidationService.isUniqueEmail.mockResolvedValueOnce(false); - }; - it('should throw username already exists', async () => { - setup(); - const params: AccountSaveDto = { - username: 'john.doe@mail.tld', - password: 'JohnsPassword', - }; - await expect(accountService.saveWithValidation(params)).rejects.toThrow('Username already exists'); - }); + await expect(accountService.updatePassword('accountId', 'password')).resolves.not.toThrow(); + expect(accountServiceIdm.updatePassword).not.toHaveBeenCalled(); }); - describe('When identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; + }); - it('should call idm implementation', async () => { - setup(); - await expect( - accountService.saveWithValidation({ username: 'username@mail.tld', password: 'password' }) - ).resolves.not.toThrow(); - expect(accountServiceIdm.save).toHaveBeenCalledTimes(1); - }); + describe('validatePassword', () => { + const setup = () => { + configService.get.mockReturnValue(true); + return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); + }; + it('should call validatePassword in accountServiceDb', async () => { + await expect(accountService.validatePassword({} as AccountDto, 'password')).resolves.not.toThrow(); + expect(accountServiceIdm.validatePassword).toHaveBeenCalledTimes(0); + expect(accountServiceDb.validatePassword).toHaveBeenCalledTimes(1); + }); + it('should call validatePassword in accountServiceIdm if feature is enabled', async () => { + const service = setup(); + await expect(service.validatePassword({} as AccountDto, 'password')).resolves.not.toThrow(); + expect(accountServiceDb.validatePassword).toHaveBeenCalledTimes(0); + expect(accountServiceIdm.validatePassword).toHaveBeenCalledTimes(1); }); }); - describe('updateUsername', () => { - describe('When calling updateUsername in accountService', () => { - it('should call updateUsername in accountServiceDb', async () => { - await expect(accountService.updateUsername('accountId', 'username')).resolves.not.toThrow(); - expect(accountServiceDb.updateUsername).toHaveBeenCalledTimes(1); - }); + describe('delete', () => { + it('should call delete in accountServiceDb', async () => { + await expect(accountService.delete('accountId')).resolves.not.toThrow(); + expect(accountServiceDb.delete).toHaveBeenCalledTimes(1); }); + it('should call delete in accountServiceIdm if feature is enabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(true); - describe('When calling updateUsername in accountService if idm feature is enabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(true); - }; - it('should call updateUsername in accountServiceIdm', async () => { - setup(); + await expect(accountService.delete('accountId')).resolves.not.toThrow(); + expect(accountServiceIdm.delete).toHaveBeenCalledTimes(1); + }); + it('should not call delete in accountServiceIdm if feature is disabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(false); - await expect(accountService.updateUsername('accountId', 'username')).resolves.not.toThrow(); - expect(accountServiceIdm.updateUsername).toHaveBeenCalledTimes(1); - }); + await expect(accountService.delete('accountId')).resolves.not.toThrow(); + expect(accountServiceIdm.delete).not.toHaveBeenCalled(); }); + }); - describe('When calling updateUsername in accountService if idm feature is disabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(false); - }; - it('should not call updateUsername in accountServiceIdm', async () => { - setup(); + describe('deleteByUserId', () => { + it('should call deleteByUserId in accountServiceDb', async () => { + await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); + expect(accountServiceDb.deleteByUserId).toHaveBeenCalledTimes(1); + }); + it('should call deleteByUserId in accountServiceIdm if feature is enabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(true); - await expect(accountService.updateUsername('accountId', 'username')).resolves.not.toThrow(); - expect(accountServiceIdm.updateUsername).not.toHaveBeenCalled(); - }); + await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); + expect(accountServiceIdm.deleteByUserId).toHaveBeenCalledTimes(1); }); - describe('When identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; + it('should not call deleteByUserId in accountServiceIdm if feature is disabled', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(false); - it('should call idm implementation', async () => { - setup(); - await expect(accountService.updateUsername('accountId', 'username')).resolves.not.toThrow(); - expect(accountServiceIdm.updateUsername).toHaveBeenCalledTimes(1); - }); + await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); + expect(accountServiceIdm.deleteByUserId).not.toHaveBeenCalled(); }); }); - describe('updateLastTriedFailedLogin', () => { - describe('When calling updateLastTriedFailedLogin in accountService', () => { - it('should call updateLastTriedFailedLogin in accountServiceDb', async () => { - await expect(accountService.updateLastTriedFailedLogin('accountId', {} as Date)).resolves.not.toThrow(); - expect(accountServiceDb.updateLastTriedFailedLogin).toHaveBeenCalledTimes(1); - }); + describe('findMany', () => { + it('should call findMany in accountServiceDb', async () => { + await expect(accountService.findMany()).resolves.not.toThrow(); + expect(accountServiceDb.findMany).toHaveBeenCalledTimes(1); }); - describe('when identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; + }); - it('should call idm implementation', async () => { - setup(); - await expect(accountService.updateLastTriedFailedLogin('accountId', new Date())).resolves.not.toThrow(); - expect(accountServiceIdm.updateLastTriedFailedLogin).toHaveBeenCalledTimes(1); - }); + describe('searchByUsernamePartialMatch', () => { + it('should call searchByUsernamePartialMatch in accountServiceDb', async () => { + await expect(accountService.searchByUsernamePartialMatch('username', 1, 1)).resolves.not.toThrow(); + expect(accountServiceDb.searchByUsernamePartialMatch).toHaveBeenCalledTimes(1); }); }); - describe('updatePassword', () => { - describe('When calling updatePassword in accountService', () => { - it('should call updatePassword in accountServiceDb', async () => { - await expect(accountService.updatePassword('accountId', 'password')).resolves.not.toThrow(); - expect(accountServiceDb.updatePassword).toHaveBeenCalledTimes(1); - }); + describe('searchByUsernameExactMatch', () => { + it('should call searchByUsernameExactMatch in accountServiceDb', async () => { + await expect(accountService.searchByUsernameExactMatch('username')).resolves.not.toThrow(); + expect(accountServiceDb.searchByUsernameExactMatch).toHaveBeenCalledTimes(1); }); + }); - describe('When calling updatePassword in accountService if feature is enabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(true); - }; - it('should call updatePassword in accountServiceIdm', async () => { - setup(); + describe('executeIdmMethod', () => { + it('should throw an error object', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(true); + const spyLogger = jest.spyOn(logger, 'error'); + const testError = new Error('error'); - await expect(accountService.updatePassword('accountId', 'password')).resolves.not.toThrow(); - expect(accountServiceIdm.updatePassword).toHaveBeenCalledTimes(1); + const deleteByUserIdMock = jest.spyOn(accountServiceIdm, 'deleteByUserId'); + deleteByUserIdMock.mockImplementationOnce(() => { + throw testError; }); + + await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); + expect(spyLogger).toHaveBeenCalledWith(testError, expect.anything()); }); - describe('When calling updatePassword in accountService if feature is disabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(false); - }; - it('should not call updatePassword in accountServiceIdm', async () => { - setup(); + it('should throw an non error object', async () => { + const spy = jest.spyOn(configService, 'get'); + spy.mockReturnValueOnce(true); + const spyLogger = jest.spyOn(logger, 'error'); - await expect(accountService.updatePassword('accountId', 'password')).resolves.not.toThrow(); - expect(accountServiceIdm.updatePassword).not.toHaveBeenCalled(); + const deleteByUserIdMock = jest.spyOn(accountServiceIdm, 'deleteByUserId'); + deleteByUserIdMock.mockImplementationOnce(() => { + // eslint-disable-next-line @typescript-eslint/no-throw-literal + throw 'a non error object'; }); - }); - describe('When identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; - it('should call idm implementation', async () => { - setup(); - await expect(accountService.updatePassword('accountId', 'password')).resolves.not.toThrow(); - expect(accountServiceIdm.updatePassword).toHaveBeenCalledTimes(1); - }); + await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); + expect(spyLogger).toHaveBeenCalledWith('a non error object'); }); }); - describe('validatePassword', () => { - describe('When calling validatePassword in accountService', () => { - it('should call validatePassword in accountServiceDb', async () => { - await expect(accountService.validatePassword({} as AccountDto, 'password')).resolves.not.toThrow(); - expect(accountServiceIdm.validatePassword).toHaveBeenCalledTimes(0); - expect(accountServiceDb.validatePassword).toHaveBeenCalledTimes(1); - }); - }); + describe('when identity management is primary', () => { + const setup = () => { + configService.get.mockReturnValue(true); + return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); + }; - describe('When calling validatePassword in accountService if feature is enabled', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; - it('should call validatePassword in accountServiceIdm', async () => { + describe('findById', () => { + it('should call idm implementation', async () => { const service = setup(); - await expect(service.validatePassword({} as AccountDto, 'password')).resolves.not.toThrow(); - expect(accountServiceDb.validatePassword).toHaveBeenCalledTimes(0); - expect(accountServiceIdm.validatePassword).toHaveBeenCalledTimes(1); + await expect(service.findById('accountId')).resolves.not.toThrow(); + expect(accountServiceIdm.findById).toHaveBeenCalledTimes(1); }); }); - }); - describe('delete', () => { - describe('When calling delete in accountService', () => { - it('should call delete in accountServiceDb', async () => { - await expect(accountService.delete('accountId')).resolves.not.toThrow(); - expect(accountServiceDb.delete).toHaveBeenCalledTimes(1); + describe('findMultipleByUserId', () => { + it('should call idm implementation', async () => { + const service = setup(); + await expect(service.findMultipleByUserId(['userId'])).resolves.not.toThrow(); + expect(accountServiceIdm.findMultipleByUserId).toHaveBeenCalledTimes(1); }); }); - describe('When calling delete in accountService if feature is enabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(true); - }; - it('should call delete in accountServiceIdm', async () => { - setup(); - - await expect(accountService.delete('accountId')).resolves.not.toThrow(); - expect(accountServiceIdm.delete).toHaveBeenCalledTimes(1); + describe('findByUserId', () => { + it('should call idm implementation', async () => { + const service = setup(); + await expect(service.findByUserId('userId')).resolves.not.toThrow(); + expect(accountServiceIdm.findByUserId).toHaveBeenCalledTimes(1); }); }); - describe('When calling delete in accountService if feature is disabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(false); - }; - it('should not call delete in accountServiceIdm', async () => { - setup(); - - await expect(accountService.delete('accountId')).resolves.not.toThrow(); - expect(accountServiceIdm.delete).not.toHaveBeenCalled(); + describe('findByUserIdOrFail', () => { + it('should call idm implementation', async () => { + const service = setup(); + await expect(service.findByUserIdOrFail('userId')).resolves.not.toThrow(); + expect(accountServiceIdm.findByUserIdOrFail).toHaveBeenCalledTimes(1); }); }); - describe('When identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; + describe('findByUsernameAndSystemId', () => { it('should call idm implementation', async () => { - setup(); - await expect(accountService.delete('accountId')).resolves.not.toThrow(); - expect(accountServiceIdm.delete).toHaveBeenCalledTimes(1); + const service = setup(); + await expect(service.findByUsernameAndSystemId('username', 'systemId')).resolves.not.toThrow(); + expect(accountServiceIdm.findByUsernameAndSystemId).toHaveBeenCalledTimes(1); }); }); - }); - describe('deleteByUserId', () => { - describe('When calling deleteByUserId in accountService', () => { - it('should call deleteByUserId in accountServiceDb', async () => { - await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); - expect(accountServiceDb.deleteByUserId).toHaveBeenCalledTimes(1); + describe('searchByUsernamePartialMatch', () => { + it('should call idm implementation', async () => { + const service = setup(); + await expect(service.searchByUsernamePartialMatch('username', 0, 1)).resolves.not.toThrow(); + expect(accountServiceIdm.searchByUsernamePartialMatch).toHaveBeenCalledTimes(1); }); }); - describe('When calling deleteByUserId in accountService if feature is enabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(true); - }; - it('should call deleteByUserId in accountServiceIdm', async () => { - setup(); - - await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); - expect(accountServiceIdm.deleteByUserId).toHaveBeenCalledTimes(1); + describe('searchByUsernameExactMatch', () => { + it('should call idm implementation', async () => { + const service = setup(); + await expect(service.searchByUsernameExactMatch('username')).resolves.not.toThrow(); + expect(accountServiceIdm.searchByUsernameExactMatch).toHaveBeenCalledTimes(1); }); }); - describe('When calling deleteByUserId in accountService if feature is disabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(false); - }; - it('should not call deleteByUserId in accountServiceIdm', async () => { + describe('save', () => { + it('should call idm implementation', async () => { setup(); - - await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); - expect(accountServiceIdm.deleteByUserId).not.toHaveBeenCalled(); + await expect(accountService.save({ username: 'username' })).resolves.not.toThrow(); + expect(accountServiceIdm.save).toHaveBeenCalledTimes(1); }); }); - describe('When identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; + describe('saveWithValidation', () => { it('should call idm implementation', async () => { setup(); - await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); - expect(accountServiceIdm.deleteByUserId).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe('findMany', () => { - describe('When calling findMany in accountService', () => { - it('should call findMany in accountServiceDb', async () => { - await expect(accountService.findMany()).resolves.not.toThrow(); - expect(accountServiceDb.findMany).toHaveBeenCalledTimes(1); - }); - }); - }); - - describe('searchByUsernamePartialMatch', () => { - describe('When calling searchByUsernamePartialMatch in accountService', () => { - it('should call searchByUsernamePartialMatch in accountServiceDb', async () => { - await expect(accountService.searchByUsernamePartialMatch('username', 1, 1)).resolves.not.toThrow(); - expect(accountServiceDb.searchByUsernamePartialMatch).toHaveBeenCalledTimes(1); + await expect( + accountService.saveWithValidation({ username: 'username@mail.tld', password: 'password' }) + ).resolves.not.toThrow(); + expect(accountServiceIdm.save).toHaveBeenCalledTimes(1); }); }); - describe('when identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; + describe('updateUsername', () => { it('should call idm implementation', async () => { - const service = setup(); - await expect(service.searchByUsernamePartialMatch('username', 0, 1)).resolves.not.toThrow(); - expect(accountServiceIdm.searchByUsernamePartialMatch).toHaveBeenCalledTimes(1); + setup(); + await expect(accountService.updateUsername('accountId', 'username')).resolves.not.toThrow(); + expect(accountServiceIdm.updateUsername).toHaveBeenCalledTimes(1); }); }); - }); - describe('searchByUsernameExactMatch', () => { - describe('When calling searchByUsernameExactMatch in accountService', () => { - it('should call searchByUsernameExactMatch in accountServiceDb', async () => { - await expect(accountService.searchByUsernameExactMatch('username')).resolves.not.toThrow(); - expect(accountServiceDb.searchByUsernameExactMatch).toHaveBeenCalledTimes(1); + describe('updateLastTriedFailedLogin', () => { + it('should call idm implementation', async () => { + setup(); + await expect(accountService.updateLastTriedFailedLogin('accountId', new Date())).resolves.not.toThrow(); + expect(accountServiceIdm.updateLastTriedFailedLogin).toHaveBeenCalledTimes(1); }); }); - describe('when identity management is primary', () => { - const setup = () => { - configService.get.mockReturnValue(true); - return new AccountService(accountServiceDb, accountServiceIdm, configService, accountValidationService, logger); - }; + describe('updatePassword', () => { it('should call idm implementation', async () => { - const service = setup(); - await expect(service.searchByUsernameExactMatch('username')).resolves.not.toThrow(); - expect(accountServiceIdm.searchByUsernameExactMatch).toHaveBeenCalledTimes(1); + setup(); + await expect(accountService.updatePassword('accountId', 'password')).resolves.not.toThrow(); + expect(accountServiceIdm.updatePassword).toHaveBeenCalledTimes(1); }); }); - }); - - describe('executeIdmMethod', () => { - describe('When idm feature is enabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(true); - const testError = new Error('error'); - accountServiceIdm.deleteByUserId.mockImplementationOnce(() => { - throw testError; - }); - - const spyLogger = jest.spyOn(logger, 'error'); - return { testError, spyLogger }; - }; - it('should call executeIdmMethod and throw an error object', async () => { - const { testError, spyLogger } = setup(); - - await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); - expect(spyLogger).toHaveBeenCalledWith(testError, expect.anything()); + describe('delete', () => { + it('should call idm implementation', async () => { + setup(); + await expect(accountService.delete('accountId')).resolves.not.toThrow(); + expect(accountServiceIdm.delete).toHaveBeenCalledTimes(1); }); }); - describe('When idm feature is enabled', () => { - const setup = () => { - configService.get.mockReturnValueOnce(true); - const spyLogger = jest.spyOn(logger, 'error'); - const deleteByUserIdMock = jest.spyOn(accountServiceIdm, 'deleteByUserId'); - deleteByUserIdMock.mockImplementationOnce(() => { - // eslint-disable-next-line @typescript-eslint/no-throw-literal - throw 'a non error object'; - }); - return { spyLogger }; - }; - it('should call executeIdmMethod and throw an error object', async () => { - const { spyLogger } = setup(); - + describe('deleteByUserId', () => { + it('should call idm implementation', async () => { + setup(); await expect(accountService.deleteByUserId('userId')).resolves.not.toThrow(); - expect(spyLogger).toHaveBeenCalledWith('a non error object'); + expect(accountServiceIdm.deleteByUserId).toHaveBeenCalledTimes(1); }); }); }); diff --git a/apps/server/src/modules/account/services/account.service.ts b/apps/server/src/modules/account/services/account.service.ts index 3c8a5ff2058..6c2070550ab 100644 --- a/apps/server/src/modules/account/services/account.service.ts +++ b/apps/server/src/modules/account/services/account.service.ts @@ -4,8 +4,7 @@ import { ConfigService } from '@nestjs/config'; import { ValidationError } from '@shared/common'; import { Counted } from '@shared/domain'; import { isEmail, validateOrReject } from 'class-validator'; -import { LegacyLogger } from '../../../core/logger'; // TODO: use path alias -// TODO: account needs to define its own config, which is made available for the server +import { LegacyLogger } from '../../../core/logger'; import { IServerConfig } from '../../server/server.config'; import { AccountServiceDb } from './account-db.service'; import { AccountServiceIdm } from './account-idm.service'; @@ -13,11 +12,6 @@ import { AbstractAccountService } from './account.service.abstract'; import { AccountValidationService } from './account.validation.service'; import { AccountDto, AccountSaveDto } from './dto'; -/* TODO: extract a service that contains all things required by feathers, -which is responsible for the additionally required validation - -it should be clearly visible which functions are only needed for feathers, and easy to remove them */ - @Injectable() export class AccountService extends AbstractAccountService { private readonly accountImpl: AbstractAccountService; @@ -84,7 +78,6 @@ export class AccountService extends AbstractAccountService { } async saveWithValidation(dto: AccountSaveDto): Promise { - // TODO: move as much as possible into the class validator await validateOrReject(dto); // sanatizeUsername ✔ if (!dto.systemId) { @@ -115,7 +108,6 @@ export class AccountService extends AbstractAccountService { // dto.password = undefined; // } - // TODO: split validation from saving, so it can be used independently await this.save(dto); } diff --git a/apps/server/src/modules/account/services/account.validation.service.spec.ts b/apps/server/src/modules/account/services/account.validation.service.spec.ts index c152f01a59b..dba1e2bf02a 100644 --- a/apps/server/src/modules/account/services/account.validation.service.spec.ts +++ b/apps/server/src/modules/account/services/account.validation.service.spec.ts @@ -1,9 +1,9 @@ import { Test, TestingModule } from '@nestjs/testing'; -import { Permission, Role, RoleName } from '@shared/domain'; +import { EntityNotFoundError } from '@shared/common'; +import { Account, EntityId, Permission, Role, RoleName, User } from '@shared/domain'; import { UserRepo } from '@shared/repo'; import { accountFactory, setupEntities, systemFactory, userFactory } from '@shared/testing'; import { ObjectId } from 'bson'; -import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { AccountRepo } from '../repo/account.repo'; import { AccountValidationService } from './account.validation.service'; @@ -11,8 +11,26 @@ describe('AccountValidationService', () => { let module: TestingModule; let accountValidationService: AccountValidationService; - let userRepo: DeepMocked; - let accountRepo: DeepMocked; + let mockTeacherUser: User; + let mockTeacherAccount: Account; + + let mockStudentUser: User; + let mockStudentAccount: Account; + + let mockOtherTeacherUser: User; + let mockOtherTeacherAccount: Account; + + let mockAdminUser: User; + + let mockExternalUser: User; + let mockExternalUserAccount: Account; + let mockOtherExternalUser: User; + let mockOtherExternalUserAccount: Account; + + let oprhanAccount: Account; + + let mockUsers: User[]; + let mockAccounts: Account[]; afterAll(async () => { await module.close(); @@ -24,405 +42,237 @@ describe('AccountValidationService', () => { AccountValidationService, { provide: AccountRepo, - useValue: createMock(), + useValue: { + findById: jest.fn().mockImplementation((accountId: EntityId): Promise => { + const account = mockAccounts.find((tempAccount) => tempAccount.id === accountId); + + if (account) { + return Promise.resolve(account); + } + throw new EntityNotFoundError(Account.name); + }), + searchByUsernameExactMatch: jest + .fn() + .mockImplementation((username: string): Promise<[Account[], number]> => { + const account = mockAccounts.find((tempAccount) => tempAccount.username === username); + + if (account) { + return Promise.resolve([[account], 1]); + } + if (username === 'not@available.username') { + return Promise.resolve([[mockOtherTeacherAccount], 1]); + } + if (username === 'multiple@account.username') { + return Promise.resolve([mockAccounts, mockAccounts.length]); + } + return Promise.resolve([[], 0]); + }), + findByUserId: (userId: EntityId): Promise => { + const account = mockAccounts.find((tempAccount) => tempAccount.userId?.toString() === userId); + if (account) { + return Promise.resolve(account); + } + return Promise.resolve(null); + }, + }, }, { provide: UserRepo, - useValue: createMock(), + useValue: { + findById: jest.fn().mockImplementation((userId: EntityId): Promise => { + const user = mockUsers.find((tempUser) => tempUser.id === userId); + if (user) { + return Promise.resolve(user); + } + throw new EntityNotFoundError(User.name); + }), + findByEmail: jest.fn().mockImplementation((email: string): Promise => { + const user = mockUsers.find((tempUser) => tempUser.email === email); + + if (user) { + return Promise.resolve([user]); + } + if (email === 'multiple@user.email') { + return Promise.resolve(mockUsers); + } + return Promise.resolve([]); + }), + }, }, ], }).compile(); accountValidationService = module.get(AccountValidationService); - - userRepo = module.get(UserRepo); - accountRepo = module.get(AccountRepo); - await setupEntities(); }); beforeEach(() => { - jest.resetAllMocks(); - }); - - describe('isUniqueEmail', () => { - describe('When new email is available', () => { - const setup = () => { - userRepo.findByEmail.mockResolvedValueOnce([]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[], 0]); - }; - it('should return true', async () => { - setup(); - - const res = await accountValidationService.isUniqueEmail('an@available.email'); - expect(res).toBe(true); - }); + mockTeacherUser = userFactory.buildWithId({ + roles: [new Role({ name: RoleName.TEACHER, permissions: [Permission.STUDENT_EDIT] })], }); - - describe('When new email is available', () => { - const setup = () => { - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - userRepo.findByEmail.mockResolvedValueOnce([mockStudentUser]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[], 0]); - - return { mockStudentUser }; - }; - it('should return true and ignore current user', async () => { - const { mockStudentUser } = setup(); - const res = await accountValidationService.isUniqueEmail(mockStudentUser.email, mockStudentUser.id); - expect(res).toBe(true); - }); + mockStudentUser = userFactory.buildWithId({ + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], }); - - describe('When new email is available', () => { - const setup = () => { - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); - - userRepo.findByEmail.mockResolvedValueOnce([mockStudentUser]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[mockStudentAccount], 1]); - - return { mockStudentUser, mockStudentAccount }; - }; - it('should return true and ignore current users account', async () => { - const { mockStudentUser, mockStudentAccount } = setup(); - const res = await accountValidationService.isUniqueEmail( - mockStudentAccount.username, - mockStudentUser.id, - mockStudentAccount.id - ); - expect(res).toBe(true); - }); + mockOtherTeacherUser = userFactory.buildWithId({ + roles: [ + new Role({ + name: RoleName.TEACHER, + permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], + }), + ], }); - - describe('When new email already in use by another user', () => { - const setup = () => { - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockAdminUser = userFactory.buildWithId({ - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockAdminAccount = accountFactory.buildWithId({ userId: mockAdminUser.id }); - const mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); - - userRepo.findByEmail.mockResolvedValueOnce([mockAdminUser]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[mockAdminAccount], 1]); - - return { mockAdminUser, mockStudentUser, mockStudentAccount }; - }; - it('should return false', async () => { - const { mockAdminUser, mockStudentUser, mockStudentAccount } = setup(); - const res = await accountValidationService.isUniqueEmail( - mockAdminUser.email, - mockStudentUser.id, - mockStudentAccount.id - ); - expect(res).toBe(false); - }); + mockExternalUser = userFactory.buildWithId({ + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], }); - - describe('When new email already in use by any user and system id is given', () => { - const setup = () => { - const mockTeacherUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.TEACHER, permissions: [Permission.STUDENT_EDIT] })], - }); - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockTeacherAccount = accountFactory.buildWithId({ userId: mockTeacherUser.id }); - const mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); - - userRepo.findByEmail.mockResolvedValueOnce([mockTeacherUser]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[mockTeacherAccount], 1]); - - return { mockTeacherAccount, mockStudentUser, mockStudentAccount }; - }; - it('should return false', async () => { - const { mockTeacherAccount, mockStudentUser, mockStudentAccount } = setup(); - const res = await accountValidationService.isUniqueEmail( - mockTeacherAccount.username, - mockStudentUser.id, - mockStudentAccount.id, - mockStudentAccount.systemId?.toString() - ); - expect(res).toBe(false); - }); + mockOtherExternalUser = userFactory.buildWithId({ + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], }); - describe('When new email already in use by multiple users', () => { - const setup = () => { - const mockTeacherUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.TEACHER, permissions: [Permission.STUDENT_EDIT] })], - }); - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockOtherTeacherUser = userFactory.buildWithId({ - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); - - const mockUsers = [mockTeacherUser, mockStudentUser, mockOtherTeacherUser]; - - userRepo.findByEmail.mockResolvedValueOnce(mockUsers); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[], 0]); + mockTeacherAccount = accountFactory.buildWithId({ userId: mockTeacherUser.id }); + mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); + mockOtherTeacherAccount = accountFactory.buildWithId({ + userId: mockOtherTeacherUser.id, + }); + const externalSystemA = systemFactory.buildWithId(); + const externalSystemB = systemFactory.buildWithId(); + mockExternalUserAccount = accountFactory.buildWithId({ + userId: mockExternalUser.id, + username: 'unique.within@system', + systemId: externalSystemA.id, + }); + mockOtherExternalUserAccount = accountFactory.buildWithId({ + userId: mockOtherExternalUser.id, + username: 'unique.within@system', + systemId: externalSystemB.id, + }); - return { mockStudentUser, mockStudentAccount }; - }; - it('should return false', async () => { - const { mockStudentUser, mockStudentAccount } = setup(); - const res = await accountValidationService.isUniqueEmail( - 'multiple@user.email', - mockStudentUser.id, - mockStudentAccount.id, - mockStudentAccount.systemId?.toString() - ); - expect(res).toBe(false); - }); + oprhanAccount = accountFactory.buildWithId({ + username: 'orphan@account', + userId: undefined, + systemId: new ObjectId(), }); - describe('When new email already in use by multiple accounts', () => { - const setup = () => { - const mockTeacherUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.TEACHER, permissions: [Permission.STUDENT_EDIT] })], - }); - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockOtherTeacherUser = userFactory.buildWithId({ - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), + mockAccounts = [ + mockTeacherAccount, + mockStudentAccount, + mockOtherTeacherAccount, + mockExternalUserAccount, + mockOtherExternalUserAccount, + oprhanAccount, + ]; + mockAdminUser = userFactory.buildWithId({ + roles: [ + new Role({ + name: RoleName.ADMINISTRATOR, + permissions: [ + Permission.TEACHER_EDIT, + Permission.STUDENT_EDIT, + Permission.STUDENT_LIST, + Permission.TEACHER_LIST, + Permission.TEACHER_CREATE, + Permission.STUDENT_CREATE, + Permission.TEACHER_DELETE, + Permission.STUDENT_DELETE, ], - }); - - const mockTeacherAccount = accountFactory.buildWithId({ userId: mockTeacherUser.id }); - const mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); - const mockOtherTeacherAccount = accountFactory.buildWithId({ - userId: mockOtherTeacherUser.id, - }); - - const mockAccounts = [mockTeacherAccount, mockStudentAccount, mockOtherTeacherAccount]; - userRepo.findByEmail.mockResolvedValueOnce([]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([mockAccounts, mockAccounts.length]); - - return { mockStudentUser, mockStudentAccount }; - }; - it('should return false', async () => { - const { mockStudentUser, mockStudentAccount } = setup(); - const res = await accountValidationService.isUniqueEmail( - 'multiple@account.username', - mockStudentUser.id, - mockStudentAccount.id, - mockStudentAccount.systemId?.toString() - ); - expect(res).toBe(false); - }); + }), + ], }); + mockUsers = [ + mockTeacherUser, + mockStudentUser, + mockOtherTeacherUser, + mockAdminUser, + mockExternalUser, + mockOtherExternalUser, + ]; + }); - describe('When its another system', () => { - const setup = () => { - const mockExternalUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockOtherExternalUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const externalSystemA = systemFactory.buildWithId(); - const externalSystemB = systemFactory.buildWithId(); - const mockExternalUserAccount = accountFactory.buildWithId({ - userId: mockExternalUser.id, - username: 'unique.within@system', - systemId: externalSystemA.id, - }); - const mockOtherExternalUserAccount = accountFactory.buildWithId({ - userId: mockOtherExternalUser.id, - username: 'unique.within@system', - systemId: externalSystemB.id, - }); - - userRepo.findByEmail.mockResolvedValueOnce([mockExternalUser]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[mockExternalUserAccount], 1]); - - return { mockExternalUser, mockExternalUserAccount, mockOtherExternalUserAccount }; - }; - it('should ignore existing username', async () => { - const { mockExternalUser, mockExternalUserAccount, mockOtherExternalUserAccount } = setup(); - const res = await accountValidationService.isUniqueEmail( - mockExternalUser.email, - mockExternalUser.id, - mockExternalUserAccount.id, - mockOtherExternalUserAccount.systemId?.toString() - ); - expect(res).toBe(true); - }); + describe('isUniqueEmail', () => { + it('should return true if new email is available', async () => { + const res = await accountValidationService.isUniqueEmail('an@available.email'); + expect(res).toBe(true); + }); + it('should return true if new email is available and ignore current user', async () => { + const res = await accountValidationService.isUniqueEmail(mockStudentUser.email, mockStudentUser.id); + expect(res).toBe(true); + }); + it('should return true if new email is available and ignore current users account', async () => { + const res = await accountValidationService.isUniqueEmail( + mockStudentAccount.username, + mockStudentUser.id, + mockStudentAccount.id + ); + expect(res).toBe(true); + }); + it('should return false if new email already in use by another user', async () => { + const res = await accountValidationService.isUniqueEmail( + mockAdminUser.email, + mockStudentUser.id, + mockStudentAccount.id + ); + expect(res).toBe(false); + }); + it('should return false if new email is already in use by any user, system id is given', async () => { + const res = await accountValidationService.isUniqueEmail( + mockTeacherAccount.username, + mockStudentUser.id, + mockStudentAccount.id, + mockStudentAccount.systemId?.toString() + ); + expect(res).toBe(false); + }); + it('should return false if new email already in use by multiple users', async () => { + const res = await accountValidationService.isUniqueEmail( + 'multiple@user.email', + mockStudentUser.id, + mockStudentAccount.id, + mockStudentAccount.systemId?.toString() + ); + expect(res).toBe(false); + }); + it('should return false if new email already in use by multiple accounts', async () => { + const res = await accountValidationService.isUniqueEmail( + 'multiple@account.username', + mockStudentUser.id, + mockStudentAccount.id, + mockStudentAccount.systemId?.toString() + ); + expect(res).toBe(false); + }); + it('should ignore existing username if other system', async () => { + const res = await accountValidationService.isUniqueEmail( + mockExternalUser.email, + mockExternalUser.id, + mockExternalUserAccount.id, + mockOtherExternalUserAccount.systemId?.toString() + ); + expect(res).toBe(true); }); }); describe('isUniqueEmailForUser', () => { - describe('When its the email of the given user', () => { - const setup = () => { - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); - - userRepo.findByEmail.mockResolvedValueOnce([mockStudentUser]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[mockStudentAccount], 1]); - accountRepo.findByUserId.mockResolvedValueOnce(mockStudentAccount); - - return { mockStudentUser }; - }; - it('should return true', async () => { - const { mockStudentUser } = setup(); - const res = await accountValidationService.isUniqueEmailForUser(mockStudentUser.email, mockStudentUser.id); - expect(res).toBe(true); - }); + it('should return true, if its the email of the given user', async () => { + const res = await accountValidationService.isUniqueEmailForUser(mockStudentUser.email, mockStudentUser.id); + expect(res).toBe(true); }); - - describe('When its not the given users email', () => { - const setup = () => { - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); - - const mockAdminUser = userFactory.buildWithId({ - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockAdminAccount = accountFactory.buildWithId({ userId: mockAdminUser.id }); - - userRepo.findByEmail.mockResolvedValueOnce([mockStudentUser]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[mockStudentAccount], 1]); - accountRepo.findByUserId.mockResolvedValueOnce(mockAdminAccount); - - return { mockStudentUser, mockAdminUser }; - }; - it('should return false', async () => { - const { mockStudentUser, mockAdminUser } = setup(); - const res = await accountValidationService.isUniqueEmailForUser(mockStudentUser.email, mockAdminUser.id); - expect(res).toBe(false); - }); + it('should return false, if not the given users email', async () => { + const res = await accountValidationService.isUniqueEmailForUser(mockStudentUser.email, mockAdminUser.id); + expect(res).toBe(false); }); }); describe('isUniqueEmailForAccount', () => { - describe('When its the email of the given user', () => { - const setup = () => { - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); - - userRepo.findByEmail.mockResolvedValueOnce([mockStudentUser]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[mockStudentAccount], 1]); - accountRepo.findById.mockResolvedValueOnce(mockStudentAccount); - - return { mockStudentUser, mockStudentAccount }; - }; - it('should return true', async () => { - const { mockStudentUser, mockStudentAccount } = setup(); - const res = await accountValidationService.isUniqueEmailForAccount( - mockStudentUser.email, - mockStudentAccount.id - ); - expect(res).toBe(true); - }); + it('should return true, if its the email of the given user', async () => { + const res = await accountValidationService.isUniqueEmailForAccount(mockStudentUser.email, mockStudentAccount.id); + expect(res).toBe(true); }); - describe('When its not the given users email', () => { - const setup = () => { - const mockTeacherUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.TEACHER, permissions: [Permission.STUDENT_EDIT] })], - }); - const mockStudentUser = userFactory.buildWithId({ - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockTeacherAccount = accountFactory.buildWithId({ userId: mockTeacherUser.id }); - const mockStudentAccount = accountFactory.buildWithId({ userId: mockStudentUser.id }); - - userRepo.findByEmail.mockResolvedValueOnce([mockStudentUser]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[mockStudentAccount], 1]); - accountRepo.findById.mockResolvedValueOnce(mockTeacherAccount); - - return { mockStudentUser, mockTeacherAccount }; - }; - it('should return false', async () => { - const { mockStudentUser, mockTeacherAccount } = setup(); - const res = await accountValidationService.isUniqueEmailForAccount( - mockStudentUser.email, - mockTeacherAccount.id - ); - expect(res).toBe(false); - }); + it('should return false, if not the given users email', async () => { + const res = await accountValidationService.isUniqueEmailForAccount(mockStudentUser.email, mockTeacherAccount.id); + expect(res).toBe(false); }); - - describe('When user is missing in account', () => { - const setup = () => { - const oprhanAccount = accountFactory.buildWithId({ - username: 'orphan@account', - userId: undefined, - systemId: new ObjectId(), - }); - - userRepo.findByEmail.mockResolvedValueOnce([]); - accountRepo.searchByUsernameExactMatch.mockResolvedValueOnce([[], 0]); - accountRepo.findById.mockResolvedValueOnce(oprhanAccount); - - return { oprhanAccount }; - }; - it('should ignore missing user for given account', async () => { - const { oprhanAccount } = setup(); - const res = await accountValidationService.isUniqueEmailForAccount(oprhanAccount.username, oprhanAccount.id); - expect(res).toBe(true); - }); + it('should ignore missing user for a given account', async () => { + const res = await accountValidationService.isUniqueEmailForAccount(oprhanAccount.username, oprhanAccount.id); + expect(res).toBe(true); }); }); }); diff --git a/apps/server/src/modules/account/services/account.validation.service.ts b/apps/server/src/modules/account/services/account.validation.service.ts index 2cabc9eabb3..fc47569ed71 100644 --- a/apps/server/src/modules/account/services/account.validation.service.ts +++ b/apps/server/src/modules/account/services/account.validation.service.ts @@ -5,11 +5,9 @@ import { AccountEntityToDtoMapper } from '../mapper/account-entity-to-dto.mapper import { AccountRepo } from '../repo/account.repo'; @Injectable() -// TODO: naming? export class AccountValidationService { constructor(private accountRepo: AccountRepo, private userRepo: UserRepo) {} - // TODO: this should be refactored and rewritten more nicely async isUniqueEmail(email: string, userId?: EntityId, accountId?: EntityId, systemId?: EntityId): Promise { const [foundUsers, [accounts]] = await Promise.all([ // Test coverage: Missing branch null check; unreachable @@ -29,12 +27,12 @@ export class AccountValidationService { } async isUniqueEmailForUser(email: string, userId: EntityId): Promise { - const account = await this.accountRepo.findByUserId(userId); // TODO: findOrFail? + const account = await this.accountRepo.findByUserId(userId); return this.isUniqueEmail(email, userId, account?.id, account?.systemId?.toString()); } async isUniqueEmailForAccount(email: string, accountId: EntityId): Promise { - const account = await this.accountRepo.findById(accountId); // TODO: findOrFail? + const account = await this.accountRepo.findById(accountId); return this.isUniqueEmail(email, account.userId?.toString(), account.id, account?.systemId?.toString()); } } diff --git a/apps/server/src/modules/account/services/dto/account.dto.ts b/apps/server/src/modules/account/services/dto/account.dto.ts index c3765576e50..760be1f2453 100644 --- a/apps/server/src/modules/account/services/dto/account.dto.ts +++ b/apps/server/src/modules/account/services/dto/account.dto.ts @@ -1,7 +1,6 @@ import { EntityId } from '@shared/domain'; import { AccountSaveDto } from './account-save.dto'; -// TODO: this vs account-save.dto? please clean up :) export class AccountDto extends AccountSaveDto { readonly id: EntityId; diff --git a/apps/server/src/modules/account/uc/account.uc.spec.ts b/apps/server/src/modules/account/uc/account.uc.spec.ts index 526720c43f7..aa4cdf56a82 100644 --- a/apps/server/src/modules/account/uc/account.uc.spec.ts +++ b/apps/server/src/modules/account/uc/account.uc.spec.ts @@ -4,11 +4,13 @@ import { Test, TestingModule } from '@nestjs/testing'; import { AuthorizationError, EntityNotFoundError, ForbiddenOperationError, ValidationError } from '@shared/common'; import { Account, + Counted, EntityId, Permission, PermissionService, Role, RoleName, + SchoolEntity, SchoolRolePermission, SchoolRoles, User, @@ -35,19 +37,62 @@ import { AccountUc } from './account.uc'; describe('AccountUc', () => { let module: TestingModule; let accountUc: AccountUc; - let userRepo: DeepMocked; - let accountService: DeepMocked; - let accountValidationService: DeepMocked; + let userRepo: UserRepo; + let accountService: AccountService; + let accountValidationService: AccountValidationService; let configService: DeepMocked; + let mockSchool: SchoolEntity; + let mockOtherSchool: SchoolEntity; + let mockSchoolWithStudentVisibility: SchoolEntity; + + let mockSuperheroUser: User; + let mockAdminUser: User; + let mockTeacherUser: User; + let mockOtherTeacherUser: User; + let mockTeacherNoUserNoSchoolPermissionUser: User; + let mockTeacherNoUserPermissionUser: User; + let mockStudentSchoolPermissionUser: User; + let mockStudentUser: User; + let mockOtherStudentUser: User; + let mockDifferentSchoolAdminUser: User; + let mockDifferentSchoolTeacherUser: User; + let mockDifferentSchoolStudentUser: User; + let mockUnknownRoleUser: User; + let mockExternalUser: User; + let mockUserWithoutAccount: User; + let mockUserWithoutRole: User; + let mockStudentUserWithoutAccount: User; + let mockOtherStudentSchoolPermissionUser: User; + + let mockSuperheroAccount: Account; + let mockTeacherAccount: Account; + let mockOtherTeacherAccount: Account; + let mockTeacherNoUserPermissionAccount: Account; + let mockTeacherNoUserNoSchoolPermissionAccount: Account; + let mockAdminAccount: Account; + let mockStudentAccount: Account; + let mockStudentSchoolPermissionAccount: Account; + let mockDifferentSchoolAdminAccount: Account; + let mockDifferentSchoolTeacherAccount: Account; + let mockDifferentSchoolStudentAccount: Account; + let mockUnknownRoleUserAccount: Account; + let mockExternalUserAccount: Account; + let mockAccountWithoutRole: Account; + let mockAccountWithoutUser: Account; + let mockAccountWithSystemId: Account; + let mockAccountWithLastFailedLogin: Account; + let mockAccountWithOldLastFailedLogin: Account; + let mockAccountWithNoLastFailedLogin: Account; + let mockAccounts: Account[]; + let mockUsers: User[]; + const defaultPassword = 'DummyPasswd!1'; const otherPassword = 'DummyPasswd!2'; const defaultPasswordHash = '$2a$10$/DsztV5o6P5piW2eWJsxw.4nHovmJGBA.QNwiTmuZ/uvUc40b.Uhu'; const LOGIN_BLOCK_TIME = 15; afterAll(async () => { - jest.restoreAllMocks(); - jest.resetAllMocks(); await module.close(); }); @@ -57,7 +102,103 @@ describe('AccountUc', () => { AccountUc, { provide: AccountService, - useValue: createMock(), + useValue: { + saveWithValidation: jest.fn().mockImplementation((account: AccountDto): Promise => { + if (account.username === 'fail@to.update') { + return Promise.reject(); + } + const accountEntity = mockAccounts.find( + (tempAccount) => tempAccount.userId?.toString() === account.userId + ); + if (accountEntity) { + Object.assign(accountEntity, account); + return Promise.resolve(); + } + return Promise.reject(); + }), + save: jest.fn().mockImplementation((account: AccountDto): Promise => { + if (account.username === 'fail@to.update') { + return Promise.reject(); + } + const accountEntity = mockAccounts.find( + (tempAccount) => tempAccount.userId?.toString() === account.userId + ); + if (accountEntity) { + Object.assign(accountEntity, account); + return Promise.resolve(); + } + return Promise.reject(); + }), + delete: (id: EntityId): Promise => { + const account = mockAccounts.find((tempAccount) => tempAccount.id?.toString() === id); + + if (account) { + return Promise.resolve(AccountEntityToDtoMapper.mapToDto(account)); + } + throw new EntityNotFoundError(Account.name); + }, + create: (): Promise => Promise.resolve(), + findByUserId: (userId: EntityId): Promise => { + const account = mockAccounts.find((tempAccount) => tempAccount.userId?.toString() === userId); + + if (account) { + return Promise.resolve(AccountEntityToDtoMapper.mapToDto(account)); + } + return Promise.resolve(null); + }, + findByUserIdOrFail: (userId: EntityId): Promise => { + const account = mockAccounts.find((tempAccount) => tempAccount.userId?.toString() === userId); + + if (account) { + return Promise.resolve(AccountEntityToDtoMapper.mapToDto(account)); + } + if (userId === 'accountWithoutUser') { + return Promise.resolve(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); + } + throw new EntityNotFoundError(Account.name); + }, + findById: (accountId: EntityId): Promise => { + const account = mockAccounts.find((tempAccount) => tempAccount.id === accountId); + + if (account) { + return Promise.resolve(AccountEntityToDtoMapper.mapToDto(account)); + } + throw new EntityNotFoundError(Account.name); + }, + findByUsernameAndSystemId: (username: string, systemId: EntityId | ObjectId): Promise => { + const account = mockAccounts.find( + (tempAccount) => tempAccount.username === username && tempAccount.systemId === systemId + ); + if (account) { + return Promise.resolve(AccountEntityToDtoMapper.mapToDto(account)); + } + throw new EntityNotFoundError(Account.name); + }, + searchByUsernameExactMatch: (username: string): Promise> => { + const account = mockAccounts.find((tempAccount) => tempAccount.username === username); + + if (account) { + return Promise.resolve([[AccountEntityToDtoMapper.mapToDto(account)], 1]); + } + if (username === 'not@available.username') { + return Promise.resolve([[AccountEntityToDtoMapper.mapToDto(mockOtherTeacherAccount)], 1]); + } + if (username === 'multiple@account.username') { + return Promise.resolve([ + mockAccounts.map((mockAccount) => AccountEntityToDtoMapper.mapToDto(mockAccount)), + mockAccounts.length, + ]); + } + return Promise.resolve([[], 0]); + }, + searchByUsernamePartialMatch: (): Promise> => + Promise.resolve([ + mockAccounts.map((mockAccount) => AccountEntityToDtoMapper.mapToDto(mockAccount)), + mockAccounts.length, + ]), + updateLastTriedFailedLogin: jest.fn(), + validatePassword: jest.fn().mockResolvedValue(true), + }, }, { provide: ConfigService, @@ -65,12 +206,42 @@ describe('AccountUc', () => { }, { provide: UserRepo, - useValue: createMock(), + useValue: { + findById: (userId: EntityId): Promise => { + const user = mockUsers.find((tempUser) => tempUser.id === userId); + if (user) { + return Promise.resolve(user); + } + throw new EntityNotFoundError(User.name); + }, + findByEmail: (email: string): Promise => { + const user = mockUsers.find((tempUser) => tempUser.email === email); + + if (user) { + return Promise.resolve([user]); + } + if (email === 'not@available.email') { + return Promise.resolve([mockExternalUser]); + } + if (email === 'multiple@user.email') { + return Promise.resolve(mockUsers); + } + return Promise.resolve([]); + }, + save: jest.fn().mockImplementation((user: User): Promise => { + if (user.firstName === 'failToUpdate' || user.email === 'user-fail@to.update') { + return Promise.reject(); + } + return Promise.resolve(); + }), + }, }, PermissionService, { provide: AccountValidationService, - useValue: createMock(), + useValue: { + isUniqueEmail: jest.fn().mockResolvedValue(true), + }, }, ], }).compile(); @@ -78,3052 +249,983 @@ describe('AccountUc', () => { accountUc = module.get(AccountUc); userRepo = module.get(UserRepo); accountService = module.get(AccountService); + await setupEntities(); accountValidationService = module.get(AccountValidationService); configService = module.get(ConfigService); - await setupEntities(); }); beforeEach(() => { - jest.clearAllMocks(); - jest.resetAllMocks(); - }); - - describe('updateMyAccount', () => { - describe('When user does not exist', () => { - const setup = () => { - userRepo.findById.mockImplementation(() => { - throw new EntityNotFoundError(User.name); - }); - }; - - it('should throw EntityNotFoundError', async () => { - setup(); - await expect(accountUc.updateMyAccount('accountWithoutUser', { passwordOld: defaultPassword })).rejects.toThrow( - EntityNotFoundError - ); - }); + mockSchool = schoolFactory.buildWithId(); + mockOtherSchool = schoolFactory.buildWithId(); + mockSchoolWithStudentVisibility = schoolFactory.buildWithId(); + mockSchoolWithStudentVisibility.permissions = new SchoolRoles(); + mockSchoolWithStudentVisibility.permissions.teacher = new SchoolRolePermission(); + mockSchoolWithStudentVisibility.permissions.teacher.STUDENT_LIST = true; + + mockSuperheroUser = userFactory.buildWithId({ + school: mockSchool, + roles: [ + new Role({ + name: RoleName.SUPERHERO, + permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], + }), + ], }); - - describe('When account does not exists', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockUserWithoutAccount = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), + mockAdminUser = userFactory.buildWithId({ + school: mockSchool, + roles: [ + new Role({ + name: RoleName.ADMINISTRATOR, + permissions: [ + Permission.TEACHER_EDIT, + Permission.STUDENT_EDIT, + Permission.STUDENT_LIST, + Permission.TEACHER_LIST, + Permission.TEACHER_CREATE, + Permission.STUDENT_CREATE, + Permission.TEACHER_DELETE, + Permission.STUDENT_DELETE, ], - }); - - accountService.findByUserIdOrFail.mockImplementation((): Promise => { - throw new EntityNotFoundError(Account.name); - }); - - return { mockUserWithoutAccount }; - }; - - it('should throw entity not found error', async () => { - const { mockUserWithoutAccount } = setup(); - await expect( - accountUc.updateMyAccount(mockUserWithoutAccount.id, { - passwordOld: defaultPassword, - }) - ).rejects.toThrow(EntityNotFoundError); - }); + }), + ], }); - describe('When account is external', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockExternalUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const externalSystem = systemFactory.buildWithId(); - const mockExternalUserAccount = accountFactory.buildWithId({ - userId: mockExternalUser.id, - password: defaultPasswordHash, - systemId: externalSystem.id, - }); - - accountService.findByUserIdOrFail.mockResolvedValueOnce( - AccountEntityToDtoMapper.mapToDto(mockExternalUserAccount) - ); - - return { mockExternalUserAccount }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockExternalUserAccount } = setup(); - - await expect( - accountUc.updateMyAccount(mockExternalUserAccount.userId?.toString() ?? '', { - passwordOld: defaultPassword, - }) - ).rejects.toThrow(ForbiddenOperationError); - }); + mockTeacherUser = userFactory.buildWithId({ + school: mockSchool, + roles: [ + new Role({ + name: RoleName.TEACHER, + permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], + }), + ], }); - - describe('When password does not match', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValueOnce(false); - - return { mockStudentUser }; - }; - it('should throw AuthorizationError', async () => { - const { mockStudentUser } = setup(); - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: 'DoesNotMatch', - }) - ).rejects.toThrow(AuthorizationError); - }); + mockOtherTeacherUser = userFactory.buildWithId({ + school: mockSchool, + roles: [ + new Role({ + name: RoleName.TEACHER, + permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], + }), + ], }); - - describe('When changing own name is not allowed', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValue(true); - - return { mockStudentUser }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockStudentUser } = setup(); - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: defaultPassword, - firstName: 'newFirstName', - }) - ).rejects.toThrow(ForbiddenOperationError); - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: defaultPassword, - lastName: 'newLastName', - }) - ).rejects.toThrow(ForbiddenOperationError); - }); + mockTeacherNoUserPermissionUser = userFactory.buildWithId({ + school: mockSchoolWithStudentVisibility, + roles: [ + new Role({ + name: RoleName.TEACHER, + permissions: [], + }), + ], }); - - describe('When using student user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValue(true); - - return { mockStudentUser }; - }; - it('should allow to update email', async () => { - const { mockStudentUser } = setup(); - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: defaultPassword, - email: 'an@available.mail', - }) - ).resolves.not.toThrow(); - }); + mockTeacherNoUserNoSchoolPermissionUser = userFactory.buildWithId({ + school: mockSchool, + roles: [ + new Role({ + name: RoleName.TEACHER, + permissions: [], + }), + ], }); - describe('When using student user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValue(true); - accountValidationService.isUniqueEmail.mockResolvedValueOnce(true); - const accountSaveSpy = jest.spyOn(accountService, 'save'); - - return { mockStudentUser, accountSaveSpy }; - }; - it('should use email as account user name in lower case', async () => { - const { mockStudentUser, accountSaveSpy } = setup(); - - const testMail = 'AN@AVAILABLE.MAIL'; - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: defaultPassword, - email: testMail, - }) - ).resolves.not.toThrow(); - expect(accountSaveSpy).toBeCalledWith(expect.objectContaining({ username: testMail.toLowerCase() })); - }); + mockStudentSchoolPermissionUser = userFactory.buildWithId({ + school: mockSchoolWithStudentVisibility, + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], }); - - describe('When using student user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValue(true); - accountValidationService.isUniqueEmail.mockResolvedValueOnce(true); - - const userUpdateSpy = jest.spyOn(userRepo, 'save'); - - return { mockStudentUser, userUpdateSpy }; - }; - it('should use email as user email in lower case', async () => { - const { mockStudentUser, userUpdateSpy } = setup(); - const testMail = 'AN@AVAILABLE.MAIL'; - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: defaultPassword, - email: testMail, - }) - ).resolves.not.toThrow(); - expect(userUpdateSpy).toBeCalledWith(expect.objectContaining({ email: testMail.toLowerCase() })); - }); + mockOtherStudentSchoolPermissionUser = userFactory.buildWithId({ + school: mockSchoolWithStudentVisibility, + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], }); - describe('When using student user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValue(true); - accountValidationService.isUniqueEmail.mockResolvedValueOnce(true); - - const accountSaveSpy = jest.spyOn(accountService, 'save'); - const userUpdateSpy = jest.spyOn(userRepo, 'save'); - - return { mockStudentUser, accountSaveSpy, userUpdateSpy }; - }; - it('should always update account user name AND user email together.', async () => { - const { mockStudentUser, accountSaveSpy, userUpdateSpy } = setup(); - const testMail = 'an@available.mail'; - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: defaultPassword, - email: testMail, - }) - ).resolves.not.toThrow(); - expect(userUpdateSpy).toBeCalledWith(expect.objectContaining({ email: testMail.toLowerCase() })); - expect(accountSaveSpy).toBeCalledWith(expect.objectContaining({ username: testMail.toLowerCase() })); - }); + mockStudentUser = userFactory.buildWithId({ + school: mockSchool, + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], }); - - describe('When using student user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValue(true); - accountValidationService.isUniqueEmail.mockResolvedValueOnce(false); - - return { mockStudentUser }; - }; - it('should throw if new email already in use', async () => { - const { mockStudentUser } = setup(); - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: defaultPassword, - email: 'already@in.use', - }) - ).rejects.toThrow(ValidationError); - }); + mockOtherStudentUser = userFactory.buildWithId({ + school: mockSchool, + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], }); - - describe('When using student user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValue(true); - - return { mockStudentUser }; - }; - it('should allow to update with strong password', async () => { - const { mockStudentUser } = setup(); - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: defaultPassword, - passwordNew: otherPassword, - }) - ).resolves.not.toThrow(); - }); + mockDifferentSchoolAdminUser = userFactory.buildWithId({ + school: mockOtherSchool, + roles: [...mockAdminUser.roles], }); - - describe('When using teacher user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - - const mockTeacherAccount = accountFactory.buildWithId({ - userId: mockTeacherUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockTeacherUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - accountService.validatePassword.mockResolvedValue(true); - - return { mockTeacherUser }; - }; - it('should allow to update first and last name', async () => { - const { mockTeacherUser } = setup(); - await expect( - accountUc.updateMyAccount(mockTeacherUser.id, { - passwordOld: defaultPassword, - firstName: 'newFirstName', - }) - ).resolves.not.toThrow(); - await expect( - accountUc.updateMyAccount(mockTeacherUser.id, { - passwordOld: defaultPassword, - lastName: 'newLastName', - }) - ).resolves.not.toThrow(); - }); + mockDifferentSchoolTeacherUser = userFactory.buildWithId({ + school: mockOtherSchool, + roles: [...mockTeacherUser.roles], }); - - describe('When using admin user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - - const mockAdminAccount = accountFactory.buildWithId({ - userId: mockAdminUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockAdminUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockAdminAccount)); - accountService.validatePassword.mockResolvedValue(true); - - return { mockAdminUser }; - }; - it('should allow to update first and last name', async () => { - const { mockAdminUser } = setup(); - await expect( - accountUc.updateMyAccount(mockAdminUser.id, { - passwordOld: defaultPassword, - firstName: 'newFirstName', - }) - ).resolves.not.toThrow(); - await expect( - accountUc.updateMyAccount(mockAdminUser.id, { - passwordOld: defaultPassword, - lastName: 'newLastName', - }) - ).resolves.not.toThrow(); - }); + mockDifferentSchoolStudentUser = userFactory.buildWithId({ + school: mockOtherSchool, + roles: [...mockStudentUser.roles], }); - - describe('When using superhero user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockSuperheroAccount = accountFactory.buildWithId({ - userId: mockSuperheroUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockSuperheroUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockSuperheroAccount)); - accountService.validatePassword.mockResolvedValue(true); - - return { mockSuperheroUser }; - }; - it('should allow to update first and last name ', async () => { - const { mockSuperheroUser } = setup(); - await expect( - accountUc.updateMyAccount(mockSuperheroUser.id, { - passwordOld: defaultPassword, - firstName: 'newFirstName', - }) - ).resolves.not.toThrow(); - await expect( - accountUc.updateMyAccount(mockSuperheroUser.id, { - passwordOld: defaultPassword, - lastName: 'newLastName', - }) - ).resolves.not.toThrow(); - }); + mockUserWithoutAccount = userFactory.buildWithId({ + school: mockSchool, + roles: [ + new Role({ + name: RoleName.ADMINISTRATOR, + permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], + }), + ], }); - - describe('When user can not be updated', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockTeacherAccount = accountFactory.buildWithId({ - userId: mockTeacherUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockTeacherUser); - userRepo.save.mockRejectedValueOnce(undefined); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - accountService.validatePassword.mockResolvedValue(true); - - return { mockTeacherUser, mockTeacherAccount }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockTeacherUser } = setup(); - await expect( - accountUc.updateMyAccount(mockTeacherUser.id, { - passwordOld: defaultPassword, - firstName: 'failToUpdate', - }) - ).rejects.toThrow(EntityNotFoundError); - }); + mockUserWithoutRole = userFactory.buildWithId({ + school: mockSchool, + roles: [], }); - - describe('When account can not be updated', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockStudentUser); - userRepo.save.mockResolvedValueOnce(undefined); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValue(true); - accountService.save.mockRejectedValueOnce(undefined); - - accountValidationService.isUniqueEmail.mockResolvedValue(true); - - return { mockStudentUser }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockStudentUser } = setup(); - await expect( - accountUc.updateMyAccount(mockStudentUser.id, { - passwordOld: defaultPassword, - email: 'fail@to.update', - }) - ).rejects.toThrow(EntityNotFoundError); - }); + mockUnknownRoleUser = userFactory.buildWithId({ + school: mockSchool, + roles: [new Role({ name: 'undefinedRole' as RoleName, permissions: ['' as Permission] })], + }); + mockExternalUser = userFactory.buildWithId({ + school: mockSchool, + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], + }); + mockStudentUserWithoutAccount = userFactory.buildWithId({ + school: mockSchool, + roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], }); - describe('When no new password is given', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValue(true); - const spyAccountServiceSave = jest.spyOn(accountService, 'save'); + mockSuperheroAccount = accountFactory.buildWithId({ + userId: mockSuperheroUser.id, + password: defaultPasswordHash, + }); + mockTeacherAccount = accountFactory.buildWithId({ + userId: mockTeacherUser.id, + password: defaultPasswordHash, + }); + mockOtherTeacherAccount = accountFactory.buildWithId({ + userId: mockOtherTeacherUser.id, + password: defaultPasswordHash, + }); + mockTeacherNoUserPermissionAccount = accountFactory.buildWithId({ + userId: mockTeacherNoUserPermissionUser.id, + password: defaultPasswordHash, + }); + mockTeacherNoUserNoSchoolPermissionAccount = accountFactory.buildWithId({ + userId: mockTeacherNoUserNoSchoolPermissionUser.id, + password: defaultPasswordHash, + }); + mockAdminAccount = accountFactory.buildWithId({ + userId: mockAdminUser.id, + password: defaultPasswordHash, + }); + mockStudentAccount = accountFactory.buildWithId({ + userId: mockStudentUser.id, + password: defaultPasswordHash, + }); + mockStudentSchoolPermissionAccount = accountFactory.buildWithId({ + userId: mockStudentSchoolPermissionUser.id, + password: defaultPasswordHash, + }); + mockAccountWithoutRole = accountFactory.buildWithId({ + userId: mockUserWithoutRole.id, + password: defaultPasswordHash, + }); + mockDifferentSchoolAdminAccount = accountFactory.buildWithId({ + userId: mockDifferentSchoolAdminUser.id, + password: defaultPasswordHash, + }); + mockDifferentSchoolTeacherAccount = accountFactory.buildWithId({ + userId: mockDifferentSchoolTeacherUser.id, + password: defaultPasswordHash, + }); + mockDifferentSchoolStudentAccount = accountFactory.buildWithId({ + userId: mockDifferentSchoolStudentUser.id, + password: defaultPasswordHash, + }); + mockUnknownRoleUserAccount = accountFactory.buildWithId({ + userId: mockUnknownRoleUser.id, + password: defaultPasswordHash, + }); + const externalSystem = systemFactory.buildWithId(); + mockExternalUserAccount = accountFactory.buildWithId({ + userId: mockExternalUser.id, + password: defaultPasswordHash, + systemId: externalSystem.id, + }); + mockAccountWithoutUser = accountFactory.buildWithId({ + userId: undefined, + password: defaultPasswordHash, + systemId: systemFactory.buildWithId().id, + }); + mockAccountWithSystemId = accountFactory.withSystemId(new ObjectId(10)).build(); + mockAccountWithLastFailedLogin = accountFactory.buildWithId({ + userId: undefined, + password: defaultPasswordHash, + systemId: systemFactory.buildWithId().id, + lasttriedFailedLogin: new Date(), + }); + mockAccountWithOldLastFailedLogin = accountFactory.buildWithId({ + userId: undefined, + password: defaultPasswordHash, + systemId: systemFactory.buildWithId().id, + lasttriedFailedLogin: new Date(new Date().getTime() - LOGIN_BLOCK_TIME - 1), + }); + mockAccountWithNoLastFailedLogin = accountFactory.buildWithId({ + userId: undefined, + password: defaultPasswordHash, + systemId: systemFactory.buildWithId().id, + lasttriedFailedLogin: undefined, + }); - accountValidationService.isUniqueEmail.mockResolvedValue(true); + mockUsers = [ + mockSuperheroUser, + mockAdminUser, + mockTeacherUser, + mockOtherTeacherUser, + mockTeacherNoUserPermissionUser, + mockTeacherNoUserNoSchoolPermissionUser, + mockStudentUser, + mockStudentSchoolPermissionUser, + mockDifferentSchoolAdminUser, + mockDifferentSchoolTeacherUser, + mockDifferentSchoolStudentUser, + mockUnknownRoleUser, + mockExternalUser, + mockUserWithoutRole, + mockUserWithoutAccount, + mockStudentUserWithoutAccount, + mockOtherStudentUser, + mockOtherStudentSchoolPermissionUser, + ]; + + mockAccounts = [ + mockSuperheroAccount, + mockAdminAccount, + mockTeacherAccount, + mockOtherTeacherAccount, + mockTeacherNoUserPermissionAccount, + mockTeacherNoUserNoSchoolPermissionAccount, + mockStudentAccount, + mockStudentSchoolPermissionAccount, + mockDifferentSchoolAdminAccount, + mockDifferentSchoolTeacherAccount, + mockDifferentSchoolStudentAccount, + mockUnknownRoleUserAccount, + mockExternalUserAccount, + mockAccountWithoutRole, + mockAccountWithoutUser, + mockAccountWithSystemId, + mockAccountWithLastFailedLogin, + mockAccountWithOldLastFailedLogin, + mockAccountWithNoLastFailedLogin, + ]; + }); - return { mockStudentUser, spyAccountServiceSave }; - }; - it('should not update password', async () => { - const { mockStudentUser, spyAccountServiceSave } = setup(); - await accountUc.updateMyAccount(mockStudentUser.id, { + describe('updateMyAccount', () => { + it('should throw if user does not exist', async () => { + mockStudentUser.forcePasswordChange = true; + mockStudentUser.preferences = { firstLogin: true }; + await expect(accountUc.updateMyAccount('accountWithoutUser', { passwordOld: defaultPassword })).rejects.toThrow( + EntityNotFoundError + ); + }); + it('should throw if account does not exist', async () => { + await expect( + accountUc.updateMyAccount(mockUserWithoutAccount.id, { passwordOld: defaultPassword, - passwordNew: undefined, - email: 'newemail@to.update', - }); - expect(spyAccountServiceSave).toHaveBeenCalledWith( - expect.objectContaining({ - password: undefined, - }) - ); - }); + }) + ).rejects.toThrow(EntityNotFoundError); + }); + it('should throw if account is external', async () => { + await expect( + accountUc.updateMyAccount(mockExternalUserAccount.userId?.toString() ?? '', { + passwordOld: defaultPassword, + }) + ).rejects.toThrow(ForbiddenOperationError); + }); + it('should throw if password does not match', async () => { + jest.spyOn(accountService, 'validatePassword').mockResolvedValueOnce(false); + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: 'DoesNotMatch', + }) + ).rejects.toThrow(AuthorizationError); + }); + it('should throw if changing own name is not allowed', async () => { + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + firstName: 'newFirstName', + }) + ).rejects.toThrow(ForbiddenOperationError); + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + lastName: 'newLastName', + }) + ).rejects.toThrow(ForbiddenOperationError); + }); + it('should allow to update email', async () => { + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + email: 'an@available.mail', + }) + ).resolves.not.toThrow(); + }); + it('should use email as account user name in lower case', async () => { + const accountSaveSpy = jest.spyOn(accountService, 'save'); + const testMail = 'AN@AVAILABLE.MAIL'; + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + email: testMail, + }) + ).resolves.not.toThrow(); + expect(accountSaveSpy).toBeCalledWith(expect.objectContaining({ username: testMail.toLowerCase() })); + }); + it('should use email as user email in lower case', async () => { + const userUpdateSpy = jest.spyOn(userRepo, 'save'); + const testMail = 'AN@AVAILABLE.MAIL'; + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + email: testMail, + }) + ).resolves.not.toThrow(); + expect(userUpdateSpy).toBeCalledWith(expect.objectContaining({ email: testMail.toLowerCase() })); + }); + it('should always update account user name AND user email together.', async () => { + const accountSaveSpy = jest.spyOn(accountService, 'save'); + const userUpdateSpy = jest.spyOn(userRepo, 'save'); + const testMail = 'an@available.mail'; + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + email: testMail, + }) + ).resolves.not.toThrow(); + expect(userUpdateSpy).toBeCalledWith(expect.objectContaining({ email: testMail.toLowerCase() })); + expect(accountSaveSpy).toBeCalledWith(expect.objectContaining({ username: testMail.toLowerCase() })); + }); + it('should throw if new email already in use', async () => { + const accountIsUniqueEmailSpy = jest.spyOn(accountValidationService, 'isUniqueEmail'); + accountIsUniqueEmailSpy.mockResolvedValueOnce(false); + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + email: mockAdminUser.email, + }) + ).rejects.toThrow(ValidationError); + }); + it('should allow to update with strong password', async () => { + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + passwordNew: otherPassword, + }) + ).resolves.not.toThrow(); + }); + it('should allow to update first and last name if teacher', async () => { + await expect( + accountUc.updateMyAccount(mockTeacherUser.id, { + passwordOld: defaultPassword, + firstName: 'newFirstName', + }) + ).resolves.not.toThrow(); + await expect( + accountUc.updateMyAccount(mockTeacherUser.id, { + passwordOld: defaultPassword, + lastName: 'newLastName', + }) + ).resolves.not.toThrow(); + }); + it('should allow to update first and last name if admin', async () => { + await expect( + accountUc.updateMyAccount(mockAdminUser.id, { + passwordOld: defaultPassword, + firstName: 'newFirstName', + }) + ).resolves.not.toThrow(); + await expect( + accountUc.updateMyAccount(mockAdminUser.id, { + passwordOld: defaultPassword, + lastName: 'newLastName', + }) + ).resolves.not.toThrow(); + }); + it('should allow to update first and last name if superhero', async () => { + await expect( + accountUc.updateMyAccount(mockSuperheroUser.id, { + passwordOld: defaultPassword, + firstName: 'newFirstName', + }) + ).resolves.not.toThrow(); + await expect( + accountUc.updateMyAccount(mockSuperheroUser.id, { + passwordOld: defaultPassword, + lastName: 'newLastName', + }) + ).resolves.not.toThrow(); + }); + it('should throw if user can not be updated', async () => { + await expect( + accountUc.updateMyAccount(mockTeacherUser.id, { + passwordOld: defaultPassword, + firstName: 'failToUpdate', + }) + ).rejects.toThrow(EntityNotFoundError); + }); + it('should throw if account can not be updated', async () => { + await expect( + accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + email: 'fail@to.update', + }) + ).rejects.toThrow(EntityNotFoundError); + }); + it('should not update password if no new password', async () => { + const spy = jest.spyOn(accountService, 'save'); + await accountUc.updateMyAccount(mockStudentUser.id, { + passwordOld: defaultPassword, + passwordNew: undefined, + email: 'newemail@to.update', + }); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ + password: undefined, + }) + ); }); }); describe('replaceMyTemporaryPassword', () => { - describe('When passwords do not match', () => { - it('should throw ForbiddenOperationError', async () => { - await expect(accountUc.replaceMyTemporaryPassword('userId', defaultPassword, 'FooPasswd!1')).rejects.toThrow( - ForbiddenOperationError - ); - }); + it('should throw if passwords do not match', async () => { + await expect( + accountUc.replaceMyTemporaryPassword( + mockStudentAccount.userId?.toString() ?? '', + defaultPassword, + 'FooPasswd!1' + ) + ).rejects.toThrow(ForbiddenOperationError); }); - describe('When account does not exists', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - const mockUserWithoutAccount = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - - userRepo.findById.mockResolvedValueOnce(mockUserWithoutAccount); - accountService.findByUserIdOrFail.mockImplementation(() => { - throw new EntityNotFoundError(Account.name); - }); - - return { mockUserWithoutAccount }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockUserWithoutAccount } = setup(); - await expect( - accountUc.replaceMyTemporaryPassword(mockUserWithoutAccount.id, defaultPassword, defaultPassword) - ).rejects.toThrow(EntityNotFoundError); - }); + it('should throw if account does not exist', async () => { + await expect( + accountUc.replaceMyTemporaryPassword(mockUserWithoutAccount.id, defaultPassword, defaultPassword) + ).rejects.toThrow(EntityNotFoundError); }); - - describe('When user does not exist', () => { - const setup = () => { - userRepo.findById.mockRejectedValueOnce(undefined); - }; - it('should throw EntityNotFoundError', async () => { - setup(); - await expect( - accountUc.replaceMyTemporaryPassword('accountWithoutUser', defaultPassword, defaultPassword) - ).rejects.toThrow(EntityNotFoundError); - }); + it('should throw if user does not exist', async () => { + await expect( + accountUc.replaceMyTemporaryPassword('accountWithoutUser', defaultPassword, defaultPassword) + ).rejects.toThrow(EntityNotFoundError); }); + it('should throw if account is external', async () => { + await expect( + accountUc.replaceMyTemporaryPassword( + mockExternalUserAccount.userId?.toString() ?? '', + defaultPassword, + defaultPassword + ) + ).rejects.toThrow(ForbiddenOperationError); + }); + it('should throw if not the users password is temporary', async () => { + mockStudentUser.forcePasswordChange = false; + mockStudentUser.preferences = { firstLogin: true }; + await expect( + accountUc.replaceMyTemporaryPassword( + mockStudentAccount.userId?.toString() ?? '', + defaultPassword, + defaultPassword + ) + ).rejects.toThrow(ForbiddenOperationError); + }); + it('should throw, if old password is the same as new password', async () => { + mockStudentUser.forcePasswordChange = false; + mockStudentUser.preferences = { firstLogin: false }; + await expect( + accountUc.replaceMyTemporaryPassword( + mockStudentAccount.userId?.toString() ?? '', + defaultPassword, + defaultPassword + ) + ).rejects.toThrow(ForbiddenOperationError); + }); + it('should throw, if old password is undefined', async () => { + mockStudentUser.forcePasswordChange = false; + mockStudentUser.preferences = { firstLogin: false }; + mockStudentAccount.password = undefined; + await expect( + accountUc.replaceMyTemporaryPassword( + mockStudentAccount.userId?.toString() ?? '', + defaultPassword, + defaultPassword + ) + ).rejects.toThrow(Error); + }); + it('should allow to set strong password, if the admin manipulated the users password', async () => { + mockStudentUser.forcePasswordChange = true; + mockStudentUser.preferences = { firstLogin: true }; + jest.spyOn(accountService, 'validatePassword').mockResolvedValueOnce(false); + await expect( + accountUc.replaceMyTemporaryPassword(mockStudentAccount.userId?.toString() ?? '', otherPassword, otherPassword) + ).resolves.not.toThrow(); + }); + it('should allow to set strong password, if this is the users first login', async () => { + mockStudentUser.forcePasswordChange = false; + mockStudentUser.preferences = { firstLogin: false }; + jest.spyOn(accountService, 'validatePassword').mockResolvedValueOnce(false); + await expect( + accountUc.replaceMyTemporaryPassword(mockStudentAccount.userId?.toString() ?? '', otherPassword, otherPassword) + ).resolves.not.toThrow(); + }); + it('should allow to set strong password, if this is the users first login (if undefined)', async () => { + mockStudentUser.forcePasswordChange = false; + mockStudentUser.preferences = undefined; + jest.spyOn(accountService, 'validatePassword').mockResolvedValueOnce(false); + await expect( + accountUc.replaceMyTemporaryPassword(mockStudentAccount.userId?.toString() ?? '', otherPassword, otherPassword) + ).resolves.not.toThrow(); + }); + it('should throw if user can not be updated', async () => { + mockStudentUser.forcePasswordChange = false; + mockStudentUser.preferences = { firstLogin: false }; + mockStudentUser.firstName = 'failToUpdate'; + jest.spyOn(accountService, 'validatePassword').mockResolvedValueOnce(false); + await expect( + accountUc.replaceMyTemporaryPassword(mockStudentAccount.userId?.toString() ?? '', otherPassword, otherPassword) + ).rejects.toThrow(EntityNotFoundError); + }); + it('should throw if account can not be updated', async () => { + mockStudentUser.forcePasswordChange = false; + mockStudentUser.preferences = { firstLogin: false }; + mockStudentAccount.username = 'fail@to.update'; + jest.spyOn(accountService, 'validatePassword').mockResolvedValueOnce(false); + await expect( + accountUc.replaceMyTemporaryPassword(mockStudentAccount.userId?.toString() ?? '', otherPassword, otherPassword) + ).rejects.toThrow(EntityNotFoundError); + }); + }); - describe('When account is external', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockExternalUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const externalSystem = systemFactory.buildWithId(); - const mockExternalUserAccount = accountFactory.buildWithId({ - userId: mockExternalUser.id, - password: defaultPasswordHash, - systemId: externalSystem.id, - }); - - userRepo.findById.mockResolvedValueOnce(mockExternalUser); - accountService.findByUserIdOrFail.mockResolvedValueOnce( - AccountEntityToDtoMapper.mapToDto(mockExternalUserAccount) - ); + describe('searchAccounts', () => { + it('should return one account, if search type is userId', async () => { + const accounts = await accountUc.searchAccounts( + { userId: mockSuperheroUser.id } as ICurrentUser, + { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams + ); + const expected = new AccountSearchListResponse( + [AccountResponseMapper.mapToResponseFromEntity(mockStudentAccount)], + 1, + 0, + 1 + ); + expect(accounts).toStrictEqual(expected); + }); + it('should return empty list, if account is not found', async () => { + const accounts = await accountUc.searchAccounts( + { userId: mockSuperheroUser.id } as ICurrentUser, + { type: AccountSearchType.USER_ID, value: mockUserWithoutAccount.id } as AccountSearchQueryParams + ); + const expected = new AccountSearchListResponse([], 0, 0, 0); + expect(accounts).toStrictEqual(expected); + }); + it('should return one or more accounts, if search type is username', async () => { + const accounts = await accountUc.searchAccounts( + { userId: mockSuperheroUser.id } as ICurrentUser, + { type: AccountSearchType.USERNAME, value: '' } as AccountSearchQueryParams + ); + expect(accounts.skip).toEqual(0); + expect(accounts.limit).toEqual(10); + expect(accounts.total).toBeGreaterThan(1); + expect(accounts.data.length).toBeGreaterThan(1); + }); + it('should throw, if user has not the right permissions', async () => { + await expect( + accountUc.searchAccounts( + { userId: mockTeacherUser.id } as ICurrentUser, + { type: AccountSearchType.USER_ID, value: mockAdminUser.id } as AccountSearchQueryParams + ) + ).rejects.toThrow(ForbiddenOperationError); + + await expect( + accountUc.searchAccounts( + { userId: mockStudentUser.id } as ICurrentUser, + { type: AccountSearchType.USER_ID, value: mockOtherStudentUser.id } as AccountSearchQueryParams + ) + ).rejects.toThrow(ForbiddenOperationError); + + await expect( + accountUc.searchAccounts( + { userId: mockStudentUser.id } as ICurrentUser, + { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams + ) + ).rejects.toThrow(ForbiddenOperationError); + }); + it('should throw, if search type is unknown', async () => { + await expect( + accountUc.searchAccounts( + { userId: mockSuperheroUser.id } as ICurrentUser, + { type: '' as AccountSearchType } as AccountSearchQueryParams + ) + ).rejects.toThrow('Invalid search type.'); + }); + it('should throw, if user is no superhero', async () => { + await expect( + accountUc.searchAccounts( + { userId: mockTeacherUser.id } as ICurrentUser, + { type: AccountSearchType.USERNAME, value: mockStudentUser.id } as AccountSearchQueryParams + ) + ).rejects.toThrow(ForbiddenOperationError); + }); - return { mockExternalUserAccount }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockExternalUserAccount } = setup(); - await expect( - accountUc.replaceMyTemporaryPassword( - mockExternalUserAccount.userId?.toString() ?? '', - defaultPassword, - defaultPassword - ) - ).rejects.toThrow(ForbiddenOperationError); + describe('hasPermissionsToAccessAccount', () => { + beforeEach(() => { + configService.get.mockReturnValue(false); }); - }); - describe('When not the users password is temporary', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); + it('admin can access teacher of the same school via user id', async () => { + const currentUser = { userId: mockAdminUser.id } as ICurrentUser; + const params = { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); + }); + it('admin can access student of the same school via user id', async () => { + const currentUser = { userId: mockAdminUser.id } as ICurrentUser; + const params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); + }); + it('admin can not access admin of the same school via user id', async () => { + const currentUser = { userId: mockAdminUser.id } as ICurrentUser; + const params = { type: AccountSearchType.USER_ID, value: mockAdminUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); + }); + it('admin can not access any account of a foreign school via user id', async () => { + const currentUser = { userId: mockDifferentSchoolAdminUser.id } as ICurrentUser; - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - forcePasswordChange: false, - preferences: { firstLogin: true }, - }); + let params = { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); + params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); + }); + it('teacher can access teacher of the same school via user id', async () => { + const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; + const params = { type: AccountSearchType.USER_ID, value: mockOtherTeacherUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); + }); + it('teacher can access student of the same school via user id', async () => { + const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; + const params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); + }); + it('teacher can not access admin of the same school via user id', async () => { + const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; + const params = { type: AccountSearchType.USER_ID, value: mockAdminUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); + }); + it('teacher can not access any account of a foreign school via user id', async () => { + const currentUser = { userId: mockDifferentSchoolTeacherUser.id } as ICurrentUser; - userRepo.findById.mockResolvedValueOnce(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); + let params = { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - return { mockStudentAccount }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockStudentAccount } = setup(); - await expect( - accountUc.replaceMyTemporaryPassword( - mockStudentAccount.userId?.toString() ?? '', - defaultPassword, - defaultPassword - ) - ).rejects.toThrow(ForbiddenOperationError); + params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); + }); + it('teacher can access student of the same school via user id if school has global permission', async () => { + configService.get.mockReturnValue(true); + const currentUser = { userId: mockTeacherNoUserPermissionUser.id } as ICurrentUser; + const params = { + type: AccountSearchType.USER_ID, + value: mockStudentSchoolPermissionUser.id, + } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); + }); + it('teacher can not access student of the same school if school has no global permission', async () => { + configService.get.mockReturnValue(true); + const currentUser = { userId: mockTeacherNoUserNoSchoolPermissionUser.id } as ICurrentUser; + const params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(ForbiddenOperationError); }); - }); - - describe('When old password is the same as new password', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - forcePasswordChange: false, - preferences: { firstLogin: false }, - }); + it('student can not access student of the same school if school has global permission', async () => { + configService.get.mockReturnValue(true); + const currentUser = { userId: mockStudentSchoolPermissionUser.id } as ICurrentUser; + const params = { + type: AccountSearchType.USER_ID, + value: mockOtherStudentSchoolPermissionUser.id, + } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(ForbiddenOperationError); + }); + it('student can not access any other account via user id', async () => { + const currentUser = { userId: mockStudentUser.id } as ICurrentUser; - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); + let params = { type: AccountSearchType.USER_ID, value: mockAdminUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - userRepo.findById.mockResolvedValueOnce(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValueOnce(true); + params = { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - return { mockStudentAccount }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockStudentAccount } = setup(); - await expect( - accountUc.replaceMyTemporaryPassword( - mockStudentAccount.userId?.toString() ?? '', - defaultPassword, - defaultPassword - ) - ).rejects.toThrow(ForbiddenOperationError); + params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); }); - }); + it('superhero can access any account via username', async () => { + const currentUser = { userId: mockSuperheroUser.id } as ICurrentUser; - describe('When old password is undefined', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); + let params = { type: AccountSearchType.USERNAME, value: mockAdminAccount.username } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - forcePasswordChange: false, - preferences: { firstLogin: false }, - }); + params = { type: AccountSearchType.USERNAME, value: mockTeacherAccount.username } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: undefined, - }); + params = { type: AccountSearchType.USERNAME, value: mockStudentAccount.username } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - userRepo.findById.mockResolvedValueOnce(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValueOnce(true); + params = { + type: AccountSearchType.USERNAME, + value: mockDifferentSchoolAdminAccount.username, + } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - return { mockStudentAccount }; - }; - it('should throw Error', async () => { - const { mockStudentAccount } = setup(); - await expect( - accountUc.replaceMyTemporaryPassword( - mockStudentAccount.userId?.toString() ?? '', - defaultPassword, - defaultPassword - ) - ).rejects.toThrow(Error); + params = { + type: AccountSearchType.USERNAME, + value: mockDifferentSchoolTeacherAccount.username, + } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); + + params = { + type: AccountSearchType.USERNAME, + value: mockDifferentSchoolStudentAccount.username, + } as AccountSearchQueryParams; + await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); }); }); + }); - describe('When the admin manipulate the users password', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - forcePasswordChange: true, - preferences: { firstLogin: true }, - }); - const mockStudentAccount = accountFactory.buildWithId({ + describe('findAccountById', () => { + it('should return an account, if the current user is a superhero', async () => { + const account = await accountUc.findAccountById( + { userId: mockSuperheroUser.id } as ICurrentUser, + { id: mockStudentAccount.id } as AccountByIdParams + ); + expect(account).toStrictEqual( + expect.objectContaining({ + id: mockStudentAccount.id, + username: mockStudentAccount.username, userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValueOnce(false); - return { mockStudentAccount }; - }; - it('should allow to set strong password', async () => { - const { mockStudentAccount } = setup(); - - await expect( - accountUc.replaceMyTemporaryPassword( - mockStudentAccount.userId?.toString() ?? '', - otherPassword, - otherPassword - ) - ).resolves.not.toThrow(); - }); - }); - - describe('when a user logs in for the first time', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - forcePasswordChange: false, - preferences: { firstLogin: false }, - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValueOnce(false); - - return { mockStudentAccount }; - }; - it('should allow to set strong password', async () => { - const { mockStudentAccount } = setup(); - - await expect( - accountUc.replaceMyTemporaryPassword( - mockStudentAccount.userId?.toString() ?? '', - otherPassword, - otherPassword - ) - ).resolves.not.toThrow(); - }); + activated: mockStudentAccount.activated, + }) + ); }); - - describe('when a user logs in for the first time (if undefined)', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - forcePasswordChange: false, - }); - mockStudentUser.preferences = undefined; - - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockStudentUser); - accountService.findByUserIdOrFail.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValueOnce(false); - - return { mockStudentAccount }; - }; - it('should allow to set strong password', async () => { - const { mockStudentAccount } = setup(); - await expect( - accountUc.replaceMyTemporaryPassword( - mockStudentAccount.userId?.toString() ?? '', - otherPassword, - otherPassword - ) - ).resolves.not.toThrow(); - }); + it('should throw, if the current user is no superhero', async () => { + await expect( + accountUc.findAccountById( + { userId: mockTeacherUser.id } as ICurrentUser, + { id: mockStudentAccount.id } as AccountByIdParams + ) + ).rejects.toThrow(ForbiddenOperationError); }); - - describe('When user can not be updated', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - firstName: 'failToUpdate', - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - preferences: { firstLogin: false }, - forcePasswordChange: false, - }); - - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockStudentUser); - userRepo.save.mockRejectedValueOnce(undefined); - accountService.findByUserIdOrFail.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.validatePassword.mockResolvedValueOnce(false); - - return { mockStudentAccount }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockStudentAccount } = setup(); - await expect( - accountUc.replaceMyTemporaryPassword( - mockStudentAccount.userId?.toString() ?? '', - otherPassword, - otherPassword - ) - ).rejects.toThrow(EntityNotFoundError); - }); + it('should throw, if no account matches the search term', async () => { + await expect( + accountUc.findAccountById({ userId: mockSuperheroUser.id } as ICurrentUser, { id: 'xxx' } as AccountByIdParams) + ).rejects.toThrow(EntityNotFoundError); }); - - describe('When account can not be updated', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - forcePasswordChange: false, - preferences: { firstLogin: false }, - }); - - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - username: 'fail@to.update', - }); - - userRepo.findById.mockResolvedValueOnce(mockStudentUser); - userRepo.save.mockResolvedValueOnce(); - accountService.findByUserIdOrFail.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - accountService.save.mockRejectedValueOnce(undefined); - accountService.validatePassword.mockResolvedValueOnce(false); - - return { mockStudentAccount }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockStudentAccount } = setup(); - await expect( - accountUc.replaceMyTemporaryPassword( - mockStudentAccount.userId?.toString() ?? '', - otherPassword, - otherPassword - ) - ).rejects.toThrow(EntityNotFoundError); - }); + it('should throw, if target account has no user', async () => { + await expect( + accountUc.findAccountById({ userId: mockSuperheroUser.id } as ICurrentUser, { id: 'xxx' } as AccountByIdParams) + ).rejects.toThrow(EntityNotFoundError); }); }); - describe('searchAccounts', () => { - describe('When search type is userId', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - const mockSchoolWithStudentVisibility = schoolFactory.buildWithId(); - mockSchoolWithStudentVisibility.permissions = new SchoolRoles(); - mockSchoolWithStudentVisibility.permissions.teacher = new SchoolRolePermission(); - mockSchoolWithStudentVisibility.permissions.teacher.STUDENT_LIST = true; - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser).mockResolvedValueOnce(mockStudentUser); - accountService.findByUserId.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - return { mockSuperheroUser, mockStudentUser, mockStudentAccount }; - }; - it('should return one account', async () => { - const { mockSuperheroUser, mockStudentUser, mockStudentAccount } = setup(); - const accounts = await accountUc.searchAccounts( - { userId: mockSuperheroUser.id } as ICurrentUser, - { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams - ); - const expected = new AccountSearchListResponse( - [AccountResponseMapper.mapToResponseFromEntity(mockStudentAccount)], - 1, - 0, - 1 - ); - expect(accounts).toStrictEqual(expected); - }); + describe('saveAccount', () => { + afterEach(() => { + jest.clearAllMocks(); }); - describe('When account is not found', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockUserWithoutAccount = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser).mockResolvedValueOnce(mockUserWithoutAccount); - - return { mockSuperheroUser, mockUserWithoutAccount }; + it('should call account service', async () => { + const spy = jest.spyOn(accountService, 'saveWithValidation'); + const params: AccountSaveDto = { + username: 'john.doe@domain.tld', + password: defaultPassword, }; - it('should return empty list', async () => { - const { mockSuperheroUser, mockUserWithoutAccount } = setup(); - const accounts = await accountUc.searchAccounts( - { userId: mockSuperheroUser.id } as ICurrentUser, - { type: AccountSearchType.USER_ID, value: mockUserWithoutAccount.id } as AccountSearchQueryParams - ); - const expected = new AccountSearchListResponse([], 0, 0, 0); - expect(accounts).toStrictEqual(expected); - }); + await accountUc.saveAccount(params); + expect(spy).toHaveBeenCalledWith( + expect.objectContaining({ + username: 'john.doe@domain.tld', + }) + ); }); - describe('When search type is username', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser).mockResolvedValueOnce(mockSuperheroUser); - accountService.searchByUsernamePartialMatch.mockResolvedValueOnce([ - [ - AccountEntityToDtoMapper.mapToDto(mockStudentAccount), - AccountEntityToDtoMapper.mapToDto(mockStudentAccount), - ], - 2, - ]); + }); - return { mockSuperheroUser }; - }; - it('should return one or more accounts, ', async () => { - const { mockSuperheroUser } = setup(); - const accounts = await accountUc.searchAccounts( - { userId: mockSuperheroUser.id } as ICurrentUser, - { type: AccountSearchType.USERNAME, value: '' } as AccountSearchQueryParams - ); - expect(accounts.skip).toEqual(0); - expect(accounts.limit).toEqual(10); - expect(accounts.total).toBeGreaterThan(1); - expect(accounts.data.length).toBeGreaterThan(1); - }); + describe('updateAccountById', () => { + it('should throw if executing user does not exist', async () => { + const currentUser = { userId: '000000000000000' } as ICurrentUser; + const params = { id: mockStudentAccount.id } as AccountByIdParams; + const body = {} as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(EntityNotFoundError); }); - - describe('When user has not the right permissions', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockOtherStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - userRepo.findById.mockResolvedValueOnce(mockTeacherUser).mockResolvedValueOnce(mockAdminUser); - userRepo.findById.mockResolvedValueOnce(mockStudentUser).mockResolvedValueOnce(mockOtherStudentUser); - userRepo.findById.mockResolvedValueOnce(mockStudentUser).mockResolvedValueOnce(mockTeacherUser); - - return { mockTeacherUser, mockAdminUser, mockStudentUser, mockOtherStudentUser }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockTeacherUser, mockAdminUser, mockStudentUser, mockOtherStudentUser } = setup(); - await expect( - accountUc.searchAccounts( - { userId: mockTeacherUser.id } as ICurrentUser, - { type: AccountSearchType.USER_ID, value: mockAdminUser.id } as AccountSearchQueryParams - ) - ).rejects.toThrow(ForbiddenOperationError); - - await expect( - accountUc.searchAccounts( - { userId: mockStudentUser.id } as ICurrentUser, - { type: AccountSearchType.USER_ID, value: mockOtherStudentUser.id } as AccountSearchQueryParams - ) - ).rejects.toThrow(ForbiddenOperationError); - - await expect( - accountUc.searchAccounts( - { userId: mockStudentUser.id } as ICurrentUser, - { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams - ) - ).rejects.toThrow(ForbiddenOperationError); - }); + it('should throw if target account does not exist', async () => { + const currentUser = { userId: mockAdminUser.id } as ICurrentUser; + const params = { id: '000000000000000' } as AccountByIdParams; + const body = {} as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(EntityNotFoundError); }); - - describe('When search type is unknown', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser); - - return { mockSuperheroUser }; - }; - it('should throw Invalid search type', async () => { - const { mockSuperheroUser } = setup(); - await expect( - accountUc.searchAccounts( - { userId: mockSuperheroUser.id } as ICurrentUser, - { type: '' as AccountSearchType } as AccountSearchQueryParams - ) - ).rejects.toThrow('Invalid search type.'); - }); + it('should update target account password', async () => { + const previousPasswordHash = mockStudentAccount.password; + const currentUser = { userId: mockSuperheroUser.id } as ICurrentUser; + const params = { id: mockStudentAccount.id } as AccountByIdParams; + const body = { password: defaultPassword } as AccountByIdBodyParams; + expect(mockStudentUser.forcePasswordChange).toBeFalsy(); + await accountUc.updateAccountById(currentUser, params, body); + expect(mockStudentAccount.password).not.toBe(previousPasswordHash); + expect(mockStudentUser.forcePasswordChange).toBeTruthy(); }); - - describe('When user is not superhero', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - userRepo.findById.mockResolvedValueOnce(mockTeacherUser).mockResolvedValueOnce(mockStudentUser); - - return { mockStudentUser, mockTeacherUser }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockTeacherUser, mockStudentUser } = setup(); - await expect( - accountUc.searchAccounts( - { userId: mockTeacherUser.id } as ICurrentUser, - { type: AccountSearchType.USERNAME, value: mockStudentUser.id } as AccountSearchQueryParams - ) - ).rejects.toThrow(ForbiddenOperationError); - }); + it('should update target account username', async () => { + const newUsername = 'newUsername'; + const currentUser = { userId: mockSuperheroUser.id } as ICurrentUser; + const params = { id: mockStudentAccount.id } as AccountByIdParams; + const body = { username: newUsername } as AccountByIdBodyParams; + expect(mockStudentAccount.username).not.toBe(newUsername); + await accountUc.updateAccountById(currentUser, params, body); + expect(mockStudentAccount.username).toBe(newUsername.toLowerCase()); }); - describe('hasPermissionsToAccessAccount', () => { - describe('When using an admin', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - - configService.get.mockReturnValue(false); - userRepo.findById.mockResolvedValueOnce(mockAdminUser).mockResolvedValueOnce(mockTeacherUser); - - return { mockAdminUser, mockTeacherUser }; - }; - it('should be able to access teacher of the same school via user id', async () => { - const { mockAdminUser, mockTeacherUser } = setup(); - const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - }); - }); - - describe('When using an admin', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - configService.get.mockReturnValue(false); - userRepo.findById.mockResolvedValueOnce(mockAdminUser).mockResolvedValueOnce(mockStudentUser); - - return { mockAdminUser, mockStudentUser }; - }; - it('should be able to access student of the same school via user id', async () => { - const { mockAdminUser, mockStudentUser } = setup(); - const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - }); - }); - - describe('When using an admin', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - - configService.get.mockReturnValue(false); - userRepo.findById.mockResolvedValueOnce(mockAdminUser).mockResolvedValueOnce(mockAdminUser); - - return { mockAdminUser }; - }; - - it('should not be able to access admin of the same school via user id', async () => { - const { mockAdminUser } = setup(); - const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { type: AccountSearchType.USER_ID, value: mockAdminUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - }); - }); - - describe('When using an admin', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - const mockOtherSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockDifferentSchoolAdminUser = userFactory.buildWithId({ - school: mockOtherSchool, - roles: [...mockAdminUser.roles], - }); - - configService.get.mockReturnValue(false); - userRepo.findById.mockResolvedValueOnce(mockDifferentSchoolAdminUser).mockResolvedValueOnce(mockTeacherUser); - userRepo.findById.mockResolvedValueOnce(mockDifferentSchoolAdminUser).mockResolvedValueOnce(mockStudentUser); - - return { mockDifferentSchoolAdminUser, mockTeacherUser, mockStudentUser }; - }; - it('should not be able to access any account of a foreign school via user id', async () => { - const { mockDifferentSchoolAdminUser, mockTeacherUser, mockStudentUser } = setup(); - const currentUser = { userId: mockDifferentSchoolAdminUser.id } as ICurrentUser; - - let params = { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - - params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - }); - }); - - describe('When using a teacher', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockOtherTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - - configService.get.mockReturnValue(false); - userRepo.findById.mockResolvedValueOnce(mockTeacherUser).mockResolvedValueOnce(mockOtherTeacherUser); - - return { mockTeacherUser, mockOtherTeacherUser }; - }; - it('should be able to access teacher of the same school via user id', async () => { - const { mockTeacherUser, mockOtherTeacherUser } = setup(); - const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; - const params = { - type: AccountSearchType.USER_ID, - value: mockOtherTeacherUser.id, - } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - }); - }); - - describe('When using a teacher', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - configService.get.mockReturnValue(false); - userRepo.findById.mockResolvedValueOnce(mockTeacherUser).mockResolvedValueOnce(mockStudentUser); - - return { mockTeacherUser, mockStudentUser }; - }; - it('should be able to access student of the same school via user id', async () => { - const { mockTeacherUser, mockStudentUser } = setup(); - const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; - const params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - }); - }); - - describe('When using a teacher', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - - configService.get.mockReturnValue(false); - userRepo.findById.mockResolvedValueOnce(mockTeacherUser).mockResolvedValueOnce(mockAdminUser); - - return { mockTeacherUser, mockAdminUser }; - }; - it('should not be able to access admin of the same school via user id', async () => { - const { mockTeacherUser, mockAdminUser } = setup(); - const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; - const params = { type: AccountSearchType.USER_ID, value: mockAdminUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - }); - }); - - describe('When using a teacher', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - const mockOtherSchool = schoolFactory.buildWithId(); - - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockDifferentSchoolTeacherUser = userFactory.buildWithId({ - school: mockOtherSchool, - roles: [...mockTeacherUser.roles], - }); - - configService.get.mockReturnValue(false); - userRepo.findById - .mockResolvedValueOnce(mockDifferentSchoolTeacherUser) - .mockResolvedValueOnce(mockTeacherUser); - userRepo.findById - .mockResolvedValueOnce(mockDifferentSchoolTeacherUser) - .mockResolvedValueOnce(mockStudentUser); - - return { mockDifferentSchoolTeacherUser, mockTeacherUser, mockStudentUser }; - }; - it('should not be able to access any account of a foreign school via user id', async () => { - const { mockDifferentSchoolTeacherUser, mockTeacherUser, mockStudentUser } = setup(); - const currentUser = { userId: mockDifferentSchoolTeacherUser.id } as ICurrentUser; - - let params = { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - - params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - }); - }); - describe('When using a teacher', () => { - const setup = () => { - const mockSchoolWithStudentVisibility = schoolFactory.buildWithId(); - mockSchoolWithStudentVisibility.permissions = new SchoolRoles(); - mockSchoolWithStudentVisibility.permissions.teacher = new SchoolRolePermission(); - mockSchoolWithStudentVisibility.permissions.teacher.STUDENT_LIST = true; - - const mockTeacherNoUserPermissionUser = userFactory.buildWithId({ - school: mockSchoolWithStudentVisibility, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [], - }), - ], - }); - const mockStudentSchoolPermissionUser = userFactory.buildWithId({ - school: mockSchoolWithStudentVisibility, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - configService.get.mockReturnValue(true); - userRepo.findById - .mockResolvedValueOnce(mockTeacherNoUserPermissionUser) - .mockResolvedValueOnce(mockStudentSchoolPermissionUser); - - return { mockTeacherNoUserPermissionUser, mockStudentSchoolPermissionUser }; - }; - it('should be able to access student of the same school via user id if school has global permission', async () => { - const { mockTeacherNoUserPermissionUser, mockStudentSchoolPermissionUser } = setup(); - - const currentUser = { userId: mockTeacherNoUserPermissionUser.id } as ICurrentUser; - const params = { - type: AccountSearchType.USER_ID, - value: mockStudentSchoolPermissionUser.id, - } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - }); - }); - - describe('When using a teacher', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockTeacherNoUserNoSchoolPermissionUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - configService.get.mockReturnValue(false); - userRepo.findById - .mockResolvedValueOnce(mockTeacherNoUserNoSchoolPermissionUser) - .mockResolvedValueOnce(mockStudentUser); - - return { mockTeacherNoUserNoSchoolPermissionUser, mockStudentUser }; - }; - it('should not be able to access student of the same school if school has no global permission', async () => { - const { mockTeacherNoUserNoSchoolPermissionUser, mockStudentUser } = setup(); - configService.get.mockReturnValue(true); - const currentUser = { userId: mockTeacherNoUserNoSchoolPermissionUser.id } as ICurrentUser; - const params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(ForbiddenOperationError); - }); - }); - - describe('When using a student', () => { - const setup = () => { - const mockSchoolWithStudentVisibility = schoolFactory.buildWithId(); - mockSchoolWithStudentVisibility.permissions = new SchoolRoles(); - mockSchoolWithStudentVisibility.permissions.teacher = new SchoolRolePermission(); - mockSchoolWithStudentVisibility.permissions.teacher.STUDENT_LIST = true; - - const mockStudentSchoolPermissionUser = userFactory.buildWithId({ - school: mockSchoolWithStudentVisibility, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockOtherStudentSchoolPermissionUser = userFactory.buildWithId({ - school: mockSchoolWithStudentVisibility, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - configService.get.mockReturnValue(false); - userRepo.findById - .mockResolvedValueOnce(mockStudentSchoolPermissionUser) - .mockResolvedValueOnce(mockOtherStudentSchoolPermissionUser); - - return { mockStudentSchoolPermissionUser, mockOtherStudentSchoolPermissionUser }; - }; - it('should not be able to access student of the same school if school has global permission', async () => { - const { mockStudentSchoolPermissionUser, mockOtherStudentSchoolPermissionUser } = setup(); - configService.get.mockReturnValue(true); - const currentUser = { userId: mockStudentSchoolPermissionUser.id } as ICurrentUser; - const params = { - type: AccountSearchType.USER_ID, - value: mockOtherStudentSchoolPermissionUser.id, - } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(ForbiddenOperationError); - }); - }); - - describe('When using a student', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - configService.get.mockReturnValue(false); - userRepo.findById.mockResolvedValueOnce(mockStudentUser).mockResolvedValueOnce(mockAdminUser); - userRepo.findById.mockResolvedValueOnce(mockStudentUser).mockResolvedValueOnce(mockTeacherUser); - userRepo.findById.mockResolvedValueOnce(mockStudentUser).mockResolvedValueOnce(mockStudentUser); - - return { mockStudentUser, mockAdminUser, mockTeacherUser }; - }; - it('should not be able to access any other account via user id', async () => { - const { mockStudentUser, mockAdminUser, mockTeacherUser } = setup(); - const currentUser = { userId: mockStudentUser.id } as ICurrentUser; - - let params = { type: AccountSearchType.USER_ID, value: mockAdminUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - - params = { type: AccountSearchType.USER_ID, value: mockTeacherUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - - params = { type: AccountSearchType.USER_ID, value: mockStudentUser.id } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).rejects.toThrow(); - }); - }); - - describe('When using a superhero', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - const mockOtherSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockDifferentSchoolAdminUser = userFactory.buildWithId({ - school: mockOtherSchool, - roles: [...mockAdminUser.roles], - }); - const mockDifferentSchoolTeacherUser = userFactory.buildWithId({ - school: mockOtherSchool, - roles: [...mockTeacherUser.roles], - }); - const mockDifferentSchoolStudentUser = userFactory.buildWithId({ - school: mockOtherSchool, - roles: [...mockStudentUser.roles], - }); - - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - const mockDifferentSchoolAdminAccount = accountFactory.buildWithId({ - userId: mockDifferentSchoolAdminUser.id, - password: defaultPasswordHash, - }); - const mockDifferentSchoolTeacherAccount = accountFactory.buildWithId({ - userId: mockDifferentSchoolTeacherUser.id, - password: defaultPasswordHash, - }); - const mockAdminAccount = accountFactory.buildWithId({ - userId: mockAdminUser.id, - password: defaultPasswordHash, - }); - const mockTeacherAccount = accountFactory.buildWithId({ - userId: mockTeacherUser.id, - password: defaultPasswordHash, - }); - - const mockDifferentSchoolStudentAccount = accountFactory.buildWithId({ - userId: mockDifferentSchoolStudentUser.id, - password: defaultPasswordHash, - }); - - configService.get.mockReturnValue(false); - userRepo.findById.mockResolvedValue(mockSuperheroUser); - accountService.searchByUsernamePartialMatch.mockResolvedValue([[], 0]); - - return { - mockSuperheroUser, - mockAdminAccount, - mockTeacherAccount, - mockStudentAccount, - mockDifferentSchoolAdminAccount, - mockDifferentSchoolTeacherAccount, - mockDifferentSchoolStudentAccount, - }; - }; - it('should be able to access any account via username', async () => { - const { - mockSuperheroUser, - mockAdminAccount, - mockTeacherAccount, - mockStudentAccount, - mockDifferentSchoolAdminAccount, - mockDifferentSchoolTeacherAccount, - mockDifferentSchoolStudentAccount, - } = setup(); - - const currentUser = { userId: mockSuperheroUser.id } as ICurrentUser; - - let params = { - type: AccountSearchType.USERNAME, - value: mockAdminAccount.username, - } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - - params = { type: AccountSearchType.USERNAME, value: mockTeacherAccount.username } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - - params = { type: AccountSearchType.USERNAME, value: mockStudentAccount.username } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - - params = { - type: AccountSearchType.USERNAME, - value: mockDifferentSchoolAdminAccount.username, - } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - - params = { - type: AccountSearchType.USERNAME, - value: mockDifferentSchoolTeacherAccount.username, - } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - - params = { - type: AccountSearchType.USERNAME, - value: mockDifferentSchoolStudentAccount.username, - } as AccountSearchQueryParams; - await expect(accountUc.searchAccounts(currentUser, params)).resolves.not.toThrow(); - }); - }); + it('should update target account activation state', async () => { + const currentUser = { userId: mockSuperheroUser.id } as ICurrentUser; + const params = { id: mockStudentAccount.id } as AccountByIdParams; + const body = { activated: false } as AccountByIdBodyParams; + await accountUc.updateAccountById(currentUser, params, body); + expect(mockStudentAccount.activated).toBeFalsy(); }); - }); - - describe('findAccountById', () => { - describe('When the current user is a superhero', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - return { mockSuperheroUser, mockStudentUser, mockStudentAccount }; - }; - it('should return an account', async () => { - const { mockSuperheroUser, mockStudentUser, mockStudentAccount } = setup(); - const account = await accountUc.findAccountById( - { userId: mockSuperheroUser.id } as ICurrentUser, - { id: mockStudentAccount.id } as AccountByIdParams - ); - expect(account).toStrictEqual( - expect.objectContaining({ - id: mockStudentAccount.id, - username: mockStudentAccount.username, - userId: mockStudentUser.id, - activated: mockStudentAccount.activated, - }) - ); - }); + it('should throw if account can not be updated', async () => { + const currentUser = { userId: mockAdminUser.id } as ICurrentUser; + const params = { id: mockStudentAccount.id } as AccountByIdParams; + const body = { username: 'fail@to.update' } as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(EntityNotFoundError); }); - - describe('When the current user is no superhero', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockTeacherUser); - - return { mockTeacherUser, mockStudentAccount }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockTeacherUser, mockStudentAccount } = setup(); - await expect( - accountUc.findAccountById( - { userId: mockTeacherUser.id } as ICurrentUser, - { id: mockStudentAccount.id } as AccountByIdParams - ) - ).rejects.toThrow(ForbiddenOperationError); - }); + it('should throw if user can not be updated', async () => { + const currentUser = { userId: mockAdminUser.id } as ICurrentUser; + const params = { id: mockStudentAccount.id } as AccountByIdParams; + const body = { username: 'user-fail@to.update' } as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(EntityNotFoundError); }); - - describe('When no account matches the search term', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser); - accountService.findById.mockImplementation((): Promise => { - throw new EntityNotFoundError(Account.name); - }); - - return { mockSuperheroUser }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockSuperheroUser } = setup(); - await expect( - accountUc.findAccountById( - { userId: mockSuperheroUser.id } as ICurrentUser, - { id: 'xxx' } as AccountByIdParams - ) - ).rejects.toThrow(EntityNotFoundError); - }); + it('should throw if target account has no user', async () => { + await expect( + accountUc.updateAccountById( + { userId: mockSuperheroUser.id } as ICurrentUser, + { id: mockAccountWithoutUser.id } as AccountByIdParams, + { username: 'user-fail@to.update' } as AccountByIdBodyParams + ) + ).rejects.toThrow(EntityNotFoundError); }); - - describe('When target account has no user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser); - accountService.findById.mockImplementation((): Promise => { - throw new EntityNotFoundError(Account.name); - }); - - return { mockSuperheroUser }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockSuperheroUser } = setup(); - await expect( - accountUc.findAccountById( - { userId: mockSuperheroUser.id } as ICurrentUser, - { id: 'xxx' } as AccountByIdParams - ) - ).rejects.toThrow(EntityNotFoundError); - }); + it('should throw if new username already in use', async () => { + const accountIsUniqueEmailSpy = jest.spyOn(accountValidationService, 'isUniqueEmail'); + accountIsUniqueEmailSpy.mockResolvedValueOnce(false); + const currentUser = { userId: mockAdminUser.id } as ICurrentUser; + const params = { id: mockStudentAccount.id } as AccountByIdParams; + const body = { username: mockOtherTeacherAccount.username } as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ValidationError); }); - }); - describe('saveAccount', () => { - describe('When saving an account', () => { - const setup = () => { - const spy = jest.spyOn(accountService, 'saveWithValidation'); - - return { spy }; - }; - it('should call account service', async () => { - const { spy } = setup(); - - const params: AccountSaveDto = { - username: 'john.doe@domain.tld', - password: defaultPassword, - }; - await accountUc.saveAccount(params); - - expect(spy).toHaveBeenCalledWith( - expect.objectContaining({ - username: 'john.doe@domain.tld', - }) - ); + describe('hasPermissionsToUpdateAccount', () => { + it('admin can edit teacher', async () => { + const currentUser = { userId: mockAdminUser.id } as ICurrentUser; + const params = { id: mockTeacherAccount.id } as AccountByIdParams; + const body = {} as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).resolves.not.toThrow(); }); - }); - }); - - describe('updateAccountById', () => { - describe('when updating a user that does not exist', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockImplementation((): Promise => { - throw new EntityNotFoundError(User.name); - }); - - return { mockStudentAccount }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockStudentAccount } = setup(); - const currentUser = { userId: '000000000000000' } as ICurrentUser; + it('teacher can edit student', async () => { + const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; const params = { id: mockStudentAccount.id } as AccountByIdParams; const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(EntityNotFoundError); + await expect(accountUc.updateAccountById(currentUser, params, body)).resolves.not.toThrow(); }); - }); - - describe('When target account does not exist', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - - const mockAccountWithoutUser = accountFactory.buildWithId({ - userId: undefined, - password: defaultPasswordHash, - systemId: systemFactory.buildWithId().id, - }); - - userRepo.findById.mockResolvedValue(mockAdminUser); - accountService.findById.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockAccountWithoutUser)); - - return { mockAdminUser }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockAdminUser } = setup(); + it('admin can edit student', async () => { const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { id: '000000000000000' } as AccountByIdParams; + const params = { id: mockStudentAccount.id } as AccountByIdParams; const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(EntityNotFoundError); + await expect(accountUc.updateAccountById(currentUser, params, body)).resolves.not.toThrow(); }); - }); - - describe('When using superhero user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser).mockResolvedValueOnce(mockStudentUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - userRepo.save.mockResolvedValue(); - accountService.save.mockImplementation((account: AccountSaveDto): Promise => { - Object.assign(mockStudentAccount, account); - return Promise.resolve(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - }); - - return { mockStudentAccount, mockStudentUser, mockSuperheroUser }; - }; - it('should update target account password', async () => { - const { mockStudentAccount, mockSuperheroUser, mockStudentUser } = setup(); - const previousPasswordHash = mockStudentAccount.password; - const currentUser = { userId: mockSuperheroUser.id } as ICurrentUser; - const params = { id: mockStudentAccount.id } as AccountByIdParams; - const body = { password: defaultPassword } as AccountByIdBodyParams; - expect(mockStudentUser.forcePasswordChange).toBeFalsy(); - await accountUc.updateAccountById(currentUser, params, body); - expect(mockStudentAccount.password).not.toBe(previousPasswordHash); - expect(mockStudentUser.forcePasswordChange).toBeTruthy(); + it('teacher cannot edit other teacher', async () => { + const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; + const params = { id: mockOtherTeacherAccount.id } as AccountByIdParams; + const body = {} as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ForbiddenOperationError); }); - }); - - describe('When using superhero user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser).mockResolvedValueOnce(mockStudentUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - userRepo.save.mockResolvedValue(); - accountService.save.mockImplementation((account: AccountSaveDto): Promise => { - Object.assign(mockStudentAccount, account); - return Promise.resolve(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - }); - accountValidationService.isUniqueEmail.mockResolvedValue(true); - - return { mockStudentAccount, mockSuperheroUser }; - }; - it('should update target account username', async () => { - const { mockStudentAccount, mockSuperheroUser } = setup(); - const newUsername = 'newUsername'; - const currentUser = { userId: mockSuperheroUser.id } as ICurrentUser; - const params = { id: mockStudentAccount.id } as AccountByIdParams; - const body = { username: newUsername } as AccountByIdBodyParams; - expect(mockStudentAccount.username).not.toBe(newUsername); - await accountUc.updateAccountById(currentUser, params, body); - expect(mockStudentAccount.username).toBe(newUsername.toLowerCase()); + it("other school's admin cannot edit teacher", async () => { + const currentUser = { userId: mockDifferentSchoolAdminUser.id } as ICurrentUser; + const params = { id: mockTeacherAccount.id } as AccountByIdParams; + const body = {} as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ForbiddenOperationError); }); - }); - - describe('When using superhero user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser).mockResolvedValueOnce(mockStudentUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - userRepo.save.mockResolvedValue(); - accountService.save.mockImplementation((account: AccountSaveDto): Promise => { - Object.assign(mockStudentAccount, account); - return Promise.resolve(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - }); - - return { mockStudentAccount, mockSuperheroUser }; - }; - it('should update target account activation state', async () => { - const { mockStudentAccount, mockSuperheroUser } = setup(); + it('superhero can edit admin', async () => { const currentUser = { userId: mockSuperheroUser.id } as ICurrentUser; - const params = { id: mockStudentAccount.id } as AccountByIdParams; - const body = { activated: false } as AccountByIdBodyParams; - await accountUc.updateAccountById(currentUser, params, body); - expect(mockStudentAccount.activated).toBeFalsy(); + const params = { id: mockAdminAccount.id } as AccountByIdParams; + const body = {} as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).resolves.not.toThrow(); }); - }); - - describe('When using an admin user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockAdminUser).mockResolvedValueOnce(mockStudentUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - userRepo.save.mockResolvedValue(); - accountService.save.mockRejectedValueOnce(undefined); - - accountValidationService.isUniqueEmail.mockResolvedValue(true); - - return { mockStudentAccount, mockAdminUser }; - }; - it('should throw if account can not be updated', async () => { - const { mockStudentAccount, mockAdminUser } = setup(); - const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { id: mockStudentAccount.id } as AccountByIdParams; - const body = { username: 'fail@to.update' } as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(EntityNotFoundError); + it('undefined user role fails by default', async () => { + const currentUser = { userId: mockUnknownRoleUser.id } as ICurrentUser; + const params = { id: mockAccountWithoutRole.id } as AccountByIdParams; + const body = {} as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ForbiddenOperationError); }); - }); - - describe('When user can not be updated', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockAdminUser).mockResolvedValueOnce(mockStudentUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - userRepo.save.mockRejectedValueOnce(undefined); - - accountValidationService.isUniqueEmail.mockResolvedValue(true); - - return { mockStudentAccount, mockAdminUser }; - }; - it('should throw EntityNotFoundError', async () => { - const { mockStudentAccount, mockAdminUser } = setup(); + it('user without role cannot be edited', async () => { const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { id: mockStudentAccount.id } as AccountByIdParams; - const body = { username: 'user-fail@to.update' } as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(EntityNotFoundError); - }); - }); - - describe('if target account has no user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockAccountWithoutUser = accountFactory.buildWithId({ - userId: undefined, - password: defaultPasswordHash, - systemId: systemFactory.buildWithId().id, - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockAccountWithoutUser)); - - return { mockSuperheroUser, mockAccountWithoutUser }; - }; - - it('should throw EntityNotFoundError', async () => { - const { mockSuperheroUser, mockAccountWithoutUser } = setup(); - await expect( - accountUc.updateAccountById( - { userId: mockSuperheroUser.id } as ICurrentUser, - { id: mockAccountWithoutUser.id } as AccountByIdParams, - { username: 'user-fail@to.update' } as AccountByIdBodyParams - ) - ).rejects.toThrow(EntityNotFoundError); - }); - }); - - describe('When new username already in use', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockOtherTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockOtherTeacherAccount = accountFactory.buildWithId({ - userId: mockOtherTeacherUser.id, - password: defaultPasswordHash, - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockAdminUser).mockResolvedValueOnce(mockStudentUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - userRepo.save.mockRejectedValueOnce(undefined); - - accountValidationService.isUniqueEmail.mockResolvedValueOnce(false); - - return { mockStudentAccount, mockAdminUser, mockOtherTeacherAccount }; - }; - it('should throw ValidationError', async () => { - const { mockStudentAccount, mockAdminUser, mockOtherTeacherAccount } = setup(); - const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { id: mockStudentAccount.id } as AccountByIdParams; - const body = { username: mockOtherTeacherAccount.username } as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ValidationError); - }); - }); - - describe('hasPermissionsToUpdateAccount', () => { - describe('When using an admin user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - - const mockTeacherAccount = accountFactory.buildWithId({ - userId: mockTeacherUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockAdminUser).mockResolvedValueOnce(mockTeacherUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - - return { mockAdminUser, mockTeacherAccount }; - }; - it('should not throw error when editing a teacher', async () => { - const { mockAdminUser, mockTeacherAccount } = setup(); - const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { id: mockTeacherAccount.id } as AccountByIdParams; - const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).resolves.not.toThrow(); - }); - }); - - describe('When using a teacher user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockTeacherUser).mockResolvedValueOnce(mockStudentUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - return { mockStudentAccount, mockTeacherUser }; - }; - it('should not throw error when editing a student', async () => { - const { mockTeacherUser, mockStudentAccount } = setup(); - const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; - const params = { id: mockStudentAccount.id } as AccountByIdParams; - const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).resolves.not.toThrow(); - }); - }); - describe('When using an admin user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockAdminUser).mockResolvedValueOnce(mockStudentUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - return { mockStudentAccount, mockAdminUser }; - }; - it('should not throw error when editing a student', async () => { - const { mockAdminUser, mockStudentAccount } = setup(); - const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { id: mockStudentAccount.id } as AccountByIdParams; - const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).resolves.not.toThrow(); - }); - }); - - describe('When using a teacher user to edit another teacher', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockOtherTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockOtherTeacherAccount = accountFactory.buildWithId({ - userId: mockOtherTeacherUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockTeacherUser).mockResolvedValueOnce(mockOtherTeacherUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockOtherTeacherAccount)); - - return { mockOtherTeacherAccount, mockTeacherUser }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockTeacherUser, mockOtherTeacherAccount } = setup(); - const currentUser = { userId: mockTeacherUser.id } as ICurrentUser; - const params = { id: mockOtherTeacherAccount.id } as AccountByIdParams; - const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ForbiddenOperationError); - }); - }); - - describe('When using an admin user of other school', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - const mockOtherSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockTeacherUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.TEACHER, - permissions: [Permission.STUDENT_EDIT, Permission.STUDENT_LIST, Permission.TEACHER_LIST], - }), - ], - }); - const mockDifferentSchoolAdminUser = userFactory.buildWithId({ - school: mockOtherSchool, - roles: [...mockAdminUser.roles], - }); - - const mockTeacherAccount = accountFactory.buildWithId({ - userId: mockTeacherUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockDifferentSchoolAdminUser).mockResolvedValueOnce(mockTeacherUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockTeacherAccount)); - - return { mockDifferentSchoolAdminUser, mockTeacherAccount }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockDifferentSchoolAdminUser, mockTeacherAccount } = setup(); - const currentUser = { userId: mockDifferentSchoolAdminUser.id } as ICurrentUser; - const params = { id: mockTeacherAccount.id } as AccountByIdParams; - const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ForbiddenOperationError); - }); - }); - - describe('When using a superhero user', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockAdminAccount = accountFactory.buildWithId({ - userId: mockAdminUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockSuperheroUser).mockResolvedValueOnce(mockAdminUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockAdminAccount)); - - return { mockAdminAccount, mockSuperheroUser }; - }; - it('should not throw error when editing a admin', async () => { - const { mockSuperheroUser, mockAdminAccount } = setup(); - const currentUser = { userId: mockSuperheroUser.id } as ICurrentUser; - const params = { id: mockAdminAccount.id } as AccountByIdParams; - const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).resolves.not.toThrow(); - }); - }); - - describe('When using an user with undefined role', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockUserWithoutRole = userFactory.buildWithId({ - school: mockSchool, - roles: [], - }); - const mockUnknownRoleUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: 'undefinedRole' as RoleName, permissions: ['' as Permission] })], - }); - const mockAccountWithoutRole = accountFactory.buildWithId({ - userId: mockUserWithoutRole.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockUnknownRoleUser).mockResolvedValueOnce(mockUserWithoutRole); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockAccountWithoutRole)); - - return { mockAccountWithoutRole, mockUnknownRoleUser }; - }; - it('should fail by default', async () => { - const { mockUnknownRoleUser, mockAccountWithoutRole } = setup(); - const currentUser = { userId: mockUnknownRoleUser.id } as ICurrentUser; - const params = { id: mockAccountWithoutRole.id } as AccountByIdParams; - const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ForbiddenOperationError); - }); - }); - - describe('When editing an user without role', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - const mockUnknownRoleUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: 'undefinedRole' as RoleName, permissions: ['' as Permission] })], - }); - const mockUnknownRoleUserAccount = accountFactory.buildWithId({ - userId: mockUnknownRoleUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValueOnce(mockAdminUser).mockResolvedValueOnce(mockUnknownRoleUser); - accountService.findById.mockResolvedValueOnce(AccountEntityToDtoMapper.mapToDto(mockUnknownRoleUserAccount)); - - return { mockAdminUser, mockUnknownRoleUserAccount }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockAdminUser, mockUnknownRoleUserAccount } = setup(); - const currentUser = { userId: mockAdminUser.id } as ICurrentUser; - const params = { id: mockUnknownRoleUserAccount.id } as AccountByIdParams; - const body = {} as AccountByIdBodyParams; - await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ForbiddenOperationError); - }); + const params = { id: mockUnknownRoleUserAccount.id } as AccountByIdParams; + const body = {} as AccountByIdBodyParams; + await expect(accountUc.updateAccountById(currentUser, params, body)).rejects.toThrow(ForbiddenOperationError); }); }); }); describe('deleteAccountById', () => { - describe('When current user is authorized', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockResolvedValue(mockSuperheroUser); - - accountService.findById.mockResolvedValue(AccountEntityToDtoMapper.mapToDto(mockStudentAccount)); - - return { mockSuperheroUser, mockStudentAccount }; - }; - it('should delete an account', async () => { - const { mockSuperheroUser, mockStudentAccount } = setup(); - await expect( - accountUc.deleteAccountById( - { userId: mockSuperheroUser.id } as ICurrentUser, - { id: mockStudentAccount.id } as AccountByIdParams - ) - ).resolves.not.toThrow(); - }); + it('should delete an account, if current user is authorized', async () => { + await expect( + accountUc.deleteAccountById( + { userId: mockSuperheroUser.id } as ICurrentUser, + { id: mockStudentAccount.id } as AccountByIdParams + ) + ).resolves.not.toThrow(); }); - - describe('When the current user is not superhero', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockAdminUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.ADMINISTRATOR, - permissions: [ - Permission.TEACHER_EDIT, - Permission.STUDENT_EDIT, - Permission.STUDENT_LIST, - Permission.TEACHER_LIST, - Permission.TEACHER_CREATE, - Permission.STUDENT_CREATE, - Permission.TEACHER_DELETE, - Permission.STUDENT_DELETE, - ], - }), - ], - }); - - const mockStudentUser = userFactory.buildWithId({ - school: mockSchool, - roles: [new Role({ name: RoleName.STUDENT, permissions: [] })], - }); - - const mockStudentAccount = accountFactory.buildWithId({ - userId: mockStudentUser.id, - password: defaultPasswordHash, - }); - - userRepo.findById.mockImplementation((userId: EntityId): Promise => { - if (mockAdminUser.id === userId) { - return Promise.resolve(mockAdminUser); - } - throw new EntityNotFoundError(User.name); - }); - - return { mockAdminUser, mockStudentAccount }; - }; - it('should throw ForbiddenOperationError', async () => { - const { mockAdminUser, mockStudentAccount } = setup(); - await expect( - accountUc.deleteAccountById( - { userId: mockAdminUser.id } as ICurrentUser, - { id: mockStudentAccount.id } as AccountByIdParams - ) - ).rejects.toThrow(ForbiddenOperationError); - }); + it('should throw, if the current user is no superhero', async () => { + await expect( + accountUc.deleteAccountById( + { userId: mockAdminUser.id } as ICurrentUser, + { id: mockStudentAccount.id } as AccountByIdParams + ) + ).rejects.toThrow(ForbiddenOperationError); }); - - describe('When no account matches the search term', () => { - const setup = () => { - const mockSchool = schoolFactory.buildWithId(); - - const mockSuperheroUser = userFactory.buildWithId({ - school: mockSchool, - roles: [ - new Role({ - name: RoleName.SUPERHERO, - permissions: [Permission.TEACHER_EDIT, Permission.STUDENT_EDIT], - }), - ], - }); - - userRepo.findById.mockImplementation((userId: EntityId): Promise => { - if (mockSuperheroUser.id === userId) { - return Promise.resolve(mockSuperheroUser); - } - throw new EntityNotFoundError(User.name); - }); - - accountService.findById.mockImplementation((id: EntityId): Promise => { - if (id === 'xxx') { - throw new EntityNotFoundError(Account.name); - } - return Promise.reject(); - }); - - return { mockSuperheroUser }; - }; - it('should throw, if no account matches the search term', async () => { - const { mockSuperheroUser } = setup(); - await expect( - accountUc.deleteAccountById( - { userId: mockSuperheroUser.id } as ICurrentUser, - { id: 'xxx' } as AccountByIdParams - ) - ).rejects.toThrow(EntityNotFoundError); - }); + it('should throw, if no account matches the search term', async () => { + await expect( + accountUc.deleteAccountById( + { userId: mockSuperheroUser.id } as ICurrentUser, + { id: 'xxx' } as AccountByIdParams + ) + ).rejects.toThrow(EntityNotFoundError); }); }); describe('checkBrutForce', () => { - describe('When time difference < the allowed time', () => { - const setup = () => { - const mockAccountWithLastFailedLogin = accountFactory.buildWithId({ - userId: undefined, - password: defaultPasswordHash, - systemId: systemFactory.buildWithId().id, - lasttriedFailedLogin: new Date(), - }); - - configService.get.mockReturnValue(LOGIN_BLOCK_TIME); - - accountService.findByUsernameAndSystemId.mockImplementation( - (username: string, systemId: EntityId | ObjectId): Promise => { - if ( - mockAccountWithLastFailedLogin.username === username && - mockAccountWithLastFailedLogin.systemId === systemId - ) { - return Promise.resolve(AccountEntityToDtoMapper.mapToDto(mockAccountWithLastFailedLogin)); - } - throw new EntityNotFoundError(Account.name); - } - ); - - return { mockAccountWithLastFailedLogin }; - }; - - it('should throw BruteForcePrevention', async () => { - const { mockAccountWithLastFailedLogin } = setup(); - await expect( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - accountUc.checkBrutForce(mockAccountWithLastFailedLogin.username, mockAccountWithLastFailedLogin.systemId!) - ).rejects.toThrow(BruteForcePrevention); - }); + let updateMock: jest.Mock; + beforeAll(() => { + configService.get.mockReturnValue(LOGIN_BLOCK_TIME); }); - - describe('When the time difference > the allowed time', () => { - const setup = () => { - const mockAccountWithSystemId = accountFactory.withSystemId(new ObjectId(10)).build(); - - // eslint-disable-next-line jest/unbound-method - const updateMock = accountService.updateLastTriedFailedLogin as jest.Mock; - - configService.get.mockReturnValue(LOGIN_BLOCK_TIME); - - accountService.findByUsernameAndSystemId.mockImplementation( - (username: string, systemId: EntityId | ObjectId): Promise => { - if (mockAccountWithSystemId.username === username && mockAccountWithSystemId.systemId === systemId) { - return Promise.resolve(AccountEntityToDtoMapper.mapToDto(mockAccountWithSystemId)); - } - throw new EntityNotFoundError(Account.name); - } - ); - - return { mockAccountWithSystemId, updateMock }; - }; - - it('should not throw Error, ', async () => { - const { mockAccountWithSystemId, updateMock } = setup(); - - await expect( - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - accountUc.checkBrutForce(mockAccountWithSystemId.username, mockAccountWithSystemId.systemId!) - ).resolves.not.toThrow(); - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - expect(updateMock.mock.calls[0][0]).toEqual(mockAccountWithSystemId.id); - const newDate = new Date().getTime() - 10000; - // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access - expect((updateMock.mock.calls[0][1] as Date).getTime()).toBeGreaterThan(newDate); - }); + afterAll(() => { + configService.get.mockRestore(); }); - - describe('When lasttriedFailedLogin is undefined', () => { - const setup = () => { - const mockAccountWithNoLastFailedLogin = accountFactory.buildWithId({ - userId: undefined, - password: defaultPasswordHash, - systemId: systemFactory.buildWithId().id, - lasttriedFailedLogin: undefined, - }); - - configService.get.mockReturnValue(LOGIN_BLOCK_TIME); - - accountService.findByUsernameAndSystemId.mockImplementation( - (username: string, systemId: EntityId | ObjectId): Promise => { - if ( - mockAccountWithNoLastFailedLogin.username === username && - mockAccountWithNoLastFailedLogin.systemId === systemId - ) { - return Promise.resolve(AccountEntityToDtoMapper.mapToDto(mockAccountWithNoLastFailedLogin)); - } - throw new EntityNotFoundError(Account.name); - } - ); - - return { mockAccountWithNoLastFailedLogin }; - }; - it('should not throw error', async () => { - const { mockAccountWithNoLastFailedLogin } = setup(); - await expect( - accountUc.checkBrutForce( - mockAccountWithNoLastFailedLogin.username, - // eslint-disable-next-line @typescript-eslint/no-non-null-assertion - mockAccountWithNoLastFailedLogin.systemId! - ) - ).resolves.not.toThrow(); - }); + beforeEach(() => { + // eslint-disable-next-line jest/unbound-method + updateMock = accountService.updateLastTriedFailedLogin as jest.Mock; + updateMock.mockClear(); + }); + it('should throw, if time difference < the allowed time', async () => { + await expect( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + accountUc.checkBrutForce(mockAccountWithLastFailedLogin.username, mockAccountWithLastFailedLogin.systemId!) + ).rejects.toThrow(BruteForcePrevention); + }); + it('should not throw Error, if the time difference > the allowed time', async () => { + await expect( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + accountUc.checkBrutForce(mockAccountWithSystemId.username, mockAccountWithSystemId.systemId!) + ).resolves.not.toThrow(); + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect(updateMock.mock.calls[0][0]).toEqual(mockAccountWithSystemId.id); + const newDate = new Date().getTime() - 10000; + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + expect((updateMock.mock.calls[0][1] as Date).getTime()).toBeGreaterThan(newDate); + }); + it('should not throw, if lasttriedFailedLogin is undefined', async () => { + await expect( + accountUc.checkBrutForce( + mockAccountWithNoLastFailedLogin.username, + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + mockAccountWithNoLastFailedLogin.systemId! + ) + ).resolves.not.toThrow(); }); }); }); diff --git a/apps/server/src/modules/account/uc/account.uc.ts b/apps/server/src/modules/account/uc/account.uc.ts index f9e21b28c63..ac4ac053dae 100644 --- a/apps/server/src/modules/account/uc/account.uc.ts +++ b/apps/server/src/modules/account/uc/account.uc.ts @@ -8,7 +8,6 @@ import { } from '@shared/common/error'; import { Account, EntityId, Permission, PermissionService, Role, RoleName, SchoolEntity, User } from '@shared/domain'; import { UserRepo } from '@shared/repo'; -// TODO: module internals should be imported with relative paths import { AccountService } from '@modules/account/services/account.service'; import { AccountDto } from '@modules/account/services/dto/account.dto'; @@ -43,14 +42,6 @@ export class AccountUc { private readonly configService: ConfigService ) {} - /* HINT: there is a lot of logic here that would belong into service layer, - but since that wasnt decided when this code was written this work is not prioritised right now - - Also this is mostly directly ported feathers code, that needs a general refactoring/rewrite pass - - also it should use the new authorisation service - */ - /** * This method processes the request on the GET account search endpoint from the account controller. * @@ -64,9 +55,7 @@ export class AccountUc { const limit = query.limit ?? 10; const executingUser = await this.userRepo.findById(currentUser.userId, true); - // HINT: this can be extracted if (query.type === AccountSearchType.USERNAME) { - // HINT: even superheroes should in the future be permission based if (!(await this.isSuperhero(currentUser))) { throw new ForbiddenOperationError('Current user is not authorized to search for accounts.'); } @@ -83,10 +72,8 @@ export class AccountUc { } const account = await this.accountService.findByUserId(query.value); if (account) { - // HINT: skip and limit should be from the query return new AccountSearchListResponse([AccountResponseMapper.mapToResponse(account)], 1, 0, 1); } - // HINT: skip and limit should be from the query return new AccountSearchListResponse([], 0, 0, 0); } @@ -106,7 +93,7 @@ export class AccountUc { throw new ForbiddenOperationError('Current user is not authorized to search for accounts.'); } const account = await this.accountService.findById(params.id); - return AccountResponseMapper.mapToResponse(account); // TODO: mapping should be done in controller + return AccountResponseMapper.mapToResponse(account); } async saveAccount(dto: AccountSaveDto): Promise { @@ -175,8 +162,6 @@ export class AccountUc { throw new EntityNotFoundError(Account.name); } } - // TODO: mapping from domain to api dto should be a responsability of the controller - return AccountResponseMapper.mapToResponse(targetAccount); } @@ -315,7 +300,6 @@ export class AccountUc { } } - // TODO: remove /** * * @deprecated this is for legacy login strategies only. Login strategies in Nest.js should use {@link AuthenticationService} diff --git a/apps/server/src/shared/testing/factory/account.factory.ts b/apps/server/src/shared/testing/factory/account.factory.ts index a3568dbf80a..b0c0b8434c1 100644 --- a/apps/server/src/shared/testing/factory/account.factory.ts +++ b/apps/server/src/shared/testing/factory/account.factory.ts @@ -5,8 +5,6 @@ import { ObjectId } from 'bson'; import { DeepPartial } from 'fishery'; import { BaseFactory } from './base.factory'; -export const defaultTestPassword = 'DummyPasswd!1'; -export const defaultTestPasswordHash = '$2a$10$/DsztV5o6P5piW2eWJsxw.4nHovmJGBA.QNwiTmuZ/uvUc40b.Uhu'; class AccountFactory extends BaseFactory { withSystemId(id: EntityId | ObjectId): this { const params: DeepPartial = { systemId: id }; @@ -23,36 +21,10 @@ class AccountFactory extends BaseFactory { return this.params(params); } - - withAllProperties(): this { - return this.params({ - userId: new ObjectId(), - username: 'username', - activated: true, - credentialHash: 'credentialHash', - expiresAt: new Date(), - lasttriedFailedLogin: new Date(), - password: defaultTestPassword, - systemId: new ObjectId(), - token: 'token', - }).afterBuild((acc) => { - return { - ...acc, - createdAt: new Date(), - updatedAt: new Date(), - }; - }); - } - - withoutSystemAndUserId(): this { - return this.params({ - username: 'username', - systemId: undefined, - userId: undefined, - }); - } } +export const defaultTestPassword = 'DummyPasswd!1'; +export const defaultTestPasswordHash = '$2a$10$/DsztV5o6P5piW2eWJsxw.4nHovmJGBA.QNwiTmuZ/uvUc40b.Uhu'; // !!! important username should not be contain a space !!! export const accountFactory = AccountFactory.define(Account, ({ sequence }) => { return { From 664a97bf6f0f9b9905597ad7a4cb295db29b2b29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marvin=20=C3=96hlerking?= <103562092+MarvinOehlerkingCap@users.noreply.github.com> Date: Mon, 23 Oct 2023 15:44:35 +0200 Subject: [PATCH 12/14] N21-1395 Fix authorization not working for CTL board elements after authorization refactoring (#4491) --- .../modules/tool/common/common-tool.module.ts | 25 +- .../common/mapper/context-type.mapper.spec.ts | 11 - .../tool/common/mapper/context-type.mapper.ts | 13 - .../src/modules/tool/common/mapper/index.ts | 2 +- .../mapper/tool-status-response.mapper.ts | 2 +- .../tool/common/uc/tool-permission-helper.ts | 43 ++- .../common/uc/tool-permissions-helper.spec.ts | 129 +++++--- .../api-test/tool-context.api.spec.ts | 12 +- .../controller/tool-context.controller.ts | 4 +- .../context-external-tool-request.mapper.ts | 5 +- ...ontext-external-tool-validation.service.ts | 5 +- .../uc/context-external-tool.uc.spec.ts | 291 ++++++++++++++---- .../uc/context-external-tool.uc.ts | 62 +++- .../src/modules/tool/tool-api.module.ts | 12 +- .../board/board-do-authorizable.factory.ts | 14 + ...ts => external-tool-element.do.factory.ts} | 0 .../factory/domainobject/board/index.ts | 2 +- 17 files changed, 429 insertions(+), 203 deletions(-) delete mode 100644 apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts delete mode 100644 apps/server/src/modules/tool/common/mapper/context-type.mapper.ts create mode 100644 apps/server/src/shared/testing/factory/domainobject/board/board-do-authorizable.factory.ts rename apps/server/src/shared/testing/factory/domainobject/board/{external-tool.do.factory.ts => external-tool-element.do.factory.ts} (100%) diff --git a/apps/server/src/modules/tool/common/common-tool.module.ts b/apps/server/src/modules/tool/common/common-tool.module.ts index f56f595a99c..57375c67e96 100644 --- a/apps/server/src/modules/tool/common/common-tool.module.ts +++ b/apps/server/src/modules/tool/common/common-tool.module.ts @@ -1,28 +1,13 @@ -import { forwardRef, Module } from '@nestjs/common'; +import { LegacySchoolModule } from '@modules/legacy-school'; +import { Module } from '@nestjs/common'; import { ContextExternalToolRepo, SchoolExternalToolRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@modules/authorization'; -import { LegacySchoolModule } from '@modules/legacy-school'; -import { LearnroomModule } from '@modules/learnroom'; import { CommonToolService, CommonToolValidationService } from './service'; -import { ToolPermissionHelper } from './uc/tool-permission-helper'; @Module({ - imports: [LoggerModule, forwardRef(() => AuthorizationModule), LegacySchoolModule, LearnroomModule], + imports: [LoggerModule, LegacySchoolModule], // TODO: make deletion of entities cascading, adjust ExternalToolService.deleteExternalTool and remove the repos from here - providers: [ - CommonToolService, - CommonToolValidationService, - ToolPermissionHelper, - SchoolExternalToolRepo, - ContextExternalToolRepo, - ], - exports: [ - CommonToolService, - CommonToolValidationService, - ToolPermissionHelper, - SchoolExternalToolRepo, - ContextExternalToolRepo, - ], + providers: [CommonToolService, CommonToolValidationService, SchoolExternalToolRepo, ContextExternalToolRepo], + exports: [CommonToolService, CommonToolValidationService, SchoolExternalToolRepo, ContextExternalToolRepo], }) export class CommonToolModule {} diff --git a/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts b/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts deleted file mode 100644 index b05f50fc46c..00000000000 --- a/apps/server/src/modules/tool/common/mapper/context-type.mapper.spec.ts +++ /dev/null @@ -1,11 +0,0 @@ -import { AuthorizableReferenceType } from '@modules/authorization/domain'; -import { ToolContextType } from '../enum'; -import { ContextTypeMapper } from './context-type.mapper'; - -describe('context-type.mapper', () => { - it('should map ToolContextType.COURSE to AuthorizableReferenceType.Course', () => { - const mappedCourse = ContextTypeMapper.mapContextTypeToAllowedAuthorizationEntityType(ToolContextType.COURSE); - - expect(mappedCourse).toEqual(AuthorizableReferenceType.Course); - }); -}); diff --git a/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts b/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts deleted file mode 100644 index 3ae6902c232..00000000000 --- a/apps/server/src/modules/tool/common/mapper/context-type.mapper.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { AuthorizableReferenceType } from '@modules/authorization/domain/'; -import { ToolContextType } from '../enum'; - -const typeMapping: Record = { - [ToolContextType.COURSE]: AuthorizableReferenceType.Course, - [ToolContextType.BOARD_ELEMENT]: AuthorizableReferenceType.BoardNode, -}; - -export class ContextTypeMapper { - static mapContextTypeToAllowedAuthorizationEntityType(type: ToolContextType): AuthorizableReferenceType { - return typeMapping[type]; - } -} diff --git a/apps/server/src/modules/tool/common/mapper/index.ts b/apps/server/src/modules/tool/common/mapper/index.ts index 3da6b0fa28b..b6be27cdc1f 100644 --- a/apps/server/src/modules/tool/common/mapper/index.ts +++ b/apps/server/src/modules/tool/common/mapper/index.ts @@ -1 +1 @@ -export * from './context-type.mapper'; +export * from './tool-status-response.mapper'; diff --git a/apps/server/src/modules/tool/common/mapper/tool-status-response.mapper.ts b/apps/server/src/modules/tool/common/mapper/tool-status-response.mapper.ts index c199fc6f307..0c16ca50e9a 100644 --- a/apps/server/src/modules/tool/common/mapper/tool-status-response.mapper.ts +++ b/apps/server/src/modules/tool/common/mapper/tool-status-response.mapper.ts @@ -1,4 +1,4 @@ -import { ToolConfigurationStatusResponse } from '../../context-external-tool/controller/dto/tool-configuration-status.response'; +import { ToolConfigurationStatusResponse } from '../../context-external-tool/controller/dto'; import { ToolConfigurationStatus } from '../enum'; export const statusMapping: Record = { diff --git a/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts b/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts index 542903bc4be..525c8c5d3b6 100644 --- a/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts +++ b/apps/server/src/modules/tool/common/uc/tool-permission-helper.ts @@ -1,11 +1,13 @@ -import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { Course, EntityId, LegacySchoolDo, User } from '@shared/domain'; -import { AuthorizationContext, AuthorizationService } from '@modules/authorization'; -import { LegacySchoolService } from '@modules/legacy-school'; +import { AuthorizationContext, AuthorizationService, ForbiddenLoggableException } from '@modules/authorization'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; +import { BoardDoAuthorizableService, ContentElementService } from '@modules/board'; import { CourseService } from '@modules/learnroom'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { forwardRef, Inject, Injectable } from '@nestjs/common'; +import { BoardDoAuthorizable, Course, EntityId, LegacySchoolDo, User } from '@shared/domain'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; -// import { ContextTypeMapper } from '../mapper'; +import { ToolContextType } from '../enum'; @Injectable() export class ToolPermissionHelper { @@ -15,7 +17,9 @@ export class ToolPermissionHelper { // invalid dependency on this place it is in UC layer in a other module // loading of ressources should be part of service layer // if it must resolve different loadings based on the request it can be added in own service and use in UC - private readonly courseService: CourseService + private readonly courseService: CourseService, + private readonly boardElementService: ContentElementService, + private readonly boardService: BoardDoAuthorizableService ) {} // TODO build interface to get contextDO by contextType @@ -24,19 +28,24 @@ export class ToolPermissionHelper { contextExternalTool: ContextExternalTool, context: AuthorizationContext ): Promise { - // loading of ressources should be part of the UC -> unnessasary awaits - const [authorizableUser, course]: [User, Course] = await Promise.all([ - this.authorizationService.getUserWithPermissions(userId), - this.courseService.findById(contextExternalTool.contextRef.id), - ]); + const authorizableUser = await this.authorizationService.getUserWithPermissions(userId); - if (contextExternalTool.id) { - this.authorizationService.checkPermission(authorizableUser, contextExternalTool, context); - } + this.authorizationService.checkPermission(authorizableUser, contextExternalTool, context); + + if (contextExternalTool.contextRef.type === ToolContextType.COURSE) { + // loading of ressources should be part of the UC -> unnessasary awaits + const course: Course = await this.courseService.findById(contextExternalTool.contextRef.id); + + this.authorizationService.checkPermission(authorizableUser, course, context); + } else if (contextExternalTool.contextRef.type === ToolContextType.BOARD_ELEMENT) { + const boardElement = await this.boardElementService.findById(contextExternalTool.contextRef.id); - // const type = ContextTypeMapper.mapContextTypeToAllowedAuthorizationEntityType(contextExternalTool.contextRef.type); - // no different types possible until it is fixed. - this.authorizationService.checkPermission(authorizableUser, course, context); + const board: BoardDoAuthorizable = await this.boardService.getBoardAuthorizable(boardElement); + + this.authorizationService.checkPermission(authorizableUser, board, context); + } else { + throw new ForbiddenLoggableException(userId, AuthorizableReferenceType.ContextExternalToolEntity, context); + } } public async ensureSchoolPermissions( diff --git a/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts b/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts index ad697a94694..aace35579db 100644 --- a/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts +++ b/apps/server/src/modules/tool/common/uc/tool-permissions-helper.spec.ts @@ -1,29 +1,41 @@ -import { Test, TestingModule } from '@nestjs/testing'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { + AuthorizationContext, + AuthorizationContextBuilder, + AuthorizationService, + ForbiddenLoggableException, +} from '@modules/authorization'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; +import { BoardDoAuthorizableService, ContentElementService } from '@modules/board'; +import { CourseService } from '@modules/learnroom'; +import { LegacySchoolService } from '@modules/legacy-school'; +import { ForbiddenException } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { BoardDoAuthorizable, ExternalToolElement, LegacySchoolDo, Permission } from '@shared/domain'; import { contextExternalToolFactory, courseFactory, + externalToolElementFactory, legacySchoolDoFactory, schoolExternalToolFactory, setupEntities, userFactory, } from '@shared/testing'; -import { Permission, LegacySchoolDo } from '@shared/domain'; -import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; -import { LegacySchoolService } from '@modules/legacy-school'; -import { ForbiddenException } from '@nestjs/common'; -import { CourseService } from '@modules/learnroom'; -import { ContextExternalTool } from '../../context-external-tool/domain'; -import { ToolPermissionHelper } from './tool-permission-helper'; +import { boardDoAuthorizableFactory } from '@shared/testing/factory/domainobject/board/board-do-authorizable.factory'; +import { ContextExternalTool, ContextRef } from '../../context-external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; +import { ToolContextType } from '../enum'; +import { ToolPermissionHelper } from './tool-permission-helper'; describe('ToolPermissionHelper', () => { let module: TestingModule; let helper: ToolPermissionHelper; let authorizationService: DeepMocked; - let courseService: DeepMocked; let schoolService: DeepMocked; + let courseService: DeepMocked; + let contentElementService: DeepMocked; + let boardDoAuthorizableService: DeepMocked; beforeAll(async () => { await setupEntities(); @@ -34,21 +46,31 @@ describe('ToolPermissionHelper', () => { provide: AuthorizationService, useValue: createMock(), }, + { + provide: LegacySchoolService, + useValue: createMock(), + }, { provide: CourseService, useValue: createMock(), }, { - provide: LegacySchoolService, - useValue: createMock(), + provide: ContentElementService, + useValue: createMock(), + }, + { + provide: BoardDoAuthorizableService, + useValue: createMock(), }, ], }).compile(); helper = module.get(ToolPermissionHelper); authorizationService = module.get(AuthorizationService); - courseService = module.get(CourseService); schoolService = module.get(LegacySchoolService); + courseService = module.get(CourseService); + contentElementService = module.get(ContentElementService); + boardDoAuthorizableService = module.get(BoardDoAuthorizableService); }); afterAll(async () => { @@ -60,16 +82,20 @@ describe('ToolPermissionHelper', () => { }); describe('ensureContextPermissions', () => { - describe('when context external tool with id is given', () => { + describe('when a context external tool for context "course" is given', () => { const setup = () => { const user = userFactory.buildWithId(); const course = courseFactory.buildWithId(); - const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ + contextRef: new ContextRef({ + id: course.id, + type: ToolContextType.COURSE, + }), + }); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); - courseService.findById.mockResolvedValueOnce(course); authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); - authorizationService.checkPermission.mockReturnValueOnce().mockReturnValueOnce(); + courseService.findById.mockResolvedValueOnce(course); return { user, @@ -88,70 +114,97 @@ describe('ToolPermissionHelper', () => { expect(authorizationService.checkPermission).toHaveBeenNthCalledWith(1, user, contextExternalTool, context); expect(authorizationService.checkPermission).toHaveBeenNthCalledWith(2, user, course, context); }); + }); - it('should return undefined', async () => { - const { user, contextExternalTool, context } = setup(); + describe('when a context external tool for context "board element" is given', () => { + const setup = () => { + const user = userFactory.buildWithId(); + const externalToolElement: ExternalToolElement = externalToolElementFactory.build(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ + contextRef: new ContextRef({ + id: externalToolElement.id, + type: ToolContextType.BOARD_ELEMENT, + }), + }); + const board: BoardDoAuthorizable = boardDoAuthorizableFactory.build(); + const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); - const result = await helper.ensureContextPermissions(user.id, contextExternalTool, context); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + contentElementService.findById.mockResolvedValueOnce(externalToolElement); + boardDoAuthorizableService.getBoardAuthorizable.mockResolvedValueOnce(board); - expect(result).toBeUndefined(); + return { + user, + board, + contextExternalTool, + context, + }; + }; + + it('should check permission for context external tool', async () => { + const { user, board, contextExternalTool, context } = setup(); + + await helper.ensureContextPermissions(user.id, contextExternalTool, context); + + expect(authorizationService.checkPermission).toHaveBeenCalledTimes(2); + expect(authorizationService.checkPermission).toHaveBeenNthCalledWith(1, user, contextExternalTool, context); + expect(authorizationService.checkPermission).toHaveBeenNthCalledWith(2, user, board, context); }); }); - describe('when context external tool without id is given', () => { + describe('when the context external tool has an unkown context', () => { const setup = () => { const user = userFactory.buildWithId(); - const course = courseFactory.buildWithId(); - const contextExternalTool: ContextExternalTool = contextExternalToolFactory.build(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ + contextRef: { + type: 'unknown type' as unknown as ToolContextType, + }, + }); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); - courseService.findById.mockResolvedValueOnce(course); authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); return { user, - course, contextExternalTool, context, }; }; - it('should check permission for context external tool', async () => { - const { user, course, contextExternalTool, context } = setup(); - - await helper.ensureContextPermissions(user.id, contextExternalTool, context); + it('should throw a forbidden loggable exception', async () => { + const { user, contextExternalTool, context } = setup(); - expect(authorizationService.checkPermission).toHaveBeenCalledTimes(1); - expect(authorizationService.checkPermission).toHaveBeenCalledWith(user, course, context); + await expect(helper.ensureContextPermissions(user.id, contextExternalTool, context)).rejects.toThrowError( + new ForbiddenLoggableException(user.id, AuthorizableReferenceType.ContextExternalToolEntity, context) + ); }); }); describe('when user is unauthorized', () => { const setup = () => { const user = userFactory.buildWithId(); - const course = courseFactory.buildWithId(); const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId(); const context: AuthorizationContext = AuthorizationContextBuilder.read([Permission.CONTEXT_TOOL_USER]); + const error = new ForbiddenException(); - courseService.findById.mockResolvedValueOnce(course); authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); authorizationService.checkPermission.mockImplementationOnce(() => { - throw new ForbiddenException(); + throw error; }); return { user, - course, contextExternalTool, context, + error, }; }; - it('should check permission for context external tool', async () => { - const { user, contextExternalTool, context } = setup(); + it('should check permission for context external tool and fail', async () => { + const { user, contextExternalTool, context, error } = setup(); await expect(helper.ensureContextPermissions(user.id, contextExternalTool, context)).rejects.toThrowError( - new ForbiddenException() + error ); }); }); diff --git a/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts index e2dbe6ec9ac..eb570130c4e 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/api-test/tool-context.api.spec.ts @@ -1,4 +1,5 @@ import { EntityManager, MikroORM } from '@mikro-orm/core'; +import { ServerTestModule } from '@modules/server'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Account, Course, Permission, SchoolEntity, User } from '@shared/domain'; @@ -15,18 +16,17 @@ import { UserAndAccountTestFactory, userFactory, } from '@shared/testing'; -import { ServerTestModule } from '@modules/server'; import { ObjectId } from 'bson'; import { CustomParameterScope, ToolContextType } from '../../../common/enum'; +import { ExternalToolEntity } from '../../../external-tool/entity'; +import { CustomParameterEntryResponse } from '../../../school-external-tool/controller/dto'; import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; +import { ContextExternalToolEntity, ContextExternalToolType } from '../../entity'; import { ContextExternalToolPostParams, ContextExternalToolResponse, ContextExternalToolSearchListResponse, } from '../dto'; -import { ContextExternalToolEntity, ContextExternalToolType } from '../../entity'; -import { ExternalToolEntity } from '../../../external-tool/entity'; -import { CustomParameterEntryResponse } from '../../../school-external-tool/controller/dto'; describe('ToolContextController (API)', () => { let app: INestApplication; @@ -116,8 +116,8 @@ describe('ToolContextController (API)', () => { describe('when user is not authorized for the requested context', () => { const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); const school = schoolFactory.build(); + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher({ school }); const course = courseFactory.build({ teachers: [teacherUser] }); const otherCourse = courseFactory.build(); const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.build({ @@ -130,7 +130,7 @@ describe('ToolContextController (API)', () => { em.clear(); const postParams: ContextExternalToolPostParams = { - schoolToolId: school.id, + schoolToolId: schoolExternalToolEntity.id, contextId: otherCourse.id, contextType: ToolContextType.COURSE, parameters: [], diff --git a/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts b/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts index a80ad3f234f..20d8b96f795 100644 --- a/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts +++ b/apps/server/src/modules/tool/context-external-tool/controller/tool-context.controller.ts @@ -1,3 +1,4 @@ +import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { Body, Controller, Delete, Get, HttpCode, HttpStatus, Param, Post, Put } from '@nestjs/common'; import { ApiCreatedResponse, @@ -12,7 +13,6 @@ import { } from '@nestjs/swagger'; import { ValidationError } from '@shared/common'; import { LegacyLogger } from '@src/core/logger'; -import { Authenticate, CurrentUser, ICurrentUser } from '@modules/authentication'; import { ContextExternalTool } from '../domain'; import { ContextExternalToolRequestMapper, ContextExternalToolResponseMapper } from '../mapper'; import { ContextExternalToolUc } from '../uc'; @@ -50,6 +50,7 @@ export class ToolContextController { const createdTool: ContextExternalTool = await this.contextExternalToolUc.createContextExternalTool( currentUser.userId, + currentUser.schoolId, contextExternalTool ); @@ -152,6 +153,7 @@ export class ToolContextController { const updatedTool: ContextExternalTool = await this.contextExternalToolUc.updateContextExternalTool( currentUser.userId, + currentUser.schoolId, params.contextExternalToolId, contextExternalTool ); diff --git a/apps/server/src/modules/tool/context-external-tool/mapper/context-external-tool-request.mapper.ts b/apps/server/src/modules/tool/context-external-tool/mapper/context-external-tool-request.mapper.ts index 45f912bf52c..951559afbcc 100644 --- a/apps/server/src/modules/tool/context-external-tool/mapper/context-external-tool-request.mapper.ts +++ b/apps/server/src/modules/tool/context-external-tool/mapper/context-external-tool-request.mapper.ts @@ -1,12 +1,11 @@ -import { ContextExternalToolPostParams } from '../controller/dto'; +import { CustomParameterEntry } from '../../common/domain'; import { CustomParameterEntryParam } from '../../school-external-tool/controller/dto'; +import { ContextExternalToolPostParams } from '../controller/dto'; import { ContextExternalToolDto } from '../uc/dto/context-external-tool.types'; -import { CustomParameterEntry } from '../../common/domain'; export class ContextExternalToolRequestMapper { static mapContextExternalToolRequest(request: ContextExternalToolPostParams): ContextExternalToolDto { return { - id: '', schoolToolRef: { schoolToolId: request.schoolToolId, }, diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.ts index 3777273d18e..2cce83a08b2 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-validation.service.ts @@ -6,7 +6,6 @@ import { ExternalToolService } from '../../external-tool/service'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { SchoolExternalToolService } from '../../school-external-tool/service'; import { ContextExternalTool } from '../domain'; -import { ContextExternalToolDto } from '../uc/dto/context-external-tool.types'; import { ContextExternalToolService } from './context-external-tool.service'; @Injectable() @@ -18,9 +17,7 @@ export class ContextExternalToolValidationService { private readonly commonToolValidationService: CommonToolValidationService ) {} - async validate(toValidate: ContextExternalToolDto): Promise { - const contextExternalTool: ContextExternalTool = new ContextExternalTool(toValidate); - + async validate(contextExternalTool: ContextExternalTool): Promise { await this.checkDuplicateInContext(contextExternalTool); const loadedSchoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( diff --git a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts index 648ccb491fa..0fcca404049 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.spec.ts @@ -1,18 +1,20 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; -import { ForbiddenException, UnprocessableEntityException } from '@nestjs/common'; -import { Test, TestingModule } from '@nestjs/testing'; -import { EntityId, Permission, User } from '@shared/domain'; -import { contextExternalToolFactory, setupEntities, userFactory } from '@shared/testing'; -import { LegacyLogger } from '@src/core/logger'; import { Action, AuthorizationContextBuilder, AuthorizationService, ForbiddenLoggableException, } from '@modules/authorization'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; +import { ForbiddenException, UnprocessableEntityException } from '@nestjs/common'; +import { Test, TestingModule } from '@nestjs/testing'; +import { EntityId, Permission, User } from '@shared/domain'; +import { contextExternalToolFactory, schoolExternalToolFactory, setupEntities, userFactory } from '@shared/testing'; import { ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; +import { SchoolExternalTool } from '../../school-external-tool/domain'; +import { SchoolExternalToolService } from '../../school-external-tool/service'; import { ContextExternalTool } from '../domain'; import { ContextExternalToolService, ContextExternalToolValidationService } from '../service'; import { ContextExternalToolUc } from './context-external-tool.uc'; @@ -21,6 +23,7 @@ describe('ContextExternalToolUc', () => { let module: TestingModule; let uc: ContextExternalToolUc; + let schoolExternalToolService: DeepMocked; let contextExternalToolService: DeepMocked; let contextExternalToolValidationService: DeepMocked; let toolPermissionHelper: DeepMocked; @@ -31,6 +34,10 @@ describe('ContextExternalToolUc', () => { module = await Test.createTestingModule({ providers: [ ContextExternalToolUc, + { + provide: SchoolExternalToolService, + useValue: createMock(), + }, { provide: ContextExternalToolService, useValue: createMock(), @@ -39,10 +46,6 @@ describe('ContextExternalToolUc', () => { provide: ContextExternalToolValidationService, useValue: createMock(), }, - { - provide: LegacyLogger, - useValue: createMock(), - }, { provide: ToolPermissionHelper, useValue: createMock(), @@ -55,6 +58,7 @@ describe('ContextExternalToolUc', () => { }).compile(); uc = module.get(ContextExternalToolUc); + schoolExternalToolService = module.get(SchoolExternalToolService); contextExternalToolService = module.get(ContextExternalToolService); contextExternalToolValidationService = module.get(ContextExternalToolValidationService); toolPermissionHelper = module.get(ToolPermissionHelper); @@ -73,36 +77,46 @@ describe('ContextExternalToolUc', () => { describe('when contextExternalTool is given and user has permission ', () => { const setup = () => { const userId: EntityId = 'userId'; + const schoolId: EntityId = new ObjectId().toHexString(); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId({ + schoolId, + }); const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ displayName: 'Course', + schoolToolRef: { + schoolToolId: schoolExternalTool.id, + schoolId, + }, contextRef: { id: 'contextId', type: ToolContextType.COURSE, }, }); - toolPermissionHelper.ensureContextPermissions.mockResolvedValue(Promise.resolve()); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); contextExternalToolService.saveContextExternalTool.mockResolvedValue(contextExternalTool); return { contextExternalTool, userId, + schoolId, }; }; it('should call contextExternalToolService', async () => { - const { contextExternalTool, userId } = setup(); + const { contextExternalTool, userId, schoolId } = setup(); - await uc.createContextExternalTool(userId, contextExternalTool); + await uc.createContextExternalTool(userId, schoolId, contextExternalTool); expect(contextExternalToolService.saveContextExternalTool).toHaveBeenCalledWith(contextExternalTool); }); it('should call contextExternalToolService to ensure permissions', async () => { - const { contextExternalTool, userId } = setup(); + const { contextExternalTool, userId, schoolId } = setup(); - await uc.createContextExternalTool(userId, contextExternalTool); + await uc.createContextExternalTool(userId, schoolId, contextExternalTool); expect(toolPermissionHelper.ensureContextPermissions).toHaveBeenCalledWith( userId, @@ -112,28 +126,82 @@ describe('ContextExternalToolUc', () => { }); it('should call contextExternalToolValidationService', async () => { - const { contextExternalTool, userId } = setup(); + const { contextExternalTool, userId, schoolId } = setup(); - await uc.createContextExternalTool(userId, contextExternalTool); + await uc.createContextExternalTool(userId, schoolId, contextExternalTool); expect(contextExternalToolValidationService.validate).toHaveBeenCalledWith(contextExternalTool); }); it('should return the saved object', async () => { - const { contextExternalTool, userId } = setup(); + const { contextExternalTool, userId, schoolId } = setup(); - const result = await uc.createContextExternalTool(userId, contextExternalTool); + const result = await uc.createContextExternalTool(userId, schoolId, contextExternalTool); expect(result).toEqual(contextExternalTool); }); }); + describe('when the user is from a different school than the school external tool', () => { + const setup = () => { + const userId: EntityId = 'userId'; + const schoolId: EntityId = new ObjectId().toHexString(); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId({ + schoolId, + }); + + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ + displayName: 'Course', + schoolToolRef: { + schoolToolId: schoolExternalTool.id, + schoolId, + }, + contextRef: { + id: 'contextId', + type: ToolContextType.COURSE, + }, + }); + + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + + return { + contextExternalTool, + userId, + }; + }; + + it('should return UnprocessableEntity and not save', async () => { + const { contextExternalTool, userId } = setup(); + + const func = () => uc.createContextExternalTool(userId, new ObjectId().toHexString(), contextExternalTool); + + await expect(func).rejects.toThrow( + new ForbiddenLoggableException( + userId, + AuthorizableReferenceType.ContextExternalToolEntity, + AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]) + ) + ); + expect(contextExternalToolService.saveContextExternalTool).not.toHaveBeenCalled(); + }); + }); + describe('when the user does not have permission', () => { const setup = () => { const userId: EntityId = 'userId'; + const schoolId: EntityId = new ObjectId().toHexString(); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId({ + schoolId, + }); const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ displayName: 'Course', + schoolToolRef: { + schoolToolId: schoolExternalTool.id, + schoolId, + }, contextRef: { id: 'contextId', type: ToolContextType.COURSE, @@ -142,19 +210,21 @@ describe('ContextExternalToolUc', () => { const error = new ForbiddenException(); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); toolPermissionHelper.ensureContextPermissions.mockRejectedValue(error); return { contextExternalTool, userId, + schoolId, error, }; }; it('should return forbidden and not save', async () => { - const { contextExternalTool, userId, error } = setup(); + const { contextExternalTool, userId, error, schoolId } = setup(); - const func = () => uc.createContextExternalTool(userId, contextExternalTool); + const func = () => uc.createContextExternalTool(userId, schoolId, contextExternalTool); await expect(func).rejects.toThrow(error); expect(contextExternalToolService.saveContextExternalTool).not.toHaveBeenCalled(); @@ -164,9 +234,18 @@ describe('ContextExternalToolUc', () => { describe('when the validation fails', () => { const setup = () => { const userId: EntityId = 'userId'; + const schoolId: EntityId = new ObjectId().toHexString(); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId({ + schoolId, + }); const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ displayName: 'Course', + schoolToolRef: { + schoolToolId: schoolExternalTool.id, + schoolId, + }, contextRef: { id: 'contextId', type: ToolContextType.COURSE, @@ -175,19 +254,21 @@ describe('ContextExternalToolUc', () => { const error = new UnprocessableEntityException(); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); contextExternalToolValidationService.validate.mockRejectedValue(error); return { contextExternalTool, userId, + schoolId, error, }; }; it('should return UnprocessableEntity and not save', async () => { - const { contextExternalTool, userId, error } = setup(); + const { contextExternalTool, userId, error, schoolId } = setup(); - const func = () => uc.createContextExternalTool(userId, contextExternalTool); + const func = () => uc.createContextExternalTool(userId, schoolId, contextExternalTool); await expect(func).rejects.toThrow(error); expect(contextExternalToolService.saveContextExternalTool).not.toHaveBeenCalled(); @@ -199,40 +280,48 @@ describe('ContextExternalToolUc', () => { describe('when contextExternalTool is given and user has permission ', () => { const setup = () => { const userId: EntityId = 'userId'; + const schoolId: EntityId = new ObjectId().toHexString(); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId({ + schoolId, + }); - const contextExternalToolId = new ObjectId().toHexString(); - const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId( - { - displayName: 'Course', - contextRef: { - id: 'contextId', - type: ToolContextType.COURSE, - }, + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ + displayName: 'Course', + schoolToolRef: { + schoolToolId: schoolExternalTool.id, + schoolId, }, - contextExternalToolId - ); + contextRef: { + id: 'contextId', + type: ToolContextType.COURSE, + }, + }); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); contextExternalToolService.saveContextExternalTool.mockResolvedValue(contextExternalTool); + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); return { contextExternalTool, - contextExternalToolId, + contextExternalToolId: contextExternalTool.id as string, userId, + schoolId, }; }; it('should call contextExternalToolService', async () => { - const { contextExternalTool, contextExternalToolId, userId } = setup(); + const { contextExternalTool, userId, schoolId, contextExternalToolId } = setup(); - await uc.updateContextExternalTool(userId, contextExternalToolId, contextExternalTool); + await uc.updateContextExternalTool(userId, schoolId, contextExternalToolId, contextExternalTool); expect(contextExternalToolService.saveContextExternalTool).toHaveBeenCalledWith(contextExternalTool); }); it('should call contextExternalToolService to ensure permissions', async () => { - const { contextExternalTool, contextExternalToolId, userId } = setup(); + const { contextExternalTool, userId, schoolId, contextExternalToolId } = setup(); - await uc.updateContextExternalTool(userId, contextExternalToolId, contextExternalTool); + await uc.updateContextExternalTool(userId, schoolId, contextExternalToolId, contextExternalTool); expect(toolPermissionHelper.ensureContextPermissions).toHaveBeenCalledWith( userId, @@ -242,54 +331,114 @@ describe('ContextExternalToolUc', () => { }); it('should call contextExternalToolValidationService', async () => { - const { contextExternalTool, contextExternalToolId, userId } = setup(); + const { contextExternalTool, userId, schoolId, contextExternalToolId } = setup(); - await uc.updateContextExternalTool(userId, contextExternalToolId, contextExternalTool); + await uc.updateContextExternalTool(userId, schoolId, contextExternalToolId, contextExternalTool); expect(contextExternalToolValidationService.validate).toHaveBeenCalledWith(contextExternalTool); }); it('should return the saved object', async () => { - const { contextExternalTool, contextExternalToolId, userId } = setup(); + const { contextExternalTool, userId, schoolId, contextExternalToolId } = setup(); - const result = await uc.updateContextExternalTool(userId, contextExternalToolId, contextExternalTool); + const result = await uc.updateContextExternalTool(userId, schoolId, contextExternalToolId, contextExternalTool); expect(result).toEqual(contextExternalTool); }); }); - describe('when the user does not have permission', () => { + describe('when the user is from a different school than the school external tool', () => { const setup = () => { const userId: EntityId = 'userId'; + const schoolId: EntityId = new ObjectId().toHexString(); - const contextExternalToolId = new ObjectId().toHexString(); - const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId( - { - displayName: 'Course', - contextRef: { - id: 'contextId', - type: ToolContextType.COURSE, - }, + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId({ + schoolId, + }); + + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ + displayName: 'Course', + schoolToolRef: { + schoolToolId: schoolExternalTool.id, + schoolId, + }, + contextRef: { + id: 'contextId', + type: ToolContextType.COURSE, }, - contextExternalToolId + }); + + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + + return { + contextExternalTool, + contextExternalToolId: contextExternalTool.id as string, + userId, + }; + }; + + it('should return UnprocessableEntity and not save', async () => { + const { contextExternalTool, userId, contextExternalToolId } = setup(); + + const func = () => + uc.updateContextExternalTool( + userId, + new ObjectId().toHexString(), + contextExternalToolId, + contextExternalTool + ); + + await expect(func).rejects.toThrow( + new ForbiddenLoggableException( + userId, + AuthorizableReferenceType.ContextExternalToolEntity, + AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]) + ) ); + expect(contextExternalToolService.saveContextExternalTool).not.toHaveBeenCalled(); + }); + }); + + describe('when the user does not have permission', () => { + const setup = () => { + const userId: EntityId = 'userId'; + const schoolId: EntityId = new ObjectId().toHexString(); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId({ + schoolId, + }); + + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ + displayName: 'Course', + schoolToolRef: { + schoolToolId: schoolExternalTool.id, + schoolId, + }, + contextRef: { + id: 'contextId', + type: ToolContextType.COURSE, + }, + }); const error = new ForbiddenException(); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); toolPermissionHelper.ensureContextPermissions.mockRejectedValue(error); return { contextExternalTool, - contextExternalToolId, + contextExternalToolId: contextExternalTool.id as string, userId, + schoolId, error, }; }; it('should return forbidden and not save', async () => { - const { contextExternalTool, contextExternalToolId, userId, error } = setup(); + const { contextExternalTool, userId, error, schoolId, contextExternalToolId } = setup(); - const func = () => uc.updateContextExternalTool(userId, contextExternalToolId, contextExternalTool); + const func = () => uc.updateContextExternalTool(userId, schoolId, contextExternalToolId, contextExternalTool); await expect(func).rejects.toThrow(error); expect(contextExternalToolService.saveContextExternalTool).not.toHaveBeenCalled(); @@ -299,35 +448,43 @@ describe('ContextExternalToolUc', () => { describe('when the validation fails', () => { const setup = () => { const userId: EntityId = 'userId'; + const schoolId: EntityId = new ObjectId().toHexString(); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.buildWithId({ + schoolId, + }); - const contextExternalToolId = new ObjectId().toHexString(); - const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId( - { - displayName: 'Course', - contextRef: { - id: 'contextId', - type: ToolContextType.COURSE, - }, + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.buildWithId({ + displayName: 'Course', + schoolToolRef: { + schoolToolId: schoolExternalTool.id, + schoolId, }, - contextExternalToolId - ); + contextRef: { + id: 'contextId', + type: ToolContextType.COURSE, + }, + }); const error = new UnprocessableEntityException(); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + contextExternalToolService.findById.mockResolvedValueOnce(contextExternalTool); contextExternalToolValidationService.validate.mockRejectedValue(error); return { contextExternalTool, - contextExternalToolId, + contextExternalToolId: contextExternalTool.id as string, userId, + schoolId, error, }; }; it('should return UnprocessableEntity and not save', async () => { - const { contextExternalTool, contextExternalToolId, userId, error } = setup(); + const { contextExternalTool, userId, error, schoolId, contextExternalToolId } = setup(); - const func = () => uc.updateContextExternalTool(userId, contextExternalToolId, contextExternalTool); + const func = () => uc.updateContextExternalTool(userId, schoolId, contextExternalToolId, contextExternalTool); await expect(func).rejects.toThrow(error); expect(contextExternalToolService.saveContextExternalTool).not.toHaveBeenCalled(); diff --git a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts index d29cc1454d4..587ecb01c64 100644 --- a/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts +++ b/apps/server/src/modules/tool/context-external-tool/uc/context-external-tool.uc.ts @@ -1,9 +1,16 @@ +import { + AuthorizationContext, + AuthorizationContextBuilder, + AuthorizationService, + ForbiddenLoggableException, +} from '@modules/authorization'; +import { AuthorizableReferenceType } from '@modules/authorization/domain'; import { Injectable } from '@nestjs/common'; import { EntityId, Permission, User } from '@shared/domain'; -import { LegacyLogger } from '@src/core/logger'; -import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; +import { SchoolExternalTool } from '../../school-external-tool/domain'; +import { SchoolExternalToolService } from '../../school-external-tool/service'; import { ContextExternalTool, ContextRef } from '../domain'; import { ContextExternalToolService, ContextExternalToolValidationService } from '../service'; import { ContextExternalToolDto } from './dto/context-external-tool.types'; @@ -12,22 +19,32 @@ import { ContextExternalToolDto } from './dto/context-external-tool.types'; export class ContextExternalToolUc { constructor( private readonly toolPermissionHelper: ToolPermissionHelper, + private readonly schoolExternalToolService: SchoolExternalToolService, private readonly contextExternalToolService: ContextExternalToolService, private readonly contextExternalToolValidationService: ContextExternalToolValidationService, - private readonly authorizationService: AuthorizationService, - private readonly logger: LegacyLogger + private readonly authorizationService: AuthorizationService ) {} async createContextExternalTool( userId: EntityId, + schoolId: EntityId, contextExternalToolDto: ContextExternalToolDto ): Promise { - const contextExternalTool = new ContextExternalTool(contextExternalToolDto); const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( + contextExternalToolDto.schoolToolRef.schoolToolId + ); + + if (schoolExternalTool.schoolId !== schoolId) { + throw new ForbiddenLoggableException(userId, AuthorizableReferenceType.ContextExternalToolEntity, context); + } + + contextExternalToolDto.schoolToolRef.schoolId = schoolId; + const contextExternalTool = new ContextExternalTool(contextExternalToolDto); await this.toolPermissionHelper.ensureContextPermissions(userId, contextExternalTool, context); - await this.contextExternalToolValidationService.validate(contextExternalToolDto); + await this.contextExternalToolValidationService.validate(contextExternalTool); const createdTool: ContextExternalTool = await this.contextExternalToolService.saveContextExternalTool( contextExternalTool @@ -38,27 +55,38 @@ export class ContextExternalToolUc { async updateContextExternalTool( userId: EntityId, + schoolId: EntityId, contextExternalToolId: EntityId, contextExternalToolDto: ContextExternalToolDto ): Promise { - const contextExternalTool: ContextExternalTool = new ContextExternalTool(contextExternalToolDto); + const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( + contextExternalToolDto.schoolToolRef.schoolToolId + ); + + if (schoolExternalTool.schoolId !== schoolId) { + throw new ForbiddenLoggableException(userId, AuthorizableReferenceType.ContextExternalToolEntity, context); + } - await this.toolPermissionHelper.ensureContextPermissions( - userId, - contextExternalTool, - AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]) + let contextExternalTool: ContextExternalTool = await this.contextExternalToolService.findById( + contextExternalToolId ); - const updated: ContextExternalTool = new ContextExternalTool({ - ...contextExternalTool, - id: contextExternalToolId, + contextExternalTool = new ContextExternalTool({ + ...contextExternalToolDto, + id: contextExternalTool.id, }); + contextExternalTool.schoolToolRef.schoolId = schoolId; + + await this.toolPermissionHelper.ensureContextPermissions(userId, contextExternalTool, context); - await this.contextExternalToolValidationService.validate(updated); + await this.contextExternalToolValidationService.validate(contextExternalTool); - const saved: ContextExternalTool = await this.contextExternalToolService.saveContextExternalTool(updated); + const updatedTool: ContextExternalTool = await this.contextExternalToolService.saveContextExternalTool( + contextExternalTool + ); - return saved; + return updatedTool; } public async deleteContextExternalTool(userId: EntityId, contextExternalToolId: EntityId): Promise { diff --git a/apps/server/src/modules/tool/tool-api.module.ts b/apps/server/src/modules/tool/tool-api.module.ts index 8513eaed3fc..b8d12a16006 100644 --- a/apps/server/src/modules/tool/tool-api.module.ts +++ b/apps/server/src/modules/tool/tool-api.module.ts @@ -1,10 +1,13 @@ -import { Module } from '@nestjs/common'; -import { LtiToolRepo } from '@shared/repo'; -import { LoggerModule } from '@src/core/logger'; import { AuthorizationModule } from '@modules/authorization'; import { LegacySchoolModule } from '@modules/legacy-school'; import { UserModule } from '@modules/user'; +import { Module } from '@nestjs/common'; +import { LtiToolRepo } from '@shared/repo'; +import { LoggerModule } from '@src/core/logger'; +import { BoardModule } from '../board'; +import { LearnroomModule } from '../learnroom'; import { CommonToolModule } from './common'; +import { ToolPermissionHelper } from './common/uc/tool-permission-helper'; import { ToolContextController } from './context-external-tool/controller'; import { ToolReferenceController } from './context-external-tool/controller/tool-reference.controller'; import { ContextExternalToolUc, ToolReferenceUc } from './context-external-tool/uc'; @@ -29,6 +32,8 @@ import { ToolModule } from './tool.module'; LoggerModule, LegacySchoolModule, ToolConfigModule, + LearnroomModule, + BoardModule, ], controllers: [ ToolLaunchController, @@ -51,6 +56,7 @@ import { ToolModule } from './tool.module'; ContextExternalToolUc, ToolLaunchUc, ToolReferenceUc, + ToolPermissionHelper, ], }) export class ToolApiModule {} diff --git a/apps/server/src/shared/testing/factory/domainobject/board/board-do-authorizable.factory.ts b/apps/server/src/shared/testing/factory/domainobject/board/board-do-authorizable.factory.ts new file mode 100644 index 00000000000..f774ba97bb4 --- /dev/null +++ b/apps/server/src/shared/testing/factory/domainobject/board/board-do-authorizable.factory.ts @@ -0,0 +1,14 @@ +import { BoardDoAuthorizable, BoardDoAuthorizableProps, UserRoleEnum } from '@shared/domain/domainobject/board'; +import { ObjectId } from 'bson'; +import { DomainObjectFactory } from '../domain-object.factory'; + +export const boardDoAuthorizableFactory = DomainObjectFactory.define( + BoardDoAuthorizable, + () => { + return { + id: new ObjectId().toHexString(), + users: [], + requiredUserRole: UserRoleEnum.STUDENT, + }; + } +); diff --git a/apps/server/src/shared/testing/factory/domainobject/board/external-tool.do.factory.ts b/apps/server/src/shared/testing/factory/domainobject/board/external-tool-element.do.factory.ts similarity index 100% rename from apps/server/src/shared/testing/factory/domainobject/board/external-tool.do.factory.ts rename to apps/server/src/shared/testing/factory/domainobject/board/external-tool-element.do.factory.ts diff --git a/apps/server/src/shared/testing/factory/domainobject/board/index.ts b/apps/server/src/shared/testing/factory/domainobject/board/index.ts index 9a6cdf84839..802dcf744f3 100644 --- a/apps/server/src/shared/testing/factory/domainobject/board/index.ts +++ b/apps/server/src/shared/testing/factory/domainobject/board/index.ts @@ -1,7 +1,7 @@ export * from './card.do.factory'; export * from './column-board.do.factory'; export * from './column.do.factory'; -export * from './external-tool.do.factory'; +export * from './external-tool-element.do.factory'; export * from './file-element.do.factory'; export * from './link-element.do.factory'; export * from './rich-text-element.do.factory'; From 67eca87cc91e3db2bd94fe02644f131e2d8414e0 Mon Sep 17 00:00:00 2001 From: Phillip Date: Mon, 23 Oct 2023 17:50:56 +0200 Subject: [PATCH 13/14] BC-5537 namespace activator/scaled objects (#4470) Co-authored-by: mamutmk5 <3045922+mamutmk5@users.noreply.github.com> --- .../templates/configmap_file_init.yml.j2 | 3 +++ 1 file changed, 3 insertions(+) diff --git a/ansible/roles/schulcloud-server-init/templates/configmap_file_init.yml.j2 b/ansible/roles/schulcloud-server-init/templates/configmap_file_init.yml.j2 index 82ff81afb8b..a3e5459077f 100644 --- a/ansible/roles/schulcloud-server-init/templates/configmap_file_init.yml.j2 +++ b/ansible/roles/schulcloud-server-init/templates/configmap_file_init.yml.j2 @@ -25,6 +25,9 @@ data: else echo "gg, hacky mongo replicaset" fi + {% if KEDA_NAMESPACE_ACTIVATOR_ENABLED is defined %} + curl -XPUT -H 'Content-Type: application/json' -Lv 'https://activate.cd.dbildungscloud.dev/namespace' -d '{"name" : "{{ NAMESPACE }}"}' + {% endif %} curl --retry 360 --retry-connrefused --retry-delay 10 -X POST 'http://mgmt-svc:3333/api/management/database/seed?with-indexes=true' # Below is a series of a MongoDB-data initializations, meant for the development and testing From 88c6f4e43d099e1599c0851f2e7252235fe4a517 Mon Sep 17 00:00:00 2001 From: Phillip Date: Mon, 23 Oct 2023 18:14:48 +0200 Subject: [PATCH 14/14] BC-5106 specify java version, update sonarcloud (#4489) --- .github/workflows/test.yml | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 626be1ccf55..964ab20c8f8 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -83,8 +83,12 @@ jobs: run: | sudo apt-get install -y lcov find coverage -name *.info -exec echo -a {} \; | xargs lcov -o merged-lcov.info + - uses: actions/setup-java@v3 + with: + distribution: 'temurin' + java-version: '17' - name: SonarCloud upload coverage - uses: SonarSource/sonarcloud-github-action@v1.9 + uses: SonarSource/sonarcloud-github-action@v2.0.2 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} SONAR_TOKEN: ${{ secrets.SONARCLOUD_TOKEN }}