From bc0c5cd31edcd9b181d28b2728b45bfe2a08838c Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Wed, 22 Nov 2023 11:02:11 +0100 Subject: [PATCH 01/24] get tools/context-types --- .../tool/common/uc/tool-permission-helper.ts | 7 ++++++- .../service/context-external-tool.service.ts | 8 ++++++++ .../dto/response/tool-context-types-list.ts | 11 +++++++++++ .../controller/tool-configuration.controller.ts | 17 +++++++++++++++++ .../uc/external-tool-configuration.uc.ts | 11 ++++++++++- 5 files changed, 52 insertions(+), 2 deletions(-) create mode 100644 apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts 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 525c8c5d3b6..363338e144f 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 @@ -4,7 +4,7 @@ import { BoardDoAuthorizableService, ContentElementService } from '@modules/boar 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 { BoardDoAuthorizable, Course, EntityId, LegacySchoolDo, Permission, User } from '@shared/domain'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { ToolContextType } from '../enum'; @@ -61,4 +61,9 @@ export class ToolPermissionHelper { this.authorizationService.checkPermission(user, school, context); } + + public async ensurePermission(userId: EntityId, permission: Permission) { + const user: User = await this.authorizationService.getUserWithPermissions(userId); + this.authorizationService.checkAllPermissions(user, [permission]); + } } 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 63618191810..e40b7f63bec 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 @@ -3,6 +3,8 @@ import { EntityId } from '@shared/domain'; import { ContextExternalToolRepo } from '@shared/repo'; import { ContextExternalTool, ContextRef } from '../domain'; import { ContextExternalToolQuery } from '../uc/dto/context-external-tool.types'; +import { ToolContextTypesList } from '../../external-tool/controller/dto/response/tool-context-types-list'; +import { ToolContextType } from '../../common/enum'; @Injectable() export class ContextExternalToolService { @@ -47,4 +49,10 @@ export class ContextExternalToolService { return contextExternalTools; } + + getToolContextTypes(): ToolContextTypesList { + const toolContextTypes: ToolContextTypesList = { data: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT] }; + + return toolContextTypes; + } } diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts b/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts new file mode 100644 index 00000000000..0977fb0397f --- /dev/null +++ b/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts @@ -0,0 +1,11 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { ToolContextType } from '../../../../common/enum'; + +export class ToolContextTypesList { + @ApiProperty({ type: [ToolContextType] }) + data: ToolContextType[]; + + constructor(data: ToolContextType[]) { + this.data = data; + } +} 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 5dbe2db8e9b..0a7fc6f1814 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 @@ -21,6 +21,8 @@ import { SchoolExternalToolIdParams, SchoolIdParams, } from './dto'; +import { ToolContextType } from '../../common/enum'; +import { ToolContextTypesList } from './dto/response/tool-context-types-list'; @ApiTags('Tool') @Authenticate('jwt') @@ -28,6 +30,21 @@ import { export class ToolConfigurationController { constructor(private readonly externalToolConfigurationUc: ExternalToolConfigurationUc) {} + @Get('context-types') + @ApiForbiddenResponse() + @ApiOperation({ summary: 'Lists all context types available in the SVS' }) + @ApiOkResponse({ + description: 'List of available context types', + type: ToolContextTypesList, + }) + public async getToolContextTypes(@CurrentUser() currentUser: ICurrentUser): Promise { + const toolContextTypes: ToolContextTypesList = await this.externalToolConfigurationUc.getToolContextTypes( + currentUser.userId + ); + + return toolContextTypes; + } + @Get('school/:schoolId/available-tools') @ApiForbiddenResponse() @ApiOperation({ summary: 'Lists all available tools that can be added for a given school' }) 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 f0c1af811fb..4bf3651fc1d 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 @@ -1,6 +1,6 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common/exceptions/not-found.exception'; -import { EntityId, Permission } from '@shared/domain'; +import { EntityId, Permission, User } from '@shared/domain'; import { Page } from '@shared/domain/domainobject/page'; import { AuthorizationContext, AuthorizationContextBuilder } from '@modules/authorization'; import { CustomParameterScope, ToolContextType } from '../../common/enum'; @@ -12,6 +12,7 @@ import { SchoolExternalToolService } from '../../school-external-tool/service'; import { ExternalTool } from '../domain'; import { ExternalToolConfigurationService, ExternalToolLogoService, ExternalToolService } from '../service'; import { ContextExternalToolTemplateInfo } from './dto'; +import { ToolContextTypesList } from '../controller/dto/response/tool-context-types-list'; @Injectable() export class ExternalToolConfigurationUc { @@ -25,6 +26,14 @@ export class ExternalToolConfigurationUc { private readonly externalToolLogoService: ExternalToolLogoService ) {} + public async getToolContextTypes(userId: EntityId): Promise { + await this.toolPermissionHelper.ensurePermission(userId, Permission.TOOL_ADMIN); + + const toolContextTypes: ToolContextTypesList = this.contextExternalToolService.getToolContextTypes(); + + return toolContextTypes; + } + public async getAvailableToolsForSchool(userId: EntityId, schoolId: EntityId): Promise { const externalTools: Page = await this.externalToolService.findExternalTools({}); From 2eabe715d77d3e92ebb9ee7bdc8351c86d10ca2f Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Wed, 22 Nov 2023 14:10:35 +0100 Subject: [PATCH 02/24] get tools/context-types unit tests --- .../common/uc/tool-permissions-helper.spec.ts | 20 ++++++++ .../context-external-tool.service.spec.ts | 11 +++++ .../service/context-external-tool.service.ts | 16 +++---- .../api-test/tool-configuration.api.spec.ts | 47 ++++++++++++++++++- .../uc/external-tool-configuration.uc.spec.ts | 26 ++++++++++ 5 files changed, 111 insertions(+), 9 deletions(-) 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 aace35579db..fd9c926ee98 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 @@ -247,4 +247,24 @@ describe('ToolPermissionHelper', () => { }); }); }); + + describe('ensurePermission', () => { + describe('when it is called', () => { + const setup = () => { + const user = userFactory.buildWithId(); + + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + + return { + user, + }; + }; + + it('should check permissions', async () => { + const { user } = setup(); + + await helper.ensurePermission(user.id, Permission.TOOL_ADMIN); + }); + }); + }); }); 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 60275c71d0d..0693c07e2c7 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 @@ -12,6 +12,7 @@ import { ToolContextType } from '../../common/enum'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { ContextExternalTool, ContextRef } from '../domain'; import { ContextExternalToolService } from './context-external-tool.service'; +import { ToolContextTypesList } from '../../external-tool/controller/dto/response/tool-context-types-list'; describe('ContextExternalToolService', () => { let module: TestingModule; @@ -216,4 +217,14 @@ describe('ContextExternalToolService', () => { }); }); }); + + describe('getToolContextTypes', () => { + describe('when it is called', () => { + it('should return ToolContextTypes', () => { + const types: ToolContextTypesList = service.getToolContextTypes(); + + expect(types).toEqual({ data: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT] }); + }); + }); + }); }); 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 e40b7f63bec..ea18ced5c86 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 @@ -10,25 +10,25 @@ import { ToolContextType } from '../../common/enum'; export class ContextExternalToolService { constructor(private readonly contextExternalToolRepo: ContextExternalToolRepo) {} - async findContextExternalTools(query: ContextExternalToolQuery): Promise { + public async findContextExternalTools(query: ContextExternalToolQuery): Promise { const contextExternalTools: ContextExternalTool[] = await this.contextExternalToolRepo.find(query); return contextExternalTools; } - async findById(contextExternalToolId: EntityId): Promise { + public async findById(contextExternalToolId: EntityId): Promise { const tool: ContextExternalTool = await this.contextExternalToolRepo.findById(contextExternalToolId); return tool; } - async saveContextExternalTool(contextExternalTool: ContextExternalTool): Promise { + public async saveContextExternalTool(contextExternalTool: ContextExternalTool): Promise { const savedContextExternalTool: ContextExternalTool = await this.contextExternalToolRepo.save(contextExternalTool); return savedContextExternalTool; } - async deleteBySchoolExternalToolId(schoolExternalToolId: EntityId) { + public async deleteBySchoolExternalToolId(schoolExternalToolId: EntityId) { const contextExternalTools: ContextExternalTool[] = await this.contextExternalToolRepo.find({ schoolToolRef: { schoolToolId: schoolExternalToolId, @@ -38,11 +38,11 @@ export class ContextExternalToolService { await this.contextExternalToolRepo.delete(contextExternalTools); } - async deleteContextExternalTool(contextExternalTool: ContextExternalTool): Promise { + public async deleteContextExternalTool(contextExternalTool: ContextExternalTool): Promise { await this.contextExternalToolRepo.delete(contextExternalTool); } - async findAllByContext(contextRef: ContextRef): Promise { + public async findAllByContext(contextRef: ContextRef): Promise { const contextExternalTools: ContextExternalTool[] = await this.contextExternalToolRepo.find({ context: contextRef, }); @@ -50,8 +50,8 @@ export class ContextExternalToolService { return contextExternalTools; } - getToolContextTypes(): ToolContextTypesList { - const toolContextTypes: ToolContextTypesList = { data: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT] }; + public getToolContextTypes(): ToolContextTypesList { + const toolContextTypes: ToolContextTypesList = { data: Object.values(ToolContextType) }; return toolContextTypes; } 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 68543982174..ee71cab8a0b 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 @@ -16,7 +16,7 @@ import { userFactory, } from '@shared/testing'; import { ServerTestModule } from '@modules/server'; -import { CustomParameterTypeParams } from '@modules/tool/common/enum'; +import { CustomParameterTypeParams, ToolContextType } from '@modules/tool/common/enum'; import { Response } from 'supertest'; import { CustomParameterLocationParams, CustomParameterScopeTypeParams } from '../../../common/enum'; import { ContextExternalToolEntity, ContextExternalToolType } from '../../../context-external-tool/entity'; @@ -633,8 +633,53 @@ describe('ToolConfigurationController (API)', () => { const response: Response = await loggedInClient.get( `context-external-tools/${contextExternalTool.id}/configuration-template` ); + expect(response.status).toEqual(HttpStatus.NOT_FOUND); }); }); }); + + describe('GET tools/context-types', () => { + describe('when user is not authorized', () => { + const setup = async () => { + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); + + await em.persistAndFlush([teacherAccount, teacherUser]); + em.clear(); + + const loggedInClient: TestApiClient = await testApiClient.login(teacherAccount); + + return { loggedInClient }; + }; + + it('should return unauthorized status', async () => { + const { loggedInClient } = await setup(); + + const response = await loggedInClient.get('context-types'); + + expect(response.status).toEqual(HttpStatus.UNAUTHORIZED); + }); + }); + + describe('when user is authorized', () => { + const setup = async () => { + const { adminAccount, adminUser } = UserAndAccountTestFactory.buildAdmin({}, [Permission.TOOL_ADMIN]); + + await em.persistAndFlush([adminAccount, adminUser]); + em.clear(); + + const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); + + return { loggedInClient }; + }; + + it('should return all context types', async () => { + const { loggedInClient } = await setup(); + + const response = await loggedInClient.get('context-types'); + + expect(response.body).toEqual({ data: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT] }); + }); + }); + }); }); 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 5327c8d4f80..526aba9fabc 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 @@ -650,4 +650,30 @@ describe('ExternalToolConfigurationUc', () => { }); }); }); + + describe('getToolContextTypes', () => { + describe('when it is called', () => { + const setup = () => { + const userId: string = new ObjectId().toHexString(); + + return { userId }; + }; + + it('should check Permission', async () => { + const { userId } = setup(); + + await uc.getToolContextTypes(userId); + + expect(toolPermissionHelper.ensurePermission).toHaveBeenCalledWith(userId, 'TOOL_ADMIN'); + }); + + it('should get context types', async () => { + const { userId } = setup(); + + await uc.getToolContextTypes(userId); + + expect(contextExternalToolService.getToolContextTypes).toHaveBeenCalled(); + }); + }); + }); }); From 8e52e0c73568354cbeb05988e5072e4d0e95da48 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Wed, 22 Nov 2023 15:48:18 +0100 Subject: [PATCH 03/24] extend external tool data model by restrictToContexts --- .../controller/dto/request/external-tool-create.params.ts | 5 +++++ .../controller/dto/request/external-tool-update.params.ts | 5 +++++ .../controller/dto/response/external-tool.response.ts | 5 +++++ .../modules/tool/external-tool/domain/external-tool.do.ts | 5 +++++ .../tool/external-tool/entity/external-tool.entity.ts | 5 +++++ .../external-tool/mapper/external-tool-request.mapper.ts | 2 ++ .../external-tool/mapper/external-tool-response.mapper.ts | 1 + .../modules/tool/external-tool/uc/dto/external-tool.types.ts | 3 +++ 8 files changed, 31 insertions(+) diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts index b094bbe7f04..1a50d381a4d 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts @@ -61,4 +61,9 @@ export class ExternalToolCreateParams { @IsBoolean() @ApiProperty() openNewTab!: boolean; + + @IsArray() + @IsOptional() + @ApiPropertyOptional() + restrictToContexts?: ToolConfigType[]; } diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts index 6d34f738e5c..4e2e70eb065 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts @@ -65,4 +65,9 @@ export class ExternalToolUpdateParams { @IsBoolean() @ApiProperty() openNewTab!: boolean; + + @IsArray() + @IsOptional() + @ApiPropertyOptional() + restrictToContexts?: ToolConfigType[]; } diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/external-tool.response.ts b/apps/server/src/modules/tool/external-tool/controller/dto/response/external-tool.response.ts index ef74247176d..f01208bcf55 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/response/external-tool.response.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/response/external-tool.response.ts @@ -1,6 +1,7 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { BasicToolConfigResponse, Oauth2ToolConfigResponse, Lti11ToolConfigResponse } from './config'; import { CustomParameterResponse } from './custom-parameter.response'; +import { ToolConfigType } from '../../../../common/enum'; export class ExternalToolResponse { @ApiProperty() @@ -30,6 +31,9 @@ export class ExternalToolResponse { @ApiProperty() version: number; + @ApiPropertyOptional() + restrictToContexts?: ToolConfigType[]; + constructor(response: ExternalToolResponse) { this.id = response.id; this.name = response.name; @@ -40,5 +44,6 @@ export class ExternalToolResponse { this.isHidden = response.isHidden; this.openNewTab = response.openNewTab; this.version = response.version; + this.restrictToContexts = response.restrictToContexts; } } diff --git a/apps/server/src/modules/tool/external-tool/domain/external-tool.do.ts b/apps/server/src/modules/tool/external-tool/domain/external-tool.do.ts index ba9f1d6e84d..d939d50b236 100644 --- a/apps/server/src/modules/tool/external-tool/domain/external-tool.do.ts +++ b/apps/server/src/modules/tool/external-tool/domain/external-tool.do.ts @@ -24,6 +24,8 @@ export interface ExternalToolProps { openNewTab: boolean; version: number; + + restrictToContexts?: ToolConfigType[]; } export class ExternalTool extends BaseDO implements ToolVersion { @@ -45,6 +47,8 @@ export class ExternalTool extends BaseDO implements ToolVersion { version: number; + restrictToContexts?: ToolConfigType[]; + constructor(props: ExternalToolProps) { super(props.id); @@ -57,6 +61,7 @@ export class ExternalTool extends BaseDO implements ToolVersion { this.isHidden = props.isHidden; this.openNewTab = props.openNewTab; this.version = props.version; + this.restrictToContexts = props.restrictToContexts; } getVersion(): number { diff --git a/apps/server/src/modules/tool/external-tool/entity/external-tool.entity.ts b/apps/server/src/modules/tool/external-tool/entity/external-tool.entity.ts index 481ed3b7c2d..124980e18f9 100644 --- a/apps/server/src/modules/tool/external-tool/entity/external-tool.entity.ts +++ b/apps/server/src/modules/tool/external-tool/entity/external-tool.entity.ts @@ -3,6 +3,7 @@ import { Embedded, Entity, Property, Unique } from '@mikro-orm/core'; import { BaseEntityWithTimestamps } from '@shared/domain/entity/base.entity'; import { CustomParameterEntity } from './custom-parameter'; import { BasicToolConfigEntity, Lti11ToolConfigEntity, Oauth2ToolConfigEntity } from './config'; +import { ToolConfigType } from '../../common/enum'; export type IExternalToolProperties = Readonly>; @@ -36,6 +37,9 @@ export class ExternalToolEntity extends BaseEntityWithTimestamps { @Property() version: number; + @Property({ nullable: true }) + restrictToContexts?: ToolConfigType[]; + constructor(props: IExternalToolProperties) { super(); this.name = props.name; @@ -47,5 +51,6 @@ export class ExternalToolEntity extends BaseEntityWithTimestamps { this.isHidden = props.isHidden; this.openNewTab = props.openNewTab; this.version = props.version; + this.restrictToContexts = props.restrictToContexts; } } diff --git a/apps/server/src/modules/tool/external-tool/mapper/external-tool-request.mapper.ts b/apps/server/src/modules/tool/external-tool/mapper/external-tool-request.mapper.ts index eed114902a0..6b1f1bdc60c 100644 --- a/apps/server/src/modules/tool/external-tool/mapper/external-tool-request.mapper.ts +++ b/apps/server/src/modules/tool/external-tool/mapper/external-tool-request.mapper.ts @@ -81,6 +81,7 @@ export class ExternalToolRequestMapper { isHidden: externalToolUpdateParams.isHidden, openNewTab: externalToolUpdateParams.openNewTab, version, + restrictToContexts: externalToolUpdateParams.restrictToContexts, }; } @@ -107,6 +108,7 @@ export class ExternalToolRequestMapper { isHidden: externalToolCreateParams.isHidden, openNewTab: externalToolCreateParams.openNewTab, version, + restrictToContexts: externalToolCreateParams.restrictToContexts, }; } 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 b2035e66477..eadbc20c50a 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 @@ -65,6 +65,7 @@ export class ExternalToolResponseMapper { isHidden: externalTool.isHidden, openNewTab: externalTool.openNewTab, version: externalTool.version, + restrictToContexts: externalTool.restrictToContexts, }); } diff --git a/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts b/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts index 1c086cb9c96..4ef7fdc86d3 100644 --- a/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts +++ b/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts @@ -1,5 +1,6 @@ import { BasicToolConfig, Lti11ToolConfig, Oauth2ToolConfig } from '../../domain'; import { CustomParameter } from '../../../common/domain'; +import { ToolConfigType } from '../../../common/enum'; type PartialBy = Omit & Partial>; @@ -33,6 +34,8 @@ export type ExternalToolDto = { openNewTab: boolean; version: number; + + restrictToContexts?: ToolConfigType[]; }; export type ExternalToolCreate = ExternalToolDto; From 9b1b4e9324fc62d00450fbeed1d4e0c00fbdd567 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Wed, 22 Nov 2023 16:10:02 +0100 Subject: [PATCH 04/24] fix dependency cycle WIP --- .../service/context-external-tool.service.spec.ts | 10 ---------- .../service/context-external-tool.service.ts | 6 ------ .../external-tool/controller/dto/response/index.ts | 1 + .../controller/tool-configuration.controller.ts | 3 +-- .../external-tool-configuration.service.spec.ts | 13 ++++++++++++- .../service/external-tool-configuration.service.ts | 9 ++++++++- .../uc/external-tool-configuration.uc.spec.ts | 2 +- .../uc/external-tool-configuration.uc.ts | 2 +- 8 files changed, 24 insertions(+), 22 deletions(-) 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 0693c07e2c7..d5568def5b8 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 @@ -217,14 +217,4 @@ describe('ContextExternalToolService', () => { }); }); }); - - describe('getToolContextTypes', () => { - describe('when it is called', () => { - it('should return ToolContextTypes', () => { - const types: ToolContextTypesList = service.getToolContextTypes(); - - expect(types).toEqual({ data: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT] }); - }); - }); - }); }); 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 ea18ced5c86..37cc4fde3d0 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 @@ -49,10 +49,4 @@ export class ContextExternalToolService { return contextExternalTools; } - - public getToolContextTypes(): ToolContextTypesList { - const toolContextTypes: ToolContextTypesList = { data: Object.values(ToolContextType) }; - - return toolContextTypes; - } } 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 4f532238863..620a0abaf6f 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 @@ -7,3 +7,4 @@ export * from './context-external-tool-configuration-template-list.response'; export * from './school-external-tool-configuration-template.response'; export * from './school-external-tool-configuration-template-list.response'; export * from './external-tool-metadata.response'; +export * from './tool-context-types-list'; 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 0a7fc6f1814..569fb58a233 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 @@ -20,9 +20,8 @@ import { SchoolExternalToolConfigurationTemplateResponse, SchoolExternalToolIdParams, SchoolIdParams, + ToolContextTypesList, } from './dto'; -import { ToolContextType } from '../../common/enum'; -import { ToolContextTypesList } from './dto/response/tool-context-types-list'; @ApiTags('Tool') @Authenticate('jwt') diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts index 6d0064c1136..2ae332fba26 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts @@ -8,13 +8,14 @@ import { setupEntities, } from '@shared/testing'; import { CustomParameter } from '../../common/domain'; -import { CustomParameterScope } from '../../common/enum'; +import { CustomParameterScope, ToolContextType } from '../../common/enum'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; import { ContextExternalToolTemplateInfo } from '../uc'; import { ExternalToolConfigurationService } from './external-tool-configuration.service'; +import { ToolContextTypesList } from '../controller/dto/response/tool-context-types-list'; describe('ExternalToolConfigurationService', () => { let module: TestingModule; @@ -210,4 +211,14 @@ describe('ExternalToolConfigurationService', () => { }); }); }); + + describe('getToolContextTypes', () => { + describe('when it is called', () => { + it('should return ToolContextTypes', () => { + const types: ToolContextTypesList = service.getToolContextTypes(); + + expect(types).toEqual({ data: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT] }); + }); + }); + }); }); diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts index 3d021c592fc..9a8cf6a8164 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts @@ -1,12 +1,13 @@ import { Inject, Injectable } from '@nestjs/common'; import { EntityId, Page } from '@shared/domain'; import { CustomParameter } from '../../common/domain'; -import { CustomParameterScope } from '../../common/enum'; +import { CustomParameterScope, ToolContextType } from '../../common/enum'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; import { ContextExternalToolTemplateInfo } from '../uc/dto'; +import { ToolContextTypesList } from '../controller/dto/response/tool-context-types-list'; @Injectable() export class ExternalToolConfigurationService { @@ -81,4 +82,10 @@ export class ExternalToolConfigurationService { ); } } + + public getToolContextTypes(): ToolContextTypesList { + const toolContextTypes: ToolContextTypesList = { data: Object.values(ToolContextType) }; + + return toolContextTypes; + } } 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 526aba9fabc..7443bd9d8ab 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 @@ -672,7 +672,7 @@ describe('ExternalToolConfigurationUc', () => { await uc.getToolContextTypes(userId); - expect(contextExternalToolService.getToolContextTypes).toHaveBeenCalled(); + expect(externalToolConfigurationService.getToolContextTypes).toHaveBeenCalled(); }); }); }); 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 4bf3651fc1d..f4a962c8577 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 @@ -29,7 +29,7 @@ export class ExternalToolConfigurationUc { public async getToolContextTypes(userId: EntityId): Promise { await this.toolPermissionHelper.ensurePermission(userId, Permission.TOOL_ADMIN); - const toolContextTypes: ToolContextTypesList = this.contextExternalToolService.getToolContextTypes(); + const toolContextTypes: ToolContextTypesList = this.externalToolConfigurationService.getToolContextTypes(); return toolContextTypes; } From 59c9e2228776bf7f8bae9fc0a3d8294615462c8e Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Wed, 22 Nov 2023 16:33:36 +0100 Subject: [PATCH 05/24] fix dependency cycle --- .../controller/dto/response/tool-context-types-list.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts b/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts index 0977fb0397f..e6c735e5604 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts @@ -1,8 +1,8 @@ import { ApiProperty } from '@nestjs/swagger'; -import { ToolContextType } from '../../../../common/enum'; +import { ToolContextType } from '../../../../common/enum/tool-context-type.enum'; export class ToolContextTypesList { - @ApiProperty({ type: [ToolContextType] }) + @ApiProperty({ enum: ToolContextType, enumName: 'ToolContextType', isArray: true }) data: ToolContextType[]; constructor(data: ToolContextType[]) { From 481ebbb16cb219348d9a3bc5c6d0600cdd541765 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Thu, 23 Nov 2023 11:19:32 +0100 Subject: [PATCH 06/24] fix swagger and nuxt types --- .../controller/dto/request/external-tool-create.params.ts | 6 +++--- .../controller/dto/request/external-tool-update.params.ts | 6 +++--- .../controller/dto/response/external-tool.response.ts | 6 +++--- .../controller/dto/response/tool-context-types-list.ts | 2 +- .../modules/tool/external-tool/domain/external-tool.do.ts | 6 +++--- .../tool/external-tool/entity/external-tool.entity.ts | 4 ++-- .../tool/external-tool/uc/dto/external-tool.types.ts | 4 ++-- 7 files changed, 17 insertions(+), 17 deletions(-) diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts index 1a50d381a4d..8cf438ed718 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts @@ -1,7 +1,7 @@ import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsArray, IsBoolean, IsOptional, IsString, ValidateNested } from 'class-validator'; -import { ToolConfigType } from '../../../../common/enum'; +import { ToolConfigType, ToolContextType } from '../../../../common/enum'; import { BasicToolConfigParams, ExternalToolConfigCreateParams, @@ -64,6 +64,6 @@ export class ExternalToolCreateParams { @IsArray() @IsOptional() - @ApiPropertyOptional() - restrictToContexts?: ToolConfigType[]; + @ApiPropertyOptional({ enum: ToolContextType, enumName: 'ToolContextType' }) + restrictToContexts?: ToolContextType[]; } diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts index 4e2e70eb065..65a579b0c2d 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts @@ -1,7 +1,7 @@ import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; import { Type } from 'class-transformer'; import { IsArray, IsBoolean, IsOptional, IsString, ValidateNested } from 'class-validator'; -import { ToolConfigType } from '../../../../common/enum'; +import { ToolConfigType, ToolContextType } from '../../../../common/enum'; import { BasicToolConfigParams, ExternalToolConfigCreateParams, @@ -68,6 +68,6 @@ export class ExternalToolUpdateParams { @IsArray() @IsOptional() - @ApiPropertyOptional() - restrictToContexts?: ToolConfigType[]; + @ApiPropertyOptional({ enum: ToolContextType, enumName: 'ToolContextType' }) + restrictToContexts?: ToolContextType[]; } diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/external-tool.response.ts b/apps/server/src/modules/tool/external-tool/controller/dto/response/external-tool.response.ts index f01208bcf55..dc20b85b520 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/response/external-tool.response.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/response/external-tool.response.ts @@ -1,7 +1,7 @@ import { ApiProperty, ApiPropertyOptional } from '@nestjs/swagger'; import { BasicToolConfigResponse, Oauth2ToolConfigResponse, Lti11ToolConfigResponse } from './config'; import { CustomParameterResponse } from './custom-parameter.response'; -import { ToolConfigType } from '../../../../common/enum'; +import { ToolContextType } from '../../../../common/enum'; export class ExternalToolResponse { @ApiProperty() @@ -31,8 +31,8 @@ export class ExternalToolResponse { @ApiProperty() version: number; - @ApiPropertyOptional() - restrictToContexts?: ToolConfigType[]; + @ApiPropertyOptional({ enum: ToolContextType, enumName: 'ToolContextType', isArray: true }) + restrictToContexts?: ToolContextType[]; constructor(response: ExternalToolResponse) { this.id = response.id; diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts b/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts index e6c735e5604..c45accc99b1 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts @@ -1,5 +1,5 @@ import { ApiProperty } from '@nestjs/swagger'; -import { ToolContextType } from '../../../../common/enum/tool-context-type.enum'; +import { ToolContextType } from '../../../../common/enum'; export class ToolContextTypesList { @ApiProperty({ enum: ToolContextType, enumName: 'ToolContextType', isArray: true }) diff --git a/apps/server/src/modules/tool/external-tool/domain/external-tool.do.ts b/apps/server/src/modules/tool/external-tool/domain/external-tool.do.ts index d939d50b236..28daa0e975f 100644 --- a/apps/server/src/modules/tool/external-tool/domain/external-tool.do.ts +++ b/apps/server/src/modules/tool/external-tool/domain/external-tool.do.ts @@ -2,7 +2,7 @@ import { BaseDO } from '@shared/domain/domainobject/base.do'; import { ToolVersion } from '../../common/interface'; import { Oauth2ToolConfig, BasicToolConfig, Lti11ToolConfig, ExternalToolConfig } from './config'; import { CustomParameter } from '../../common/domain'; -import { ToolConfigType } from '../../common/enum'; +import { ToolConfigType, ToolContextType } from '../../common/enum'; export interface ExternalToolProps { id?: string; @@ -25,7 +25,7 @@ export interface ExternalToolProps { version: number; - restrictToContexts?: ToolConfigType[]; + restrictToContexts?: ToolContextType[]; } export class ExternalTool extends BaseDO implements ToolVersion { @@ -47,7 +47,7 @@ export class ExternalTool extends BaseDO implements ToolVersion { version: number; - restrictToContexts?: ToolConfigType[]; + restrictToContexts?: ToolContextType[]; constructor(props: ExternalToolProps) { super(props.id); diff --git a/apps/server/src/modules/tool/external-tool/entity/external-tool.entity.ts b/apps/server/src/modules/tool/external-tool/entity/external-tool.entity.ts index 124980e18f9..8a80404fe14 100644 --- a/apps/server/src/modules/tool/external-tool/entity/external-tool.entity.ts +++ b/apps/server/src/modules/tool/external-tool/entity/external-tool.entity.ts @@ -3,7 +3,7 @@ import { Embedded, Entity, Property, Unique } from '@mikro-orm/core'; import { BaseEntityWithTimestamps } from '@shared/domain/entity/base.entity'; import { CustomParameterEntity } from './custom-parameter'; import { BasicToolConfigEntity, Lti11ToolConfigEntity, Oauth2ToolConfigEntity } from './config'; -import { ToolConfigType } from '../../common/enum'; +import { ToolContextType } from '../../common/enum'; export type IExternalToolProperties = Readonly>; @@ -38,7 +38,7 @@ export class ExternalToolEntity extends BaseEntityWithTimestamps { version: number; @Property({ nullable: true }) - restrictToContexts?: ToolConfigType[]; + restrictToContexts?: ToolContextType[]; constructor(props: IExternalToolProperties) { super(); diff --git a/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts b/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts index 4ef7fdc86d3..12daf9b5f1c 100644 --- a/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts +++ b/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts @@ -1,6 +1,6 @@ import { BasicToolConfig, Lti11ToolConfig, Oauth2ToolConfig } from '../../domain'; import { CustomParameter } from '../../../common/domain'; -import { ToolConfigType } from '../../../common/enum'; +import { ToolConfigType, ToolContextType } from '../../../common/enum'; type PartialBy = Omit & Partial>; @@ -35,7 +35,7 @@ export type ExternalToolDto = { version: number; - restrictToContexts?: ToolConfigType[]; + restrictToContexts?: ToolContextType[]; }; export type ExternalToolCreate = ExternalToolDto; From 69ed45e02068f5860cab37a433afdda55ed6f901 Mon Sep 17 00:00:00 2001 From: Malte Berg Date: Thu, 23 Nov 2023 16:38:41 +0100 Subject: [PATCH 07/24] add attribute to repo mapper --- .../src/shared/repo/externaltool/external-tool.repo.mapper.ts | 2 ++ 1 file changed, 2 insertions(+) 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 ab35c7b5fda..7e072b9c66a 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 @@ -42,6 +42,7 @@ export class ExternalToolRepoMapper { isHidden: entity.isHidden, openNewTab: entity.openNewTab, version: entity.version, + restrictToContexts: entity.restrictToContexts, }); } @@ -101,6 +102,7 @@ export class ExternalToolRepoMapper { isHidden: entityDO.isHidden, openNewTab: entityDO.openNewTab, version: entityDO.version, + restrictToContexts: entityDO.restrictToContexts, }; } From 435f9b2421118484527c3f097de62e9953bb56f8 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Fri, 24 Nov 2023 12:14:28 +0100 Subject: [PATCH 08/24] available tools filter for contexts --- .../api-test/tool-configuration.api.spec.ts | 64 ++++++++++++++- ...xternal-tool-configuration.service.spec.ts | 82 +++++++++++++++++++ .../external-tool-configuration.service.ts | 13 +++ .../uc/external-tool-configuration.uc.spec.ts | 11 +++ .../uc/external-tool-configuration.uc.ts | 11 ++- .../externaltool/external-tool.repo.mapper.ts | 2 + 6 files changed, 176 insertions(+), 7 deletions(-) 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 ee71cab8a0b..201999ad182 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 @@ -2,9 +2,10 @@ import { EntityManager, MikroORM } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { Account, Course, Permission, SchoolEntity, User } from '@shared/domain'; +import { Account, Board, Course, Permission, SchoolEntity, User } from '@shared/domain'; import { accountFactory, + boardFactory, contextExternalToolEntityFactory, courseFactory, customParameterFactory, @@ -111,7 +112,7 @@ describe('ToolConfigurationController (API)', () => { }); }); - describe('when tools are available for a course', () => { + describe('when tools are available for a context', () => { const setup = async () => { const school: SchoolEntity = schoolFactory.buildWithId(); @@ -120,35 +121,65 @@ describe('ToolConfigurationController (API)', () => { ]); const course: Course = courseFactory.buildWithId({ teachers: [teacherUser], school }); + const board: Board = boardFactory.buildWithId({ course }); const [globalParameter, schoolParameter, contextParameter] = customParameterFactory.buildListWithEachType(); const externalTool: ExternalToolEntity = externalToolEntityFactory.buildWithId({ + restrictToContexts: [ToolContextType.COURSE], logoBase64: 'logo', parameters: [globalParameter, schoolParameter, contextParameter], }); externalTool.logoUrl = `http://localhost:3030/api/v3/tools/external-tools/${externalTool.id}/logo`; + const externalToolWithoutContextRestriction: ExternalToolEntity = externalToolEntityFactory.buildWithId(); + const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ school, tool: externalTool, }); - await em.persistAndFlush([school, course, teacherUser, teacherAccount, externalTool, schoolExternalTool]); + const schoolExternalTool2: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + school, + tool: externalToolWithoutContextRestriction, + }); + + await em.persistAndFlush([ + school, + course, + board, + teacherUser, + teacherAccount, + externalTool, + externalToolWithoutContextRestriction, + schoolExternalTool, + schoolExternalTool2, + ]); em.clear(); const loggedInClient: TestApiClient = await testApiClient.login(teacherAccount); return { course, + board, externalTool, + externalToolWithoutContextRestriction, schoolExternalTool, + schoolExternalTool2, contextParameter, loggedInClient, }; }; it('should return an array of available tools with parameters of scope context', async () => { - const { course, externalTool, contextParameter, schoolExternalTool, loggedInClient } = await setup(); + const { + course, + externalTool, + externalToolWithoutContextRestriction, + contextParameter, + schoolExternalTool, + schoolExternalTool2, + loggedInClient, + } = await setup(); const response: Response = await loggedInClient.get(`course/${course.id}/available-tools`); @@ -175,6 +206,31 @@ describe('ToolConfigurationController (API)', () => { ], version: externalTool.version, }, + { + externalToolId: externalToolWithoutContextRestriction.id, + name: externalToolWithoutContextRestriction.name, + parameters: [], + schoolExternalToolId: schoolExternalTool2.id, + version: externalToolWithoutContextRestriction.version, + }, + ], + }); + }); + + it('should not return context restricted tool', async () => { + const { board, loggedInClient, externalToolWithoutContextRestriction, schoolExternalTool2 } = await setup(); + + const response: Response = await loggedInClient.get(`board-element/${board.id}/available-tools`); + + expect(response.body).toEqual({ + data: [ + { + externalToolId: externalToolWithoutContextRestriction.id, + name: externalToolWithoutContextRestriction.name, + parameters: [], + schoolExternalToolId: schoolExternalTool2.id, + version: externalToolWithoutContextRestriction.version, + }, ], }); }); diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts index 2ae332fba26..0f48b03570d 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts @@ -187,6 +187,88 @@ describe('ExternalToolConfigurationService', () => { }); }); + describe('filterForContextRestrictions', () => { + describe('when tool has no context restrictions', () => { + const setup = () => { + const contextType = ToolContextType.COURSE; + + const externalTool: ExternalTool = externalToolFactory.build(); + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); + + const availableTools: ContextExternalToolTemplateInfo[] = [{ externalTool, schoolExternalTool }]; + + return { + contextType, + availableTools, + }; + }; + + it('should pass the filter', () => { + const { contextType, availableTools } = setup(); + + const result: ContextExternalToolTemplateInfo[] = service.filterForContextRestrictions( + availableTools, + contextType + ); + + expect(result).toEqual(availableTools); + }); + }); + + describe('when context restrictions are given', () => { + const setup = () => { + const contextType = ToolContextType.COURSE; + + const externalToolWithCourseRestriction: ExternalTool = externalToolFactory.build({ + restrictToContexts: [ToolContextType.COURSE], + }); + const externalToolWithBoardRestriction: ExternalTool = externalToolFactory.build({ + restrictToContexts: [ToolContextType.BOARD_ELEMENT], + }); + + const externalToolWithAllRestrictions: ExternalTool = externalToolFactory.build({ + restrictToContexts: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT], + }); + + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); + + const availableTools: ContextExternalToolTemplateInfo[] = [ + { externalTool: externalToolWithCourseRestriction, schoolExternalTool }, + { externalTool: externalToolWithBoardRestriction, schoolExternalTool }, + { externalTool: externalToolWithAllRestrictions, schoolExternalTool }, + ]; + + return { + contextType, + availableTools, + externalToolWithCourseRestriction, + externalToolWithAllRestrictions, + schoolExternalTool, + }; + }; + + it('should only return tools restricted to this context', () => { + const { + contextType, + availableTools, + externalToolWithCourseRestriction, + externalToolWithAllRestrictions, + schoolExternalTool, + } = setup(); + + const result: ContextExternalToolTemplateInfo[] = service.filterForContextRestrictions( + availableTools, + contextType + ); + + expect(result).toEqual([ + { externalTool: externalToolWithCourseRestriction, schoolExternalTool }, + { externalTool: externalToolWithAllRestrictions, schoolExternalTool }, + ]); + }); + }); + }); + describe('filterParametersForScope', () => { describe('when filtering parameters for scope', () => { const setup = () => { diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts index 9a8cf6a8164..785d5c2c9ed 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts @@ -75,6 +75,19 @@ export class ExternalToolConfigurationService { return availableTools; } + public filterForContextRestrictions( + availableTools: ContextExternalToolTemplateInfo[], + contextType: ToolContextType + ): ContextExternalToolTemplateInfo[] { + const availableToolsForContext: ContextExternalToolTemplateInfo[] = availableTools.filter((availableTool) => { + if (availableTool.externalTool.restrictToContexts) { + return availableTool.externalTool.restrictToContexts.includes(contextType); + } + return true; + }); + return availableToolsForContext; + } + public filterParametersForScope(externalTool: ExternalTool, scope: CustomParameterScope) { if (externalTool.parameters) { externalTool.parameters = externalTool.parameters.filter( 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 7443bd9d8ab..80b632e073e 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 @@ -325,6 +325,17 @@ describe('ExternalToolConfigurationUc', () => { ); }); + it('should filter for restricted contexts', async () => { + const { usedTool, usedSchoolExternalTool } = setup(); + + await uc.getAvailableToolsForContext('userId', 'schoolId', 'contextId', ToolContextType.COURSE); + + expect(externalToolConfigurationService.filterForContextRestrictions).toHaveBeenCalledWith( + [{ externalTool: usedTool, schoolExternalTool: usedSchoolExternalTool }], + ToolContextType.COURSE + ); + }); + it('should call filterParametersForScope', async () => { const { usedTool } = setup(); 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 f4a962c8577..fdb06194186 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 @@ -1,6 +1,6 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common/exceptions/not-found.exception'; -import { EntityId, Permission, User } from '@shared/domain'; +import { EntityId, Permission } from '@shared/domain'; import { Page } from '@shared/domain/domainobject/page'; import { AuthorizationContext, AuthorizationContextBuilder } from '@modules/authorization'; import { CustomParameterScope, ToolContextType } from '../../common/enum'; @@ -12,7 +12,7 @@ import { SchoolExternalToolService } from '../../school-external-tool/service'; import { ExternalTool } from '../domain'; import { ExternalToolConfigurationService, ExternalToolLogoService, ExternalToolService } from '../service'; import { ContextExternalToolTemplateInfo } from './dto'; -import { ToolContextTypesList } from '../controller/dto/response/tool-context-types-list'; +import { ToolContextTypesList } from '../controller/dto/response'; @Injectable() export class ExternalToolConfigurationUc { @@ -99,12 +99,17 @@ export class ExternalToolConfigurationUc { contextExternalToolsInUse ); - const availableToolsForContext: ContextExternalToolTemplateInfo[] = + let availableToolsForContext: ContextExternalToolTemplateInfo[] = this.externalToolConfigurationService.filterForAvailableExternalTools( externalTools.data, availableSchoolExternalTools ); + availableToolsForContext = this.externalToolConfigurationService.filterForContextRestrictions( + availableToolsForContext, + contextType + ); + availableToolsForContext.forEach((toolTemplateInfo) => { this.externalToolConfigurationService.filterParametersForScope( toolTemplateInfo.externalTool, 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 ab35c7b5fda..7e072b9c66a 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 @@ -42,6 +42,7 @@ export class ExternalToolRepoMapper { isHidden: entity.isHidden, openNewTab: entity.openNewTab, version: entity.version, + restrictToContexts: entity.restrictToContexts, }); } @@ -101,6 +102,7 @@ export class ExternalToolRepoMapper { isHidden: entityDO.isHidden, openNewTab: entityDO.openNewTab, version: entityDO.version, + restrictToContexts: entityDO.restrictToContexts, }; } From 134390fdaef0d08ed457aef771061238f6895d1c Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Fri, 24 Nov 2023 15:17:10 +0100 Subject: [PATCH 09/24] create context external tool validation for restricted contexts --- .../api-test/tool-context.api.spec.ts | 110 ++++++++++++++ .../context-external-tool.service.spec.ts | 135 +++++++++++++++++- .../service/context-external-tool.service.ts | 31 +++- .../uc/context-external-tool.uc.spec.ts | 16 +++ .../uc/context-external-tool.uc.ts | 2 + .../api-test/tool-configuration.api.spec.ts | 4 +- ...xternal-tool-configuration.service.spec.ts | 2 +- .../external-tool-configuration.service.ts | 2 +- 8 files changed, 294 insertions(+), 8 deletions(-) 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 af14eb379f3..0f4d8285039 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 @@ -80,6 +80,7 @@ describe('ToolContextController (API)', () => { isOptional: true, }), ], + restrictToContexts: [ToolContextType.COURSE], version: 1, }); const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ @@ -173,6 +174,115 @@ describe('ToolContextController (API)', () => { // expected body is missed }); }); + + describe('when external tool has no restrictions ', () => { + const setup = async () => { + const school: SchoolEntity = schoolFactory.buildWithId(); + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher({ school }, [ + Permission.CONTEXT_TOOL_ADMIN, + ]); + + const course: Course = courseFactory.buildWithId({ teachers: [teacherUser], school }); + + const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ + parameters: [], + restrictToContexts: [], + version: 1, + }); + const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + tool: externalToolEntity, + school, + schoolParameters: [], + toolVersion: 1, + }); + + const postParams: ContextExternalToolPostParams = { + schoolToolId: schoolExternalToolEntity.id, + contextId: course.id, + displayName: course.name, + contextType: ToolContextType.COURSE, + parameters: [], + toolVersion: 1, + }; + + await em.persistAndFlush([teacherUser, teacherAccount, course, school, schoolExternalToolEntity]); + em.clear(); + + const loggedInClient: TestApiClient = await testApiClient.login(teacherAccount); + + return { + loggedInClient, + postParams, + }; + }; + + it('should create tool', async () => { + const { postParams, loggedInClient } = await setup(); + + const response = await loggedInClient.post().send(postParams); + + expect(response.statusCode).toEqual(HttpStatus.CREATED); + expect(response.body).toEqual({ + id: expect.any(String), + schoolToolId: postParams.schoolToolId, + contextId: postParams.contextId, + displayName: postParams.displayName, + contextType: postParams.contextType, + parameters: [], + toolVersion: postParams.toolVersion, + }); + }); + }); + + describe('when external tool restricts to wrong context ', () => { + const setup = async () => { + const school: SchoolEntity = schoolFactory.buildWithId(); + const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher({ school }, [ + Permission.CONTEXT_TOOL_ADMIN, + ]); + + const course: Course = courseFactory.buildWithId({ teachers: [teacherUser], school }); + + const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ + parameters: [], + restrictToContexts: [ToolContextType.BOARD_ELEMENT], + version: 1, + }); + const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ + tool: externalToolEntity, + school, + schoolParameters: [], + toolVersion: 1, + }); + + const postParams: ContextExternalToolPostParams = { + schoolToolId: schoolExternalToolEntity.id, + contextId: course.id, + displayName: course.name, + contextType: ToolContextType.COURSE, + parameters: [], + toolVersion: 1, + }; + + await em.persistAndFlush([teacherUser, teacherAccount, course, school, schoolExternalToolEntity]); + em.clear(); + + const loggedInClient: TestApiClient = await testApiClient.login(teacherAccount); + + return { + loggedInClient, + postParams, + }; + }; + + it('should return forbidden', async () => { + const { postParams, loggedInClient } = await setup(); + + const response = await loggedInClient.post().send(postParams); + + expect(response.statusCode).toEqual(HttpStatus.FORBIDDEN); + }); + }); }); describe('[DELETE] tools/context-external-tools/:contextExternalToolId', () => { 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 d5568def5b8..ed27a5201d2 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 @@ -4,19 +4,31 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ContextExternalToolRepo } from '@shared/repo'; import { contextExternalToolFactory, + externalToolFactory, legacySchoolDoFactory, schoolExternalToolFactory, } from '@shared/testing/factory/domainobject'; -import { AuthorizationService } from '@modules/authorization'; +import { + AuthorizationContext, + AuthorizationContextBuilder, + AuthorizationService, + ForbiddenLoggableException, +} from '@modules/authorization'; +import { ObjectId } from '@mikro-orm/mongodb'; +import { Permission } from '@shared/domain'; import { ToolContextType } from '../../common/enum'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { ContextExternalTool, ContextRef } from '../domain'; import { ContextExternalToolService } from './context-external-tool.service'; -import { ToolContextTypesList } from '../../external-tool/controller/dto/response/tool-context-types-list'; +import { ExternalTool } from '../../external-tool/domain'; +import { ExternalToolService } from '../../external-tool/service'; +import { SchoolExternalToolService } from '../../school-external-tool/service'; describe('ContextExternalToolService', () => { let module: TestingModule; let service: ContextExternalToolService; + let externalToolService: DeepMocked; + let schoolExternalToolService: DeepMocked; let contextExternalToolRepo: DeepMocked; @@ -32,11 +44,21 @@ describe('ContextExternalToolService', () => { provide: AuthorizationService, useValue: createMock(), }, + { + provide: ExternalToolService, + useValue: createMock(), + }, + { + provide: SchoolExternalToolService, + useValue: createMock(), + }, ], }).compile(); service = module.get(ContextExternalToolService); contextExternalToolRepo = module.get(ContextExternalToolRepo); + externalToolService = module.get(ExternalToolService); + schoolExternalToolService = module.get(SchoolExternalToolService); }); afterAll(async () => { @@ -217,4 +239,113 @@ describe('ContextExternalToolService', () => { }); }); }); + + describe('checkContextRestrictions', () => { + describe('when contexts are not restricted', () => { + const setup = () => { + const userId = new ObjectId().toHexString(); + const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); + + const externalTool: ExternalTool = externalToolFactory.build({ restrictToContexts: [] }); + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.build(); + + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + externalToolService.findById.mockResolvedValueOnce(externalTool); + + return { + userId, + context, + contextExternalTool, + schoolExternalTool, + }; + }; + + it('should find SchoolExternalTool', async () => { + const { userId, context, contextExternalTool } = setup(); + + await service.checkContextRestrictions(contextExternalTool, userId, context); + + expect(schoolExternalToolService.findById).toHaveBeenCalledWith(contextExternalTool.schoolToolRef.schoolToolId); + }); + + it('should find ExternalTool', async () => { + const { userId, context, contextExternalTool, schoolExternalTool } = setup(); + + await service.checkContextRestrictions(contextExternalTool, userId, context); + + expect(externalToolService.findById).toHaveBeenCalledWith(schoolExternalTool.toolId); + }); + + it('should not throw', async () => { + const { userId, context, contextExternalTool } = setup(); + + const func = async () => service.checkContextRestrictions(contextExternalTool, userId, context); + + await expect(func()).resolves.not.toThrow(); + }); + }); + + describe('when context is restricted to correct context type', () => { + const setup = () => { + const userId = new ObjectId().toHexString(); + const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); + + const externalTool: ExternalTool = externalToolFactory.build({ restrictToContexts: [ToolContextType.COURSE] }); + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.build({ + contextRef: { type: ToolContextType.COURSE }, + }); + + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + externalToolService.findById.mockResolvedValueOnce(externalTool); + + return { + userId, + context, + contextExternalTool, + }; + }; + + it('should not throw', async () => { + const { userId, context, contextExternalTool } = setup(); + + const func = async () => service.checkContextRestrictions(contextExternalTool, userId, context); + + await expect(func()).resolves.not.toThrow(); + }); + }); + + describe('when context is restricted to wrong context type', () => { + const setup = () => { + const userId = new ObjectId().toHexString(); + const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); + + const externalTool: ExternalTool = externalToolFactory.build({ + restrictToContexts: [ToolContextType.BOARD_ELEMENT], + }); + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); + const contextExternalTool: ContextExternalTool = contextExternalToolFactory.build({ + contextRef: { type: ToolContextType.COURSE }, + }); + + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + externalToolService.findById.mockResolvedValueOnce(externalTool); + + return { + userId, + context, + contextExternalTool, + }; + }; + + it('should throw ForbiddenLoggableException', async () => { + const { userId, context, contextExternalTool } = setup(); + + const func = async () => service.checkContextRestrictions(contextExternalTool, userId, context); + + await expect(func()).rejects.toThrow(new ForbiddenLoggableException(userId, 'ContextExternalTool', context)); + }); + }); + }); }); 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 37cc4fde3d0..3b60d7d5725 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 @@ -3,12 +3,19 @@ import { EntityId } from '@shared/domain'; import { ContextExternalToolRepo } from '@shared/repo'; import { ContextExternalTool, ContextRef } from '../domain'; import { ContextExternalToolQuery } from '../uc/dto/context-external-tool.types'; -import { ToolContextTypesList } from '../../external-tool/controller/dto/response/tool-context-types-list'; -import { ToolContextType } from '../../common/enum'; +import { SchoolExternalTool } from '../../school-external-tool/domain'; +import { ExternalTool } from '../../external-tool/domain'; +import { ExternalToolService } from '../../external-tool/service'; +import { SchoolExternalToolService } from '../../school-external-tool/service'; +import { AuthorizationContext, ForbiddenLoggableException } from '../../../authorization'; @Injectable() export class ContextExternalToolService { - constructor(private readonly contextExternalToolRepo: ContextExternalToolRepo) {} + constructor( + private readonly contextExternalToolRepo: ContextExternalToolRepo, + private readonly externalToolService: ExternalToolService, + private readonly schoolExternalToolService: SchoolExternalToolService + ) {} public async findContextExternalTools(query: ContextExternalToolQuery): Promise { const contextExternalTools: ContextExternalTool[] = await this.contextExternalToolRepo.find(query); @@ -49,4 +56,22 @@ export class ContextExternalToolService { return contextExternalTools; } + + public async checkContextRestrictions( + contextExternalTool: ContextExternalTool, + userId: EntityId, + context: AuthorizationContext + ): Promise { + const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( + contextExternalTool.schoolToolRef.schoolToolId + ); + + const externalTool: ExternalTool = await this.externalToolService.findById(schoolExternalTool.toolId); + + if (externalTool.restrictToContexts && externalTool.restrictToContexts[0]) { + if (!externalTool.restrictToContexts.includes(contextExternalTool.contextRef.type)) { + throw new ForbiddenLoggableException(userId, 'ContextExternalTool', context); + } + } + } } 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 9239e4db2a9..610adfe005c 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 @@ -2,6 +2,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { Action, + AuthorizationContext, AuthorizationContextBuilder, AuthorizationService, ForbiddenLoggableException, @@ -96,6 +97,8 @@ describe('ContextExternalToolUc', () => { }, }); + const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); contextExternalToolService.saveContextExternalTool.mockResolvedValue(contextExternalTool); @@ -103,6 +106,7 @@ describe('ContextExternalToolUc', () => { contextExternalTool, userId, schoolId, + context, }; }; @@ -126,6 +130,18 @@ describe('ContextExternalToolUc', () => { ); }); + it('should check for context restrictions', async () => { + const { contextExternalTool, userId, schoolId, context } = setup(); + + await uc.createContextExternalTool(userId, schoolId, contextExternalTool); + + expect(contextExternalToolService.checkContextRestrictions).toHaveBeenCalledWith( + contextExternalTool, + userId, + context + ); + }); + it('should call contextExternalToolValidationService', async () => { const { contextExternalTool, userId, schoolId } = setup(); 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 80a85321933..e1875ba1300 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 @@ -45,6 +45,8 @@ export class ContextExternalToolUc { await this.toolPermissionHelper.ensureContextPermissions(userId, contextExternalTool, context); + await this.contextExternalToolService.checkContextRestrictions(contextExternalTool, userId, context); + await this.contextExternalToolValidationService.validate(contextExternalTool); const createdTool: ContextExternalTool = await this.contextExternalToolService.saveContextExternalTool( 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 201999ad182..3595f7e45aa 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 @@ -131,7 +131,9 @@ describe('ToolConfigurationController (API)', () => { }); externalTool.logoUrl = `http://localhost:3030/api/v3/tools/external-tools/${externalTool.id}/logo`; - const externalToolWithoutContextRestriction: ExternalToolEntity = externalToolEntityFactory.buildWithId(); + const externalToolWithoutContextRestriction: ExternalToolEntity = externalToolEntityFactory.buildWithId({ + restrictToContexts: [], + }); const schoolExternalTool: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ school, diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts index 0f48b03570d..475b823d7b8 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts @@ -192,7 +192,7 @@ describe('ExternalToolConfigurationService', () => { const setup = () => { const contextType = ToolContextType.COURSE; - const externalTool: ExternalTool = externalToolFactory.build(); + const externalTool: ExternalTool = externalToolFactory.build({ restrictToContexts: [] }); const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); const availableTools: ContextExternalToolTemplateInfo[] = [{ externalTool, schoolExternalTool }]; diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts index 785d5c2c9ed..8a76f3c45a4 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts @@ -80,7 +80,7 @@ export class ExternalToolConfigurationService { contextType: ToolContextType ): ContextExternalToolTemplateInfo[] { const availableToolsForContext: ContextExternalToolTemplateInfo[] = availableTools.filter((availableTool) => { - if (availableTool.externalTool.restrictToContexts) { + if (availableTool.externalTool.restrictToContexts && availableTool.externalTool.restrictToContexts[0]) { return availableTool.externalTool.restrictToContexts.includes(contextType); } return true; From 6db51dc5b0520a4c99f30858c2dea5a10e4583c1 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Fri, 24 Nov 2023 16:34:40 +0100 Subject: [PATCH 10/24] fix imports --- .../service/context-external-tool.service.ts | 2 +- .../modules/tool/external-tool/uc/dto/external-tool.types.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) 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 3b60d7d5725..1c91c47bb76 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,13 +1,13 @@ import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain'; import { ContextExternalToolRepo } from '@shared/repo'; +import { AuthorizationContext, ForbiddenLoggableException } from '@modules/authorization'; import { ContextExternalTool, ContextRef } from '../domain'; import { ContextExternalToolQuery } from '../uc/dto/context-external-tool.types'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { ExternalTool } from '../../external-tool/domain'; import { ExternalToolService } from '../../external-tool/service'; import { SchoolExternalToolService } from '../../school-external-tool/service'; -import { AuthorizationContext, ForbiddenLoggableException } from '../../../authorization'; @Injectable() export class ContextExternalToolService { diff --git a/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts b/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts index 12daf9b5f1c..707546ba55e 100644 --- a/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts +++ b/apps/server/src/modules/tool/external-tool/uc/dto/external-tool.types.ts @@ -1,6 +1,6 @@ import { BasicToolConfig, Lti11ToolConfig, Oauth2ToolConfig } from '../../domain'; import { CustomParameter } from '../../../common/domain'; -import { ToolConfigType, ToolContextType } from '../../../common/enum'; +import { ToolContextType } from '../../../common/enum'; type PartialBy = Omit & Partial>; From 25745b107528af6fc4b003bb452e92a8df9ff249 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Mon, 27 Nov 2023 11:08:03 +0100 Subject: [PATCH 11/24] fix dependency cycle --- .../service/context-external-tool.service.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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 1c91c47bb76..f212019185e 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,7 +1,8 @@ import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain'; import { ContextExternalToolRepo } from '@shared/repo'; -import { AuthorizationContext, ForbiddenLoggableException } from '@modules/authorization'; +import { AuthorizationContext } from '@modules/authorization'; +import { ForbiddenLoggableException } from '@modules/authorization/domain/error'; import { ContextExternalTool, ContextRef } from '../domain'; import { ContextExternalToolQuery } from '../uc/dto/context-external-tool.types'; import { SchoolExternalTool } from '../../school-external-tool/domain'; From 7df6b8568248080196d26ad8bd90128022cb88ff Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Mon, 27 Nov 2023 13:12:16 +0100 Subject: [PATCH 12/24] fix dependency unit tests --- .../api-test/tool-reference.api.spec.ts | 43 ++++++++++++++++++- .../uc/external-tool-configuration.uc.spec.ts | 7 +++ .../tool-launch.controller.api.spec.ts | 25 ++++++++++- .../context-external-tool-entity.factory.ts | 2 +- .../school-external-tool-entity.factory.ts | 2 +- 5 files changed, 75 insertions(+), 4 deletions(-) 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 9eae3c7298e..a348bc9fa31 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 @@ -14,7 +14,12 @@ import { } from '@shared/testing'; import { ServerTestModule } from '@modules/server'; import { Response } from 'supertest'; -import { ToolContextType } from '../../../common/enum'; +import { + CustomParameterLocation, + CustomParameterScope, + CustomParameterType, + ToolContextType, +} from '../../../common/enum'; import { ExternalToolEntity } from '../../../external-tool/entity'; import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; import { ContextExternalToolEntity, ContextExternalToolType } from '../../entity'; @@ -114,6 +119,24 @@ describe('ToolReferenceController (API)', () => { const course: Course = courseFactory.buildWithId({ school, teachers: [adminUser] }); const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ logoBase64: 'logoBase64', + parameters: [ + { + name: 'schoolMockParameter', + displayName: 'MockParameter', + scope: CustomParameterScope.SCHOOL, + type: CustomParameterType.STRING, + location: CustomParameterLocation.PATH, + isOptional: false, + }, + { + name: 'contextMockParameter', + displayName: 'MockParameter', + scope: CustomParameterScope.CONTEXT, + type: CustomParameterType.STRING, + location: CustomParameterLocation.PATH, + isOptional: false, + }, + ], }); const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ school, @@ -233,6 +256,24 @@ describe('ToolReferenceController (API)', () => { const course: Course = courseFactory.buildWithId({ school, teachers: [adminUser] }); const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ logoBase64: 'logoBase64', + parameters: [ + { + name: 'schoolMockParameter', + displayName: 'MockParameter', + scope: CustomParameterScope.SCHOOL, + type: CustomParameterType.STRING, + location: CustomParameterLocation.PATH, + isOptional: false, + }, + { + name: 'contextMockParameter', + displayName: 'MockParameter', + scope: CustomParameterScope.CONTEXT, + type: CustomParameterType.STRING, + location: CustomParameterLocation.PATH, + isOptional: false, + }, + ], }); const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ school, 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 80b632e073e..875f6fd3e69 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 @@ -268,6 +268,9 @@ describe('ExternalToolConfigurationUc', () => { externalToolConfigurationService.filterForAvailableExternalTools.mockReturnValue([ { externalTool: usedTool, schoolExternalTool: usedSchoolExternalTool }, ]); + externalToolConfigurationService.filterForContextRestrictions.mockReturnValue([ + { externalTool: usedTool, schoolExternalTool: usedSchoolExternalTool }, + ]); return { toolIds, @@ -368,6 +371,7 @@ describe('ExternalToolConfigurationUc', () => { unusedSchoolExternalTool, ]); externalToolConfigurationService.filterForAvailableExternalTools.mockReturnValue([]); + externalToolConfigurationService.filterForContextRestrictions.mockReturnValue([]); return {}; }; @@ -409,6 +413,9 @@ describe('ExternalToolConfigurationUc', () => { externalToolConfigurationService.filterForAvailableExternalTools.mockReturnValue([ { externalTool: usedTool, schoolExternalTool: usedSchoolExternalTool }, ]); + externalToolConfigurationService.filterForContextRestrictions.mockReturnValue([ + { externalTool: usedTool, schoolExternalTool: usedSchoolExternalTool }, + ]); return { usedTool, 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 04defeeb0dc..23133a1654e 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 @@ -19,7 +19,12 @@ import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; import { LaunchRequestMethod } from '../../types'; import { ToolLaunchRequestResponse, ToolLaunchParams } from '../dto'; import { ContextExternalToolEntity, ContextExternalToolType } from '../../../context-external-tool/entity'; -import { ExternalToolEntity } from '../../../external-tool/entity'; +import { + CustomParameterLocation, + CustomParameterScope, + CustomParameterType, + ExternalToolEntity, +} from '../../../external-tool/entity'; import { ToolConfigType } from '../../../common/enum'; describe('ToolLaunchController (API)', () => { @@ -64,6 +69,24 @@ describe('ToolLaunchController (API)', () => { const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ config: basicToolConfigFactory.build({ baseUrl: 'https://mockurl.de', type: ToolConfigType.BASIC }), version: 0, + parameters: [ + { + name: 'schoolMockParameter', + displayName: 'MockParameter', + scope: CustomParameterScope.SCHOOL, + type: CustomParameterType.STRING, + location: CustomParameterLocation.PATH, + isOptional: false, + }, + { + name: 'contextMockParameter', + displayName: 'MockParameter', + scope: CustomParameterScope.CONTEXT, + type: CustomParameterType.STRING, + location: CustomParameterLocation.PATH, + isOptional: false, + }, + ], }); const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ tool: externalToolEntity, 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 3e7f4e4b1cb..2d421ab99c8 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 @@ -17,7 +17,7 @@ export const contextExternalToolEntityFactory = BaseFactory.define< contextType: ContextExternalToolType.COURSE, displayName: 'My Course Tool 1', schoolTool: schoolExternalToolEntityFactory.buildWithId(), - parameters: [new CustomParameterEntryEntity({ name: 'mockParamater', value: 'mockValue' })], + parameters: [new CustomParameterEntryEntity({ name: 'contextMockParameter', value: 'mockValue' })], toolVersion: 1, }; }); 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 023e3a2626d..ff8e62fa4bc 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 @@ -10,7 +10,7 @@ export const schoolExternalToolEntityFactory = BaseFactory.define< return { tool: externalToolEntityFactory.buildWithId(), school: schoolFactory.buildWithId(), - schoolParameters: [{ name: 'mockParamater', value: 'mockValue' }], + schoolParameters: [{ name: 'schoolMockParameter', value: 'mockValue' }], toolVersion: 0, }; }); From fbcd329d483d8f76b6152eb5eb2bdf973f824e17 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Mon, 27 Nov 2023 13:42:23 +0100 Subject: [PATCH 13/24] coverage up --- .../api-test/tool-configuration.api.spec.ts | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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 3595f7e45aa..cc5c422d12c 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 @@ -28,6 +28,7 @@ import { ContextExternalToolConfigurationTemplateResponse, SchoolExternalToolConfigurationTemplateListResponse, SchoolExternalToolConfigurationTemplateResponse, + ToolContextTypesList, } from '../dto'; describe('ToolConfigurationController (API)', () => { @@ -728,15 +729,20 @@ describe('ToolConfigurationController (API)', () => { const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); - return { loggedInClient }; + const contextTypeList: ToolContextTypesList = new ToolContextTypesList([ + ToolContextType.COURSE, + ToolContextType.BOARD_ELEMENT, + ]); + + return { loggedInClient, contextTypeList }; }; it('should return all context types', async () => { - const { loggedInClient } = await setup(); + const { loggedInClient, contextTypeList } = await setup(); const response = await loggedInClient.get('context-types'); - expect(response.body).toEqual({ data: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT] }); + expect(response.body).toEqual(contextTypeList); }); }); }); From f746e8634c9278960116cdec1d60bdc88952d8e4 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Tue, 28 Nov 2023 16:56:20 +0100 Subject: [PATCH 14/24] requested changes WIP --- .../common/service/common-tool.service.ts | 11 ++++- .../service/context-external-tool.service.ts | 19 +++----- ...tricted-context-mismatch-loggabble.spec.ts | 47 +++++++++++++++++++ .../restricted-context-mismatch-loggabble.ts | 24 ++++++++++ .../uc/context-external-tool.uc.ts | 2 +- ...xternal-tool-configuration.service.spec.ts | 10 ++++ .../external-tool-configuration.service.ts | 15 +++--- .../tool-launch.controller.api.spec.ts | 22 +++------ 8 files changed, 112 insertions(+), 38 deletions(-) create mode 100644 apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.spec.ts create mode 100644 apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.ts diff --git a/apps/server/src/modules/tool/common/service/common-tool.service.ts b/apps/server/src/modules/tool/common/service/common-tool.service.ts index 1dccc42ab1f..eea1706aaf1 100644 --- a/apps/server/src/modules/tool/common/service/common-tool.service.ts +++ b/apps/server/src/modules/tool/common/service/common-tool.service.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { ExternalTool } from '../../external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { ContextExternalTool } from '../../context-external-tool/domain'; -import { ToolConfigurationStatus } from '../enum'; +import { ToolConfigurationStatus, ToolContextType } from '../enum'; import { ToolVersion } from '../interface'; // TODO N21-1337 remove class when tool versioning is removed @@ -11,7 +11,7 @@ export class CommonToolService { /** * @deprecated use ToolVersionService */ - determineToolConfigurationStatus( + public determineToolConfigurationStatus( externalTool: ExternalTool, schoolExternalTool: SchoolExternalTool, contextExternalTool: ContextExternalTool @@ -30,4 +30,11 @@ export class CommonToolService { private isLatest(tool1: ToolVersion, tool2: ToolVersion): boolean { return tool1.getVersion() >= tool2.getVersion(); } + + public isContextRestricted(externalTool: ExternalTool, context: ToolContextType): boolean { + if (externalTool.restrictToContexts?.length && !externalTool.restrictToContexts.includes(context)) { + return true; + } + return false; + } } 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 f212019185e..c75dbc2a5d5 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,21 +1,22 @@ import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain'; import { ContextExternalToolRepo } from '@shared/repo'; -import { AuthorizationContext } from '@modules/authorization'; -import { ForbiddenLoggableException } from '@modules/authorization/domain/error'; import { ContextExternalTool, ContextRef } from '../domain'; import { ContextExternalToolQuery } from '../uc/dto/context-external-tool.types'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { ExternalTool } from '../../external-tool/domain'; import { ExternalToolService } from '../../external-tool/service'; import { SchoolExternalToolService } from '../../school-external-tool/service'; +import { RestrictedContextMismatchLoggable } from './restricted-context-mismatch-loggabble'; +import { CommonToolService } from '../../common/service'; @Injectable() export class ContextExternalToolService { constructor( private readonly contextExternalToolRepo: ContextExternalToolRepo, private readonly externalToolService: ExternalToolService, - private readonly schoolExternalToolService: SchoolExternalToolService + private readonly schoolExternalToolService: SchoolExternalToolService, + private readonly commonToolService: CommonToolService ) {} public async findContextExternalTools(query: ContextExternalToolQuery): Promise { @@ -58,21 +59,15 @@ export class ContextExternalToolService { return contextExternalTools; } - public async checkContextRestrictions( - contextExternalTool: ContextExternalTool, - userId: EntityId, - context: AuthorizationContext - ): Promise { + public async checkContextRestrictions(contextExternalTool: ContextExternalTool): Promise { const schoolExternalTool: SchoolExternalTool = await this.schoolExternalToolService.findById( contextExternalTool.schoolToolRef.schoolToolId ); const externalTool: ExternalTool = await this.externalToolService.findById(schoolExternalTool.toolId); - if (externalTool.restrictToContexts && externalTool.restrictToContexts[0]) { - if (!externalTool.restrictToContexts.includes(contextExternalTool.contextRef.type)) { - throw new ForbiddenLoggableException(userId, 'ContextExternalTool', context); - } + if (this.commonToolService.isContextRestricted(externalTool, contextExternalTool.contextRef.type)) { + throw new RestrictedContextMismatchLoggable(externalTool.name, contextExternalTool.contextRef.type); } } } diff --git a/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.spec.ts b/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.spec.ts new file mode 100644 index 00000000000..c7de14f196e --- /dev/null +++ b/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.spec.ts @@ -0,0 +1,47 @@ +import { ToolContextType } from '../../common/enum'; +import { RestrictedContextMismatchLoggable } from './restricted-context-mismatch-loggabble'; + +describe('RestrictedContextMismatchLoggable', () => { + describe('constructor', () => { + const setup = () => { + const externalToolName = 'name'; + const context: ToolContextType = ToolContextType.COURSE; + + return { externalToolName, context }; + }; + + it('should create an instance of RestrictedContextMismatchLoggable', () => { + const { externalToolName, context } = setup(); + + const loggable = new RestrictedContextMismatchLoggable(externalToolName, context); + + expect(loggable).toBeInstanceOf(RestrictedContextMismatchLoggable); + }); + }); + + describe('getLogMessage', () => { + const setup = () => { + const externalToolName = 'name'; + const context: ToolContextType = ToolContextType.COURSE; + const loggable = new RestrictedContextMismatchLoggable(externalToolName, context); + + return { loggable, externalToolName, context }; + }; + + it('should return a loggable message', () => { + const { loggable, externalToolName, context } = setup(); + + const message = loggable.getLogMessage(); + + expect(message).toEqual({ + type: 'UNPROCESSABLE_ENTITY_EXCEPTION', + message: `Could not create an instance of ${externalToolName} in context: ${context} because of the context restrictions of the tool.`, + stack: loggable.stack, + data: { + externalToolName, + context, + }, + }); + }); + }); +}); diff --git a/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.ts b/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.ts new file mode 100644 index 00000000000..8e6fc4e643a --- /dev/null +++ b/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.ts @@ -0,0 +1,24 @@ +import { UnprocessableEntityException } from '@nestjs/common'; +import { Loggable } from '@src/core/logger/interfaces'; +import { ErrorLogMessage, LogMessage, ValidationErrorLogMessage } from '@src/core/logger'; +import { ToolContextType } from '../../common/enum'; + +export class RestrictedContextMismatchLoggable extends UnprocessableEntityException implements Loggable { + constructor(private readonly externalToolName: string, private readonly context: ToolContextType) { + super(); + } + + getLogMessage(): LogMessage | ErrorLogMessage | ValidationErrorLogMessage { + const message: LogMessage | ErrorLogMessage | ValidationErrorLogMessage = { + type: 'UNPROCESSABLE_ENTITY_EXCEPTION', + message: `Could not create an instance of ${this.externalToolName} in context: ${this.context} because of the context restrictions of the tool.`, + stack: this.stack, + data: { + externalToolName: this.externalToolName, + context: this.context, + }, + }; + + return message; + } +} 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 e1875ba1300..c0e218f3beb 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 @@ -45,7 +45,7 @@ export class ContextExternalToolUc { await this.toolPermissionHelper.ensureContextPermissions(userId, contextExternalTool, context); - await this.contextExternalToolService.checkContextRestrictions(contextExternalTool, userId, context); + await this.contextExternalToolService.checkContextRestrictions(contextExternalTool); await this.contextExternalToolValidationService.validate(contextExternalTool); diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts index 475b823d7b8..aec1dd8353c 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts @@ -7,6 +7,7 @@ import { schoolExternalToolFactory, setupEntities, } from '@shared/testing'; +import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { CustomParameter } from '../../common/domain'; import { CustomParameterScope, ToolContextType } from '../../common/enum'; import { ContextExternalTool } from '../../context-external-tool/domain'; @@ -16,10 +17,12 @@ import { ExternalTool } from '../domain'; import { ContextExternalToolTemplateInfo } from '../uc'; import { ExternalToolConfigurationService } from './external-tool-configuration.service'; import { ToolContextTypesList } from '../controller/dto/response/tool-context-types-list'; +import { CommonToolService } from '../../common/service'; describe('ExternalToolConfigurationService', () => { let module: TestingModule; let service: ExternalToolConfigurationService; + let commonToolservice: DeepMocked; let toolFeatures: IToolFeatures; @@ -35,11 +38,16 @@ describe('ExternalToolConfigurationService', () => { contextConfigurationEnabled: false, }, }, + { + provide: CommonToolService, + useValue: createMock(), + }, ], }).compile(); service = module.get(ExternalToolConfigurationService); toolFeatures = module.get(ToolFeatures); + commonToolservice = module.get(CommonToolService); }); afterEach(() => { @@ -197,6 +205,8 @@ describe('ExternalToolConfigurationService', () => { const availableTools: ContextExternalToolTemplateInfo[] = [{ externalTool, schoolExternalTool }]; + commonToolservice.isContextRestricted.mockReturnValueOnce(false); + return { contextType, availableTools, diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts index 8a76f3c45a4..b0738aa691c 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts @@ -8,10 +8,14 @@ import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; import { ContextExternalToolTemplateInfo } from '../uc/dto'; import { ToolContextTypesList } from '../controller/dto/response/tool-context-types-list'; +import { CommonToolService } from '../../common/service'; @Injectable() export class ExternalToolConfigurationService { - constructor(@Inject(ToolFeatures) private readonly toolFeatures: IToolFeatures) {} + constructor( + @Inject(ToolFeatures) private readonly toolFeatures: IToolFeatures, + private readonly commonTooLService: CommonToolService + ) {} public filterForAvailableTools(externalTools: Page, toolIdsInUse: EntityId[]): ExternalTool[] { const visibleTools: ExternalTool[] = externalTools.data.filter((tool: ExternalTool): boolean => !tool.isHidden); @@ -79,12 +83,9 @@ export class ExternalToolConfigurationService { availableTools: ContextExternalToolTemplateInfo[], contextType: ToolContextType ): ContextExternalToolTemplateInfo[] { - const availableToolsForContext: ContextExternalToolTemplateInfo[] = availableTools.filter((availableTool) => { - if (availableTool.externalTool.restrictToContexts && availableTool.externalTool.restrictToContexts[0]) { - return availableTool.externalTool.restrictToContexts.includes(contextType); - } - return true; - }); + const availableToolsForContext: ContextExternalToolTemplateInfo[] = availableTools.filter( + (availableTool) => !this.commonTooLService.isContextRestricted(availableTool.externalTool, contextType) + ); return availableToolsForContext; } 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 23133a1654e..0cd0e59cb26 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 @@ -12,6 +12,7 @@ import { schoolFactory, TestApiClient, UserAndAccountTestFactory, + customParameterFactory, } from '@shared/testing'; import { ServerTestModule } from '@modules/server'; import { Response } from 'supertest'; @@ -19,12 +20,7 @@ import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; import { LaunchRequestMethod } from '../../types'; import { ToolLaunchRequestResponse, ToolLaunchParams } from '../dto'; import { ContextExternalToolEntity, ContextExternalToolType } from '../../../context-external-tool/entity'; -import { - CustomParameterLocation, - CustomParameterScope, - CustomParameterType, - ExternalToolEntity, -} from '../../../external-tool/entity'; +import { CustomParameterLocation, CustomParameterScope, ExternalToolEntity } from '../../../external-tool/entity'; import { ToolConfigType } from '../../../common/enum'; describe('ToolLaunchController (API)', () => { @@ -70,22 +66,16 @@ describe('ToolLaunchController (API)', () => { config: basicToolConfigFactory.build({ baseUrl: 'https://mockurl.de', type: ToolConfigType.BASIC }), version: 0, parameters: [ - { + customParameterFactory.build({ name: 'schoolMockParameter', - displayName: 'MockParameter', scope: CustomParameterScope.SCHOOL, - type: CustomParameterType.STRING, location: CustomParameterLocation.PATH, - isOptional: false, - }, - { + }), + customParameterFactory.build({ name: 'contextMockParameter', - displayName: 'MockParameter', scope: CustomParameterScope.CONTEXT, - type: CustomParameterType.STRING, location: CustomParameterLocation.PATH, - isOptional: false, - }, + }), ], }); const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ From 3db5438e69a6cf7de6e42469dc7fa0a590ae8b8d Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Wed, 29 Nov 2023 13:23:56 +0100 Subject: [PATCH 15/24] requested changes WIP 2 --- .../service/common-tool.service.spec.ts | 76 ++++++++++++++++++- .../tool/common/uc/tool-permission-helper.ts | 5 -- .../common/uc/tool-permissions-helper.spec.ts | 20 ----- .../api-test/tool-reference.api.spec.ts | 36 +++------ .../context-external-tool.service.spec.ts | 60 +++++++++------ .../uc/context-external-tool.uc.spec.ts | 47 ++++++++++++ .../api-test/tool-configuration.api.spec.ts | 17 +---- .../request/external-tool-create.params.ts | 5 +- .../request/external-tool-update.params.ts | 5 +- ...xternal-tool-configuration.service.spec.ts | 34 ++++----- .../uc/external-tool-configuration.uc.spec.ts | 29 +++++-- .../uc/external-tool-configuration.uc.ts | 10 ++- 12 files changed, 222 insertions(+), 122 deletions(-) diff --git a/apps/server/src/modules/tool/common/service/common-tool.service.spec.ts b/apps/server/src/modules/tool/common/service/common-tool.service.spec.ts index 11677e6e916..7327fa65abf 100644 --- a/apps/server/src/modules/tool/common/service/common-tool.service.spec.ts +++ b/apps/server/src/modules/tool/common/service/common-tool.service.spec.ts @@ -3,7 +3,7 @@ import { contextExternalToolFactory, externalToolFactory, schoolExternalToolFact import { CommonToolService } from './common-tool.service'; import { ExternalTool } from '../../external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; -import { ToolConfigurationStatus } from '../enum'; +import { ToolConfigurationStatus, ToolContextType } from '../enum'; import { ContextExternalTool } from '../../context-external-tool/domain'; describe('CommonToolService', () => { @@ -205,4 +205,78 @@ describe('CommonToolService', () => { }); }); }); + + describe('isContextRestricted', () => { + describe('when tool is not restricted to context', () => { + const setup = () => { + const externalTool: ExternalTool = externalToolFactory.build({ restrictToContexts: [] }); + const context: ToolContextType = ToolContextType.COURSE; + + return { externalTool, context }; + }; + + it('should return false', () => { + const { externalTool, context } = setup(); + + const result = service.isContextRestricted(externalTool, context); + + expect(result).toBe(false); + }); + }); + + describe('when tool is restricted to all contexts', () => { + const setup = () => { + const externalTool: ExternalTool = externalToolFactory.build({ + restrictToContexts: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT], + }); + const context: ToolContextType = ToolContextType.COURSE; + + return { externalTool, context }; + }; + + it('should return false', () => { + const { externalTool, context } = setup(); + + const result = service.isContextRestricted(externalTool, context); + + expect(result).toBe(false); + }); + }); + + describe('when tool is restricted to correct context', () => { + const setup = () => { + const externalTool: ExternalTool = externalToolFactory.build({ restrictToContexts: [ToolContextType.COURSE] }); + const context: ToolContextType = ToolContextType.COURSE; + + return { externalTool, context }; + }; + + it('should return false', () => { + const { externalTool, context } = setup(); + + const result = service.isContextRestricted(externalTool, context); + + expect(result).toBe(false); + }); + }); + + describe('when tool is restricted to wrong context', () => { + const setup = () => { + const externalTool: ExternalTool = externalToolFactory.build({ + restrictToContexts: [ToolContextType.BOARD_ELEMENT], + }); + const context: ToolContextType = ToolContextType.COURSE; + + return { externalTool, context }; + }; + + it('should return true', () => { + const { externalTool, context } = setup(); + + const result = service.isContextRestricted(externalTool, context); + + expect(result).toBe(true); + }); + }); + }); }); 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 363338e144f..0c1677f7a70 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 @@ -61,9 +61,4 @@ export class ToolPermissionHelper { this.authorizationService.checkPermission(user, school, context); } - - public async ensurePermission(userId: EntityId, permission: Permission) { - const user: User = await this.authorizationService.getUserWithPermissions(userId); - this.authorizationService.checkAllPermissions(user, [permission]); - } } 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 fd9c926ee98..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 @@ -247,24 +247,4 @@ describe('ToolPermissionHelper', () => { }); }); }); - - describe('ensurePermission', () => { - describe('when it is called', () => { - const setup = () => { - const user = userFactory.buildWithId(); - - authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); - - return { - user, - }; - }; - - it('should check permissions', async () => { - const { user } = setup(); - - await helper.ensurePermission(user.id, Permission.TOOL_ADMIN); - }); - }); - }); }); 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 a348bc9fa31..ee7595ab671 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 @@ -6,6 +6,7 @@ import { cleanupCollections, contextExternalToolEntityFactory, courseFactory, + customParameterFactory, externalToolEntityFactory, schoolExternalToolEntityFactory, schoolFactory, @@ -14,12 +15,7 @@ import { } from '@shared/testing'; import { ServerTestModule } from '@modules/server'; import { Response } from 'supertest'; -import { - CustomParameterLocation, - CustomParameterScope, - CustomParameterType, - ToolContextType, -} from '../../../common/enum'; +import { CustomParameterLocation, CustomParameterScope, ToolContextType } from '../../../common/enum'; import { ExternalToolEntity } from '../../../external-tool/entity'; import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; import { ContextExternalToolEntity, ContextExternalToolType } from '../../entity'; @@ -120,22 +116,16 @@ describe('ToolReferenceController (API)', () => { const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ logoBase64: 'logoBase64', parameters: [ - { + customParameterFactory.build({ name: 'schoolMockParameter', - displayName: 'MockParameter', scope: CustomParameterScope.SCHOOL, - type: CustomParameterType.STRING, location: CustomParameterLocation.PATH, - isOptional: false, - }, - { + }), + customParameterFactory.build({ name: 'contextMockParameter', - displayName: 'MockParameter', scope: CustomParameterScope.CONTEXT, - type: CustomParameterType.STRING, location: CustomParameterLocation.PATH, - isOptional: false, - }, + }), ], }); const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ @@ -257,22 +247,16 @@ describe('ToolReferenceController (API)', () => { const externalToolEntity: ExternalToolEntity = externalToolEntityFactory.buildWithId({ logoBase64: 'logoBase64', parameters: [ - { + customParameterFactory.build({ name: 'schoolMockParameter', - displayName: 'MockParameter', scope: CustomParameterScope.SCHOOL, - type: CustomParameterType.STRING, location: CustomParameterLocation.PATH, - isOptional: false, - }, - { + }), + customParameterFactory.build({ name: 'contextMockParameter', - displayName: 'MockParameter', scope: CustomParameterScope.CONTEXT, - type: CustomParameterType.STRING, location: CustomParameterLocation.PATH, - isOptional: false, - }, + }), ], }); const schoolExternalToolEntity: SchoolExternalToolEntity = schoolExternalToolEntityFactory.buildWithId({ 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 ed27a5201d2..9a4f4d4031c 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 @@ -8,12 +8,7 @@ import { legacySchoolDoFactory, schoolExternalToolFactory, } from '@shared/testing/factory/domainobject'; -import { - AuthorizationContext, - AuthorizationContextBuilder, - AuthorizationService, - ForbiddenLoggableException, -} from '@modules/authorization'; +import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { ObjectId } from '@mikro-orm/mongodb'; import { Permission } from '@shared/domain'; import { ToolContextType } from '../../common/enum'; @@ -23,12 +18,15 @@ import { ContextExternalToolService } from './context-external-tool.service'; import { ExternalTool } from '../../external-tool/domain'; import { ExternalToolService } from '../../external-tool/service'; import { SchoolExternalToolService } from '../../school-external-tool/service'; +import { RestrictedContextMismatchLoggable } from './restricted-context-mismatch-loggabble'; +import { CommonToolService } from '../../common/service'; describe('ContextExternalToolService', () => { let module: TestingModule; let service: ContextExternalToolService; let externalToolService: DeepMocked; let schoolExternalToolService: DeepMocked; + let commonToolService: DeepMocked; let contextExternalToolRepo: DeepMocked; @@ -52,6 +50,10 @@ describe('ContextExternalToolService', () => { provide: SchoolExternalToolService, useValue: createMock(), }, + { + provide: CommonToolService, + useValue: createMock(), + }, ], }).compile(); @@ -59,6 +61,7 @@ describe('ContextExternalToolService', () => { contextExternalToolRepo = module.get(ContextExternalToolRepo); externalToolService = module.get(ExternalToolService); schoolExternalToolService = module.get(SchoolExternalToolService); + commonToolService = module.get(CommonToolService); }); afterAll(async () => { @@ -243,44 +246,52 @@ describe('ContextExternalToolService', () => { describe('checkContextRestrictions', () => { describe('when contexts are not restricted', () => { const setup = () => { - const userId = new ObjectId().toHexString(); - const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); - const externalTool: ExternalTool = externalToolFactory.build({ restrictToContexts: [] }); const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); const contextExternalTool: ContextExternalTool = contextExternalToolFactory.build(); schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); externalToolService.findById.mockResolvedValueOnce(externalTool); + commonToolService.isContextRestricted.mockReturnValueOnce(false); return { - userId, - context, contextExternalTool, schoolExternalTool, + externalTool, }; }; it('should find SchoolExternalTool', async () => { - const { userId, context, contextExternalTool } = setup(); + const { contextExternalTool } = setup(); - await service.checkContextRestrictions(contextExternalTool, userId, context); + await service.checkContextRestrictions(contextExternalTool); expect(schoolExternalToolService.findById).toHaveBeenCalledWith(contextExternalTool.schoolToolRef.schoolToolId); }); it('should find ExternalTool', async () => { - const { userId, context, contextExternalTool, schoolExternalTool } = setup(); + const { contextExternalTool, schoolExternalTool } = setup(); - await service.checkContextRestrictions(contextExternalTool, userId, context); + await service.checkContextRestrictions(contextExternalTool); expect(externalToolService.findById).toHaveBeenCalledWith(schoolExternalTool.toolId); }); + it('should check if context is restricted', async () => { + const { contextExternalTool, externalTool } = setup(); + + await service.checkContextRestrictions(contextExternalTool); + + expect(commonToolService.isContextRestricted).toHaveBeenCalledWith( + externalTool, + contextExternalTool.contextRef.type + ); + }); + it('should not throw', async () => { - const { userId, context, contextExternalTool } = setup(); + const { contextExternalTool } = setup(); - const func = async () => service.checkContextRestrictions(contextExternalTool, userId, context); + const func = async () => service.checkContextRestrictions(contextExternalTool); await expect(func()).resolves.not.toThrow(); }); @@ -308,9 +319,9 @@ describe('ContextExternalToolService', () => { }; it('should not throw', async () => { - const { userId, context, contextExternalTool } = setup(); + const { contextExternalTool } = setup(); - const func = async () => service.checkContextRestrictions(contextExternalTool, userId, context); + const func = async () => service.checkContextRestrictions(contextExternalTool); await expect(func()).resolves.not.toThrow(); }); @@ -336,15 +347,16 @@ describe('ContextExternalToolService', () => { userId, context, contextExternalTool, + externalTool, }; }; - it('should throw ForbiddenLoggableException', async () => { - const { userId, context, contextExternalTool } = setup(); - - const func = async () => service.checkContextRestrictions(contextExternalTool, userId, context); + it('should throw RestrictedContextMismatchLoggable', async () => { + const { contextExternalTool, externalTool } = setup(); - await expect(func()).rejects.toThrow(new ForbiddenLoggableException(userId, 'ContextExternalTool', context)); + await expect(service.checkContextRestrictions(contextExternalTool)).rejects.toThrow( + new RestrictedContextMismatchLoggable(externalTool.name, contextExternalTool.contextRef.type) + ); }); }); }); 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 610adfe005c..22e46ea7cee 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 @@ -159,6 +159,53 @@ describe('ContextExternalToolUc', () => { }); }); + describe('when tool is restricted to a different context', () => { + const setup = () => { + const userId: EntityId = new ObjectId().toHexString(); + 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 context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); + + const error: Error = new Error(); + + schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); + contextExternalToolService.saveContextExternalTool.mockResolvedValue(contextExternalTool); + contextExternalToolService.checkContextRestrictions.mockRejectedValueOnce(error); + + return { + contextExternalTool, + userId, + schoolId, + context, + error, + }; + }; + + it('should throw an error and not save the contextExternalTool', async () => { + const { contextExternalTool, userId, error, schoolId } = setup(); + + await expect(uc.createContextExternalTool(userId, schoolId, contextExternalTool)).rejects.toThrow(error); + + expect(contextExternalToolService.saveContextExternalTool).not.toHaveBeenCalled(); + }); + }); + describe('when the user is from a different school than the school external tool', () => { const setup = () => { const userId: EntityId = 'userId'; 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 cc5c422d12c..3c2262e943a 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 @@ -220,7 +220,7 @@ describe('ToolConfigurationController (API)', () => { }); }); - it('should not return context restricted tool', async () => { + it('should not return the context restricted tool', async () => { const { board, loggedInClient, externalToolWithoutContextRestriction, schoolExternalTool2 } = await setup(); const response: Response = await loggedInClient.get(`board-element/${board.id}/available-tools`); @@ -700,21 +700,8 @@ describe('ToolConfigurationController (API)', () => { describe('GET tools/context-types', () => { describe('when user is not authorized', () => { - const setup = async () => { - const { teacherAccount, teacherUser } = UserAndAccountTestFactory.buildTeacher(); - - await em.persistAndFlush([teacherAccount, teacherUser]); - em.clear(); - - const loggedInClient: TestApiClient = await testApiClient.login(teacherAccount); - - return { loggedInClient }; - }; - it('should return unauthorized status', async () => { - const { loggedInClient } = await setup(); - - const response = await loggedInClient.get('context-types'); + const response = await testApiClient.get('context-types'); expect(response.status).toEqual(HttpStatus.UNAUTHORIZED); }); diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts index 8cf438ed718..780535f3626 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-create.params.ts @@ -1,6 +1,6 @@ import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsArray, IsBoolean, IsOptional, IsString, ValidateNested } from 'class-validator'; +import { IsArray, IsBoolean, IsEnum, IsOptional, IsString, ValidateNested } from 'class-validator'; import { ToolConfigType, ToolContextType } from '../../../../common/enum'; import { BasicToolConfigParams, @@ -64,6 +64,7 @@ export class ExternalToolCreateParams { @IsArray() @IsOptional() - @ApiPropertyOptional({ enum: ToolContextType, enumName: 'ToolContextType' }) + @IsEnum(ToolContextType, { each: true }) + @ApiPropertyOptional({ enum: ToolContextType, enumName: 'ToolContextType', isArray: true }) restrictToContexts?: ToolContextType[]; } diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts index 65a579b0c2d..be19e9c0bde 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/request/external-tool-update.params.ts @@ -1,6 +1,6 @@ import { ApiExtraModels, ApiProperty, ApiPropertyOptional, getSchemaPath } from '@nestjs/swagger'; import { Type } from 'class-transformer'; -import { IsArray, IsBoolean, IsOptional, IsString, ValidateNested } from 'class-validator'; +import { IsArray, IsBoolean, IsEnum, IsOptional, IsString, ValidateNested } from 'class-validator'; import { ToolConfigType, ToolContextType } from '../../../../common/enum'; import { BasicToolConfigParams, @@ -68,6 +68,7 @@ export class ExternalToolUpdateParams { @IsArray() @IsOptional() - @ApiPropertyOptional({ enum: ToolContextType, enumName: 'ToolContextType' }) + @IsEnum(ToolContextType, { each: true }) + @ApiPropertyOptional({ enum: ToolContextType, enumName: 'ToolContextType', isArray: true }) restrictToContexts?: ToolContextType[]; } diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts index aec1dd8353c..1e3f397a2da 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts @@ -22,7 +22,7 @@ import { CommonToolService } from '../../common/service'; describe('ExternalToolConfigurationService', () => { let module: TestingModule; let service: ExternalToolConfigurationService; - let commonToolservice: DeepMocked; + let commonToolService: DeepMocked; let toolFeatures: IToolFeatures; @@ -47,7 +47,7 @@ describe('ExternalToolConfigurationService', () => { service = module.get(ExternalToolConfigurationService); toolFeatures = module.get(ToolFeatures); - commonToolservice = module.get(CommonToolService); + commonToolService = module.get(CommonToolService); }); afterEach(() => { @@ -205,7 +205,7 @@ describe('ExternalToolConfigurationService', () => { const availableTools: ContextExternalToolTemplateInfo[] = [{ externalTool, schoolExternalTool }]; - commonToolservice.isContextRestricted.mockReturnValueOnce(false); + commonToolService.isContextRestricted.mockReturnValueOnce(false); return { contextType, @@ -213,6 +213,14 @@ describe('ExternalToolConfigurationService', () => { }; }; + it('should check if context is restricted', () => { + const { contextType, availableTools } = setup(); + + service.filterForContextRestrictions(availableTools, contextType); + + expect(commonToolService.isContextRestricted).toHaveBeenCalledWith(availableTools[0].externalTool, contextType); + }); + it('should pass the filter', () => { const { contextType, availableTools } = setup(); @@ -227,7 +235,7 @@ describe('ExternalToolConfigurationService', () => { describe('when context restrictions are given', () => { const setup = () => { - const contextType = ToolContextType.COURSE; + const contextType: ToolContextType = ToolContextType.COURSE; const externalToolWithCourseRestriction: ExternalTool = externalToolFactory.build({ restrictToContexts: [ToolContextType.COURSE], @@ -236,35 +244,26 @@ describe('ExternalToolConfigurationService', () => { restrictToContexts: [ToolContextType.BOARD_ELEMENT], }); - const externalToolWithAllRestrictions: ExternalTool = externalToolFactory.build({ - restrictToContexts: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT], - }); - const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build(); const availableTools: ContextExternalToolTemplateInfo[] = [ { externalTool: externalToolWithCourseRestriction, schoolExternalTool }, { externalTool: externalToolWithBoardRestriction, schoolExternalTool }, - { externalTool: externalToolWithAllRestrictions, schoolExternalTool }, ]; + commonToolService.isContextRestricted.mockReturnValueOnce(false); + commonToolService.isContextRestricted.mockReturnValueOnce(true); + return { contextType, availableTools, externalToolWithCourseRestriction, - externalToolWithAllRestrictions, schoolExternalTool, }; }; it('should only return tools restricted to this context', () => { - const { - contextType, - availableTools, - externalToolWithCourseRestriction, - externalToolWithAllRestrictions, - schoolExternalTool, - } = setup(); + const { contextType, availableTools, externalToolWithCourseRestriction, schoolExternalTool } = setup(); const result: ContextExternalToolTemplateInfo[] = service.filterForContextRestrictions( availableTools, @@ -273,7 +272,6 @@ describe('ExternalToolConfigurationService', () => { expect(result).toEqual([ { externalTool: externalToolWithCourseRestriction, schoolExternalTool }, - { externalTool: externalToolWithAllRestrictions, schoolExternalTool }, ]); }); }); 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 875f6fd3e69..cea8b3c8751 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 @@ -2,15 +2,16 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { ForbiddenException, NotFoundException, UnauthorizedException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; -import { Page, Permission } from '@shared/domain'; +import { Page, Permission, User } from '@shared/domain'; import { contextExternalToolFactory, customParameterFactory, externalToolFactory, schoolExternalToolFactory, setupEntities, + userFactory, } from '@shared/testing'; -import { AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { CustomParameterScope, ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../../context-external-tool/domain'; @@ -31,6 +32,7 @@ describe('ExternalToolConfigurationUc', () => { let contextExternalToolService: DeepMocked; let toolPermissionHelper: DeepMocked; let logoService: DeepMocked; + let authorizationServie: DeepMocked; beforeAll(async () => { await setupEntities(); @@ -62,6 +64,10 @@ describe('ExternalToolConfigurationUc', () => { provide: ExternalToolLogoService, useValue: createMock(), }, + { + provide: AuthorizationService, + useValue: createMock(), + }, ], }).compile(); @@ -72,6 +78,7 @@ describe('ExternalToolConfigurationUc', () => { contextExternalToolService = module.get(ContextExternalToolService); toolPermissionHelper = module.get(ToolPermissionHelper); logoService = module.get(ExternalToolLogoService); + authorizationServie = module.get(AuthorizationService); }); afterEach(() => { @@ -673,16 +680,28 @@ describe('ExternalToolConfigurationUc', () => { describe('when it is called', () => { const setup = () => { const userId: string = new ObjectId().toHexString(); + const user: User = userFactory.build(); + user.id = userId; + + authorizationServie.getUserWithPermissions.mockResolvedValueOnce(user); - return { userId }; + return { userId, user }; }; - it('should check Permission', async () => { + it('should get User', async () => { const { userId } = setup(); await uc.getToolContextTypes(userId); - expect(toolPermissionHelper.ensurePermission).toHaveBeenCalledWith(userId, 'TOOL_ADMIN'); + expect(authorizationServie.getUserWithPermissions).toHaveBeenCalledWith(userId); + }); + + it('should check Permission', async () => { + const { userId, user } = setup(); + + await uc.getToolContextTypes(userId); + + expect(authorizationServie.checkAllPermissions).toHaveBeenCalledWith(user, ['TOOL_ADMIN']); }); it('should get context types', async () => { 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 fdb06194186..483195bdde5 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 @@ -1,8 +1,8 @@ import { forwardRef, Inject, Injectable } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common/exceptions/not-found.exception'; -import { EntityId, Permission } from '@shared/domain'; +import { EntityId, Permission, User } from '@shared/domain'; import { Page } from '@shared/domain/domainobject/page'; -import { AuthorizationContext, AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { CustomParameterScope, ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool } from '../../context-external-tool/domain'; @@ -23,11 +23,13 @@ export class ExternalToolConfigurationUc { @Inject(forwardRef(() => ToolPermissionHelper)) private readonly toolPermissionHelper: ToolPermissionHelper, private readonly externalToolConfigurationService: ExternalToolConfigurationService, - private readonly externalToolLogoService: ExternalToolLogoService + private readonly externalToolLogoService: ExternalToolLogoService, + private readonly authorizationService: AuthorizationService ) {} public async getToolContextTypes(userId: EntityId): Promise { - await this.toolPermissionHelper.ensurePermission(userId, Permission.TOOL_ADMIN); + const user: User = await this.authorizationService.getUserWithPermissions(userId); + this.authorizationService.checkAllPermissions(user, [Permission.TOOL_ADMIN]); const toolContextTypes: ToolContextTypesList = this.externalToolConfigurationService.getToolContextTypes(); From 955066563f51a42fa8c88e9fcb93e20ccf7d1261 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Wed, 29 Nov 2023 17:05:08 +0100 Subject: [PATCH 16/24] requested changes --- .../controller/api-test/tool-context.api.spec.ts | 2 +- .../service/context-external-tool.service.spec.ts | 2 +- .../api-test/tool-configuration.api.spec.ts | 12 ++++++++---- .../external-tool/controller/dto/response/index.ts | 2 +- ...list.ts => tool-context-types-list.response.ts} | 2 +- .../controller/tool-configuration.controller.ts | 14 +++++++++----- .../mapper/tool-configuration.mapper.ts | 8 ++++++++ .../external-tool-configuration.service.spec.ts | 5 ++--- .../service/external-tool-configuration.service.ts | 6 +++--- .../uc/external-tool-configuration.uc.spec.ts | 13 ++++++++++++- .../uc/external-tool-configuration.uc.ts | 5 ++--- 11 files changed, 48 insertions(+), 23 deletions(-) rename apps/server/src/modules/tool/external-tool/controller/dto/response/{tool-context-types-list.ts => tool-context-types-list.response.ts} (86%) 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 0f4d8285039..3cbf215e501 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 @@ -280,7 +280,7 @@ describe('ToolContextController (API)', () => { const response = await loggedInClient.post().send(postParams); - expect(response.statusCode).toEqual(HttpStatus.FORBIDDEN); + expect(response.statusCode).toEqual(HttpStatus.UNPROCESSABLE_ENTITY); }); }); }); 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 bfe25eb1f77..78b9e4cb47f 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 @@ -1,5 +1,4 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { AuthorizationService } from '@modules/authorization'; import { NotFoundException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { ContextExternalToolRepo } from '@shared/repo'; @@ -385,6 +384,7 @@ describe('ContextExternalToolService', () => { schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); externalToolService.findById.mockResolvedValueOnce(externalTool); + commonToolService.isContextRestricted.mockReturnValueOnce(true); return { userId, 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 3c2262e943a..81b5ea1bdd7 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 @@ -17,9 +17,13 @@ import { userFactory, } from '@shared/testing'; import { ServerTestModule } from '@modules/server'; -import { CustomParameterTypeParams, ToolContextType } from '@modules/tool/common/enum'; import { Response } from 'supertest'; -import { CustomParameterLocationParams, CustomParameterScopeTypeParams } from '../../../common/enum'; +import { + CustomParameterLocationParams, + CustomParameterScopeTypeParams, + CustomParameterTypeParams, + ToolContextType, +} from '../../../common/enum'; import { ContextExternalToolEntity, ContextExternalToolType } from '../../../context-external-tool/entity'; import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; import { ExternalToolEntity } from '../../entity'; @@ -28,7 +32,7 @@ import { ContextExternalToolConfigurationTemplateResponse, SchoolExternalToolConfigurationTemplateListResponse, SchoolExternalToolConfigurationTemplateResponse, - ToolContextTypesList, + ToolContextTypesListResponse, } from '../dto'; describe('ToolConfigurationController (API)', () => { @@ -716,7 +720,7 @@ describe('ToolConfigurationController (API)', () => { const loggedInClient: TestApiClient = await testApiClient.login(adminAccount); - const contextTypeList: ToolContextTypesList = new ToolContextTypesList([ + const contextTypeList: ToolContextTypesListResponse = new ToolContextTypesListResponse([ ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT, ]); 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 620a0abaf6f..e621ade2020 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 @@ -7,4 +7,4 @@ export * from './context-external-tool-configuration-template-list.response'; export * from './school-external-tool-configuration-template.response'; export * from './school-external-tool-configuration-template-list.response'; export * from './external-tool-metadata.response'; -export * from './tool-context-types-list'; +export * from './tool-context-types-list.response'; diff --git a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts b/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.response.ts similarity index 86% rename from apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts rename to apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.response.ts index c45accc99b1..9d1b4518514 100644 --- a/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.ts +++ b/apps/server/src/modules/tool/external-tool/controller/dto/response/tool-context-types-list.response.ts @@ -1,7 +1,7 @@ import { ApiProperty } from '@nestjs/swagger'; import { ToolContextType } from '../../../../common/enum'; -export class ToolContextTypesList { +export class ToolContextTypesListResponse { @ApiProperty({ enum: ToolContextType, enumName: 'ToolContextType', isArray: true }) data: ToolContextType[]; 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 569fb58a233..48a3ff0031d 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 @@ -20,8 +20,9 @@ import { SchoolExternalToolConfigurationTemplateResponse, SchoolExternalToolIdParams, SchoolIdParams, - ToolContextTypesList, + ToolContextTypesListResponse, } from './dto'; +import { ToolContextType } from '../../common/enum'; @ApiTags('Tool') @Authenticate('jwt') @@ -34,14 +35,17 @@ export class ToolConfigurationController { @ApiOperation({ summary: 'Lists all context types available in the SVS' }) @ApiOkResponse({ description: 'List of available context types', - type: ToolContextTypesList, + type: ToolContextTypesListResponse, }) - public async getToolContextTypes(@CurrentUser() currentUser: ICurrentUser): Promise { - const toolContextTypes: ToolContextTypesList = await this.externalToolConfigurationUc.getToolContextTypes( + public async getToolContextTypes(@CurrentUser() currentUser: ICurrentUser): Promise { + const toolContextTypes: ToolContextType[] = await this.externalToolConfigurationUc.getToolContextTypes( currentUser.userId ); - return toolContextTypes; + const mapped: ToolContextTypesListResponse = + ToolConfigurationMapper.mapToToolContextTypesListResponse(toolContextTypes); + + return mapped; } @Get('school/:schoolId/available-tools') diff --git a/apps/server/src/modules/tool/external-tool/mapper/tool-configuration.mapper.ts b/apps/server/src/modules/tool/external-tool/mapper/tool-configuration.mapper.ts index a3aebbd6302..b1846d60ea0 100644 --- a/apps/server/src/modules/tool/external-tool/mapper/tool-configuration.mapper.ts +++ b/apps/server/src/modules/tool/external-tool/mapper/tool-configuration.mapper.ts @@ -3,10 +3,12 @@ import { ContextExternalToolConfigurationTemplateResponse, SchoolExternalToolConfigurationTemplateListResponse, SchoolExternalToolConfigurationTemplateResponse, + ToolContextTypesListResponse, } from '../controller/dto'; import { ExternalTool } from '../domain'; import { ContextExternalToolTemplateInfo } from '../uc'; import { ExternalToolResponseMapper } from './external-tool-response.mapper'; +import { ToolContextType } from '../../common/enum'; export class ToolConfigurationMapper { static mapToSchoolExternalToolConfigurationTemplateResponse( @@ -69,4 +71,10 @@ export class ToolConfigurationMapper { return mapped; } + + static mapToToolContextTypesListResponse(toolContextTypes: ToolContextType[]): ToolContextTypesListResponse { + const mappedTypes = new ToolContextTypesListResponse(toolContextTypes); + + return mappedTypes; + } } diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts index 1e3f397a2da..e27016ccbb5 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.spec.ts @@ -16,7 +16,6 @@ import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; import { ContextExternalToolTemplateInfo } from '../uc'; import { ExternalToolConfigurationService } from './external-tool-configuration.service'; -import { ToolContextTypesList } from '../controller/dto/response/tool-context-types-list'; import { CommonToolService } from '../../common/service'; describe('ExternalToolConfigurationService', () => { @@ -305,9 +304,9 @@ describe('ExternalToolConfigurationService', () => { describe('getToolContextTypes', () => { describe('when it is called', () => { it('should return ToolContextTypes', () => { - const types: ToolContextTypesList = service.getToolContextTypes(); + const types: ToolContextType[] = service.getToolContextTypes(); - expect(types).toEqual({ data: [ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT] }); + expect(types).toEqual([ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT]); }); }); }); diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts index b0738aa691c..6b17ccc39a6 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts @@ -7,7 +7,7 @@ import { SchoolExternalTool } from '../../school-external-tool/domain'; import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; import { ContextExternalToolTemplateInfo } from '../uc/dto'; -import { ToolContextTypesList } from '../controller/dto/response/tool-context-types-list'; +import { ToolContextTypesListResponse } from '../controller/dto/response/tool-context-types-list.response'; import { CommonToolService } from '../../common/service'; @Injectable() @@ -97,8 +97,8 @@ export class ExternalToolConfigurationService { } } - public getToolContextTypes(): ToolContextTypesList { - const toolContextTypes: ToolContextTypesList = { data: Object.values(ToolContextType) }; + public getToolContextTypes(): ToolContextType[] { + const toolContextTypes: ToolContextType[] = Object.values(ToolContextType); return toolContextTypes; } 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 17ec8756b6c..d1f45a857b8 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 @@ -682,10 +682,13 @@ describe('ExternalToolConfigurationUc', () => { const userId: string = new ObjectId().toHexString(); const user: User = userFactory.build(); user.id = userId; + const contextTypes: ToolContextType[] = Object.values(ToolContextType); authorizationServie.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationServie.checkAllPermissions.mockReturnValueOnce(); + externalToolConfigurationService.getToolContextTypes.mockReturnValueOnce(contextTypes); - return { userId, user }; + return { userId, user, contextTypes }; }; it('should get User', async () => { @@ -711,6 +714,14 @@ describe('ExternalToolConfigurationUc', () => { expect(externalToolConfigurationService.getToolContextTypes).toHaveBeenCalled(); }); + + it('should return all context types', async () => { + const { userId } = setup(); + + const result = await uc.getToolContextTypes(userId); + + expect(result).toEqual([ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT]); + }); }); }); }); 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 f614eeb7fbe..333fa1042e5 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 @@ -12,7 +12,6 @@ import { SchoolExternalToolService } from '../../school-external-tool/service'; import { ExternalTool } from '../domain'; import { ExternalToolConfigurationService, ExternalToolLogoService, ExternalToolService } from '../service'; import { ContextExternalToolTemplateInfo } from './dto'; -import { ToolContextTypesList } from '../controller/dto/response'; @Injectable() export class ExternalToolConfigurationUc { @@ -27,11 +26,11 @@ export class ExternalToolConfigurationUc { private readonly authorizationService: AuthorizationService ) {} - public async getToolContextTypes(userId: EntityId): Promise { + public async getToolContextTypes(userId: EntityId): Promise { const user: User = await this.authorizationService.getUserWithPermissions(userId); this.authorizationService.checkAllPermissions(user, [Permission.TOOL_ADMIN]); - const toolContextTypes: ToolContextTypesList = this.externalToolConfigurationService.getToolContextTypes(); + const toolContextTypes: ToolContextType[] = this.externalToolConfigurationService.getToolContextTypes(); return toolContextTypes; } From 04fa2c7c2449d0e92ff318da87f3d79c3622ab04 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Thu, 30 Nov 2023 09:36:01 +0100 Subject: [PATCH 17/24] fix tests and imports --- .../modules/tool/common/uc/tool-permission-helper.ts | 2 +- .../uc/context-external-tool.uc.spec.ts | 11 ++--------- .../service/external-tool-configuration.service.ts | 1 - 3 files changed, 3 insertions(+), 11 deletions(-) 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 0c1677f7a70..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 @@ -4,7 +4,7 @@ import { BoardDoAuthorizableService, ContentElementService } from '@modules/boar import { CourseService } from '@modules/learnroom'; import { LegacySchoolService } from '@modules/legacy-school'; import { forwardRef, Inject, Injectable } from '@nestjs/common'; -import { BoardDoAuthorizable, Course, EntityId, LegacySchoolDo, Permission, User } from '@shared/domain'; +import { BoardDoAuthorizable, Course, EntityId, LegacySchoolDo, User } from '@shared/domain'; import { ContextExternalTool } from '../../context-external-tool/domain'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { ToolContextType } from '../enum'; 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 989002aab4e..b71d6305b19 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 @@ -97,8 +97,6 @@ describe('ContextExternalToolUc', () => { }, }); - const context: AuthorizationContext = AuthorizationContextBuilder.write([Permission.CONTEXT_TOOL_ADMIN]); - schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); contextExternalToolService.saveContextExternalTool.mockResolvedValue(contextExternalTool); @@ -106,7 +104,6 @@ describe('ContextExternalToolUc', () => { contextExternalTool, userId, schoolId, - context, }; }; @@ -131,15 +128,11 @@ describe('ContextExternalToolUc', () => { }); it('should check for context restrictions', async () => { - const { contextExternalTool, userId, schoolId, context } = setup(); + const { contextExternalTool, userId, schoolId } = setup(); await uc.createContextExternalTool(userId, schoolId, contextExternalTool); - expect(contextExternalToolService.checkContextRestrictions).toHaveBeenCalledWith( - contextExternalTool, - userId, - context - ); + expect(contextExternalToolService.checkContextRestrictions).toHaveBeenCalledWith(contextExternalTool); }); it('should call contextExternalToolValidationService', async () => { diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts index 6b17ccc39a6..b4fb636caae 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts @@ -7,7 +7,6 @@ import { SchoolExternalTool } from '../../school-external-tool/domain'; import { IToolFeatures, ToolFeatures } from '../../tool-config'; import { ExternalTool } from '../domain'; import { ContextExternalToolTemplateInfo } from '../uc/dto'; -import { ToolContextTypesListResponse } from '../controller/dto/response/tool-context-types-list.response'; import { CommonToolService } from '../../common/service'; @Injectable() From 8282351ea45c1897e8bbf202896bbf924e6edebf Mon Sep 17 00:00:00 2001 From: Malte Berg Date: Thu, 30 Nov 2023 11:06:06 +0100 Subject: [PATCH 18/24] add seed data --- backup/setup/external-tools.json | 24 ++++++++++++++++++++++++ backup/setup/school-external-tools.json | 23 +++++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/backup/setup/external-tools.json b/backup/setup/external-tools.json index 2a3fd937a37..5c6dbd53622 100644 --- a/backup/setup/external-tools.json +++ b/backup/setup/external-tools.json @@ -99,5 +99,29 @@ "isHidden": false, "openNewTab": false, "version": 2 + }, + { + "_id": { + "$oid": "644a4593d0a8301e6cf25d86" + }, + "createdAt": { + "$date": "2023-04-27T09:51:15.592Z" + }, + "updatedAt": { + "$date": "2023-04-27T09:51:15.592Z" + }, + "name": "CY Test Tool Board-Element Restriction", + "url": "https://google.de/", + "logoUrl": "https://www.google.de/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", + "logoBase64": "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", + "config_type": "basic", + "config_baseUrl": "https://google.de/", + "parameters": [], + "isHidden": false, + "openNewTab": true, + "version": 1, + "restrictToContexts": [ + "board-element" + ] } ] diff --git a/backup/setup/school-external-tools.json b/backup/setup/school-external-tools.json index 59befc464af..081baf8ba1f 100644 --- a/backup/setup/school-external-tools.json +++ b/backup/setup/school-external-tools.json @@ -68,5 +68,28 @@ }, "schoolParameters": [], "toolVersion": 2 + }, + { + "_id": { + "$oid": "647de374cf6a427b9d39e5bc" + }, + "createdAt": { + "$date": { + "$numberLong": "1685971828284" + } + }, + "updatedAt": { + "$date": { + "$numberLong": "1685971828284" + } + }, + "tool": { + "$oid": "644a4593d0a8301e6cf25d86" + }, + "school": { + "$oid": "5fa2c5ccb229544f2c69666c" + }, + "schoolParameters": [], + "toolVersion": 1 } ] From 8a9aa358af6a1feb1e7e169b2693db91b54d5447 Mon Sep 17 00:00:00 2001 From: Malte Berg Date: Thu, 30 Nov 2023 11:36:45 +0100 Subject: [PATCH 19/24] remove logo --- backup/setup/external-tools.json | 2 -- 1 file changed, 2 deletions(-) diff --git a/backup/setup/external-tools.json b/backup/setup/external-tools.json index 5c6dbd53622..ffec19620bd 100644 --- a/backup/setup/external-tools.json +++ b/backup/setup/external-tools.json @@ -112,8 +112,6 @@ }, "name": "CY Test Tool Board-Element Restriction", "url": "https://google.de/", - "logoUrl": "https://www.google.de/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png", - "logoBase64": "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", "config_type": "basic", "config_baseUrl": "https://google.de/", "parameters": [], From 2f2601bbfcfafdfd33f494591030395c38459d20 Mon Sep 17 00:00:00 2001 From: Malte Berg Date: Fri, 1 Dec 2023 11:57:48 +0100 Subject: [PATCH 20/24] change seed data --- backup/setup/school-external-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/setup/school-external-tools.json b/backup/setup/school-external-tools.json index 081baf8ba1f..a9240f2b122 100644 --- a/backup/setup/school-external-tools.json +++ b/backup/setup/school-external-tools.json @@ -71,7 +71,7 @@ }, { "_id": { - "$oid": "647de374cf6a427b9d39e5bc" + "$oid": "647de374cf6a427b9d39e5zz" }, "createdAt": { "$date": { From 3ad8e7e45fa22a35cfe10ca99cb15b36ef47ec21 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Fri, 1 Dec 2023 13:41:34 +0100 Subject: [PATCH 21/24] requested changes --- .../controller/api-test/tool-context.api.spec.ts | 2 +- .../service/context-external-tool.service.spec.ts | 11 ++++------- .../service/context-external-tool.service.ts | 4 ++-- .../api-test/tool-configuration.api.spec.ts | 1 - .../service/external-tool-configuration.service.ts | 4 ++-- .../uc/external-tool-configuration.uc.spec.ts | 3 +-- .../uc/external-tool-configuration.uc.ts | 4 ++-- .../api-test/tool-launch.controller.api.spec.ts | 3 ++- 8 files changed, 14 insertions(+), 18 deletions(-) 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 42bbbae2936..6a645cc944d 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 @@ -276,7 +276,7 @@ describe('ToolContextController (API)', () => { }; }; - it('should return forbidden', async () => { + it('should return unprocessable entity', async () => { const { postParams, loggedInClient } = await setup(); const response = await loggedInClient.post().send(postParams); 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 78b9e4cb47f..a05a3bd7370 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 @@ -10,7 +10,7 @@ import { } from '@shared/testing/factory/domainobject'; import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { ObjectId } from '@mikro-orm/mongodb'; -import { Permission } from '@shared/domain'; +import { Permission } from '@shared/domain/interface'; import { ToolContextType } from '../../common/enum'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { ContextExternalTool, ContextRef } from '../domain'; @@ -333,9 +333,7 @@ describe('ContextExternalToolService', () => { it('should not throw', async () => { const { contextExternalTool } = setup(); - const func = async () => service.checkContextRestrictions(contextExternalTool); - - await expect(func()).resolves.not.toThrow(); + await expect(service.checkContextRestrictions(contextExternalTool)).resolves.not.toThrow(); }); }); @@ -352,6 +350,7 @@ describe('ContextExternalToolService', () => { schoolExternalToolService.findById.mockResolvedValueOnce(schoolExternalTool); externalToolService.findById.mockResolvedValueOnce(externalTool); + commonToolService.isContextRestricted.mockReturnValueOnce(false); return { userId, @@ -363,9 +362,7 @@ describe('ContextExternalToolService', () => { it('should not throw', async () => { const { contextExternalTool } = setup(); - const func = async () => service.checkContextRestrictions(contextExternalTool); - - await expect(func()).resolves.not.toThrow(); + await expect(service.checkContextRestrictions(contextExternalTool)).resolves.not.toThrow(); }); }); 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 ca9a76399f5..baf6bd82b06 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 @@ -25,13 +25,13 @@ export class ContextExternalToolService { return contextExternalTools; } - async findByIdOrFail(contextExternalToolId: EntityId): Promise { + public async findByIdOrFail(contextExternalToolId: EntityId): Promise { const tool: ContextExternalTool = await this.contextExternalToolRepo.findById(contextExternalToolId); return tool; } - async findById(contextExternalToolId: EntityId): Promise { + public async findById(contextExternalToolId: EntityId): Promise { const tool: ContextExternalTool | null = await this.contextExternalToolRepo.findByIdOrNull(contextExternalToolId); return tool; 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 2aed170074d..1207c326ea8 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 @@ -1,7 +1,6 @@ import { EntityManager, MikroORM } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; import { ServerTestModule } from '@modules/server'; -import { CustomParameterTypeParams } from '@modules/tool/common/enum'; import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Account, Board, Course, SchoolEntity, User } from '@shared/domain/entity'; diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts index 84a218263ed..da852d195c0 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-configuration.service.ts @@ -14,7 +14,7 @@ import { CommonToolService } from '../../common/service'; export class ExternalToolConfigurationService { constructor( @Inject(ToolFeatures) private readonly toolFeatures: IToolFeatures, - private readonly commonTooLService: CommonToolService + private readonly commonToolService: CommonToolService ) {} public filterForAvailableTools(externalTools: Page, toolIdsInUse: EntityId[]): ExternalTool[] { @@ -84,7 +84,7 @@ export class ExternalToolConfigurationService { contextType: ToolContextType ): ContextExternalToolTemplateInfo[] { const availableToolsForContext: ContextExternalToolTemplateInfo[] = availableTools.filter( - (availableTool) => !this.commonTooLService.isContextRestricted(availableTool.externalTool, contextType) + (availableTool) => !this.commonToolService.isContextRestricted(availableTool.externalTool, contextType) ); return availableToolsForContext; } 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 2974194099d..e53e0c6cfb7 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 @@ -2,10 +2,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { ForbiddenException, NotFoundException, UnauthorizedException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; - import { Page } from '@shared/domain/domainobject'; import { Permission } from '@shared/domain/interface'; -// import { Page, Permission, User } from '@shared/domain'; import { contextExternalToolFactory, customParameterFactory, @@ -15,6 +13,7 @@ import { userFactory, } from '@shared/testing'; import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; +import { User } from '@shared/domain/entity'; 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 c2cd1849736..90d9f8f718c 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 @@ -1,10 +1,10 @@ -import { AuthorizationContext, AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationContext, AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { Inject, Injectable, forwardRef } from '@nestjs/common'; import { NotFoundException } from '@nestjs/common/exceptions/not-found.exception'; -// import { EntityId, Permission, User } from '@shared/domain'; import { Page } from '@shared/domain/domainobject/page'; import { Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; +import { User } from '@shared/domain/entity'; 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/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 f449a593458..aa35059b564 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 @@ -14,9 +14,10 @@ import { externalToolEntityFactory, schoolExternalToolEntityFactory, schoolFactory, + customParameterFactory, } from '@shared/testing'; import { Response } from 'supertest'; -import { ToolConfigType } from '../../../common/enum'; +import { CustomParameterLocation, CustomParameterScope, ToolConfigType } from '../../../common/enum'; import { ContextExternalToolEntity, ContextExternalToolType } from '../../../context-external-tool/entity'; import { ExternalToolEntity } from '../../../external-tool/entity'; import { SchoolExternalToolEntity } from '../../../school-external-tool/entity'; From 18d3f8af1197899ef84bb1eb9409cc6a4c4fd8c3 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Fri, 1 Dec 2023 14:16:25 +0100 Subject: [PATCH 22/24] delete loggable constructor tests --- ...-token-creation-exception.loggable.spec.ts | 17 ---------------- .../group-role-unknown.loggable.spec.ts | 19 ------------------ ...chool-for-group-not-found.loggable.spec.ts | 16 --------------- .../user-for-group-not-found.loggable.spec.ts | 19 ------------------ ...many-pseudonyms.loggable-exception.spec.ts | 16 --------------- ...tricted-context-mismatch-loggabble.spec.ts | 17 ---------------- ...go-fetch-failed-loggable-exception.spec.ts | 16 --------------- ...xternal-tool-logo-fetched-loggable.spec.ts | 16 --------------- ...-logo-not-found-loggable-exception.spec.ts | 16 --------------- ...o-size-exceeded-loggable-exception.spec.ts | 20 ------------------- ...wrong-file-type-loggable-exception.spec.ts | 8 -------- 11 files changed, 180 deletions(-) diff --git a/apps/server/src/modules/oauth-provider/error/id-token-creation-exception.loggable.spec.ts b/apps/server/src/modules/oauth-provider/error/id-token-creation-exception.loggable.spec.ts index 921049458e4..db9cd13b9a1 100644 --- a/apps/server/src/modules/oauth-provider/error/id-token-creation-exception.loggable.spec.ts +++ b/apps/server/src/modules/oauth-provider/error/id-token-creation-exception.loggable.spec.ts @@ -1,23 +1,6 @@ import { IdTokenCreationLoggableException } from './id-token-creation-exception.loggable'; describe('IdTokenCreationExceptionLoggable', () => { - describe('constructor', () => { - const setup = () => { - const clientId = 'clientId'; - const userId = 'userId'; - - return { clientId, userId }; - }; - - it('should create an instance of IdTokenCreationExceptionLoggable', () => { - const { clientId, userId } = setup(); - - const loggable = new IdTokenCreationLoggableException(clientId, userId); - - expect(loggable).toBeInstanceOf(IdTokenCreationLoggableException); - }); - }); - describe('getLogMessage', () => { const setup = () => { const clientId = 'clientId'; diff --git a/apps/server/src/modules/provisioning/loggable/group-role-unknown.loggable.spec.ts b/apps/server/src/modules/provisioning/loggable/group-role-unknown.loggable.spec.ts index 04b2ad3cba5..46d250f0f24 100644 --- a/apps/server/src/modules/provisioning/loggable/group-role-unknown.loggable.spec.ts +++ b/apps/server/src/modules/provisioning/loggable/group-role-unknown.loggable.spec.ts @@ -2,25 +2,6 @@ import { SanisGroupRole, SanisSonstigeGruppenzugehoerigeResponse } from '../stra import { GroupRoleUnknownLoggable } from './group-role-unknown.loggable'; describe('GroupRoleUnknownLoggable', () => { - describe('constructor', () => { - const setup = () => { - const sanisSonstigeGruppenzugehoerigeResponse: SanisSonstigeGruppenzugehoerigeResponse = { - ktid: 'ktid', - rollen: [SanisGroupRole.TEACHER], - }; - - return { sanisSonstigeGruppenzugehoerigeResponse }; - }; - - it('should create an instance of UserForGroupNotFoundLoggable', () => { - const { sanisSonstigeGruppenzugehoerigeResponse } = setup(); - - const loggable = new GroupRoleUnknownLoggable(sanisSonstigeGruppenzugehoerigeResponse); - - expect(loggable).toBeInstanceOf(GroupRoleUnknownLoggable); - }); - }); - describe('getLogMessage', () => { const setup = () => { const sanisSonstigeGruppenzugehoerigeResponse: SanisSonstigeGruppenzugehoerigeResponse = { diff --git a/apps/server/src/modules/provisioning/loggable/school-for-group-not-found.loggable.spec.ts b/apps/server/src/modules/provisioning/loggable/school-for-group-not-found.loggable.spec.ts index 205c6481529..eb17654e1a9 100644 --- a/apps/server/src/modules/provisioning/loggable/school-for-group-not-found.loggable.spec.ts +++ b/apps/server/src/modules/provisioning/loggable/school-for-group-not-found.loggable.spec.ts @@ -3,22 +3,6 @@ import { ExternalGroupDto } from '../dto'; import { SchoolForGroupNotFoundLoggable } from './school-for-group-not-found.loggable'; describe('SchoolForGroupNotFoundLoggable', () => { - describe('constructor', () => { - const setup = () => { - const externalGroupDto: ExternalGroupDto = externalGroupDtoFactory.build(); - - return { externalGroupDto }; - }; - - it('should create an instance of UserForGroupNotFoundLoggable', () => { - const { externalGroupDto } = setup(); - - const loggable = new SchoolForGroupNotFoundLoggable(externalGroupDto); - - expect(loggable).toBeInstanceOf(SchoolForGroupNotFoundLoggable); - }); - }); - describe('getLogMessage', () => { const setup = () => { const externalGroupDto: ExternalGroupDto = externalGroupDtoFactory.build(); diff --git a/apps/server/src/modules/provisioning/loggable/user-for-group-not-found.loggable.spec.ts b/apps/server/src/modules/provisioning/loggable/user-for-group-not-found.loggable.spec.ts index 9bb8ee631bc..96095619d5e 100644 --- a/apps/server/src/modules/provisioning/loggable/user-for-group-not-found.loggable.spec.ts +++ b/apps/server/src/modules/provisioning/loggable/user-for-group-not-found.loggable.spec.ts @@ -3,25 +3,6 @@ import { ExternalGroupUserDto } from '../dto'; import { UserForGroupNotFoundLoggable } from './user-for-group-not-found.loggable'; describe('UserForGroupNotFoundLoggable', () => { - describe('constructor', () => { - const setup = () => { - const externalGroupUserDto: ExternalGroupUserDto = new ExternalGroupUserDto({ - externalUserId: 'externalUserId', - roleName: RoleName.TEACHER, - }); - - return { externalGroupUserDto }; - }; - - it('should create an instance of UserForGroupNotFoundLoggable', () => { - const { externalGroupUserDto } = setup(); - - const loggable = new UserForGroupNotFoundLoggable(externalGroupUserDto); - - expect(loggable).toBeInstanceOf(UserForGroupNotFoundLoggable); - }); - }); - describe('getLogMessage', () => { const setup = () => { const externalGroupUserDto: ExternalGroupUserDto = new ExternalGroupUserDto({ diff --git a/apps/server/src/modules/pseudonym/loggable/too-many-pseudonyms.loggable-exception.spec.ts b/apps/server/src/modules/pseudonym/loggable/too-many-pseudonyms.loggable-exception.spec.ts index b03350c38ba..9d9cf50165e 100644 --- a/apps/server/src/modules/pseudonym/loggable/too-many-pseudonyms.loggable-exception.spec.ts +++ b/apps/server/src/modules/pseudonym/loggable/too-many-pseudonyms.loggable-exception.spec.ts @@ -1,22 +1,6 @@ import { TooManyPseudonymsLoggableException } from './too-many-pseudonyms.loggable-exception'; describe('TooManyPseudonymsLoggableException', () => { - describe('constructor', () => { - const setup = () => { - const pseudonym = 'pseudonym'; - - return { pseudonym }; - }; - - it('should create an instance of TooManyPseudonymsLoggableException', () => { - const { pseudonym } = setup(); - - const loggable = new TooManyPseudonymsLoggableException(pseudonym); - - expect(loggable).toBeInstanceOf(TooManyPseudonymsLoggableException); - }); - }); - describe('getLogMessage', () => { const setup = () => { const pseudonym = 'pseudonym'; diff --git a/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.spec.ts b/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.spec.ts index c7de14f196e..7e57cb79cc5 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/restricted-context-mismatch-loggabble.spec.ts @@ -2,23 +2,6 @@ import { ToolContextType } from '../../common/enum'; import { RestrictedContextMismatchLoggable } from './restricted-context-mismatch-loggabble'; describe('RestrictedContextMismatchLoggable', () => { - describe('constructor', () => { - const setup = () => { - const externalToolName = 'name'; - const context: ToolContextType = ToolContextType.COURSE; - - return { externalToolName, context }; - }; - - it('should create an instance of RestrictedContextMismatchLoggable', () => { - const { externalToolName, context } = setup(); - - const loggable = new RestrictedContextMismatchLoggable(externalToolName, context); - - expect(loggable).toBeInstanceOf(RestrictedContextMismatchLoggable); - }); - }); - describe('getLogMessage', () => { const setup = () => { const externalToolName = 'name'; diff --git a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-fetch-failed-loggable-exception.spec.ts b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-fetch-failed-loggable-exception.spec.ts index 8674cba0412..e5537972eeb 100644 --- a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-fetch-failed-loggable-exception.spec.ts +++ b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-fetch-failed-loggable-exception.spec.ts @@ -1,22 +1,6 @@ import { ExternalToolLogoFetchFailedLoggableException } from './external-tool-logo-fetch-failed-loggable-exception'; describe('ExternalToolLogoFetchFailedLoggableException', () => { - describe('constructor', () => { - const setup = () => { - const logoUrl = 'logoUrl'; - - return { logoUrl }; - }; - - it('should create an instance of ExternalToolLogoNotFoundLoggableException', () => { - const { logoUrl } = setup(); - - const loggable = new ExternalToolLogoFetchFailedLoggableException(logoUrl, undefined); - - expect(loggable).toBeInstanceOf(ExternalToolLogoFetchFailedLoggableException); - }); - }); - describe('getLogMessage', () => { const setup = () => { const logoUrl = 'logoUrl'; diff --git a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-fetched-loggable.spec.ts b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-fetched-loggable.spec.ts index 56d32494f9d..ab3dd7c6cc2 100644 --- a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-fetched-loggable.spec.ts +++ b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-fetched-loggable.spec.ts @@ -1,22 +1,6 @@ import { ExternalToolLogoFetchedLoggable } from './external-tool-logo-fetched-loggable'; describe('ExternalToolLogoFetchedLoggable', () => { - describe('constructor', () => { - const setup = () => { - const logoUrl = 'logoUrl'; - - return { logoUrl }; - }; - - it('should create an instance of ExternalToolLogoFetchedLoggable', () => { - const { logoUrl } = setup(); - - const loggable = new ExternalToolLogoFetchedLoggable(logoUrl); - - expect(loggable).toBeInstanceOf(ExternalToolLogoFetchedLoggable); - }); - }); - describe('getLogMessage', () => { const setup = () => { const logoUrl = 'logoUrl'; diff --git a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-not-found-loggable-exception.spec.ts b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-not-found-loggable-exception.spec.ts index de6c298a3b2..aa8ae13cd28 100644 --- a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-not-found-loggable-exception.spec.ts +++ b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-not-found-loggable-exception.spec.ts @@ -1,22 +1,6 @@ import { ExternalToolLogoNotFoundLoggableException } from './external-tool-logo-not-found-loggable-exception'; describe('ExternalToolLogoNotFoundLoggableException', () => { - describe('constructor', () => { - const setup = () => { - const externalToolId = 'externalToolId'; - - return { externalToolId }; - }; - - it('should create an instance of ExternalToolLogoNotFoundLoggableException', () => { - const { externalToolId } = setup(); - - const loggable = new ExternalToolLogoNotFoundLoggableException(externalToolId); - - expect(loggable).toBeInstanceOf(ExternalToolLogoNotFoundLoggableException); - }); - }); - describe('getLogMessage', () => { const setup = () => { const externalToolId = 'externalToolId'; diff --git a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-size-exceeded-loggable-exception.spec.ts b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-size-exceeded-loggable-exception.spec.ts index e22b66d49fd..36636722eba 100644 --- a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-size-exceeded-loggable-exception.spec.ts +++ b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-size-exceeded-loggable-exception.spec.ts @@ -1,26 +1,6 @@ import { ExternalToolLogoSizeExceededLoggableException } from './external-tool-logo-size-exceeded-loggable-exception'; describe('ExternalToolLogoSizeExceededLoggableException', () => { - describe('constructor', () => { - const setup = () => { - const externalToolId = 'externalToolId'; - const maxExternalToolLogoSizeInBytes = 100; - - return { externalToolId, maxExternalToolLogoSizeInBytes }; - }; - - it('should create an instance of ExternalToolLogoSizeExceededLoggableException', () => { - const { externalToolId, maxExternalToolLogoSizeInBytes } = setup(); - - const loggable = new ExternalToolLogoSizeExceededLoggableException( - externalToolId, - maxExternalToolLogoSizeInBytes - ); - - expect(loggable).toBeInstanceOf(ExternalToolLogoSizeExceededLoggableException); - }); - }); - describe('getLogMessage', () => { const setup = () => { const externalToolId = 'externalToolId'; diff --git a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-wrong-file-type-loggable-exception.spec.ts b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-wrong-file-type-loggable-exception.spec.ts index cd5e5fa87d3..822e61c613f 100644 --- a/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-wrong-file-type-loggable-exception.spec.ts +++ b/apps/server/src/modules/tool/external-tool/loggable/external-tool-logo-wrong-file-type-loggable-exception.spec.ts @@ -1,14 +1,6 @@ import { ExternalToolLogoWrongFileTypeLoggableException } from './external-tool-logo-wrong-file-type-loggable-exception'; describe('ExternalToolLogoWrongFileTypeLoggableException', () => { - describe('constructor', () => { - it('should create an instance of ExternalToolLogoSizeExceededLoggableException', () => { - const loggable = new ExternalToolLogoWrongFileTypeLoggableException(); - - expect(loggable).toBeInstanceOf(ExternalToolLogoWrongFileTypeLoggableException); - }); - }); - describe('getLogMessage', () => { const setup = () => { const loggable = new ExternalToolLogoWrongFileTypeLoggableException(); From c7885a677baa8c150c185ba33265f5367f977206 Mon Sep 17 00:00:00 2001 From: Malte Berg Date: Fri, 1 Dec 2023 15:02:40 +0100 Subject: [PATCH 23/24] change seed data --- backup/setup/school-external-tools.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backup/setup/school-external-tools.json b/backup/setup/school-external-tools.json index a9240f2b122..b3d4e4b0197 100644 --- a/backup/setup/school-external-tools.json +++ b/backup/setup/school-external-tools.json @@ -71,7 +71,7 @@ }, { "_id": { - "$oid": "647de374cf6a427b9d39e5zz" + "$oid": "65685717ed7c14d921698602" }, "createdAt": { "$date": { From 78c0af512602bf47c3f2d059e8c7fca481612bd3 Mon Sep 17 00:00:00 2001 From: Igor Richter Date: Fri, 1 Dec 2023 15:29:06 +0100 Subject: [PATCH 24/24] add unauthorized test --- .../uc/external-tool-configuration.uc.spec.ts | 35 +++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) 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 e53e0c6cfb7..933ff82c9a2 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 @@ -34,7 +34,7 @@ describe('ExternalToolConfigurationUc', () => { let contextExternalToolService: DeepMocked; let toolPermissionHelper: DeepMocked; let logoService: DeepMocked; - let authorizationServie: DeepMocked; + let authorizationService: DeepMocked; beforeAll(async () => { await setupEntities(); @@ -80,7 +80,7 @@ describe('ExternalToolConfigurationUc', () => { contextExternalToolService = module.get(ContextExternalToolService); toolPermissionHelper = module.get(ToolPermissionHelper); logoService = module.get(ExternalToolLogoService); - authorizationServie = module.get(AuthorizationService); + authorizationService = module.get(AuthorizationService); }); afterEach(() => { @@ -686,8 +686,8 @@ describe('ExternalToolConfigurationUc', () => { user.id = userId; const contextTypes: ToolContextType[] = Object.values(ToolContextType); - authorizationServie.getUserWithPermissions.mockResolvedValueOnce(user); - authorizationServie.checkAllPermissions.mockReturnValueOnce(); + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationService.checkAllPermissions.mockReturnValueOnce(); externalToolConfigurationService.getToolContextTypes.mockReturnValueOnce(contextTypes); return { userId, user, contextTypes }; @@ -698,7 +698,7 @@ describe('ExternalToolConfigurationUc', () => { await uc.getToolContextTypes(userId); - expect(authorizationServie.getUserWithPermissions).toHaveBeenCalledWith(userId); + expect(authorizationService.getUserWithPermissions).toHaveBeenCalledWith(userId); }); it('should check Permission', async () => { @@ -706,7 +706,7 @@ describe('ExternalToolConfigurationUc', () => { await uc.getToolContextTypes(userId); - expect(authorizationServie.checkAllPermissions).toHaveBeenCalledWith(user, ['TOOL_ADMIN']); + expect(authorizationService.checkAllPermissions).toHaveBeenCalledWith(user, ['TOOL_ADMIN']); }); it('should get context types', async () => { @@ -725,5 +725,28 @@ describe('ExternalToolConfigurationUc', () => { expect(result).toEqual([ToolContextType.COURSE, ToolContextType.BOARD_ELEMENT]); }); }); + + describe('when user does not have enough Permission', () => { + const setup = () => { + const userId: string = new ObjectId().toHexString(); + const user: User = userFactory.build(); + user.id = userId; + const contextTypes: ToolContextType[] = Object.values(ToolContextType); + + authorizationService.getUserWithPermissions.mockResolvedValueOnce(user); + authorizationService.checkAllPermissions.mockImplementation(() => { + throw new UnauthorizedException(); + }); + externalToolConfigurationService.getToolContextTypes.mockReturnValueOnce(contextTypes); + + return { userId }; + }; + + it('should throw unauthorized', async () => { + const { userId } = setup(); + + await expect(uc.getToolContextTypes(userId)).rejects.toThrow(new UnauthorizedException()); + }); + }); }); });