From c7dee3d3aafe1741e68462c82cef97dd8ac6f819 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 27 Sep 2024 13:38:10 +0200 Subject: [PATCH 01/23] move context external tool authorisation code --- .../modules/authorization/authorization.module.ts | 1 - .../modules/authorization/domain/rules/index.ts | 2 +- .../domain/service/reference.loader.ts | 7 ------- .../authorization/domain/service/rule-manager.ts | 3 --- .../context-external-tool.rule.spec.ts | 3 +-- .../authorisation}/context-external-tool.rule.ts | 15 ++++++++++++--- .../context-external-tool.module.ts | 2 ++ .../context-external-tool-authorizable.service.ts | 13 +++++++++++-- 8 files changed, 27 insertions(+), 19 deletions(-) rename apps/server/src/modules/{authorization/domain/rules => tool/context-external-tool/authorisation}/context-external-tool.rule.spec.ts (97%) rename apps/server/src/modules/{authorization/domain/rules => tool/context-external-tool/authorisation}/context-external-tool.rule.ts (77%) diff --git a/apps/server/src/modules/authorization/authorization.module.ts b/apps/server/src/modules/authorization/authorization.module.ts index 289b7209f49..9383ec6ef2e 100644 --- a/apps/server/src/modules/authorization/authorization.module.ts +++ b/apps/server/src/modules/authorization/authorization.module.ts @@ -35,7 +35,6 @@ import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; RuleManager, AuthorizationHelper, // rules - ContextExternalToolRule, CourseGroupRule, CourseRule, GroupRule, diff --git a/apps/server/src/modules/authorization/domain/rules/index.ts b/apps/server/src/modules/authorization/domain/rules/index.ts index 437d8d2d6df..697b3c5769c 100644 --- a/apps/server/src/modules/authorization/domain/rules/index.ts +++ b/apps/server/src/modules/authorization/domain/rules/index.ts @@ -2,7 +2,7 @@ * Rules are currently placed in authorization module to avoid dependency cycles. * In future they must be moved to the feature modules and register it in registration service. */ -export * from './context-external-tool.rule'; +export * from '../../../tool/context-external-tool/authorisation/context-external-tool.rule'; export * from './course-group.rule'; export * from './course.rule'; export * from './legacy-school.rule'; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts index 43aecf0f1e3..890fec52bd2 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -1,7 +1,5 @@ // TODO fix modules circular dependency // eslint-disable-next-line @typescript-eslint/no-restricted-imports -import { ContextExternalToolAuthorizableService } from '@modules/tool/context-external-tool/service'; -// eslint-disable-next-line @typescript-eslint/no-restricted-imports import { TeamAuthorisableService } from '@src/modules/teams/service/team-authorisable.service'; import { ExternalToolAuthorizableService } from '@modules/tool/external-tool/service'; import { LessonService } from '@modules/lesson'; @@ -34,7 +32,6 @@ export class ReferenceLoader { private readonly teamAuthorisableService: TeamAuthorisableService, private readonly submissionRepo: SubmissionRepo, private readonly schoolExternalToolRepo: SchoolExternalToolRepo, - private readonly contextExternalToolAuthorizableService: ContextExternalToolAuthorizableService, private readonly externalToolAuthorizableService: ExternalToolAuthorizableService, private readonly instanceService: InstanceService, private readonly authorizationInjectionService: AuthorizationInjectionService @@ -49,10 +46,6 @@ export class ReferenceLoader { service.injectReferenceLoader(AuthorizableReferenceType.Team, this.teamAuthorisableService); service.injectReferenceLoader(AuthorizableReferenceType.Submission, this.submissionRepo); service.injectReferenceLoader(AuthorizableReferenceType.SchoolExternalToolEntity, this.schoolExternalToolRepo); - service.injectReferenceLoader( - AuthorizableReferenceType.ContextExternalToolEntity, - this.contextExternalToolAuthorizableService - ); service.injectReferenceLoader(AuthorizableReferenceType.ExternalTool, this.externalToolAuthorizableService); service.injectReferenceLoader(AuthorizableReferenceType.Instance, this.instanceService); } diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.ts index 25738d268ab..bc3028f8269 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -3,7 +3,6 @@ import { AuthorizableObject } from '@shared/domain/domain-object'; // fix import import { BaseDO } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; import { - ContextExternalToolRule, CourseGroupRule, CourseRule, GroupRule, @@ -27,7 +26,6 @@ import { AuthorizationInjectionService } from './authorization-injection.service @Injectable() export class RuleManager { constructor( - contextExternalToolRule: ContextExternalToolRule, courseGroupRule: CourseGroupRule, courseRule: CourseRule, groupRule: GroupRule, @@ -46,7 +44,6 @@ export class RuleManager { instanceRule: InstanceRule, private readonly authorizationInjectionService: AuthorizationInjectionService ) { - this.authorizationInjectionService.injectAuthorizationRule(contextExternalToolRule); this.authorizationInjectionService.injectAuthorizationRule(courseGroupRule); this.authorizationInjectionService.injectAuthorizationRule(courseRule); this.authorizationInjectionService.injectAuthorizationRule(groupRule); diff --git a/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts similarity index 97% rename from apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts rename to apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts index 132bf8fcaf6..4730169b784 100644 --- a/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts @@ -8,9 +8,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action } from '../type'; import { ContextExternalToolRule } from './context-external-tool.rule'; +import { Action, AuthorizationHelper } from '@src/modules/authorization'; describe('ContextExternalToolRule', () => { let service: ContextExternalToolRule; diff --git a/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.ts similarity index 77% rename from apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts rename to apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.ts index 373d6debf41..9dc7128152a 100644 --- a/apps/server/src/modules/authorization/domain/rules/context-external-tool.rule.ts +++ b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.ts @@ -2,12 +2,21 @@ import { ContextExternalTool } from '@modules/tool/context-external-tool/domain' import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; +import { + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class ContextExternalToolRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched: boolean = object instanceof ContextExternalToolEntity || object instanceof ContextExternalTool; diff --git a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts index 35b04c2d3b4..b278e2784e3 100644 --- a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts +++ b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts @@ -7,6 +7,7 @@ import { SchoolExternalToolModule } from '../school-external-tool'; import { ContextExternalToolAuthorizableService, ContextExternalToolService, ToolReferenceService } from './service'; import { ContextExternalToolValidationService } from './service/context-external-tool-validation.service'; import { ToolConfigurationStatusService } from './service/tool-configuration-status.service'; +import { ContextExternalToolRule } from './authorisation/context-external-tool.rule'; @Module({ imports: [ @@ -22,6 +23,7 @@ import { ToolConfigurationStatusService } from './service/tool-configuration-sta ContextExternalToolAuthorizableService, ToolReferenceService, ToolConfigurationStatusService, + ContextExternalToolRule, ], exports: [ ContextExternalToolService, diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.ts index d803a8f3648..c68cf09b082 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.ts @@ -1,4 +1,8 @@ -import { AuthorizationLoaderService } from '@modules/authorization'; +import { + AuthorizableReferenceType, + AuthorizationInjectionService, + AuthorizationLoaderService, +} from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain/types'; import { ContextExternalToolRepo } from '@shared/repo'; @@ -6,7 +10,12 @@ import { ContextExternalTool } from '../domain'; @Injectable() export class ContextExternalToolAuthorizableService implements AuthorizationLoaderService { - constructor(private readonly contextExternalToolRepo: ContextExternalToolRepo) {} + constructor( + private readonly contextExternalToolRepo: ContextExternalToolRepo, + injectionService: AuthorizationInjectionService + ) { + injectionService.injectReferenceLoader(AuthorizableReferenceType.ContextExternalToolEntity, this); + } async findById(id: EntityId): Promise { const contextExternalTool: ContextExternalTool = await this.contextExternalToolRepo.findById(id); From f2e8acd73b9be63ffad1297fc115dc082af11794 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 27 Sep 2024 14:11:43 +0200 Subject: [PATCH 02/23] move school external tool authorisation code --- .../authorization-reference.module.ts | 11 +-- .../authorization/authorization.module.ts | 3 - .../authorization/domain/rules/index.ts | 1 - .../domain/service/reference.loader.ts | 12 +-- .../domain/service/rule-manager.ts | 3 - .../school-external-tool.rule.spec.ts | 21 ++++- .../school-external-tool.rule.ts | 15 +++- .../school-external-tool.module.ts | 14 +++- .../school-external-tool/service/index.ts | 1 + ...hool-external-tool-authorizable.service.ts | 25 ++++++ ...xternal-tool-authorization.service.spec.ts | 79 +++++++++++++++++++ 11 files changed, 149 insertions(+), 36 deletions(-) rename apps/server/src/modules/{authorization/domain/rules => tool/school-external-tool/authorization}/school-external-tool.rule.spec.ts (82%) rename apps/server/src/modules/{authorization/domain/rules => tool/school-external-tool/authorization}/school-external-tool.rule.ts (76%) create mode 100644 apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts create mode 100644 apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts diff --git a/apps/server/src/modules/authorization/authorization-reference.module.ts b/apps/server/src/modules/authorization/authorization-reference.module.ts index 08a73a33aaf..39b1a168ee9 100644 --- a/apps/server/src/modules/authorization/authorization-reference.module.ts +++ b/apps/server/src/modules/authorization/authorization-reference.module.ts @@ -2,15 +2,7 @@ import { InstanceModule } from '@modules/instance'; import { LessonModule } from '@modules/lesson'; import { ToolModule } from '@modules/tool'; import { forwardRef, Module } from '@nestjs/common'; -import { - CourseGroupRepo, - CourseRepo, - LegacySchoolRepo, - SchoolExternalToolRepo, - SubmissionRepo, - TaskRepo, - UserRepo, -} from '@shared/repo'; +import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; import { AuthorizationModule } from './authorization.module'; import { AuthorizationHelper, AuthorizationReferenceService, ReferenceLoader } from './domain'; @@ -33,7 +25,6 @@ import { TeamsModule } from '../teams'; TaskRepo, LegacySchoolRepo, SubmissionRepo, - SchoolExternalToolRepo, AuthorizationReferenceService, ], exports: [AuthorizationReferenceService], diff --git a/apps/server/src/modules/authorization/authorization.module.ts b/apps/server/src/modules/authorization/authorization.module.ts index 9383ec6ef2e..83544ba47fb 100644 --- a/apps/server/src/modules/authorization/authorization.module.ts +++ b/apps/server/src/modules/authorization/authorization.module.ts @@ -4,7 +4,6 @@ import { UserRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; import { AuthorizationHelper, AuthorizationService, RuleManager, AuthorizationInjectionService } from './domain'; import { - ContextExternalToolRule, CourseGroupRule, CourseRule, ExternalToolRule, @@ -12,7 +11,6 @@ import { InstanceRule, LegacySchoolRule, LessonRule, - SchoolExternalToolRule, SchoolRule, SchoolSystemOptionsRule, SubmissionRule, @@ -40,7 +38,6 @@ import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; GroupRule, LessonRule, SchoolRule, - SchoolExternalToolRule, SubmissionRule, TaskRule, TeamRule, diff --git a/apps/server/src/modules/authorization/domain/rules/index.ts b/apps/server/src/modules/authorization/domain/rules/index.ts index 697b3c5769c..39c4cb21a48 100644 --- a/apps/server/src/modules/authorization/domain/rules/index.ts +++ b/apps/server/src/modules/authorization/domain/rules/index.ts @@ -8,7 +8,6 @@ export * from './course.rule'; export * from './legacy-school.rule'; export * from './lesson.rule'; export * from './school.rule'; -export * from './school-external-tool.rule'; export * from './submission.rule'; export * from './task.rule'; export * from './team.rule'; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts index 890fec52bd2..46cf2e78cfe 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -7,15 +7,7 @@ import { Injectable, NotImplementedException } from '@nestjs/common'; import { AuthorizableObject } from '@shared/domain/domain-object'; import { BaseDO } from '@shared/domain/domainobject'; import { EntityId } from '@shared/domain/types'; -import { - CourseGroupRepo, - CourseRepo, - LegacySchoolRepo, - SchoolExternalToolRepo, - SubmissionRepo, - TaskRepo, - UserRepo, -} from '@shared/repo'; +import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; import { InstanceService } from '../../../instance'; import { AuthorizableReferenceType, AuthorizationLoaderService } from '../type'; import { AuthorizationInjectionService } from './authorization-injection.service'; @@ -31,7 +23,6 @@ export class ReferenceLoader { private readonly lessonService: LessonService, private readonly teamAuthorisableService: TeamAuthorisableService, private readonly submissionRepo: SubmissionRepo, - private readonly schoolExternalToolRepo: SchoolExternalToolRepo, private readonly externalToolAuthorizableService: ExternalToolAuthorizableService, private readonly instanceService: InstanceService, private readonly authorizationInjectionService: AuthorizationInjectionService @@ -45,7 +36,6 @@ export class ReferenceLoader { service.injectReferenceLoader(AuthorizableReferenceType.Lesson, this.lessonService); service.injectReferenceLoader(AuthorizableReferenceType.Team, this.teamAuthorisableService); service.injectReferenceLoader(AuthorizableReferenceType.Submission, this.submissionRepo); - service.injectReferenceLoader(AuthorizableReferenceType.SchoolExternalToolEntity, this.schoolExternalToolRepo); service.injectReferenceLoader(AuthorizableReferenceType.ExternalTool, this.externalToolAuthorizableService); service.injectReferenceLoader(AuthorizableReferenceType.Instance, this.instanceService); } diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.ts index bc3028f8269..e31fe48084a 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -9,7 +9,6 @@ import { InstanceRule, LegacySchoolRule, LessonRule, - SchoolExternalToolRule, SchoolRule, SchoolSystemOptionsRule, SubmissionRule, @@ -31,7 +30,6 @@ export class RuleManager { groupRule: GroupRule, legaySchoolRule: LegacySchoolRule, lessonRule: LessonRule, - schoolExternalToolRule: SchoolExternalToolRule, schoolRule: SchoolRule, schoolSystemOptionsRule: SchoolSystemOptionsRule, submissionRule: SubmissionRule, @@ -49,7 +47,6 @@ export class RuleManager { this.authorizationInjectionService.injectAuthorizationRule(groupRule); this.authorizationInjectionService.injectAuthorizationRule(legaySchoolRule); this.authorizationInjectionService.injectAuthorizationRule(lessonRule); - this.authorizationInjectionService.injectAuthorizationRule(schoolExternalToolRule); this.authorizationInjectionService.injectAuthorizationRule(schoolRule); this.authorizationInjectionService.injectAuthorizationRule(schoolSystemOptionsRule); this.authorizationInjectionService.injectAuthorizationRule(submissionRule); diff --git a/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts similarity index 82% rename from apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts rename to apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts index 2b7affa089c..de9ae04c774 100644 --- a/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts @@ -5,23 +5,32 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action } from '../type'; import { SchoolExternalToolRule } from './school-external-tool.rule'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; +import { DeepMocked, createMock } from '@golevelup/ts-jest'; describe('SchoolExternalToolRule', () => { let service: SchoolExternalToolRule; let authorizationHelper: AuthorizationHelper; + let injectionService: DeepMocked; beforeAll(async () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, SchoolExternalToolRule], + providers: [ + AuthorizationHelper, + SchoolExternalToolRule, + { + provide: AuthorizationInjectionService, + useValue: createMock(), + }, + ], }).compile(); service = await module.get(SchoolExternalToolRule); authorizationHelper = await module.get(AuthorizationHelper); + injectionService = await module.get(AuthorizationInjectionService); }); beforeEach(() => {}); @@ -48,6 +57,12 @@ describe('SchoolExternalToolRule', () => { }; }; + describe('constructor', () => { + it('should inject itself', () => { + expect(injectionService.injectAuthorizationRule).toHaveBeenCalledWith(service); + }); + }); + describe('hasPermission is called', () => { describe('when user has permission', () => { it('should call hasAllPermissions on AuthorizationHelper', () => { diff --git a/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.ts similarity index 76% rename from apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts rename to apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.ts index f8241bcefc7..79d256138ce 100644 --- a/apps/server/src/modules/authorization/domain/rules/school-external-tool.rule.ts +++ b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.ts @@ -2,12 +2,21 @@ import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; +import { + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class SchoolExternalToolRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched: boolean = object instanceof SchoolExternalToolEntity || object instanceof SchoolExternalTool; diff --git a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts index 8d183c1f88b..ddba2161ebb 100644 --- a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts +++ b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts @@ -1,11 +1,21 @@ import { forwardRef, Module } from '@nestjs/common'; import { CommonToolModule } from '../common'; import { ExternalToolModule } from '../external-tool'; -import { SchoolExternalToolService, SchoolExternalToolValidationService } from './service'; +import { + SchoolExternalToolAuthorizableService, + SchoolExternalToolService, + SchoolExternalToolValidationService, +} from './service'; +import { SchoolExternalToolRule } from './authorization/school-external-tool.rule'; @Module({ imports: [forwardRef(() => CommonToolModule), forwardRef(() => ExternalToolModule)], - providers: [SchoolExternalToolService, SchoolExternalToolValidationService], + providers: [ + SchoolExternalToolService, + SchoolExternalToolValidationService, + SchoolExternalToolRule, + SchoolExternalToolAuthorizableService, + ], exports: [SchoolExternalToolService, SchoolExternalToolValidationService], }) export class SchoolExternalToolModule {} diff --git a/apps/server/src/modules/tool/school-external-tool/service/index.ts b/apps/server/src/modules/tool/school-external-tool/service/index.ts index 1ceab5f3da5..fe2311705ce 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/index.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/index.ts @@ -1,2 +1,3 @@ export * from './school-external-tool.service'; export * from './school-external-tool-validation.service'; +export * from './school-external-tool-authorizable.service'; diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts new file mode 100644 index 00000000000..b03fec4c875 --- /dev/null +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts @@ -0,0 +1,25 @@ +import { Injectable } from '@nestjs/common'; +import { + AuthorizableReferenceType, + AuthorizationInjectionService, + AuthorizationLoaderServiceGeneric, +} from '@src/modules/authorization'; +import { SchoolExternalTool } from '../domain'; +import { EntityId } from '@shared/domain/types'; +import { SchoolExternalToolRepo } from '@shared/repo'; + +@Injectable() +export class SchoolExternalToolAuthorizableService implements AuthorizationLoaderServiceGeneric { + constructor( + private readonly schoolExternalToolRepo: SchoolExternalToolRepo, + injectionService: AuthorizationInjectionService + ) { + injectionService.injectReferenceLoader(AuthorizableReferenceType.SchoolExternalToolEntity, this); + } + + async findById(id: EntityId): Promise { + const schoolExternalTool = await this.schoolExternalToolRepo.findById(id); + + return schoolExternalTool; + } +} diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts new file mode 100644 index 00000000000..c7b82d05b03 --- /dev/null +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts @@ -0,0 +1,79 @@ +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { Test, TestingModule } from '@nestjs/testing'; +import { SchoolExternalToolRepo } from '@shared/repo'; +import { legacySchoolDoFactory } from '@shared/testing'; +import { SchoolExternalTool } from '../../school-external-tool/domain'; +import { schoolExternalToolFactory } from '../../school-external-tool/testing'; +import { SchoolExternalToolAuthorizableService } from './school-external-tool-authorizable.service'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; + +describe('SchoolExternalToolAuthorizableService', () => { + let module: TestingModule; + let service: SchoolExternalToolAuthorizableService; + + let schoolExternalToolRepo: DeepMocked; + let injectionService: DeepMocked; + + beforeAll(async () => { + module = await Test.createTestingModule({ + providers: [ + SchoolExternalToolAuthorizableService, + { + provide: SchoolExternalToolRepo, + useValue: createMock(), + }, + { + provide: AuthorizationInjectionService, + useValue: createMock(), + }, + ], + }).compile(); + + service = module.get(SchoolExternalToolAuthorizableService); + schoolExternalToolRepo = module.get(SchoolExternalToolRepo); + injectionService = module.get(AuthorizationInjectionService); + }); + + afterAll(async () => { + await module.close(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + expect(injectionService.injectReferenceLoader).toHaveBeenCalledWith( + AuthorizableReferenceType.SchoolExternalToolEntity, + service + ); + }); + }); + + describe('findById', () => { + describe('when id is given', () => { + const setup = () => { + const schoolId: string = legacySchoolDoFactory.buildWithId().id as string; + const schoolExternalTool: SchoolExternalTool = schoolExternalToolFactory.build({ + schoolId, + }); + + schoolExternalToolRepo.findById.mockResolvedValue(schoolExternalTool); + + return { + schoolExternalTool, + schoolExternalToolId: schoolExternalTool.id, + }; + }; + + it('should return a contextExternalTool', async () => { + const { schoolExternalTool, schoolExternalToolId } = setup(); + + const result: SchoolExternalTool = await service.findById(schoolExternalToolId); + + expect(result).toEqual(schoolExternalTool); + }); + }); + }); +}); From 128d52257251a24580d42e785f1473929db45044 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 27 Sep 2024 14:21:17 +0200 Subject: [PATCH 03/23] add context external tool tests --- .../context-external-tool.rule.spec.ts | 68 ++++++++++++------- ...external-tool-authorizable.service.spec.ts | 17 +++++ 2 files changed, 59 insertions(+), 26 deletions(-) diff --git a/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts index 4730169b784..a22d99dce55 100644 --- a/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts @@ -9,53 +9,69 @@ import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; import { ContextExternalToolRule } from './context-external-tool.rule'; -import { Action, AuthorizationHelper } from '@src/modules/authorization'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; +import { DeepMocked, createMock } from '@golevelup/ts-jest'; describe('ContextExternalToolRule', () => { let service: ContextExternalToolRule; let authorizationHelper: AuthorizationHelper; + let injectionService: DeepMocked; beforeAll(async () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, ContextExternalToolRule], + providers: [ + AuthorizationHelper, + ContextExternalToolRule, + { + provide: AuthorizationInjectionService, + useValue: createMock(), + }, + ], }).compile(); service = await module.get(ContextExternalToolRule); authorizationHelper = await module.get(AuthorizationHelper); + injectionService = await module.get(AuthorizationInjectionService); }); beforeEach(() => {}); - const setup = () => { - const permissionA = 'a' as Permission; - const permissionB = 'b' as Permission; - const permissionC = 'c' as Permission; - - const role: Role = roleFactory.build({ permissions: [permissionA, permissionB] }); + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + expect(injectionService.injectAuthorizationRule).toHaveBeenCalledWith(service); + }); + }); - const school = schoolEntityFactory.build(); - const schoolExternalToolEntity: SchoolExternalToolEntity | SchoolExternalTool = - schoolExternalToolEntityFactory.build({ - school, + describe('hasPermission is called', () => { + const setup = () => { + const permissionA = 'a' as Permission; + const permissionB = 'b' as Permission; + const permissionC = 'c' as Permission; + + const role: Role = roleFactory.build({ permissions: [permissionA, permissionB] }); + + const school = schoolEntityFactory.build(); + const schoolExternalToolEntity: SchoolExternalToolEntity | SchoolExternalTool = + schoolExternalToolEntityFactory.build({ + school, + }); + const entity: ContextExternalToolEntity | ContextExternalTool = contextExternalToolEntityFactory.build({ + schoolTool: schoolExternalToolEntity, }); - const entity: ContextExternalToolEntity | ContextExternalTool = contextExternalToolEntityFactory.build({ - schoolTool: schoolExternalToolEntity, - }); - const user: User = userFactory.build({ roles: [role], school }); - return { - permissionA, - permissionB, - permissionC, - school, - entity, - user, - role, + const user: User = userFactory.build({ roles: [role], school }); + return { + permissionA, + permissionB, + permissionC, + school, + entity, + user, + role, + }; }; - }; - describe('hasPermission is called', () => { describe('when user has permission', () => { it('should call hasAllPermissions on AuthorizationHelper', () => { const { user, entity } = setup(); diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts index 42c6eb8829a..251c4c5574b 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts @@ -7,12 +7,14 @@ import { schoolExternalToolFactory } from '../../school-external-tool/testing'; import { ContextExternalTool } from '../domain'; import { contextExternalToolFactory } from '../testing'; import { ContextExternalToolAuthorizableService } from './context-external-tool-authorizable.service'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; describe('ContextExternalToolAuthorizableService', () => { let module: TestingModule; let service: ContextExternalToolAuthorizableService; let contextExternalToolRepo: DeepMocked; + let injectionService: DeepMocked; beforeAll(async () => { module = await Test.createTestingModule({ @@ -22,11 +24,16 @@ describe('ContextExternalToolAuthorizableService', () => { provide: ContextExternalToolRepo, useValue: createMock(), }, + { + provide: AuthorizationInjectionService, + useValue: createMock(), + }, ], }).compile(); service = module.get(ContextExternalToolAuthorizableService); contextExternalToolRepo = module.get(ContextExternalToolRepo); + injectionService = module.get(AuthorizationInjectionService); }); afterAll(async () => { @@ -37,6 +44,16 @@ describe('ContextExternalToolAuthorizableService', () => { jest.resetAllMocks(); }); + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + new ContextExternalToolAuthorizableService(contextExternalToolRepo, injectionService); + expect(injectionService.injectReferenceLoader).toHaveBeenCalledWith( + AuthorizableReferenceType.ContextExternalToolEntity, + service + ); + }); + }); + describe('findById', () => { describe('when id is given', () => { const setup = () => { From 1a53e92bbf3bb348aa11ebe533bf05636040347b Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 27 Sep 2024 14:37:40 +0200 Subject: [PATCH 04/23] move external tool authorisation code --- .../authorization-reference.module.ts | 6 ++---- .../authorization/authorization.module.ts | 2 -- .../modules/authorization/domain/rules/index.ts | 1 - .../domain/service/reference.loader.ts | 3 --- .../authorization/domain/service/rule-manager.ts | 3 --- .../context-external-tool.module.ts | 1 - .../authorization}/external-tool.rule.spec.ts | 16 ++++++++++++++-- .../authorization}/external-tool.rule.ts | 15 ++++++++++++--- .../tool/external-tool/external-tool.module.ts | 1 - .../external-tool-authorizable.service.spec.ts | 16 ++++++++++++++++ .../external-tool-authorizable.service.ts | 10 ++++++++-- 11 files changed, 52 insertions(+), 22 deletions(-) rename apps/server/src/modules/{authorization/domain/rules => tool/external-tool/authorization}/external-tool.rule.spec.ts (85%) rename apps/server/src/modules/{authorization/domain/rules => tool/external-tool/authorization}/external-tool.rule.ts (66%) diff --git a/apps/server/src/modules/authorization/authorization-reference.module.ts b/apps/server/src/modules/authorization/authorization-reference.module.ts index 39b1a168ee9..94c008832e6 100644 --- a/apps/server/src/modules/authorization/authorization-reference.module.ts +++ b/apps/server/src/modules/authorization/authorization-reference.module.ts @@ -1,7 +1,6 @@ import { InstanceModule } from '@modules/instance'; import { LessonModule } from '@modules/lesson'; -import { ToolModule } from '@modules/tool'; -import { forwardRef, Module } from '@nestjs/common'; +import { Module } from '@nestjs/common'; import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; import { AuthorizationModule } from './authorization.module'; @@ -14,8 +13,7 @@ import { TeamsModule } from '../teams'; * Avoid using this module and load the needed data in your use cases and then use the normal AuthorizationModule! */ @Module({ - // TODO: remove forwardRef - imports: [AuthorizationModule, LessonModule, TeamsModule, forwardRef(() => ToolModule), LoggerModule, InstanceModule], + imports: [AuthorizationModule, LessonModule, TeamsModule, LoggerModule, InstanceModule], providers: [ AuthorizationHelper, ReferenceLoader, diff --git a/apps/server/src/modules/authorization/authorization.module.ts b/apps/server/src/modules/authorization/authorization.module.ts index 83544ba47fb..29b4e64bed6 100644 --- a/apps/server/src/modules/authorization/authorization.module.ts +++ b/apps/server/src/modules/authorization/authorization.module.ts @@ -6,7 +6,6 @@ import { AuthorizationHelper, AuthorizationService, RuleManager, AuthorizationIn import { CourseGroupRule, CourseRule, - ExternalToolRule, GroupRule, InstanceRule, LegacySchoolRule, @@ -46,7 +45,6 @@ import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; LegacySchoolRule, SystemRule, SchoolSystemOptionsRule, - ExternalToolRule, InstanceRule, ], exports: [ diff --git a/apps/server/src/modules/authorization/domain/rules/index.ts b/apps/server/src/modules/authorization/domain/rules/index.ts index 39c4cb21a48..aa7eab21e61 100644 --- a/apps/server/src/modules/authorization/domain/rules/index.ts +++ b/apps/server/src/modules/authorization/domain/rules/index.ts @@ -17,4 +17,3 @@ export * from './group.rule'; export { SystemRule } from './system.rule'; export { SchoolSystemOptionsRule } from './school-system-options.rule'; export { InstanceRule } from './instance.rule'; -export { ExternalToolRule } from './external-tool.rule'; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts index 46cf2e78cfe..6b8342bf969 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -1,7 +1,6 @@ // TODO fix modules circular dependency // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { TeamAuthorisableService } from '@src/modules/teams/service/team-authorisable.service'; -import { ExternalToolAuthorizableService } from '@modules/tool/external-tool/service'; import { LessonService } from '@modules/lesson'; import { Injectable, NotImplementedException } from '@nestjs/common'; import { AuthorizableObject } from '@shared/domain/domain-object'; @@ -23,7 +22,6 @@ export class ReferenceLoader { private readonly lessonService: LessonService, private readonly teamAuthorisableService: TeamAuthorisableService, private readonly submissionRepo: SubmissionRepo, - private readonly externalToolAuthorizableService: ExternalToolAuthorizableService, private readonly instanceService: InstanceService, private readonly authorizationInjectionService: AuthorizationInjectionService ) { @@ -36,7 +34,6 @@ export class ReferenceLoader { service.injectReferenceLoader(AuthorizableReferenceType.Lesson, this.lessonService); service.injectReferenceLoader(AuthorizableReferenceType.Team, this.teamAuthorisableService); service.injectReferenceLoader(AuthorizableReferenceType.Submission, this.submissionRepo); - service.injectReferenceLoader(AuthorizableReferenceType.ExternalTool, this.externalToolAuthorizableService); service.injectReferenceLoader(AuthorizableReferenceType.Instance, this.instanceService); } diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.ts index e31fe48084a..e601fb84c89 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -18,7 +18,6 @@ import { UserLoginMigrationRule, UserRule, } from '../rules'; -import { ExternalToolRule } from '../rules/external-tool.rule'; import type { AuthorizationContext, Rule } from '../type'; import { AuthorizationInjectionService } from './authorization-injection.service'; @@ -38,7 +37,6 @@ export class RuleManager { teamRule: TeamRule, userLoginMigrationRule: UserLoginMigrationRule, userRule: UserRule, - externalToolRule: ExternalToolRule, instanceRule: InstanceRule, private readonly authorizationInjectionService: AuthorizationInjectionService ) { @@ -55,7 +53,6 @@ export class RuleManager { this.authorizationInjectionService.injectAuthorizationRule(teamRule); this.authorizationInjectionService.injectAuthorizationRule(userLoginMigrationRule); this.authorizationInjectionService.injectAuthorizationRule(userRule); - this.authorizationInjectionService.injectAuthorizationRule(externalToolRule); this.authorizationInjectionService.injectAuthorizationRule(instanceRule); } diff --git a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts index b278e2784e3..e3252736c44 100644 --- a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts +++ b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts @@ -28,7 +28,6 @@ import { ContextExternalToolRule } from './authorisation/context-external-tool.r exports: [ ContextExternalToolService, ContextExternalToolValidationService, - ContextExternalToolAuthorizableService, ToolReferenceService, ToolConfigurationStatusService, ], diff --git a/apps/server/src/modules/authorization/domain/rules/external-tool.rule.spec.ts b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts similarity index 85% rename from apps/server/src/modules/authorization/domain/rules/external-tool.rule.spec.ts rename to apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts index 131032b0e35..ee562198e8e 100644 --- a/apps/server/src/modules/authorization/domain/rules/external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts @@ -5,15 +5,15 @@ import { Test, TestingModule } from '@nestjs/testing'; import { User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action } from '../type'; import { ExternalToolRule } from './external-tool.rule'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; describe(ExternalToolRule.name, () => { let module: TestingModule; let rule: ExternalToolRule; let authorizationHelper: DeepMocked; + let injectionService: DeepMocked; beforeAll(async () => { await setupEntities(); @@ -25,11 +25,16 @@ describe(ExternalToolRule.name, () => { provide: AuthorizationHelper, useValue: createMock(), }, + { + provide: AuthorizationInjectionService, + useValue: createMock(), + }, ], }).compile(); rule = module.get(ExternalToolRule); authorizationHelper = module.get(AuthorizationHelper); + injectionService = module.get(AuthorizationInjectionService); }); beforeEach(() => { @@ -40,6 +45,13 @@ describe(ExternalToolRule.name, () => { await module.close(); }); + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + new ExternalToolRule(authorizationHelper, injectionService); + expect(injectionService.injectAuthorizationRule).toHaveBeenCalledWith(rule); + }); + }); + describe('isApplicable', () => { describe('when the object is an external tool', () => { const setup = () => { diff --git a/apps/server/src/modules/authorization/domain/rules/external-tool.rule.ts b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.ts similarity index 66% rename from apps/server/src/modules/authorization/domain/rules/external-tool.rule.ts rename to apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.ts index 922846dbf21..51c32f1905d 100644 --- a/apps/server/src/modules/authorization/domain/rules/external-tool.rule.ts +++ b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.ts @@ -1,12 +1,21 @@ import { ExternalTool } from '@modules/tool/external-tool/domain'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; +import { + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class ExternalToolRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + injectionService: AuthorizationInjectionService + ) { + injectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched: boolean = object instanceof ExternalTool; diff --git a/apps/server/src/modules/tool/external-tool/external-tool.module.ts b/apps/server/src/modules/tool/external-tool/external-tool.module.ts index cdb07b5f74c..92f75015de3 100644 --- a/apps/server/src/modules/tool/external-tool/external-tool.module.ts +++ b/apps/server/src/modules/tool/external-tool/external-tool.module.ts @@ -40,7 +40,6 @@ import { ExternalToolConfigurationService, ExternalToolLogoService, DatasheetPdfService, - ExternalToolAuthorizableService, ExternalToolImageService, ], }) diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts index cb684f9caf4..b4d894f2752 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts @@ -3,12 +3,14 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ExternalToolRepo } from '@shared/repo'; import { externalToolFactory } from '../testing'; import { ExternalToolAuthorizableService } from './external-tool-authorizable.service'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; describe(ExternalToolAuthorizableService.name, () => { let module: TestingModule; let service: ExternalToolAuthorizableService; let externalToolRepo: DeepMocked; + let authorizationInjectionService: DeepMocked; beforeAll(async () => { module = await Test.createTestingModule({ @@ -18,11 +20,16 @@ describe(ExternalToolAuthorizableService.name, () => { provide: ExternalToolRepo, useValue: createMock(), }, + { + provide: AuthorizationInjectionService, + useValue: createMock(), + }, ], }).compile(); service = module.get(ExternalToolAuthorizableService); externalToolRepo = module.get(ExternalToolRepo); + authorizationInjectionService = module.get(AuthorizationInjectionService); }); afterAll(async () => { @@ -33,6 +40,15 @@ describe(ExternalToolAuthorizableService.name, () => { jest.resetAllMocks(); }); + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + expect(authorizationInjectionService.injectReferenceLoader).toHaveBeenCalledWith( + AuthorizableReferenceType.ExternalTool, + service + ); + }); + }); + describe('findById', () => { describe('when there is an external tool', () => { const setup = () => { diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.ts index 9d5c89b51ab..fb092f43e93 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.ts @@ -1,4 +1,8 @@ -import { AuthorizationLoaderService } from '@modules/authorization'; +import { + AuthorizableReferenceType, + AuthorizationInjectionService, + AuthorizationLoaderService, +} from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain/types'; import { ExternalToolRepo } from '@shared/repo'; @@ -6,7 +10,9 @@ import { ExternalTool } from '../domain'; @Injectable() export class ExternalToolAuthorizableService implements AuthorizationLoaderService { - constructor(private readonly externalToolRepo: ExternalToolRepo) {} + constructor(private readonly externalToolRepo: ExternalToolRepo, injectionService: AuthorizationInjectionService) { + injectionService.injectReferenceLoader(AuthorizableReferenceType.ExternalTool, this); + } async findById(id: EntityId): Promise { const externalTool: ExternalTool = await this.externalToolRepo.findById(id); From 8408a7435dc616f6e73103fb1907d1468a32a757 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 27 Sep 2024 16:05:00 +0200 Subject: [PATCH 05/23] begin injection of course rule and reference loader --- .../course.reference-loader.spec.ts | 69 +++++++++++++++++++ .../course.reference-loader.ts | 22 ++++++ .../domain/rules/course.rule.spec.ts | 14 +++- .../authorization/domain/rules/course.rule.ts | 9 ++- .../domain/service/reference.loader.ts | 4 +- .../domain/service/rule-manager.ts | 3 - 6 files changed, 110 insertions(+), 11 deletions(-) create mode 100644 apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.spec.ts create mode 100644 apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.ts diff --git a/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.spec.ts b/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.spec.ts new file mode 100644 index 00000000000..fda95dab35c --- /dev/null +++ b/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.spec.ts @@ -0,0 +1,69 @@ +import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { Test, TestingModule } from '@nestjs/testing'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; +import { CourseReferenceLoader } from './course.reference-loader'; +import { CourseDoService } from '@src/modules/learnroom'; +import { courseFactory } from '@src/modules/learnroom/testing'; + +describe('Course Reference Loader', () => { + let module: TestingModule; + let service: CourseReferenceLoader; + + let courseDoService: DeepMocked; + let injectionService: DeepMocked; + + beforeAll(async () => { + module = await Test.createTestingModule({ + providers: [ + CourseReferenceLoader, + { + provide: CourseDoService, + useValue: createMock(), + }, + { + provide: AuthorizationInjectionService, + useValue: createMock(), + }, + ], + }).compile(); + + service = module.get(CourseReferenceLoader); + courseDoService = module.get(CourseDoService); + injectionService = module.get(AuthorizationInjectionService); + }); + + afterAll(async () => { + await module.close(); + }); + + afterEach(() => { + jest.resetAllMocks(); + }); + + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + expect(injectionService.injectReferenceLoader).toHaveBeenCalledWith(AuthorizableReferenceType.Course, service); + }); + }); + + describe('findById', () => { + describe('when id is given', () => { + const setup = () => { + const course = courseFactory.buildWithId(); + courseDoService.findById.mockResolvedValue(course); + + return { + course, + }; + }; + + it('should return a contextExternalTool', async () => { + const { course } = setup(); + + const result = await service.findById(course.id); + + expect(result).toEqual(course); + }); + }); + }); +}); diff --git a/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.ts b/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.ts new file mode 100644 index 00000000000..839259d7bc4 --- /dev/null +++ b/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.ts @@ -0,0 +1,22 @@ +import { + AuthorizableReferenceType, + AuthorizationInjectionService, + AuthorizationLoaderServiceGeneric, +} from '@modules/authorization'; +import { Injectable } from '@nestjs/common'; +import { EntityId } from '@shared/domain/types'; +import { CourseDoService } from '@src/modules/learnroom'; +import { Course } from '@src/modules/learnroom/domain'; + +@Injectable() +export class CourseReferenceLoader implements AuthorizationLoaderServiceGeneric { + constructor(private readonly courseService: CourseDoService, injectionService: AuthorizationInjectionService) { + injectionService.injectReferenceLoader(AuthorizableReferenceType.User, this); + } + + public async findById(courseId: EntityId): Promise { + const course: Course = await this.courseService.findById(courseId); + + return course; + } +} diff --git a/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts index 46d616cf45b..8021eec19b7 100644 --- a/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts @@ -3,14 +3,15 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Course, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory as courseEntityFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action } from '../type'; import { CourseRule } from './course.rule'; +import { AuthorizationHelper, AuthorizationInjectionService } from '../service'; +import { Action } from '../type'; describe('CourseRule', () => { let module: TestingModule; let service: CourseRule; let authorizationHelper: AuthorizationHelper; + let injectionService: AuthorizationInjectionService; let user: User; let entity: Course; const permissionA = 'a' as Permission; @@ -21,11 +22,12 @@ describe('CourseRule', () => { await setupEntities(); module = await Test.createTestingModule({ - providers: [AuthorizationHelper, CourseRule], + providers: [AuthorizationHelper, CourseRule, AuthorizationInjectionService], }).compile(); service = await module.get(CourseRule); authorizationHelper = await module.get(AuthorizationHelper); + injectionService = await module.get(AuthorizationInjectionService); }); beforeEach(() => { @@ -41,6 +43,12 @@ describe('CourseRule', () => { await module.close(); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(service); + }); + }); + describe('when validating an entity', () => { it('should call hasAllPermissions on AuthorizationHelper', () => { entity = courseEntityFactory.build({ teachers: [user] }); diff --git a/apps/server/src/modules/authorization/domain/rules/course.rule.ts b/apps/server/src/modules/authorization/domain/rules/course.rule.ts index c97afb098fa..2e625d75552 100644 --- a/apps/server/src/modules/authorization/domain/rules/course.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/course.rule.ts @@ -2,12 +2,17 @@ import { Course } from '@modules/learnroom/domain'; import { Injectable } from '@nestjs/common'; import { Course as CourseEntity, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; -import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; +import { AuthorizationHelper, AuthorizationInjectionService } from '../service'; @Injectable() export class CourseRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof CourseEntity || object instanceof Course; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts index 6b8342bf969..b9d13cee4c7 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -6,7 +6,7 @@ import { Injectable, NotImplementedException } from '@nestjs/common'; import { AuthorizableObject } from '@shared/domain/domain-object'; import { BaseDO } from '@shared/domain/domainobject'; import { EntityId } from '@shared/domain/types'; -import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; +import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo } from '@shared/repo'; import { InstanceService } from '../../../instance'; import { AuthorizableReferenceType, AuthorizationLoaderService } from '../type'; import { AuthorizationInjectionService } from './authorization-injection.service'; @@ -14,7 +14,6 @@ import { AuthorizationInjectionService } from './authorization-injection.service @Injectable() export class ReferenceLoader { constructor( - private readonly userRepo: UserRepo, private readonly courseRepo: CourseRepo, private readonly courseGroupRepo: CourseGroupRepo, private readonly taskRepo: TaskRepo, @@ -29,7 +28,6 @@ export class ReferenceLoader { service.injectReferenceLoader(AuthorizableReferenceType.Task, this.taskRepo); service.injectReferenceLoader(AuthorizableReferenceType.Course, this.courseRepo); service.injectReferenceLoader(AuthorizableReferenceType.CourseGroup, this.courseGroupRepo); - service.injectReferenceLoader(AuthorizableReferenceType.User, this.userRepo); service.injectReferenceLoader(AuthorizableReferenceType.School, this.schoolRepo); service.injectReferenceLoader(AuthorizableReferenceType.Lesson, this.lessonService); service.injectReferenceLoader(AuthorizableReferenceType.Team, this.teamAuthorisableService); diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.ts index e601fb84c89..7cc2604acbd 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -4,7 +4,6 @@ import { BaseDO } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; import { CourseGroupRule, - CourseRule, GroupRule, InstanceRule, LegacySchoolRule, @@ -25,7 +24,6 @@ import { AuthorizationInjectionService } from './authorization-injection.service export class RuleManager { constructor( courseGroupRule: CourseGroupRule, - courseRule: CourseRule, groupRule: GroupRule, legaySchoolRule: LegacySchoolRule, lessonRule: LessonRule, @@ -41,7 +39,6 @@ export class RuleManager { private readonly authorizationInjectionService: AuthorizationInjectionService ) { this.authorizationInjectionService.injectAuthorizationRule(courseGroupRule); - this.authorizationInjectionService.injectAuthorizationRule(courseRule); this.authorizationInjectionService.injectAuthorizationRule(groupRule); this.authorizationInjectionService.injectAuthorizationRule(legaySchoolRule); this.authorizationInjectionService.injectAuthorizationRule(lessonRule); From 7acb4c476c74934ae0b5b4d058b8840e22c7bc9a Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Wed, 2 Oct 2024 12:46:28 +0200 Subject: [PATCH 06/23] Revert "begin injection of course rule and reference loader" This reverts commit 8408a7435dc616f6e73103fb1907d1468a32a757. --- .../course.reference-loader.spec.ts | 69 ------------------- .../course.reference-loader.ts | 22 ------ .../domain/rules/course.rule.spec.ts | 14 +--- .../authorization/domain/rules/course.rule.ts | 9 +-- .../domain/service/reference.loader.ts | 4 +- .../domain/service/rule-manager.ts | 3 + 6 files changed, 11 insertions(+), 110 deletions(-) delete mode 100644 apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.spec.ts delete mode 100644 apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.ts diff --git a/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.spec.ts b/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.spec.ts deleted file mode 100644 index fda95dab35c..00000000000 --- a/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.spec.ts +++ /dev/null @@ -1,69 +0,0 @@ -import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { Test, TestingModule } from '@nestjs/testing'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; -import { CourseReferenceLoader } from './course.reference-loader'; -import { CourseDoService } from '@src/modules/learnroom'; -import { courseFactory } from '@src/modules/learnroom/testing'; - -describe('Course Reference Loader', () => { - let module: TestingModule; - let service: CourseReferenceLoader; - - let courseDoService: DeepMocked; - let injectionService: DeepMocked; - - beforeAll(async () => { - module = await Test.createTestingModule({ - providers: [ - CourseReferenceLoader, - { - provide: CourseDoService, - useValue: createMock(), - }, - { - provide: AuthorizationInjectionService, - useValue: createMock(), - }, - ], - }).compile(); - - service = module.get(CourseReferenceLoader); - courseDoService = module.get(CourseDoService); - injectionService = module.get(AuthorizationInjectionService); - }); - - afterAll(async () => { - await module.close(); - }); - - afterEach(() => { - jest.resetAllMocks(); - }); - - describe('constructor', () => { - it('should inject itself into the AuthorizationInjectionService', () => { - expect(injectionService.injectReferenceLoader).toHaveBeenCalledWith(AuthorizableReferenceType.Course, service); - }); - }); - - describe('findById', () => { - describe('when id is given', () => { - const setup = () => { - const course = courseFactory.buildWithId(); - courseDoService.findById.mockResolvedValue(course); - - return { - course, - }; - }; - - it('should return a contextExternalTool', async () => { - const { course } = setup(); - - const result = await service.findById(course.id); - - expect(result).toEqual(course); - }); - }); - }); -}); diff --git a/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.ts b/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.ts deleted file mode 100644 index 839259d7bc4..00000000000 --- a/apps/server/src/modules/authorization/domain/reference-loader/course.reference-loader.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { - AuthorizableReferenceType, - AuthorizationInjectionService, - AuthorizationLoaderServiceGeneric, -} from '@modules/authorization'; -import { Injectable } from '@nestjs/common'; -import { EntityId } from '@shared/domain/types'; -import { CourseDoService } from '@src/modules/learnroom'; -import { Course } from '@src/modules/learnroom/domain'; - -@Injectable() -export class CourseReferenceLoader implements AuthorizationLoaderServiceGeneric { - constructor(private readonly courseService: CourseDoService, injectionService: AuthorizationInjectionService) { - injectionService.injectReferenceLoader(AuthorizableReferenceType.User, this); - } - - public async findById(courseId: EntityId): Promise { - const course: Course = await this.courseService.findById(courseId); - - return course; - } -} diff --git a/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts index 8021eec19b7..46d616cf45b 100644 --- a/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts @@ -3,15 +3,14 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Course, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory as courseEntityFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { CourseRule } from './course.rule'; -import { AuthorizationHelper, AuthorizationInjectionService } from '../service'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { Action } from '../type'; +import { CourseRule } from './course.rule'; describe('CourseRule', () => { let module: TestingModule; let service: CourseRule; let authorizationHelper: AuthorizationHelper; - let injectionService: AuthorizationInjectionService; let user: User; let entity: Course; const permissionA = 'a' as Permission; @@ -22,12 +21,11 @@ describe('CourseRule', () => { await setupEntities(); module = await Test.createTestingModule({ - providers: [AuthorizationHelper, CourseRule, AuthorizationInjectionService], + providers: [AuthorizationHelper, CourseRule], }).compile(); service = await module.get(CourseRule); authorizationHelper = await module.get(AuthorizationHelper); - injectionService = await module.get(AuthorizationInjectionService); }); beforeEach(() => { @@ -43,12 +41,6 @@ describe('CourseRule', () => { await module.close(); }); - describe('constructor', () => { - it('should inject into AuthorizationInjectionService', () => { - expect(injectionService.getAuthorizationRules()).toContain(service); - }); - }); - describe('when validating an entity', () => { it('should call hasAllPermissions on AuthorizationHelper', () => { entity = courseEntityFactory.build({ teachers: [user] }); diff --git a/apps/server/src/modules/authorization/domain/rules/course.rule.ts b/apps/server/src/modules/authorization/domain/rules/course.rule.ts index 2e625d75552..c97afb098fa 100644 --- a/apps/server/src/modules/authorization/domain/rules/course.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/course.rule.ts @@ -2,17 +2,12 @@ import { Course } from '@modules/learnroom/domain'; import { Injectable } from '@nestjs/common'; import { Course as CourseEntity, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; +import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; -import { AuthorizationHelper, AuthorizationInjectionService } from '../service'; @Injectable() export class CourseRule implements Rule { - constructor( - private readonly authorizationHelper: AuthorizationHelper, - authorisationInjectionService: AuthorizationInjectionService - ) { - authorisationInjectionService.injectAuthorizationRule(this); - } + constructor(private readonly authorizationHelper: AuthorizationHelper) {} public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof CourseEntity || object instanceof Course; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts index b9d13cee4c7..6b8342bf969 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -6,7 +6,7 @@ import { Injectable, NotImplementedException } from '@nestjs/common'; import { AuthorizableObject } from '@shared/domain/domain-object'; import { BaseDO } from '@shared/domain/domainobject'; import { EntityId } from '@shared/domain/types'; -import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo } from '@shared/repo'; +import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; import { InstanceService } from '../../../instance'; import { AuthorizableReferenceType, AuthorizationLoaderService } from '../type'; import { AuthorizationInjectionService } from './authorization-injection.service'; @@ -14,6 +14,7 @@ import { AuthorizationInjectionService } from './authorization-injection.service @Injectable() export class ReferenceLoader { constructor( + private readonly userRepo: UserRepo, private readonly courseRepo: CourseRepo, private readonly courseGroupRepo: CourseGroupRepo, private readonly taskRepo: TaskRepo, @@ -28,6 +29,7 @@ export class ReferenceLoader { service.injectReferenceLoader(AuthorizableReferenceType.Task, this.taskRepo); service.injectReferenceLoader(AuthorizableReferenceType.Course, this.courseRepo); service.injectReferenceLoader(AuthorizableReferenceType.CourseGroup, this.courseGroupRepo); + service.injectReferenceLoader(AuthorizableReferenceType.User, this.userRepo); service.injectReferenceLoader(AuthorizableReferenceType.School, this.schoolRepo); service.injectReferenceLoader(AuthorizableReferenceType.Lesson, this.lessonService); service.injectReferenceLoader(AuthorizableReferenceType.Team, this.teamAuthorisableService); diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.ts index 7cc2604acbd..e601fb84c89 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -4,6 +4,7 @@ import { BaseDO } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; import { CourseGroupRule, + CourseRule, GroupRule, InstanceRule, LegacySchoolRule, @@ -24,6 +25,7 @@ import { AuthorizationInjectionService } from './authorization-injection.service export class RuleManager { constructor( courseGroupRule: CourseGroupRule, + courseRule: CourseRule, groupRule: GroupRule, legaySchoolRule: LegacySchoolRule, lessonRule: LessonRule, @@ -39,6 +41,7 @@ export class RuleManager { private readonly authorizationInjectionService: AuthorizationInjectionService ) { this.authorizationInjectionService.injectAuthorizationRule(courseGroupRule); + this.authorizationInjectionService.injectAuthorizationRule(courseRule); this.authorizationInjectionService.injectAuthorizationRule(groupRule); this.authorizationInjectionService.injectAuthorizationRule(legaySchoolRule); this.authorizationInjectionService.injectAuthorizationRule(lessonRule); From 0240542a836b5cde89c9665d4fc6a0473d3ae807 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Wed, 2 Oct 2024 12:48:52 +0200 Subject: [PATCH 07/23] fix build --- .../src/modules/authorization/domain/rules/index.ts | 1 - .../context-external-tool.module.ts | 2 ++ .../tool/external-tool/external-tool.module.ts | 11 ++++++++++- .../school-external-tool.module.ts | 3 ++- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/apps/server/src/modules/authorization/domain/rules/index.ts b/apps/server/src/modules/authorization/domain/rules/index.ts index aa7eab21e61..773f74611e2 100644 --- a/apps/server/src/modules/authorization/domain/rules/index.ts +++ b/apps/server/src/modules/authorization/domain/rules/index.ts @@ -2,7 +2,6 @@ * Rules are currently placed in authorization module to avoid dependency cycles. * In future they must be moved to the feature modules and register it in registration service. */ -export * from '../../../tool/context-external-tool/authorisation/context-external-tool.rule'; export * from './course-group.rule'; export * from './course.rule'; export * from './legacy-school.rule'; diff --git a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts index e3252736c44..4bb2e0d7940 100644 --- a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts +++ b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts @@ -8,6 +8,7 @@ import { ContextExternalToolAuthorizableService, ContextExternalToolService, Too import { ContextExternalToolValidationService } from './service/context-external-tool-validation.service'; import { ToolConfigurationStatusService } from './service/tool-configuration-status.service'; import { ContextExternalToolRule } from './authorisation/context-external-tool.rule'; +import { AuthorizationModule } from '@src/modules/authorization'; @Module({ imports: [ @@ -16,6 +17,7 @@ import { ContextExternalToolRule } from './authorisation/context-external-tool.r SchoolExternalToolModule, LoggerModule, UserLicenseModule, + AuthorizationModule, ], providers: [ ContextExternalToolService, diff --git a/apps/server/src/modules/tool/external-tool/external-tool.module.ts b/apps/server/src/modules/tool/external-tool/external-tool.module.ts index 92f75015de3..960fcf674ef 100644 --- a/apps/server/src/modules/tool/external-tool/external-tool.module.ts +++ b/apps/server/src/modules/tool/external-tool/external-tool.module.ts @@ -18,9 +18,18 @@ import { ExternalToolServiceMapper, ExternalToolValidationService, } from './service'; +import { AuthorizationModule } from '@src/modules/authorization'; @Module({ - imports: [CommonToolModule, LoggerModule, OauthProviderServiceModule, EncryptionModule, HttpModule, InstanceModule], + imports: [ + CommonToolModule, + LoggerModule, + OauthProviderServiceModule, + EncryptionModule, + HttpModule, + InstanceModule, + AuthorizationModule, + ], providers: [ ExternalToolService, ExternalToolServiceMapper, diff --git a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts index ddba2161ebb..50469f0e318 100644 --- a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts +++ b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts @@ -7,9 +7,10 @@ import { SchoolExternalToolValidationService, } from './service'; import { SchoolExternalToolRule } from './authorization/school-external-tool.rule'; +import { AuthorizationModule } from '@src/modules/authorization'; @Module({ - imports: [forwardRef(() => CommonToolModule), forwardRef(() => ExternalToolModule)], + imports: [forwardRef(() => CommonToolModule), forwardRef(() => ExternalToolModule), AuthorizationModule], providers: [ SchoolExternalToolService, SchoolExternalToolValidationService, From 54d46480700a466057818f07cffe3a8f81292860 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Wed, 2 Oct 2024 13:31:24 +0200 Subject: [PATCH 08/23] make rule manager independent of all rules --- .../domain/rules/course-group.rule.spec.ts | 11 ++++- .../domain/rules/course-group.rule.ts | 9 +++- .../domain/rules/course.rule.spec.ts | 11 ++++- .../authorization/domain/rules/course.rule.ts | 8 ++- .../domain/rules/group.rule.spec.ts | 16 +++++- .../authorization/domain/rules/group.rule.ts | 8 ++- .../domain/rules/instance.rule.spec.ts | 11 ++++- .../domain/rules/instance.rule.ts | 8 ++- .../domain/rules/legacy-school.rule.spec.ts | 11 ++++- .../domain/rules/legacy-school.rule.ts | 8 ++- .../domain/rules/lesson.rule.spec.ts | 11 ++++- .../authorization/domain/rules/lesson.rule.ts | 8 ++- .../rules/school-system-options.rule.spec.ts | 10 ++++ .../rules/school-system-options.rule.ts | 8 ++- .../domain/rules/school.rule.spec.ts | 15 +++++- .../authorization/domain/rules/school.rule.ts | 8 ++- .../domain/rules/submission.rule.spec.ts | 19 ++++++- .../domain/rules/submission.rule.ts | 9 +++- .../domain/rules/system.rule.spec.ts | 10 ++++ .../authorization/domain/rules/system.rule.ts | 8 ++- .../domain/rules/task.rule.spec.ts | 18 ++++++- .../authorization/domain/rules/task.rule.ts | 8 ++- .../domain/rules/team.rule.spec.ts | 11 ++++- .../authorization/domain/rules/team.rule.ts | 8 ++- .../rules/user-login-migration.rule.spec.ts | 10 ++++ .../domain/rules/user-login-migration.rule.ts | 8 ++- .../domain/rules/user.rule.spec.ts | 11 ++++- .../authorization/domain/rules/user.rule.ts | 8 ++- .../domain/service/rule-manager.ts | 49 +------------------ 29 files changed, 263 insertions(+), 75 deletions(-) diff --git a/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts index 62c14baa138..091a3193ea4 100644 --- a/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts @@ -6,10 +6,12 @@ import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { Action } from '../type'; import { AuthorizationHelper } from '../service/authorization.helper'; +import { AuthorizationInjectionService } from '../service'; describe('CourseGroupRule', () => { let service: CourseGroupRule; let authorizationHelper: AuthorizationHelper; + let injectionService: AuthorizationInjectionService; let courseRule: CourseRule; let user: User; let entity: CourseGroup; @@ -21,11 +23,12 @@ describe('CourseGroupRule', () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, CourseRule, CourseGroupRule], + providers: [AuthorizationHelper, CourseRule, CourseGroupRule, AuthorizationInjectionService], }).compile(); service = await module.get(CourseGroupRule); authorizationHelper = await module.get(AuthorizationHelper); + injectionService = await module.get(AuthorizationInjectionService); courseRule = await module.get(CourseRule); const role = roleFactory.build({ permissions: [permissionA, permissionB] }); user = userFactory.build({ roles: [role] }); @@ -39,6 +42,12 @@ describe('CourseGroupRule', () => { expect(spy).toBeCalledWith(user, []); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(service); + }); + }); + describe('Action.read', () => { it('should call hasAccessToEntity on AuthorizationHelper', () => { const course = courseFactory.build({ teachers: [user] }); diff --git a/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts b/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts index cd21258a802..73f69b8197a 100644 --- a/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts @@ -3,10 +3,17 @@ import { CourseGroup, User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; import { CourseRule } from './course.rule'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class CourseGroupRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper, private readonly courseRule: CourseRule) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + private readonly courseRule: CourseRule, + injectionService: AuthorizationInjectionService + ) { + injectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof CourseGroup; diff --git a/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts index 46d616cf45b..4c8adcec482 100644 --- a/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts @@ -6,11 +6,13 @@ import { courseFactory as courseEntityFactory, roleFactory, setupEntities, userF import { AuthorizationHelper } from '../service/authorization.helper'; import { Action } from '../type'; import { CourseRule } from './course.rule'; +import { AuthorizationInjectionService } from '../service/authorization-injection.service'; describe('CourseRule', () => { let module: TestingModule; let service: CourseRule; let authorizationHelper: AuthorizationHelper; + let injectionService: AuthorizationInjectionService; let user: User; let entity: Course; const permissionA = 'a' as Permission; @@ -21,11 +23,12 @@ describe('CourseRule', () => { await setupEntities(); module = await Test.createTestingModule({ - providers: [AuthorizationHelper, CourseRule], + providers: [AuthorizationHelper, CourseRule, AuthorizationInjectionService], }).compile(); service = await module.get(CourseRule); authorizationHelper = await module.get(AuthorizationHelper); + injectionService = await module.get(AuthorizationInjectionService); }); beforeEach(() => { @@ -41,6 +44,12 @@ describe('CourseRule', () => { await module.close(); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(service); + }); + }); + describe('when validating an entity', () => { it('should call hasAllPermissions on AuthorizationHelper', () => { entity = courseEntityFactory.build({ teachers: [user] }); diff --git a/apps/server/src/modules/authorization/domain/rules/course.rule.ts b/apps/server/src/modules/authorization/domain/rules/course.rule.ts index c97afb098fa..25cbd4f4442 100644 --- a/apps/server/src/modules/authorization/domain/rules/course.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/course.rule.ts @@ -4,10 +4,16 @@ import { Course as CourseEntity, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service/authorization-injection.service'; @Injectable() export class CourseRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof CourseEntity || object instanceof Course; diff --git a/apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts index dcc1f39b753..50afff12426 100644 --- a/apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts @@ -3,7 +3,12 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Role, SchoolEntity, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { groupFactory, roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizationContext, AuthorizationHelper } from '@src/modules/authorization'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@src/modules/authorization'; import { Group } from '@src/modules/group'; import { ObjectId } from '@mikro-orm/mongodb'; import { GroupRule } from './group.rule'; @@ -11,6 +16,7 @@ import { GroupRule } from './group.rule'; describe('GroupRule', () => { let module: TestingModule; let rule: GroupRule; + let injectionService: AuthorizationInjectionService; let authorizationHelper: DeepMocked; @@ -24,10 +30,12 @@ describe('GroupRule', () => { provide: AuthorizationHelper, useValue: createMock(), }, + AuthorizationInjectionService, ], }).compile(); rule = module.get(GroupRule); + injectionService = module.get(AuthorizationInjectionService); authorizationHelper = module.get(AuthorizationHelper); }); @@ -39,6 +47,12 @@ describe('GroupRule', () => { jest.clearAllMocks(); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(rule); + }); + }); + describe('isApplicable', () => { describe('when the entity is applicable', () => { const setup = () => { diff --git a/apps/server/src/modules/authorization/domain/rules/group.rule.ts b/apps/server/src/modules/authorization/domain/rules/group.rule.ts index 4c07f3e060f..a962b35780e 100644 --- a/apps/server/src/modules/authorization/domain/rules/group.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/group.rule.ts @@ -3,10 +3,16 @@ import { User } from '@shared/domain/entity'; import { Group } from '@src/modules/group'; import { AuthorizationHelper } from '../service/authorization.helper'; import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class GroupRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched: boolean = object instanceof Group; diff --git a/apps/server/src/modules/authorization/domain/rules/instance.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/instance.rule.spec.ts index d2168b5d7c3..66531db2aca 100644 --- a/apps/server/src/modules/authorization/domain/rules/instance.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/instance.rule.spec.ts @@ -6,10 +6,12 @@ import { setupEntities, userFactory } from '@shared/testing'; import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext } from '../type'; import { InstanceRule } from './instance.rule'; +import { AuthorizationInjectionService } from '../service'; describe(InstanceRule.name, () => { let module: TestingModule; let rule: InstanceRule; + let injectionService: AuthorizationInjectionService; let authorizationHelper: DeepMocked; @@ -17,17 +19,24 @@ describe(InstanceRule.name, () => { await setupEntities(); module = await Test.createTestingModule({ - providers: [InstanceRule, AuthorizationHelper], + providers: [InstanceRule, AuthorizationHelper, AuthorizationInjectionService], }).compile(); rule = module.get(InstanceRule); authorizationHelper = module.get(AuthorizationHelper); + injectionService = module.get(AuthorizationInjectionService); }); afterAll(async () => { await module.close(); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(rule); + }); + }); + describe('isApplicable', () => { describe('when the entity is applicable', () => { const setup = () => { diff --git a/apps/server/src/modules/authorization/domain/rules/instance.rule.ts b/apps/server/src/modules/authorization/domain/rules/instance.rule.ts index 7389aca1023..139d1532810 100644 --- a/apps/server/src/modules/authorization/domain/rules/instance.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/instance.rule.ts @@ -5,10 +5,16 @@ import { RoleName } from '@shared/domain/interface'; import { Action } from '@infra/authorization-client'; import { AuthorizationHelper } from '../service/authorization.helper'; import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class InstanceRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof Instance; diff --git a/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts index 732c6625ce5..2f5c0425114 100644 --- a/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts @@ -5,10 +5,12 @@ import { ObjectId } from '@mikro-orm/mongodb'; import { Action } from '../type'; import { AuthorizationHelper } from '../service/authorization.helper'; import { LegacySchoolRule } from './legacy-school.rule'; +import { AuthorizationInjectionService } from '../service'; describe('LegacySchoolRule', () => { let service: LegacySchoolRule; let authorizationHelper: AuthorizationHelper; + let injectionService: AuthorizationInjectionService; const permissionA = 'a' as Permission; const permissionB = 'b' as Permission; const permissionC = 'c' as Permission; @@ -17,11 +19,12 @@ describe('LegacySchoolRule', () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, LegacySchoolRule], + providers: [AuthorizationHelper, LegacySchoolRule, AuthorizationInjectionService], }).compile(); service = await module.get(LegacySchoolRule); authorizationHelper = await module.get(AuthorizationHelper); + injectionService = await module.get(AuthorizationInjectionService); }); const setupSchoolAndUser = () => { @@ -35,6 +38,12 @@ describe('LegacySchoolRule', () => { return { school, user }; }; + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(service); + }); + }); + it('should call hasAllPermissions on AuthorizationHelper', () => { const { school, user } = setupSchoolAndUser(); const spy = jest.spyOn(authorizationHelper, 'hasAllPermissions'); diff --git a/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts b/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts index fe8eb466fbd..615a30c52a1 100644 --- a/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts @@ -3,13 +3,19 @@ import { LegacySchoolDo } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service'; /** * @deprecated because it uses the deprecated LegacySchoolDo. */ @Injectable() export class LegacySchoolRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof LegacySchoolDo; diff --git a/apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts index 433d4b26db6..393a3190e64 100644 --- a/apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts @@ -17,6 +17,7 @@ import { Action, AuthorizationContext } from '../type'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; +import { AuthorizationInjectionService } from '../service'; describe('LessonRule', () => { let rule: LessonRule; @@ -25,6 +26,7 @@ describe('LessonRule', () => { let courseGroupRule: DeepPartial; let globalUser: User; let entity: LessonEntity; + let injectionService: AuthorizationInjectionService; const permissionA = 'a' as Permission; const permissionB = 'b' as Permission; const permissionC = 'c' as Permission; @@ -33,13 +35,14 @@ describe('LessonRule', () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, LessonRule, CourseRule, CourseGroupRule], + providers: [AuthorizationHelper, LessonRule, CourseRule, CourseGroupRule, AuthorizationInjectionService], }).compile(); rule = await module.get(LessonRule); authorizationHelper = await module.get(AuthorizationHelper); courseRule = await module.get(CourseRule); courseGroupRule = await module.get(CourseGroupRule); + injectionService = await module.get(AuthorizationInjectionService); }); beforeEach(() => { @@ -47,6 +50,12 @@ describe('LessonRule', () => { globalUser = userFactory.build({ roles: [role] }); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(rule); + }); + }); + it('should call hasAllPermissions on AuthorizationHelper', () => { entity = lessonFactory.build(); const spy = jest.spyOn(authorizationHelper, 'hasAllPermissions'); diff --git a/apps/server/src/modules/authorization/domain/rules/lesson.rule.ts b/apps/server/src/modules/authorization/domain/rules/lesson.rule.ts index 48e0cfa92e6..9292a5e57b3 100644 --- a/apps/server/src/modules/authorization/domain/rules/lesson.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/lesson.rule.ts @@ -4,14 +4,18 @@ import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class LessonRule implements Rule { constructor( private readonly authorizationHelper: AuthorizationHelper, private readonly courseRule: CourseRule, - private readonly courseGroupRule: CourseGroupRule - ) {} + private readonly courseGroupRule: CourseGroupRule, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof LessonEntity; diff --git a/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.spec.ts index 1c7e93c3ad4..00fb7beb1dd 100644 --- a/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.spec.ts @@ -15,10 +15,12 @@ import { import { AuthorizationContextBuilder } from '../mapper'; import { AuthorizationHelper } from '../service/authorization.helper'; import { SchoolSystemOptionsRule } from './school-system-options.rule'; +import { AuthorizationInjectionService } from '../service'; describe(SchoolSystemOptionsRule.name, () => { let module: TestingModule; let rule: SchoolSystemOptionsRule; + let injectionService: AuthorizationInjectionService; let authorizationHelper: DeepMocked; @@ -32,11 +34,13 @@ describe(SchoolSystemOptionsRule.name, () => { provide: AuthorizationHelper, useValue: createMock(), }, + AuthorizationInjectionService, ], }).compile(); rule = module.get(SchoolSystemOptionsRule); authorizationHelper = module.get(AuthorizationHelper); + injectionService = module.get(AuthorizationInjectionService); }); afterAll(async () => { @@ -47,6 +51,12 @@ describe(SchoolSystemOptionsRule.name, () => { jest.resetAllMocks(); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(rule); + }); + }); + describe('isApplicable', () => { describe('when the entity is applicable', () => { const setup = () => { diff --git a/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.ts b/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.ts index 475697553db..47f628c551a 100644 --- a/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.ts @@ -3,10 +3,16 @@ import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class SchoolSystemOptionsRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched: boolean = object instanceof SchoolSystemOptions; diff --git a/apps/server/src/modules/authorization/domain/rules/school.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/school.rule.spec.ts index 9b1ac7446c6..cffa7116e17 100644 --- a/apps/server/src/modules/authorization/domain/rules/school.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/school.rule.spec.ts @@ -5,20 +5,27 @@ import { schoolEntityFactory, setupEntities, userFactory } from '@shared/testing import { AuthorizationContextBuilder } from '../mapper'; import { AuthorizationHelper } from '../service/authorization.helper'; import { SchoolRule } from './school.rule'; +import { AuthorizationInjectionService } from '../service'; describe('SchoolRule', () => { let rule: SchoolRule; let authorizationHelper: DeepMocked; + let injectionService: AuthorizationInjectionService; beforeAll(async () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [SchoolRule, { provide: AuthorizationHelper, useValue: createMock() }], + providers: [ + SchoolRule, + { provide: AuthorizationHelper, useValue: createMock() }, + AuthorizationInjectionService, + ], }).compile(); rule = await module.get(SchoolRule); authorizationHelper = await module.get(AuthorizationHelper); + injectionService = await module.get(AuthorizationInjectionService); }); const setupSchoolAndUser = () => { @@ -28,6 +35,12 @@ describe('SchoolRule', () => { return { school, user }; }; + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(rule); + }); + }); + describe('isApplicable', () => { describe('when object is instance of School', () => { const setup = () => { diff --git a/apps/server/src/modules/authorization/domain/rules/school.rule.ts b/apps/server/src/modules/authorization/domain/rules/school.rule.ts index d960e62c3dd..7850ae8e821 100644 --- a/apps/server/src/modules/authorization/domain/rules/school.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/school.rule.ts @@ -3,10 +3,16 @@ import { User } from '@shared/domain/entity'; import { School } from '@src/modules/school/domain/do'; import { AuthorizationHelper } from '../service/authorization.helper'; import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class SchoolRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isApplicable = object instanceof School; diff --git a/apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts index 041d583a13f..9ab8abeed50 100644 --- a/apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts @@ -17,6 +17,7 @@ import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; import { SubmissionRule } from './submission.rule'; import { TaskRule } from './task.rule'; +import { AuthorizationInjectionService } from '../service'; const buildUserWithPermission = (permission) => { const role = roleFactory.buildWithId({ permissions: [permission] }); @@ -27,21 +28,37 @@ const buildUserWithPermission = (permission) => { describe('SubmissionRule', () => { let submissionRule: SubmissionRule; + let injectionService: AuthorizationInjectionService; beforeAll(async () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, SubmissionRule, TaskRule, CourseRule, LessonRule, CourseGroupRule], + providers: [ + AuthorizationHelper, + SubmissionRule, + TaskRule, + CourseRule, + LessonRule, + CourseGroupRule, + AuthorizationInjectionService, + ], }).compile(); submissionRule = await module.get(SubmissionRule); + injectionService = await module.get(AuthorizationInjectionService); }); afterEach(() => { jest.resetAllMocks(); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(submissionRule); + }); + }); + describe('isApplicable', () => { describe('when entity is instance of Submission', () => { const setup = () => { diff --git a/apps/server/src/modules/authorization/domain/rules/submission.rule.ts b/apps/server/src/modules/authorization/domain/rules/submission.rule.ts index fda0bcca7ad..273f56ae731 100644 --- a/apps/server/src/modules/authorization/domain/rules/submission.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/submission.rule.ts @@ -3,10 +3,17 @@ import { Submission, User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; import { TaskRule } from './task.rule'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class SubmissionRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper, private readonly taskRule: TaskRule) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + private readonly taskRule: TaskRule, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof Submission; diff --git a/apps/server/src/modules/authorization/domain/rules/system.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/system.rule.spec.ts index b947ac31f68..03dc6e20e05 100644 --- a/apps/server/src/modules/authorization/domain/rules/system.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/system.rule.spec.ts @@ -9,10 +9,12 @@ import { schoolEntityFactory, setupEntities, systemEntityFactory, userFactory } import { AuthorizationContextBuilder } from '../mapper'; import { AuthorizationHelper } from '../service/authorization.helper'; import { SystemRule } from './system.rule'; +import { AuthorizationInjectionService } from '../service'; describe(SystemRule.name, () => { let module: TestingModule; let rule: SystemRule; + let injectionService: AuthorizationInjectionService; let authorizationHelper: DeepMocked; @@ -26,11 +28,13 @@ describe(SystemRule.name, () => { provide: AuthorizationHelper, useValue: createMock(), }, + AuthorizationInjectionService, ], }).compile(); rule = module.get(SystemRule); authorizationHelper = module.get(AuthorizationHelper); + injectionService = module.get(AuthorizationInjectionService); }); afterAll(async () => { @@ -41,6 +45,12 @@ describe(SystemRule.name, () => { jest.resetAllMocks(); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(rule); + }); + }); + describe('isApplicable', () => { describe('when the entity is applicable', () => { const setup = () => { diff --git a/apps/server/src/modules/authorization/domain/rules/system.rule.ts b/apps/server/src/modules/authorization/domain/rules/system.rule.ts index 253224097b6..e20594be9ef 100644 --- a/apps/server/src/modules/authorization/domain/rules/system.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/system.rule.ts @@ -3,10 +3,16 @@ import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class SystemRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched: boolean = object instanceof System; diff --git a/apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts index aa32062c7af..cdc60e427d8 100644 --- a/apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts @@ -8,12 +8,14 @@ import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; import { TaskRule } from './task.rule'; +import { AuthorizationInjectionService } from '../service'; describe('TaskRule', () => { let service: TaskRule; let authorizationHelper: AuthorizationHelper; let courseRule: DeepPartial; let lessonRule: DeepPartial; + let injectionService: AuthorizationInjectionService; const permissionA = 'a' as Permission; const permissionB = 'b' as Permission; const permissionC = 'c' as Permission; @@ -22,13 +24,27 @@ describe('TaskRule', () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, TaskRule, CourseRule, LessonRule, CourseGroupRule], + providers: [ + AuthorizationHelper, + TaskRule, + CourseRule, + LessonRule, + CourseGroupRule, + AuthorizationInjectionService, + ], }).compile(); service = await module.get(TaskRule); authorizationHelper = await module.get(AuthorizationHelper); courseRule = await module.get(CourseRule); lessonRule = await module.get(LessonRule); + injectionService = await module.get(AuthorizationInjectionService); + }); + + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(service); + }); }); describe('[method] hasPermission', () => { diff --git a/apps/server/src/modules/authorization/domain/rules/task.rule.ts b/apps/server/src/modules/authorization/domain/rules/task.rule.ts index f24dee67740..b8796805794 100644 --- a/apps/server/src/modules/authorization/domain/rules/task.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/task.rule.ts @@ -4,14 +4,18 @@ import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class TaskRule implements Rule { constructor( private readonly authorizationHelper: AuthorizationHelper, private readonly courseRule: CourseRule, - private readonly lessonRule: LessonRule - ) {} + private readonly lessonRule: LessonRule, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof Task; diff --git a/apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts index d331adb80f9..d32d492187f 100644 --- a/apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts @@ -4,9 +4,11 @@ import { roleFactory, setupEntities, teamFactory, userFactory } from '@shared/te import { AuthorizationContextBuilder } from '../mapper'; import { AuthorizationHelper } from '../service/authorization.helper'; import { TeamRule } from './team.rule'; +import { AuthorizationInjectionService } from '../service'; describe('TeamRule', () => { let rule: TeamRule; + let injectionService: AuthorizationInjectionService; const permissionA = 'a' as Permission; const permissionC = 'c' as Permission; const teamPermissionA = 'TA' as Permission; @@ -18,10 +20,17 @@ describe('TeamRule', () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, TeamRule], + providers: [AuthorizationHelper, TeamRule, AuthorizationInjectionService], }).compile(); rule = await module.get(TeamRule); + injectionService = await module.get(AuthorizationInjectionService); + }); + + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(rule); + }); }); describe('isApplicable', () => { diff --git a/apps/server/src/modules/authorization/domain/rules/team.rule.ts b/apps/server/src/modules/authorization/domain/rules/team.rule.ts index 48cca110e2a..8668897a5dc 100644 --- a/apps/server/src/modules/authorization/domain/rules/team.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/team.rule.ts @@ -2,10 +2,16 @@ import { Injectable } from '@nestjs/common'; import { TeamEntity, TeamUserEntity, User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class TeamRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { return object instanceof TeamEntity; diff --git a/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts index 6ee893fc9d2..782774030de 100644 --- a/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts @@ -7,10 +7,12 @@ import { schoolEntityFactory, setupEntities, userFactory, userLoginMigrationDOFa import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext } from '../type'; import { UserLoginMigrationRule } from './user-login-migration.rule'; +import { AuthorizationInjectionService } from '../service'; describe('UserLoginMigrationRule', () => { let module: TestingModule; let rule: UserLoginMigrationRule; + let injectionService: AuthorizationInjectionService; let authorizationHelper: DeepMocked; @@ -24,17 +26,25 @@ describe('UserLoginMigrationRule', () => { provide: AuthorizationHelper, useValue: createMock(), }, + AuthorizationInjectionService, ], }).compile(); rule = module.get(UserLoginMigrationRule); authorizationHelper = module.get(AuthorizationHelper); + injectionService = module.get(AuthorizationInjectionService); }); afterAll(async () => { await module.close(); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(rule); + }); + }); + describe('isApplicable', () => { describe('when the entity is applicable', () => { const setup = () => { diff --git a/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts b/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts index 05ca647e7ab..3ea2440bb42 100644 --- a/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts @@ -3,10 +3,16 @@ import { UserLoginMigrationDO } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class UserLoginMigrationRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched: boolean = object instanceof UserLoginMigrationDO; diff --git a/apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts index 85492348f75..0ecf3d1fb2d 100644 --- a/apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts @@ -5,6 +5,7 @@ import { roleFactory, setupEntities, userFactory } from '@shared/testing'; import { Action } from '../type'; import { AuthorizationHelper } from '../service/authorization.helper'; import { UserRule } from './user.rule'; +import { AuthorizationInjectionService } from '../service'; describe('UserRule', () => { let service: UserRule; @@ -12,6 +13,7 @@ describe('UserRule', () => { let user: User; let entity: User; let role: Role; + let injectionService: AuthorizationInjectionService; const permissionA = 'a' as Permission; const permissionB = 'b' as Permission; const permissionC = 'c' as Permission; @@ -20,11 +22,12 @@ describe('UserRule', () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, UserRule], + providers: [AuthorizationHelper, UserRule, AuthorizationInjectionService], }).compile(); service = await module.get(UserRule); authorizationHelper = await module.get(AuthorizationHelper); + injectionService = await module.get(AuthorizationInjectionService); }); beforeEach(() => { @@ -32,6 +35,12 @@ describe('UserRule', () => { user = userFactory.build({ roles: [role] }); }); + describe('constructor', () => { + it('should inject into AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(service); + }); + }); + it('should call hasAllPermissions on AuthorizationHelper', () => { entity = userFactory.build(); user = userFactory.build({ roles: [role], school: entity }); diff --git a/apps/server/src/modules/authorization/domain/rules/user.rule.ts b/apps/server/src/modules/authorization/domain/rules/user.rule.ts index 66ea8e44052..98e9ce842f5 100644 --- a/apps/server/src/modules/authorization/domain/rules/user.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/user.rule.ts @@ -2,10 +2,16 @@ import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { AuthorizationContext, Rule } from '../type'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class UserRule implements Rule { - constructor(private readonly authorizationHelper: AuthorizationHelper) {} + constructor( + private readonly authorizationHelper: AuthorizationHelper, + authorisationInjectionService: AuthorizationInjectionService + ) { + authorisationInjectionService.injectAuthorizationRule(this); + } public isApplicable(user: User, object: unknown): boolean { const isMatched = object instanceof User; diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.ts index e601fb84c89..43d9bb6b40f 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -2,59 +2,12 @@ import { Injectable, InternalServerErrorException, NotImplementedException } fro import { AuthorizableObject } from '@shared/domain/domain-object'; // fix import when it is avaible import { BaseDO } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; -import { - CourseGroupRule, - CourseRule, - GroupRule, - InstanceRule, - LegacySchoolRule, - LessonRule, - SchoolRule, - SchoolSystemOptionsRule, - SubmissionRule, - SystemRule, - TaskRule, - TeamRule, - UserLoginMigrationRule, - UserRule, -} from '../rules'; import type { AuthorizationContext, Rule } from '../type'; import { AuthorizationInjectionService } from './authorization-injection.service'; @Injectable() export class RuleManager { - constructor( - courseGroupRule: CourseGroupRule, - courseRule: CourseRule, - groupRule: GroupRule, - legaySchoolRule: LegacySchoolRule, - lessonRule: LessonRule, - schoolRule: SchoolRule, - schoolSystemOptionsRule: SchoolSystemOptionsRule, - submissionRule: SubmissionRule, - systemRule: SystemRule, - taskRule: TaskRule, - teamRule: TeamRule, - userLoginMigrationRule: UserLoginMigrationRule, - userRule: UserRule, - instanceRule: InstanceRule, - private readonly authorizationInjectionService: AuthorizationInjectionService - ) { - this.authorizationInjectionService.injectAuthorizationRule(courseGroupRule); - this.authorizationInjectionService.injectAuthorizationRule(courseRule); - this.authorizationInjectionService.injectAuthorizationRule(groupRule); - this.authorizationInjectionService.injectAuthorizationRule(legaySchoolRule); - this.authorizationInjectionService.injectAuthorizationRule(lessonRule); - this.authorizationInjectionService.injectAuthorizationRule(schoolRule); - this.authorizationInjectionService.injectAuthorizationRule(schoolSystemOptionsRule); - this.authorizationInjectionService.injectAuthorizationRule(submissionRule); - this.authorizationInjectionService.injectAuthorizationRule(systemRule); - this.authorizationInjectionService.injectAuthorizationRule(taskRule); - this.authorizationInjectionService.injectAuthorizationRule(teamRule); - this.authorizationInjectionService.injectAuthorizationRule(userLoginMigrationRule); - this.authorizationInjectionService.injectAuthorizationRule(userRule); - this.authorizationInjectionService.injectAuthorizationRule(instanceRule); - } + constructor(private readonly authorizationInjectionService: AuthorizationInjectionService) {} public selectRule(user: User, object: AuthorizableObject | BaseDO, context: AuthorizationContext): Rule { const rules = [...this.authorizationInjectionService.getAuthorizationRules()]; From bac69bc3ab5d46d506475643bfe9c99394c51014 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 4 Oct 2024 12:30:32 +0200 Subject: [PATCH 09/23] inject lesson reference loader --- .../authorization/authorization-reference.module.ts | 3 +-- .../authorization/domain/service/reference.loader.ts | 3 --- apps/server/src/modules/lesson/lesson.module.ts | 3 ++- .../src/modules/lesson/service/lesson.service.spec.ts | 10 ++++++++++ .../src/modules/lesson/service/lesson.service.ts | 8 +++++++- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/apps/server/src/modules/authorization/authorization-reference.module.ts b/apps/server/src/modules/authorization/authorization-reference.module.ts index 94c008832e6..2d871891c43 100644 --- a/apps/server/src/modules/authorization/authorization-reference.module.ts +++ b/apps/server/src/modules/authorization/authorization-reference.module.ts @@ -1,5 +1,4 @@ import { InstanceModule } from '@modules/instance'; -import { LessonModule } from '@modules/lesson'; import { Module } from '@nestjs/common'; import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; @@ -13,7 +12,7 @@ import { TeamsModule } from '../teams'; * Avoid using this module and load the needed data in your use cases and then use the normal AuthorizationModule! */ @Module({ - imports: [AuthorizationModule, LessonModule, TeamsModule, LoggerModule, InstanceModule], + imports: [AuthorizationModule, TeamsModule, LoggerModule, InstanceModule], providers: [ AuthorizationHelper, ReferenceLoader, diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts index 6b8342bf969..4403c20136e 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -1,7 +1,6 @@ // TODO fix modules circular dependency // eslint-disable-next-line @typescript-eslint/no-restricted-imports import { TeamAuthorisableService } from '@src/modules/teams/service/team-authorisable.service'; -import { LessonService } from '@modules/lesson'; import { Injectable, NotImplementedException } from '@nestjs/common'; import { AuthorizableObject } from '@shared/domain/domain-object'; import { BaseDO } from '@shared/domain/domainobject'; @@ -19,7 +18,6 @@ export class ReferenceLoader { private readonly courseGroupRepo: CourseGroupRepo, private readonly taskRepo: TaskRepo, private readonly schoolRepo: LegacySchoolRepo, - private readonly lessonService: LessonService, private readonly teamAuthorisableService: TeamAuthorisableService, private readonly submissionRepo: SubmissionRepo, private readonly instanceService: InstanceService, @@ -31,7 +29,6 @@ export class ReferenceLoader { service.injectReferenceLoader(AuthorizableReferenceType.CourseGroup, this.courseGroupRepo); service.injectReferenceLoader(AuthorizableReferenceType.User, this.userRepo); service.injectReferenceLoader(AuthorizableReferenceType.School, this.schoolRepo); - service.injectReferenceLoader(AuthorizableReferenceType.Lesson, this.lessonService); service.injectReferenceLoader(AuthorizableReferenceType.Team, this.teamAuthorisableService); service.injectReferenceLoader(AuthorizableReferenceType.Submission, this.submissionRepo); service.injectReferenceLoader(AuthorizableReferenceType.Instance, this.instanceService); diff --git a/apps/server/src/modules/lesson/lesson.module.ts b/apps/server/src/modules/lesson/lesson.module.ts index b4d72b321f5..27b81121557 100644 --- a/apps/server/src/modules/lesson/lesson.module.ts +++ b/apps/server/src/modules/lesson/lesson.module.ts @@ -7,9 +7,10 @@ import { LoggerModule } from '@src/core/logger'; import { CqrsModule } from '@nestjs/cqrs'; import { LessonRepo } from './repository'; import { EtherpadService, LessonCopyService, LessonService, NexboardService } from './service'; +import { AuthorizationModule } from '../authorization'; @Module({ - imports: [FilesStorageClientModule, LoggerModule, CopyHelperModule, TaskModule, CqrsModule], + imports: [FilesStorageClientModule, LoggerModule, CopyHelperModule, TaskModule, CqrsModule, AuthorizationModule], providers: [LessonRepo, LessonService, EtherpadService, NexboardService, LessonCopyService, FeathersServiceProvider], exports: [LessonService, LessonCopyService], }) diff --git a/apps/server/src/modules/lesson/service/lesson.service.spec.ts b/apps/server/src/modules/lesson/service/lesson.service.spec.ts index 30c462ebf95..55fce12c1d9 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.spec.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.spec.ts @@ -17,12 +17,14 @@ import { lessonFactory, setupEntities } from '@shared/testing'; import { Logger } from '@src/core/logger'; import { LessonRepo } from '../repository'; import { LessonService } from './lesson.service'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; describe('LessonService', () => { let lessonService: LessonService; let module: TestingModule; let lessonRepo: DeepMocked; + let injectionService: DeepMocked; let filesStorageClientAdapterService: DeepMocked; let eventBus: DeepMocked; @@ -32,6 +34,7 @@ describe('LessonService', () => { module = await Test.createTestingModule({ providers: [ LessonService, + AuthorizationInjectionService, { provide: LessonRepo, useValue: createMock(), @@ -59,6 +62,7 @@ describe('LessonService', () => { lessonService = module.get(LessonService); lessonRepo = module.get(LessonRepo); + injectionService = module.get(AuthorizationInjectionService); filesStorageClientAdapterService = module.get(FilesStorageClientAdapterService); eventBus = module.get(EventBus); }); @@ -93,6 +97,12 @@ describe('LessonService', () => { expect(lessonRepo.findById).toHaveBeenCalledWith(lesson.id); }); + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + expect(injectionService.getReferenceLoader(AuthorizableReferenceType.Lesson)).toEqual(lessonService); + }); + }); + describe('findByCourseIds', () => { it('should call findByCourseIds from lesson repo', async () => { const courseIds = ['course-1', 'course-2']; diff --git a/apps/server/src/modules/lesson/service/lesson.service.ts b/apps/server/src/modules/lesson/service/lesson.service.ts index 2ffd1f6543b..9fa84210ddc 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.ts @@ -17,7 +17,11 @@ import { EventBus, EventsHandler, IEventHandler } from '@nestjs/cqrs'; import { ComponentProperties, LessonEntity } from '@shared/domain/entity'; import { Counted, EntityId } from '@shared/domain/types'; import { Logger } from '@src/core/logger'; -import { AuthorizationLoaderService } from '@src/modules/authorization'; +import { + AuthorizableReferenceType, + AuthorizationInjectionService, + AuthorizationLoaderService, +} from '@src/modules/authorization'; import { LessonRepo } from '../repository'; @Injectable() @@ -26,11 +30,13 @@ export class LessonService implements AuthorizationLoaderService, DeletionServic constructor( private readonly lessonRepo: LessonRepo, private readonly filesStorageClientAdapterService: FilesStorageClientAdapterService, + injectionService: AuthorizationInjectionService, private readonly logger: Logger, private readonly eventBus: EventBus, private readonly orm: MikroORM ) { this.logger.setContext(LessonService.name); + injectionService.injectReferenceLoader(AuthorizableReferenceType.Lesson, this); } @UseRequestContext() From 2d5f05c0376c470b289c26cf2d5641deacca4362 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 4 Oct 2024 12:39:45 +0200 Subject: [PATCH 10/23] inject teams reference loader --- .../authorization/authorization-reference.module.ts | 3 +-- .../authorization/domain/service/reference.loader.ts | 2 -- .../teams/service/team-authorisable.service.spec.ts | 8 ++++++++ .../modules/teams/service/team-authorisable.service.ts | 10 ++++++++-- apps/server/src/modules/teams/teams.module.ts | 3 ++- .../service/external-tool-authorizable.service.spec.ts | 10 ++-------- 6 files changed, 21 insertions(+), 15 deletions(-) diff --git a/apps/server/src/modules/authorization/authorization-reference.module.ts b/apps/server/src/modules/authorization/authorization-reference.module.ts index 2d871891c43..5a455e44507 100644 --- a/apps/server/src/modules/authorization/authorization-reference.module.ts +++ b/apps/server/src/modules/authorization/authorization-reference.module.ts @@ -4,7 +4,6 @@ import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo import { LoggerModule } from '@src/core/logger'; import { AuthorizationModule } from './authorization.module'; import { AuthorizationHelper, AuthorizationReferenceService, ReferenceLoader } from './domain'; -import { TeamsModule } from '../teams'; /** * This module is part of an intermediate state. In the future it should be replaced by an AuthorizationApiModule. @@ -12,7 +11,7 @@ import { TeamsModule } from '../teams'; * Avoid using this module and load the needed data in your use cases and then use the normal AuthorizationModule! */ @Module({ - imports: [AuthorizationModule, TeamsModule, LoggerModule, InstanceModule], + imports: [AuthorizationModule, LoggerModule, InstanceModule], providers: [ AuthorizationHelper, ReferenceLoader, diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts index 4403c20136e..50d7d923bfa 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -18,7 +18,6 @@ export class ReferenceLoader { private readonly courseGroupRepo: CourseGroupRepo, private readonly taskRepo: TaskRepo, private readonly schoolRepo: LegacySchoolRepo, - private readonly teamAuthorisableService: TeamAuthorisableService, private readonly submissionRepo: SubmissionRepo, private readonly instanceService: InstanceService, private readonly authorizationInjectionService: AuthorizationInjectionService @@ -29,7 +28,6 @@ export class ReferenceLoader { service.injectReferenceLoader(AuthorizableReferenceType.CourseGroup, this.courseGroupRepo); service.injectReferenceLoader(AuthorizableReferenceType.User, this.userRepo); service.injectReferenceLoader(AuthorizableReferenceType.School, this.schoolRepo); - service.injectReferenceLoader(AuthorizableReferenceType.Team, this.teamAuthorisableService); service.injectReferenceLoader(AuthorizableReferenceType.Submission, this.submissionRepo); service.injectReferenceLoader(AuthorizableReferenceType.Instance, this.instanceService); } diff --git a/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts b/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts index 3321d6ce818..db904267b28 100644 --- a/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts +++ b/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts @@ -3,10 +3,12 @@ import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { TeamsRepo } from '@shared/repo'; import { setupEntities, teamFactory } from '@shared/testing'; import { TeamAuthorisableService } from './team-authorisable.service'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; describe('team authorisable service', () => { let module: TestingModule; let service: TeamAuthorisableService; + let injectionService: AuthorizationInjectionService; let teamsRepo: DeepMocked; @@ -20,13 +22,19 @@ describe('team authorisable service', () => { provide: TeamsRepo, useValue: createMock(), }, + AuthorizationInjectionService, ], }).compile(); service = module.get(TeamAuthorisableService); + injectionService = module.get(AuthorizationInjectionService); teamsRepo = module.get(TeamsRepo); }); + it('should inject intself into authorisation', () => { + expect(injectionService.getReferenceLoader(AuthorizableReferenceType.Team)).toEqual(service); + }); + it('should return entity', async () => { const team = teamFactory.buildWithId(); teamsRepo.findById.mockResolvedValue(team); diff --git a/apps/server/src/modules/teams/service/team-authorisable.service.ts b/apps/server/src/modules/teams/service/team-authorisable.service.ts index a24973c8cc0..5c86d638ef4 100644 --- a/apps/server/src/modules/teams/service/team-authorisable.service.ts +++ b/apps/server/src/modules/teams/service/team-authorisable.service.ts @@ -1,11 +1,17 @@ import { Injectable } from '@nestjs/common'; import { TeamEntity } from '@shared/domain/entity'; import { TeamsRepo } from '@shared/repo'; -import { AuthorizationLoaderServiceGeneric } from '@src/modules/authorization'; +import { + AuthorizableReferenceType, + AuthorizationInjectionService, + AuthorizationLoaderServiceGeneric, +} from '@src/modules/authorization'; @Injectable() export class TeamAuthorisableService implements AuthorizationLoaderServiceGeneric { - constructor(private readonly teamsRepo: TeamsRepo) {} + constructor(private readonly teamsRepo: TeamsRepo, injectionService: AuthorizationInjectionService) { + injectionService.injectReferenceLoader(AuthorizableReferenceType.Team, this); + } findById(id: string): Promise { return this.teamsRepo.findById(id, true); diff --git a/apps/server/src/modules/teams/teams.module.ts b/apps/server/src/modules/teams/teams.module.ts index 6e3ec526640..39a6f29395d 100644 --- a/apps/server/src/modules/teams/teams.module.ts +++ b/apps/server/src/modules/teams/teams.module.ts @@ -3,9 +3,10 @@ import { TeamsRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; import { CqrsModule } from '@nestjs/cqrs'; import { TeamAuthorisableService, TeamService } from './service'; +import { AuthorizationModule } from '../authorization'; @Module({ - imports: [CqrsModule, LoggerModule], + imports: [CqrsModule, LoggerModule, AuthorizationModule], providers: [TeamService, TeamsRepo, TeamAuthorisableService], exports: [TeamService, TeamAuthorisableService], }) diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts index b4d894f2752..f462a5653e7 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts @@ -20,10 +20,7 @@ describe(ExternalToolAuthorizableService.name, () => { provide: ExternalToolRepo, useValue: createMock(), }, - { - provide: AuthorizationInjectionService, - useValue: createMock(), - }, + AuthorizationInjectionService, ], }).compile(); @@ -42,10 +39,7 @@ describe(ExternalToolAuthorizableService.name, () => { describe('constructor', () => { it('should inject itself into the AuthorizationInjectionService', () => { - expect(authorizationInjectionService.injectReferenceLoader).toHaveBeenCalledWith( - AuthorizableReferenceType.ExternalTool, - service - ); + expect(authorizationInjectionService.getReferenceLoader(AuthorizableReferenceType.ExternalTool)).toEqual(service); }); }); From d5e36ceca33fcb9204517da09aa100a141e63bcd Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 4 Oct 2024 13:13:31 +0200 Subject: [PATCH 11/23] update reference loader tests --- .../domain/service/reference.loader.spec.ts | 75 +------------------ 1 file changed, 1 insertion(+), 74 deletions(-) diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts index 5a7dd6516f4..56003d36e5e 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts @@ -1,22 +1,11 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { InstanceService } from '@modules/instance'; -import { LessonService } from '@modules/lesson'; -import { ContextExternalToolAuthorizableService, ExternalToolAuthorizableService } from '@modules/tool'; import { NotImplementedException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityId } from '@shared/domain/types'; -import { - CourseGroupRepo, - CourseRepo, - LegacySchoolRepo, - SchoolExternalToolRepo, - SubmissionRepo, - TaskRepo, - UserRepo, -} from '@shared/repo'; +import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; import { setupEntities, userFactory } from '@shared/testing'; -import { TeamAuthorisableService } from '@src/modules/teams'; import { AuthorizableReferenceType } from '../type'; import { ReferenceLoader } from './reference.loader'; import { AuthorizationInjectionService } from './authorization-injection.service'; @@ -29,12 +18,7 @@ describe('reference.loader', () => { let courseGroupRepo: DeepMocked; let taskRepo: DeepMocked; let schoolRepo: DeepMocked; - let lessonService: DeepMocked; - let teamsAuthorisableService: DeepMocked; let submissionRepo: DeepMocked; - let schoolExternalToolRepo: DeepMocked; - let contextExternalToolAuthorizableService: DeepMocked; - let externalToolAuthorizableService: DeepMocked; let instanceService: DeepMocked; const entityId: EntityId = new ObjectId().toHexString(); @@ -68,30 +52,10 @@ describe('reference.loader', () => { provide: LegacySchoolRepo, useValue: createMock(), }, - { - provide: LessonService, - useValue: createMock(), - }, - { - provide: TeamAuthorisableService, - useValue: createMock(), - }, { provide: SubmissionRepo, useValue: createMock(), }, - { - provide: SchoolExternalToolRepo, - useValue: createMock(), - }, - { - provide: ContextExternalToolAuthorizableService, - useValue: createMock(), - }, - { - provide: ExternalToolAuthorizableService, - useValue: createMock(), - }, { provide: InstanceService, useValue: createMock(), @@ -106,12 +70,7 @@ describe('reference.loader', () => { courseGroupRepo = await module.get(CourseGroupRepo); taskRepo = await module.get(TaskRepo); schoolRepo = await module.get(LegacySchoolRepo); - lessonService = await module.get(LessonService); - teamsAuthorisableService = await module.get(TeamAuthorisableService); submissionRepo = await module.get(SubmissionRepo); - schoolExternalToolRepo = await module.get(SchoolExternalToolRepo); - contextExternalToolAuthorizableService = await module.get(ContextExternalToolAuthorizableService); - externalToolAuthorizableService = await module.get(ExternalToolAuthorizableService); instanceService = await module.get(InstanceService); }); @@ -184,17 +143,6 @@ describe('reference.loader', () => { expect(injectionService.injectReferenceLoader).toBeCalledWith(AuthorizableReferenceType.School, schoolRepo); }); - it('should inject lesson service', () => { - expect(injectionService.injectReferenceLoader).toBeCalledWith(AuthorizableReferenceType.Lesson, lessonService); - }); - - it('should inject teams repo', () => { - expect(injectionService.injectReferenceLoader).toBeCalledWith( - AuthorizableReferenceType.Team, - teamsAuthorisableService - ); - }); - it('should inject submission repo', () => { expect(injectionService.injectReferenceLoader).toBeCalledWith( AuthorizableReferenceType.Submission, @@ -202,27 +150,6 @@ describe('reference.loader', () => { ); }); - it('should inject school external tool repo', () => { - expect(injectionService.injectReferenceLoader).toBeCalledWith( - AuthorizableReferenceType.SchoolExternalToolEntity, - schoolExternalToolRepo - ); - }); - - it('should inject context external tool authorizable service', () => { - expect(injectionService.injectReferenceLoader).toBeCalledWith( - AuthorizableReferenceType.ContextExternalToolEntity, - contextExternalToolAuthorizableService - ); - }); - - it('should inject external tool authorizable service', () => { - expect(injectionService.injectReferenceLoader).toBeCalledWith( - AuthorizableReferenceType.ExternalTool, - externalToolAuthorizableService - ); - }); - it('should inject instance service', () => { expect(injectionService.injectReferenceLoader).toBeCalledWith( AuthorizableReferenceType.Instance, From dd1909acd11d6d35522de48fe0a940e382adf5e5 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 4 Oct 2024 13:46:44 +0200 Subject: [PATCH 12/23] make coursegroup rule independent of other rules --- .../domain/rules/course-group.rule.spec.ts | 51 ++++++++++++++----- .../domain/rules/course-group.rule.ts | 7 ++- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts index 091a3193ea4..82de4a7fada 100644 --- a/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts @@ -3,16 +3,16 @@ import { CourseGroup, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory, courseGroupFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; import { CourseGroupRule } from './course-group.rule'; -import { CourseRule } from './course.rule'; import { Action } from '../type'; import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationInjectionService } from '../service'; +import { AuthorizationInjectionService, AuthorizationService } from '../service'; +import { DeepMocked, createMock } from '@golevelup/ts-jest'; describe('CourseGroupRule', () => { let service: CourseGroupRule; let authorizationHelper: AuthorizationHelper; let injectionService: AuthorizationInjectionService; - let courseRule: CourseRule; + let authorizationService: DeepMocked; let user: User; let entity: CourseGroup; const permissionA = 'a' as Permission; @@ -23,17 +23,29 @@ describe('CourseGroupRule', () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [AuthorizationHelper, CourseRule, CourseGroupRule, AuthorizationInjectionService], + providers: [ + AuthorizationHelper, + CourseGroupRule, + AuthorizationInjectionService, + { + provide: AuthorizationService, + useValue: createMock(), + }, + ], }).compile(); service = await module.get(CourseGroupRule); authorizationHelper = await module.get(AuthorizationHelper); injectionService = await module.get(AuthorizationInjectionService); - courseRule = await module.get(CourseRule); + authorizationService = await module.get(AuthorizationService); const role = roleFactory.build({ permissions: [permissionA, permissionB] }); user = userFactory.build({ roles: [role] }); }); + beforeEach(() => { + jest.clearAllMocks(); + }); + it('should call hasAllPermissions on AuthorizationHelper', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course }); @@ -60,9 +72,11 @@ describe('CourseGroupRule', () => { it('should call courseRule.hasPermission', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course }); - const spy = jest.spyOn(courseRule, 'hasPermission'); service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [] }); - expect(spy).toBeCalledWith(user, entity.course, { action: Action.write, requiredPermissions: [] }); + expect(authorizationService.hasPermission).toBeCalledWith(user, entity.course, { + action: Action.write, + requiredPermissions: [], + }); }); }); @@ -78,17 +92,23 @@ describe('CourseGroupRule', () => { it('should call courseRule.hasPermission', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course }); - const spy = jest.spyOn(courseRule, 'hasPermission'); - service.hasPermission(user, entity, { action: Action.write, requiredPermissions: [] }); - expect(spy).toBeCalledWith(user, entity.course, { action: Action.write, requiredPermissions: [] }); + service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [] }); + expect(authorizationService.hasPermission).toBeCalledWith(user, entity.course, { + action: Action.write, + requiredPermissions: [], + }); }); }); describe('User [TEACHER]', () => { + const setUserIsTeacherInCourse = () => authorizationService.hasPermission.mockReturnValue(true); + const setUserNotTeacherInCourse = () => authorizationService.hasPermission.mockReturnValue(false); + describe('with passed permissions', () => { - it('should return "true" if user in scope', () => { + it('should return "true" if user is teacher in course', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course, students: [] }); + setUserIsTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [] }); expect(res).toBe(true); }); @@ -98,12 +118,14 @@ describe('CourseGroupRule', () => { it('should return "false" if user has not permission', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course }); + setUserIsTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [permissionC] }); expect(res).toBe(false); }); - it('should return "false" if user has not access to entity', () => { + it('should return "false" if user is not in course', () => { entity = courseGroupFactory.build(); + setUserNotTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [permissionA] }); expect(res).toBe(false); }); @@ -111,10 +133,13 @@ describe('CourseGroupRule', () => { }); describe('User [STUDENT]', () => { + const setUserNotTeacherInCourse = () => authorizationService.hasPermission.mockReturnValue(false); + describe('with passed permissions', () => { it('should return "true" if user in scope', () => { const course = courseFactory.build({ students: [] }); entity = courseGroupFactory.build({ course, students: [user] }); + setUserNotTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [] }); expect(res).toBe(true); }); @@ -124,12 +149,14 @@ describe('CourseGroupRule', () => { it('should return "false" if user has not permission', () => { const course = courseFactory.build({ students: [] }); entity = courseGroupFactory.build({ course, students: [user] }); + setUserNotTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.write, requiredPermissions: [permissionC] }); expect(res).toBe(false); }); it('should return "false" if user has not access to entity', () => { const course = courseFactory.build({ students: [user] }); + setUserNotTeacherInCourse(); entity = courseGroupFactory.build({ course, students: [] }); const res = service.hasPermission(user, entity, { action: Action.write, requiredPermissions: [permissionA] }); expect(res).toBe(false); diff --git a/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts b/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts index 73f69b8197a..47b595e4732 100644 --- a/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts @@ -2,14 +2,13 @@ import { Injectable } from '@nestjs/common'; import { CourseGroup, User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; -import { CourseRule } from './course.rule'; -import { AuthorizationInjectionService } from '../service'; +import { AuthorizationInjectionService, AuthorizationService } from '../service'; @Injectable() export class CourseGroupRule implements Rule { constructor( private readonly authorizationHelper: AuthorizationHelper, - private readonly courseRule: CourseRule, + private readonly authorisationService: AuthorizationService, injectionService: AuthorizationInjectionService ) { injectionService.injectAuthorizationRule(this); @@ -27,7 +26,7 @@ export class CourseGroupRule implements Rule { const hasAllPermissions = this.authorizationHelper.hasAllPermissions(user, requiredPermissions); const hasPermission = this.authorizationHelper.hasAccessToEntity(user, object, ['students']) || - this.courseRule.hasPermission(user, object.course, { action: Action.write, requiredPermissions: [] }); + this.authorisationService.hasPermission(user, object.course, { action: Action.write, requiredPermissions: [] }); return hasAllPermissions && hasPermission; } From d91c29c4e55b1f2e53479aaad3b039775b4b0740 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 4 Oct 2024 15:26:59 +0200 Subject: [PATCH 13/23] Revert "make coursegroup rule independent of other rules" This reverts commit dd1909acd11d6d35522de48fe0a940e382adf5e5. --- .../domain/rules/course-group.rule.spec.ts | 51 +++++-------------- .../domain/rules/course-group.rule.ts | 7 +-- 2 files changed, 16 insertions(+), 42 deletions(-) diff --git a/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts b/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts index 82de4a7fada..091a3193ea4 100644 --- a/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts +++ b/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts @@ -3,16 +3,16 @@ import { CourseGroup, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory, courseGroupFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; import { CourseGroupRule } from './course-group.rule'; +import { CourseRule } from './course.rule'; import { Action } from '../type'; import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationInjectionService, AuthorizationService } from '../service'; -import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { AuthorizationInjectionService } from '../service'; describe('CourseGroupRule', () => { let service: CourseGroupRule; let authorizationHelper: AuthorizationHelper; let injectionService: AuthorizationInjectionService; - let authorizationService: DeepMocked; + let courseRule: CourseRule; let user: User; let entity: CourseGroup; const permissionA = 'a' as Permission; @@ -23,29 +23,17 @@ describe('CourseGroupRule', () => { await setupEntities(); const module: TestingModule = await Test.createTestingModule({ - providers: [ - AuthorizationHelper, - CourseGroupRule, - AuthorizationInjectionService, - { - provide: AuthorizationService, - useValue: createMock(), - }, - ], + providers: [AuthorizationHelper, CourseRule, CourseGroupRule, AuthorizationInjectionService], }).compile(); service = await module.get(CourseGroupRule); authorizationHelper = await module.get(AuthorizationHelper); injectionService = await module.get(AuthorizationInjectionService); - authorizationService = await module.get(AuthorizationService); + courseRule = await module.get(CourseRule); const role = roleFactory.build({ permissions: [permissionA, permissionB] }); user = userFactory.build({ roles: [role] }); }); - beforeEach(() => { - jest.clearAllMocks(); - }); - it('should call hasAllPermissions on AuthorizationHelper', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course }); @@ -72,11 +60,9 @@ describe('CourseGroupRule', () => { it('should call courseRule.hasPermission', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course }); + const spy = jest.spyOn(courseRule, 'hasPermission'); service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [] }); - expect(authorizationService.hasPermission).toBeCalledWith(user, entity.course, { - action: Action.write, - requiredPermissions: [], - }); + expect(spy).toBeCalledWith(user, entity.course, { action: Action.write, requiredPermissions: [] }); }); }); @@ -92,23 +78,17 @@ describe('CourseGroupRule', () => { it('should call courseRule.hasPermission', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course }); - service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [] }); - expect(authorizationService.hasPermission).toBeCalledWith(user, entity.course, { - action: Action.write, - requiredPermissions: [], - }); + const spy = jest.spyOn(courseRule, 'hasPermission'); + service.hasPermission(user, entity, { action: Action.write, requiredPermissions: [] }); + expect(spy).toBeCalledWith(user, entity.course, { action: Action.write, requiredPermissions: [] }); }); }); describe('User [TEACHER]', () => { - const setUserIsTeacherInCourse = () => authorizationService.hasPermission.mockReturnValue(true); - const setUserNotTeacherInCourse = () => authorizationService.hasPermission.mockReturnValue(false); - describe('with passed permissions', () => { - it('should return "true" if user is teacher in course', () => { + it('should return "true" if user in scope', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course, students: [] }); - setUserIsTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [] }); expect(res).toBe(true); }); @@ -118,14 +98,12 @@ describe('CourseGroupRule', () => { it('should return "false" if user has not permission', () => { const course = courseFactory.build({ teachers: [user] }); entity = courseGroupFactory.build({ course }); - setUserIsTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [permissionC] }); expect(res).toBe(false); }); - it('should return "false" if user is not in course', () => { + it('should return "false" if user has not access to entity', () => { entity = courseGroupFactory.build(); - setUserNotTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [permissionA] }); expect(res).toBe(false); }); @@ -133,13 +111,10 @@ describe('CourseGroupRule', () => { }); describe('User [STUDENT]', () => { - const setUserNotTeacherInCourse = () => authorizationService.hasPermission.mockReturnValue(false); - describe('with passed permissions', () => { it('should return "true" if user in scope', () => { const course = courseFactory.build({ students: [] }); entity = courseGroupFactory.build({ course, students: [user] }); - setUserNotTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.read, requiredPermissions: [] }); expect(res).toBe(true); }); @@ -149,14 +124,12 @@ describe('CourseGroupRule', () => { it('should return "false" if user has not permission', () => { const course = courseFactory.build({ students: [] }); entity = courseGroupFactory.build({ course, students: [user] }); - setUserNotTeacherInCourse(); const res = service.hasPermission(user, entity, { action: Action.write, requiredPermissions: [permissionC] }); expect(res).toBe(false); }); it('should return "false" if user has not access to entity', () => { const course = courseFactory.build({ students: [user] }); - setUserNotTeacherInCourse(); entity = courseGroupFactory.build({ course, students: [] }); const res = service.hasPermission(user, entity, { action: Action.write, requiredPermissions: [permissionA] }); expect(res).toBe(false); diff --git a/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts b/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts index 47b595e4732..73f69b8197a 100644 --- a/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts +++ b/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts @@ -2,13 +2,14 @@ import { Injectable } from '@nestjs/common'; import { CourseGroup, User } from '@shared/domain/entity'; import { AuthorizationHelper } from '../service/authorization.helper'; import { Action, AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService, AuthorizationService } from '../service'; +import { CourseRule } from './course.rule'; +import { AuthorizationInjectionService } from '../service'; @Injectable() export class CourseGroupRule implements Rule { constructor( private readonly authorizationHelper: AuthorizationHelper, - private readonly authorisationService: AuthorizationService, + private readonly courseRule: CourseRule, injectionService: AuthorizationInjectionService ) { injectionService.injectAuthorizationRule(this); @@ -26,7 +27,7 @@ export class CourseGroupRule implements Rule { const hasAllPermissions = this.authorizationHelper.hasAllPermissions(user, requiredPermissions); const hasPermission = this.authorizationHelper.hasAccessToEntity(user, object, ['students']) || - this.authorisationService.hasPermission(user, object.course, { action: Action.write, requiredPermissions: [] }); + this.courseRule.hasPermission(user, object.course, { action: Action.write, requiredPermissions: [] }); return hasAllPermissions && hasPermission; } From 875468600bd843225970e7b0dcf9b5741b2cd0ea Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 4 Oct 2024 16:06:44 +0200 Subject: [PATCH 14/23] move rules into seperate module --- apps/server/src/apps/server.app.ts | 3 +- .../authorization-rules.config.ts | 3 ++ .../authorization-rules.module.ts | 43 +++++++++++++++++++ .../src/modules/authorization-rules/index.ts | 2 + .../rules/course-group.rule.spec.ts | 4 +- .../rules/course-group.rule.ts | 10 +++-- .../rules/course.rule.spec.ts | 4 +- .../rules/course.rule.ts | 10 +++-- .../rules/group.rule.spec.ts | 0 .../rules/group.rule.ts | 10 +++-- .../rules/index.ts | 0 .../rules/instance.rule.spec.ts | 9 ++-- .../rules/instance.rule.ts | 13 +++--- .../rules/legacy-school.rule.spec.ts | 10 +++-- .../rules/legacy-school.rule.ts | 10 +++-- .../rules/lesson.rule.spec.ts | 11 +++-- .../rules/lesson.rule.ts | 10 +++-- .../rules/school-system-options.rule.spec.ts | 8 ++-- .../rules/school-system-options.rule.ts | 9 ++-- .../rules/school.rule.spec.ts | 8 ++-- .../rules/school.rule.ts | 9 ++-- .../rules/submission.rule.spec.ts | 9 ++-- .../rules/submission.rule.ts | 10 +++-- .../rules/system.rule.spec.ts | 8 ++-- .../rules/system.rule.ts | 10 +++-- .../rules/task.rule.spec.ts | 4 +- .../rules/task.rule.ts | 10 +++-- .../rules/team.rule.spec.ts | 8 ++-- .../rules/team.rule.ts | 9 ++-- .../rules/user-login-migration.rule.spec.ts | 9 ++-- .../rules/user-login-migration.rule.ts | 9 ++-- .../rules/user.rule.spec.ts | 4 +- .../rules/user.rule.ts | 9 ++-- .../authorization/authorization.module.ts | 39 +---------------- .../src/modules/authorization/domain/index.ts | 1 - .../server/src/modules/authorization/index.ts | 2 - .../src/modules/server/server.module.ts | 2 + 37 files changed, 202 insertions(+), 127 deletions(-) create mode 100644 apps/server/src/modules/authorization-rules/authorization-rules.config.ts create mode 100644 apps/server/src/modules/authorization-rules/authorization-rules.module.ts create mode 100644 apps/server/src/modules/authorization-rules/index.ts rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/course-group.rule.spec.ts (97%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/course-group.rule.ts (84%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/course.rule.spec.ts (96%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/course.rule.ts (86%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/group.rule.spec.ts (100%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/group.rule.ts (82%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/index.ts (100%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/instance.rule.spec.ts (96%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/instance.rule.ts (79%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/legacy-school.rule.spec.ts (94%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/legacy-school.rule.ts (83%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/lesson.rule.spec.ts (96%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/lesson.rule.ts (93%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/school-system-options.rule.spec.ts (97%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/school-system-options.rule.ts (85%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/school.rule.spec.ts (95%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/school.rule.ts (83%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/submission.rule.spec.ts (99%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/submission.rule.ts (92%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/system.rule.spec.ts (97%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/system.rule.ts (87%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/task.rule.spec.ts (98%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/task.rule.ts (90%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/team.rule.spec.ts (96%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/team.rule.ts (82%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/user-login-migration.rule.spec.ts (96%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/user-login-migration.rule.ts (83%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/user.rule.spec.ts (94%) rename apps/server/src/modules/{authorization/domain => authorization-rules}/rules/user.rule.ts (80%) diff --git a/apps/server/src/apps/server.app.ts b/apps/server/src/apps/server.app.ts index b3394bf7567..8af85f2d19d 100644 --- a/apps/server/src/apps/server.app.ts +++ b/apps/server/src/apps/server.app.ts @@ -4,8 +4,7 @@ import { Mail, MailService } from '@infra/mail'; /* eslint-disable no-console */ import { MikroORM } from '@mikro-orm/core'; import { AccountService } from '@modules/account'; -import { SystemRule } from '@modules/authorization/domain/rules'; - +import { SystemRule } from '@modules/authorization-rules'; import { ColumnBoardService } from '@modules/board'; import { CollaborativeStorageUc } from '@modules/collaborative-storage/uc/collaborative-storage.uc'; import { GroupService } from '@modules/group'; diff --git a/apps/server/src/modules/authorization-rules/authorization-rules.config.ts b/apps/server/src/modules/authorization-rules/authorization-rules.config.ts new file mode 100644 index 00000000000..d01b979a80a --- /dev/null +++ b/apps/server/src/modules/authorization-rules/authorization-rules.config.ts @@ -0,0 +1,3 @@ +import { LoggerConfig } from '@src/core/logger'; + +export interface AuthorizationRulesConfig extends LoggerConfig {} diff --git a/apps/server/src/modules/authorization-rules/authorization-rules.module.ts b/apps/server/src/modules/authorization-rules/authorization-rules.module.ts new file mode 100644 index 00000000000..2c6134ddb2d --- /dev/null +++ b/apps/server/src/modules/authorization-rules/authorization-rules.module.ts @@ -0,0 +1,43 @@ +import { Module } from '@nestjs/common'; +import { AuthorizationModule } from '@modules/authorization'; +import { + CourseGroupRule, + CourseRule, + GroupRule, + InstanceRule, + LegacySchoolRule, + LessonRule, + SchoolRule, + SchoolSystemOptionsRule, + SubmissionRule, + TaskRule, + TeamRule, + UserLoginMigrationRule, + UserRule, + SystemRule, +} from './rules'; + +@Module({ + imports: [AuthorizationModule /* FeathersModule */ /* LoggerModule */], + providers: [ + // rules + CourseGroupRule, + CourseRule, + GroupRule, + LessonRule, + SchoolRule, + SubmissionRule, + TaskRule, + TeamRule, + UserRule, + UserLoginMigrationRule, + LegacySchoolRule, + SystemRule, + SchoolSystemOptionsRule, + InstanceRule, + ], + exports: [ + SystemRule, // Why export? This is a no go! + ], +}) +export class AuthorizationRulesModule {} diff --git a/apps/server/src/modules/authorization-rules/index.ts b/apps/server/src/modules/authorization-rules/index.ts new file mode 100644 index 00000000000..d32362bb7e1 --- /dev/null +++ b/apps/server/src/modules/authorization-rules/index.ts @@ -0,0 +1,2 @@ +export { AuthorizationRulesModule } from './authorization-rules.module'; +export { SystemRule } from './rules/system.rule'; diff --git a/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/course-group.rule.spec.ts similarity index 97% rename from apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/course-group.rule.spec.ts index 091a3193ea4..6dce5a62991 100644 --- a/apps/server/src/modules/authorization/domain/rules/course-group.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/course-group.rule.spec.ts @@ -2,11 +2,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { CourseGroup, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory, courseGroupFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; -import { Action } from '../type'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationInjectionService } from '../service'; describe('CourseGroupRule', () => { let service: CourseGroupRule; diff --git a/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts b/apps/server/src/modules/authorization-rules/rules/course-group.rule.ts similarity index 84% rename from apps/server/src/modules/authorization/domain/rules/course-group.rule.ts rename to apps/server/src/modules/authorization-rules/rules/course-group.rule.ts index 73f69b8197a..224cc259c0a 100644 --- a/apps/server/src/modules/authorization/domain/rules/course-group.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/course-group.rule.ts @@ -1,9 +1,13 @@ import { Injectable } from '@nestjs/common'; import { CourseGroup, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '../type'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; import { CourseRule } from './course.rule'; -import { AuthorizationInjectionService } from '../service'; @Injectable() export class CourseGroupRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/course.rule.spec.ts similarity index 96% rename from apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/course.rule.spec.ts index 4c8adcec482..935aabd6661 100644 --- a/apps/server/src/modules/authorization/domain/rules/course.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/course.rule.spec.ts @@ -3,10 +3,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Course, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory as courseEntityFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action } from '../type'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { CourseRule } from './course.rule'; -import { AuthorizationInjectionService } from '../service/authorization-injection.service'; describe('CourseRule', () => { let module: TestingModule; diff --git a/apps/server/src/modules/authorization/domain/rules/course.rule.ts b/apps/server/src/modules/authorization-rules/rules/course.rule.ts similarity index 86% rename from apps/server/src/modules/authorization/domain/rules/course.rule.ts rename to apps/server/src/modules/authorization-rules/rules/course.rule.ts index 25cbd4f4442..e0890d2feb4 100644 --- a/apps/server/src/modules/authorization/domain/rules/course.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/course.rule.ts @@ -2,9 +2,13 @@ import { Course } from '@modules/learnroom/domain'; import { Injectable } from '@nestjs/common'; import { Course as CourseEntity, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service/authorization-injection.service'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class CourseRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/group.rule.spec.ts similarity index 100% rename from apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/group.rule.spec.ts diff --git a/apps/server/src/modules/authorization/domain/rules/group.rule.ts b/apps/server/src/modules/authorization-rules/rules/group.rule.ts similarity index 82% rename from apps/server/src/modules/authorization/domain/rules/group.rule.ts rename to apps/server/src/modules/authorization-rules/rules/group.rule.ts index a962b35780e..9cde48db71c 100644 --- a/apps/server/src/modules/authorization/domain/rules/group.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/group.rule.ts @@ -1,9 +1,13 @@ import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; import { Group } from '@src/modules/group'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class GroupRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/index.ts b/apps/server/src/modules/authorization-rules/rules/index.ts similarity index 100% rename from apps/server/src/modules/authorization/domain/rules/index.ts rename to apps/server/src/modules/authorization-rules/rules/index.ts diff --git a/apps/server/src/modules/authorization/domain/rules/instance.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/instance.rule.spec.ts similarity index 96% rename from apps/server/src/modules/authorization/domain/rules/instance.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/instance.rule.spec.ts index 66531db2aca..96540919f6e 100644 --- a/apps/server/src/modules/authorization/domain/rules/instance.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/instance.rule.spec.ts @@ -3,10 +3,13 @@ import { instanceFactory } from '@modules/instance/testing'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; import { setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext } from '../type'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@src/modules/authorization'; import { InstanceRule } from './instance.rule'; -import { AuthorizationInjectionService } from '../service'; describe(InstanceRule.name, () => { let module: TestingModule; diff --git a/apps/server/src/modules/authorization/domain/rules/instance.rule.ts b/apps/server/src/modules/authorization-rules/rules/instance.rule.ts similarity index 79% rename from apps/server/src/modules/authorization/domain/rules/instance.rule.ts rename to apps/server/src/modules/authorization-rules/rules/instance.rule.ts index 139d1532810..21c215b6e58 100644 --- a/apps/server/src/modules/authorization/domain/rules/instance.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/instance.rule.ts @@ -2,10 +2,13 @@ import { Instance } from '@modules/instance'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; import { RoleName } from '@shared/domain/interface'; -import { Action } from '@infra/authorization-client'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class InstanceRule implements Rule { @@ -26,7 +29,7 @@ export class InstanceRule implements Rule { const hasPermission = this.authorizationHelper.hasAllPermissions(user, context.requiredPermissions); // As temporary solution until the user with write access to instance added as group, we must check the role. - if (context.action === Action.WRITE) { + if (context.action === Action.write) { const hasRole = this.authorizationHelper.hasRole(user, RoleName.SUPERHERO); return hasPermission && hasRole; diff --git a/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts similarity index 94% rename from apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts index 2f5c0425114..a7bf681b3de 100644 --- a/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts @@ -2,10 +2,14 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; import { roleFactory, legacySchoolDoFactory, setupEntities, userFactory } from '@shared/testing'; import { ObjectId } from '@mikro-orm/mongodb'; -import { Action } from '../type'; -import { AuthorizationHelper } from '../service/authorization.helper'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; import { LegacySchoolRule } from './legacy-school.rule'; -import { AuthorizationInjectionService } from '../service'; describe('LegacySchoolRule', () => { let service: LegacySchoolRule; diff --git a/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts similarity index 83% rename from apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts rename to apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts index 615a30c52a1..e3eac93afff 100644 --- a/apps/server/src/modules/authorization/domain/rules/legacy-school.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts @@ -1,9 +1,13 @@ import { Injectable } from '@nestjs/common'; import { LegacySchoolDo } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; /** * @deprecated because it uses the deprecated LegacySchoolDo. diff --git a/apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts similarity index 96% rename from apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts index 393a3190e64..49b50a75882 100644 --- a/apps/server/src/modules/authorization/domain/rules/lesson.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts @@ -11,13 +11,16 @@ import { setupEntities, userFactory, } from '@shared/testing'; -import { AuthorizationContextBuilder } from '../mapper'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext } from '../type'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; -import { AuthorizationInjectionService } from '../service'; +import { + Action, + AuthorizationContext, + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@src/modules/authorization'; describe('LessonRule', () => { let rule: LessonRule; diff --git a/apps/server/src/modules/authorization/domain/rules/lesson.rule.ts b/apps/server/src/modules/authorization-rules/rules/lesson.rule.ts similarity index 93% rename from apps/server/src/modules/authorization/domain/rules/lesson.rule.ts rename to apps/server/src/modules/authorization-rules/rules/lesson.rule.ts index 9292a5e57b3..71e7ad2d3e0 100644 --- a/apps/server/src/modules/authorization/domain/rules/lesson.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/lesson.rule.ts @@ -1,10 +1,14 @@ import { Injectable, NotImplementedException } from '@nestjs/common'; import { Course, CourseGroup, LessonEntity, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '../type'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; -import { AuthorizationInjectionService } from '../service'; @Injectable() export class LessonRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.spec.ts similarity index 97% rename from apps/server/src/modules/authorization/domain/rules/school-system-options.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/school-system-options.rule.spec.ts index 00fb7beb1dd..6ac394ce455 100644 --- a/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.spec.ts @@ -12,10 +12,12 @@ import { systemEntityFactory, userFactory, } from '@shared/testing'; -import { AuthorizationContextBuilder } from '../mapper'; -import { AuthorizationHelper } from '../service/authorization.helper'; +import { + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@src/modules/authorization'; import { SchoolSystemOptionsRule } from './school-system-options.rule'; -import { AuthorizationInjectionService } from '../service'; describe(SchoolSystemOptionsRule.name, () => { let module: TestingModule; diff --git a/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.ts b/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.ts similarity index 85% rename from apps/server/src/modules/authorization/domain/rules/school-system-options.rule.ts rename to apps/server/src/modules/authorization-rules/rules/school-system-options.rule.ts index 47f628c551a..d598e686881 100644 --- a/apps/server/src/modules/authorization/domain/rules/school-system-options.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.ts @@ -1,9 +1,12 @@ import { SchoolSystemOptions } from '@modules/legacy-school'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service'; +import { + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class SchoolSystemOptionsRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/school.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts similarity index 95% rename from apps/server/src/modules/authorization/domain/rules/school.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts index cffa7116e17..319a0b86790 100644 --- a/apps/server/src/modules/authorization/domain/rules/school.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts @@ -2,10 +2,12 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { schoolFactory } from '@modules/school/testing/school.factory'; import { Test, TestingModule } from '@nestjs/testing'; import { schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder } from '../mapper'; -import { AuthorizationHelper } from '../service/authorization.helper'; +import { + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@src/modules/authorization'; import { SchoolRule } from './school.rule'; -import { AuthorizationInjectionService } from '../service'; describe('SchoolRule', () => { let rule: SchoolRule; diff --git a/apps/server/src/modules/authorization/domain/rules/school.rule.ts b/apps/server/src/modules/authorization-rules/rules/school.rule.ts similarity index 83% rename from apps/server/src/modules/authorization/domain/rules/school.rule.ts rename to apps/server/src/modules/authorization-rules/rules/school.rule.ts index 7850ae8e821..2bc70f85a5e 100644 --- a/apps/server/src/modules/authorization/domain/rules/school.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/school.rule.ts @@ -1,9 +1,12 @@ import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; +import { + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; import { School } from '@src/modules/school/domain/do'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service'; @Injectable() export class SchoolRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/submission.rule.spec.ts similarity index 99% rename from apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/submission.rule.spec.ts index 9ab8abeed50..f7905c5ce5e 100644 --- a/apps/server/src/modules/authorization/domain/rules/submission.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/submission.rule.spec.ts @@ -10,14 +10,17 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext } from '../type'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@src/modules/authorization'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; import { SubmissionRule } from './submission.rule'; import { TaskRule } from './task.rule'; -import { AuthorizationInjectionService } from '../service'; const buildUserWithPermission = (permission) => { const role = roleFactory.buildWithId({ permissions: [permission] }); diff --git a/apps/server/src/modules/authorization/domain/rules/submission.rule.ts b/apps/server/src/modules/authorization-rules/rules/submission.rule.ts similarity index 92% rename from apps/server/src/modules/authorization/domain/rules/submission.rule.ts rename to apps/server/src/modules/authorization-rules/rules/submission.rule.ts index 273f56ae731..1492dd7db8a 100644 --- a/apps/server/src/modules/authorization/domain/rules/submission.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/submission.rule.ts @@ -1,9 +1,13 @@ import { Injectable, NotImplementedException } from '@nestjs/common'; import { Submission, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '../type'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; import { TaskRule } from './task.rule'; -import { AuthorizationInjectionService } from '../service'; @Injectable() export class SubmissionRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/system.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/system.rule.spec.ts similarity index 97% rename from apps/server/src/modules/authorization/domain/rules/system.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/system.rule.spec.ts index 03dc6e20e05..ad3482934de 100644 --- a/apps/server/src/modules/authorization/domain/rules/system.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/system.rule.spec.ts @@ -6,10 +6,12 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SchoolEntity, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { schoolEntityFactory, setupEntities, systemEntityFactory, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder } from '../mapper'; -import { AuthorizationHelper } from '../service/authorization.helper'; +import { + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@src/modules/authorization'; import { SystemRule } from './system.rule'; -import { AuthorizationInjectionService } from '../service'; describe(SystemRule.name, () => { let module: TestingModule; diff --git a/apps/server/src/modules/authorization/domain/rules/system.rule.ts b/apps/server/src/modules/authorization-rules/rules/system.rule.ts similarity index 87% rename from apps/server/src/modules/authorization/domain/rules/system.rule.ts rename to apps/server/src/modules/authorization-rules/rules/system.rule.ts index e20594be9ef..f9fa9502a3f 100644 --- a/apps/server/src/modules/authorization/domain/rules/system.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/system.rule.ts @@ -1,9 +1,13 @@ import { System } from '@modules/system'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class SystemRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/task.rule.spec.ts similarity index 98% rename from apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/task.rule.spec.ts index cdc60e427d8..13683e54851 100644 --- a/apps/server/src/modules/authorization/domain/rules/task.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/task.rule.spec.ts @@ -2,13 +2,11 @@ import { DeepPartial } from '@mikro-orm/core'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission, RoleName } from '@shared/domain/interface'; import { courseFactory, lessonFactory, roleFactory, setupEntities, taskFactory, userFactory } from '@shared/testing'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action } from '../type'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; import { TaskRule } from './task.rule'; -import { AuthorizationInjectionService } from '../service'; describe('TaskRule', () => { let service: TaskRule; diff --git a/apps/server/src/modules/authorization/domain/rules/task.rule.ts b/apps/server/src/modules/authorization-rules/rules/task.rule.ts similarity index 90% rename from apps/server/src/modules/authorization/domain/rules/task.rule.ts rename to apps/server/src/modules/authorization-rules/rules/task.rule.ts index b8796805794..e59b379e422 100644 --- a/apps/server/src/modules/authorization/domain/rules/task.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/task.rule.ts @@ -1,10 +1,14 @@ import { Injectable } from '@nestjs/common'; import { Task, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '../type'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; -import { AuthorizationInjectionService } from '../service'; @Injectable() export class TaskRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/team.rule.spec.ts similarity index 96% rename from apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/team.rule.spec.ts index d32d492187f..226e3d2f53d 100644 --- a/apps/server/src/modules/authorization/domain/rules/team.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/team.rule.spec.ts @@ -1,10 +1,12 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; import { roleFactory, setupEntities, teamFactory, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder } from '../mapper'; -import { AuthorizationHelper } from '../service/authorization.helper'; +import { + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@src/modules/authorization'; import { TeamRule } from './team.rule'; -import { AuthorizationInjectionService } from '../service'; describe('TeamRule', () => { let rule: TeamRule; diff --git a/apps/server/src/modules/authorization/domain/rules/team.rule.ts b/apps/server/src/modules/authorization-rules/rules/team.rule.ts similarity index 82% rename from apps/server/src/modules/authorization/domain/rules/team.rule.ts rename to apps/server/src/modules/authorization-rules/rules/team.rule.ts index 8668897a5dc..f24f4646bc8 100644 --- a/apps/server/src/modules/authorization/domain/rules/team.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/team.rule.ts @@ -1,8 +1,11 @@ import { Injectable } from '@nestjs/common'; import { TeamEntity, TeamUserEntity, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service'; +import { + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class TeamRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.spec.ts similarity index 96% rename from apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.spec.ts index 782774030de..d21895fe194 100644 --- a/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.spec.ts @@ -4,10 +4,13 @@ import { Test, TestingModule } from '@nestjs/testing'; import { UserLoginMigrationDO } from '@shared/domain/domainobject'; import { Permission } from '@shared/domain/interface'; import { schoolEntityFactory, setupEntities, userFactory, userLoginMigrationDOFactory } from '@shared/testing'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext } from '../type'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@src/modules/authorization'; import { UserLoginMigrationRule } from './user-login-migration.rule'; -import { AuthorizationInjectionService } from '../service'; describe('UserLoginMigrationRule', () => { let module: TestingModule; diff --git a/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts b/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.ts similarity index 83% rename from apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts rename to apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.ts index 3ea2440bb42..cfc509662ff 100644 --- a/apps/server/src/modules/authorization/domain/rules/user-login-migration.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.ts @@ -1,9 +1,12 @@ import { Injectable } from '@nestjs/common'; import { UserLoginMigrationDO } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service'; +import { + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class UserLoginMigrationRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/user.rule.spec.ts similarity index 94% rename from apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts rename to apps/server/src/modules/authorization-rules/rules/user.rule.spec.ts index 0ecf3d1fb2d..05785246050 100644 --- a/apps/server/src/modules/authorization/domain/rules/user.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/user.rule.spec.ts @@ -2,10 +2,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action } from '../type'; -import { AuthorizationHelper } from '../service/authorization.helper'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { UserRule } from './user.rule'; -import { AuthorizationInjectionService } from '../service'; describe('UserRule', () => { let service: UserRule; diff --git a/apps/server/src/modules/authorization/domain/rules/user.rule.ts b/apps/server/src/modules/authorization-rules/rules/user.rule.ts similarity index 80% rename from apps/server/src/modules/authorization/domain/rules/user.rule.ts rename to apps/server/src/modules/authorization-rules/rules/user.rule.ts index 98e9ce842f5..cdb7d5f1c94 100644 --- a/apps/server/src/modules/authorization/domain/rules/user.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/user.rule.ts @@ -1,8 +1,11 @@ import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; -import { AuthorizationInjectionService } from '../service'; +import { + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@src/modules/authorization'; @Injectable() export class UserRule implements Rule { diff --git a/apps/server/src/modules/authorization/authorization.module.ts b/apps/server/src/modules/authorization/authorization.module.ts index 29b4e64bed6..73e6ad197ca 100644 --- a/apps/server/src/modules/authorization/authorization.module.ts +++ b/apps/server/src/modules/authorization/authorization.module.ts @@ -3,22 +3,6 @@ import { Module } from '@nestjs/common'; import { UserRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; import { AuthorizationHelper, AuthorizationService, RuleManager, AuthorizationInjectionService } from './domain'; -import { - CourseGroupRule, - CourseRule, - GroupRule, - InstanceRule, - LegacySchoolRule, - LessonRule, - SchoolRule, - SchoolSystemOptionsRule, - SubmissionRule, - SystemRule, - TaskRule, - TeamRule, - UserLoginMigrationRule, - UserRule, -} from './domain/rules'; import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; @Module({ @@ -31,28 +15,7 @@ import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; UserRepo, RuleManager, AuthorizationHelper, - // rules - CourseGroupRule, - CourseRule, - GroupRule, - LessonRule, - SchoolRule, - SubmissionRule, - TaskRule, - TeamRule, - UserRule, - UserLoginMigrationRule, - LegacySchoolRule, - SystemRule, - SchoolSystemOptionsRule, - InstanceRule, - ], - exports: [ - FeathersAuthorizationService, - AuthorizationService, - SystemRule, // Why export? This is a no go! - AuthorizationInjectionService, - AuthorizationHelper, ], + exports: [FeathersAuthorizationService, AuthorizationService, AuthorizationInjectionService, AuthorizationHelper], }) export class AuthorizationModule {} diff --git a/apps/server/src/modules/authorization/domain/index.ts b/apps/server/src/modules/authorization/domain/index.ts index ccc52341b5b..0f5cfe67874 100644 --- a/apps/server/src/modules/authorization/domain/index.ts +++ b/apps/server/src/modules/authorization/domain/index.ts @@ -2,4 +2,3 @@ export * from './service'; export * from './mapper'; export * from './error'; export * from './type'; -export { SystemRule } from './rules'; diff --git a/apps/server/src/modules/authorization/index.ts b/apps/server/src/modules/authorization/index.ts index 115cfb338e3..f7375fe73e9 100644 --- a/apps/server/src/modules/authorization/index.ts +++ b/apps/server/src/modules/authorization/index.ts @@ -14,8 +14,6 @@ export { ForbiddenLoggableException, AuthorizationInjectionService, Rule, - // For the use in feathers - SystemRule, } from './domain'; // Should not used anymore export { FeathersAuthorizationService } from './feathers'; diff --git a/apps/server/src/modules/server/server.module.ts b/apps/server/src/modules/server/server.module.ts index af457631d66..904d4bbef8d 100644 --- a/apps/server/src/modules/server/server.module.ts +++ b/apps/server/src/modules/server/server.module.ts @@ -10,6 +10,7 @@ import { AccountApiModule } from '@modules/account/account-api.module'; import { AlertModule } from '@modules/alert/alert.module'; import { AuthenticationApiModule } from '@modules/authentication/authentication-api.module'; import { AuthorizationReferenceApiModule } from '@modules/authorization/authorization-reference.api.module'; +import { AuthorizationRulesModule } from '@modules/authorization-rules'; import { BoardApiModule } from '@modules/board/board-api.module'; import { MediaBoardApiModule } from '@modules/board/media-board-api.module'; import { CollaborativeStorageModule } from '@modules/collaborative-storage'; @@ -56,6 +57,7 @@ const serverModules = [ AuthenticationApiModule, AuthGuardModule, AuthorizationReferenceApiModule, + AuthorizationRulesModule, AccountApiModule, CollaborativeStorageModule, OauthApiModule, From 1ad69548b66b3813ba1ff7da2fa8fc8288a5b921 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 4 Oct 2024 16:38:26 +0200 Subject: [PATCH 15/23] fix building for feathers tests --- test/utils/setup.nest.services.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/utils/setup.nest.services.js b/test/utils/setup.nest.services.js index 377a700b20b..5f460618380 100644 --- a/test/utils/setup.nest.services.js +++ b/test/utils/setup.nest.services.js @@ -13,7 +13,7 @@ const { ALL_ENTITIES } = require('../../dist/apps/server/shared/domain/entity/al const { TeamService } = require('../../dist/apps/server/modules/teams/service/team.service'); const { TeamsApiModule } = require('../../dist/apps/server/modules/teams/teams-api.module'); const { AuthorizationModule } = require('../../dist/apps/server/modules/authorization'); -const { SystemRule } = require('../../dist/apps/server/modules/authorization'); +const { SystemRule, AuthorizationRulesModule } = require('../../dist/apps/server/modules/authorization-rules'); const { createConfigModuleOptions } = require('../../dist/apps/server/config/config-module-options'); const { serverConfig } = require('../../dist/apps/server/modules/server/server.config'); @@ -33,6 +33,7 @@ const setupNestServices = async (app) => { AccountApiModule, TeamsApiModule, AuthorizationModule, + AuthorizationRulesModule, ], }).compile(); const nestApp = await module.createNestApplication().init(); From 27825ba51a67e2b2b2fc6d707ad6183829be2fb1 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 4 Oct 2024 16:53:17 +0200 Subject: [PATCH 16/23] update rule manager tests --- .../domain/service/rule-manager.spec.ts | 140 ------------------ 1 file changed, 140 deletions(-) diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts index 9f4da3b753b..22526a1c3cc 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts @@ -5,47 +5,11 @@ import { courseFactory, setupEntities, userFactory } from '@shared/testing'; // IMPORTANT: RuleManager has to be imported before the rules to prevent import cycles! import { RuleManager } from '.'; import { AuthorizationContextBuilder } from '../mapper'; -import { - ContextExternalToolRule, - CourseGroupRule, - CourseRule, - GroupRule, - InstanceRule, - LegacySchoolRule, - LessonRule, - SchoolExternalToolRule, - SchoolRule, - SchoolSystemOptionsRule, - SubmissionRule, - SystemRule, - TaskRule, - TeamRule, - UserLoginMigrationRule, - UserRule, -} from '../rules'; -import { ExternalToolRule } from '../rules/external-tool.rule'; import { AuthorizationInjectionService } from './authorization-injection.service'; describe('RuleManager', () => { let service: RuleManager; let injectionService: DeepMocked; - let courseRule: DeepMocked; - let courseGroupRule: DeepMocked; - let lessonRule: DeepMocked; - let legacySchoolRule: DeepMocked; - let userRule: DeepMocked; - let taskRule: DeepMocked; - let teamRule: DeepMocked; - let submissionRule: DeepMocked; - let schoolExternalToolRule: DeepMocked; - let contextExternalToolRule: DeepMocked; - let userLoginMigrationRule: DeepMocked; - let schoolRule: DeepMocked; - let groupRule: DeepMocked; - let systemRule: DeepMocked; - let schoolSystemOptionsRule: DeepMocked; - let externalToolRule: DeepMocked; - let instanceRule: DeepMocked; beforeAll(async () => { await setupEntities(); @@ -54,45 +18,11 @@ describe('RuleManager', () => { providers: [ RuleManager, { provide: AuthorizationInjectionService, useValue: createMock() }, - { provide: CourseRule, useValue: createMock() }, - { provide: CourseGroupRule, useValue: createMock() }, - { provide: GroupRule, useValue: createMock() }, - { provide: LessonRule, useValue: createMock() }, - { provide: LegacySchoolRule, useValue: createMock() }, - { provide: UserRule, useValue: createMock() }, - { provide: TaskRule, useValue: createMock() }, - { provide: TeamRule, useValue: createMock() }, - { provide: SubmissionRule, useValue: createMock() }, - { provide: SchoolExternalToolRule, useValue: createMock() }, - { provide: ContextExternalToolRule, useValue: createMock() }, - { provide: UserLoginMigrationRule, useValue: createMock() }, - { provide: SchoolRule, useValue: createMock() }, - { provide: SystemRule, useValue: createMock() }, - { provide: SchoolSystemOptionsRule, useValue: createMock() }, - { provide: ExternalToolRule, useValue: createMock() }, - { provide: InstanceRule, useValue: createMock() }, ], }).compile(); service = await module.get(RuleManager); injectionService = module.get(AuthorizationInjectionService); - courseRule = await module.get(CourseRule); - courseGroupRule = await module.get(CourseGroupRule); - lessonRule = await module.get(LessonRule); - legacySchoolRule = await module.get(LegacySchoolRule); - userRule = await module.get(UserRule); - taskRule = await module.get(TaskRule); - teamRule = await module.get(TeamRule); - submissionRule = await module.get(SubmissionRule); - schoolExternalToolRule = await module.get(SchoolExternalToolRule); - contextExternalToolRule = await module.get(ContextExternalToolRule); - userLoginMigrationRule = await module.get(UserLoginMigrationRule); - schoolRule = await module.get(SchoolRule); - groupRule = await module.get(GroupRule); - systemRule = await module.get(SystemRule); - schoolSystemOptionsRule = await module.get(SchoolSystemOptionsRule); - externalToolRule = await module.get(ExternalToolRule); - instanceRule = await module.get(InstanceRule); }); afterEach(() => { @@ -186,74 +116,4 @@ describe('RuleManager', () => { }); }); }); - - describe('currently, most of the Rules are injected into the AuthorizationInjectionService by the RuleManager. In the future, these should go into the modules instead', () => { - it('should inject CourseRule', () => { - expect(injectionService.injectAuthorizationRule).toHaveBeenCalledWith(courseRule); - }); - - it('should inject CourseGroupRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(courseGroupRule); - }); - - it('should inject LessonRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(lessonRule); - }); - - it('should inject LegacySchoolRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(legacySchoolRule); - }); - - it('should inject UserRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(userRule); - }); - - it('should inject TaskRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(taskRule); - }); - - it('should inject TeamRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(teamRule); - }); - - it('should inject SubmissionRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(submissionRule); - }); - - it('should inject SchoolExternalToolRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(schoolExternalToolRule); - }); - - it('should inject ContextExternalToolRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(contextExternalToolRule); - }); - - it('should inject UserLoginMigrationRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(userLoginMigrationRule); - }); - - it('should inject SchoolRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(schoolRule); - }); - - it('should inject GroupRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(groupRule); - }); - - it('should inject SystemRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(systemRule); - }); - - it('should inject SchoolSystemOptionsRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(schoolSystemOptionsRule); - }); - - it('should inject ExternalToolRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(externalToolRule); - }); - - it('should inject InstanceRule', () => { - expect(injectionService.injectAuthorizationRule).toBeCalledWith(instanceRule); - }); - }); }); From 91a771d6a2f9fe9bf37ef46317ce3ddeeb219ad3 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Mon, 7 Oct 2024 10:12:08 +0200 Subject: [PATCH 17/23] linter --- .../modules/authorization-rules/rules/group.rule.ts | 3 +-- .../rules/legacy-school.rule.spec.ts | 12 +++--------- .../authorization-rules/rules/legacy-school.rule.ts | 1 - .../authorization-rules/rules/lesson.rule.spec.ts | 6 +++--- .../domain/service/reference.loader.ts | 2 -- .../modules/lesson/service/lesson.service.spec.ts | 2 +- .../teams/service/team-authorisable.service.spec.ts | 4 ++-- .../context-external-tool.rule.spec.ts | 4 ++-- .../context-external-tool.module.ts | 4 ++-- ...ntext-external-tool-authorizable.service.spec.ts | 13 +++---------- .../authorization/external-tool.rule.spec.ts | 10 +++------- .../tool/external-tool/external-tool.module.ts | 2 +- .../authorization/school-external-tool.rule.spec.ts | 4 ++-- .../school-external-tool.module.ts | 4 ++-- .../school-external-tool-authorizable.service.ts | 4 ++-- ...hool-external-tool-authorization.service.spec.ts | 2 +- 16 files changed, 28 insertions(+), 49 deletions(-) diff --git a/apps/server/src/modules/authorization-rules/rules/group.rule.ts b/apps/server/src/modules/authorization-rules/rules/group.rule.ts index 9cde48db71c..da220685dd4 100644 --- a/apps/server/src/modules/authorization-rules/rules/group.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/group.rule.ts @@ -1,13 +1,12 @@ import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { Group } from '@src/modules/group'; import { - Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule, } from '@src/modules/authorization'; +import { Group } from '@src/modules/group'; @Injectable() export class GroupRule implements Rule { diff --git a/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts index a7bf681b3de..a747e5cd13b 100644 --- a/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts @@ -1,14 +1,8 @@ +import { ObjectId } from '@mikro-orm/mongodb'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; -import { roleFactory, legacySchoolDoFactory, setupEntities, userFactory } from '@shared/testing'; -import { ObjectId } from '@mikro-orm/mongodb'; -import { - Action, - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; +import { legacySchoolDoFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { LegacySchoolRule } from './legacy-school.rule'; describe('LegacySchoolRule', () => { diff --git a/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts index e3eac93afff..b2121ebfa1a 100644 --- a/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts @@ -2,7 +2,6 @@ import { Injectable } from '@nestjs/common'; import { LegacySchoolDo } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; import { - Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, diff --git a/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts index 49b50a75882..bc26aad6626 100644 --- a/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts @@ -11,9 +11,6 @@ import { setupEntities, userFactory, } from '@shared/testing'; -import { CourseGroupRule } from './course-group.rule'; -import { CourseRule } from './course.rule'; -import { LessonRule } from './lesson.rule'; import { Action, AuthorizationContext, @@ -21,6 +18,9 @@ import { AuthorizationHelper, AuthorizationInjectionService, } from '@src/modules/authorization'; +import { CourseGroupRule } from './course-group.rule'; +import { CourseRule } from './course.rule'; +import { LessonRule } from './lesson.rule'; describe('LessonRule', () => { let rule: LessonRule; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization/domain/service/reference.loader.ts index 50d7d923bfa..9be54994466 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization/domain/service/reference.loader.ts @@ -1,6 +1,4 @@ // TODO fix modules circular dependency -// eslint-disable-next-line @typescript-eslint/no-restricted-imports -import { TeamAuthorisableService } from '@src/modules/teams/service/team-authorisable.service'; import { Injectable, NotImplementedException } from '@nestjs/common'; import { AuthorizableObject } from '@shared/domain/domain-object'; import { BaseDO } from '@shared/domain/domainobject'; diff --git a/apps/server/src/modules/lesson/service/lesson.service.spec.ts b/apps/server/src/modules/lesson/service/lesson.service.spec.ts index 55fce12c1d9..058707135a0 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.spec.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.spec.ts @@ -15,9 +15,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ComponentProperties, ComponentType } from '@shared/domain/entity'; import { lessonFactory, setupEntities } from '@shared/testing'; import { Logger } from '@src/core/logger'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; import { LessonRepo } from '../repository'; import { LessonService } from './lesson.service'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; describe('LessonService', () => { let lessonService: LessonService; diff --git a/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts b/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts index db904267b28..ab992e81d78 100644 --- a/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts +++ b/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts @@ -1,9 +1,9 @@ -import { Test, TestingModule } from '@nestjs/testing'; import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { Test, TestingModule } from '@nestjs/testing'; import { TeamsRepo } from '@shared/repo'; import { setupEntities, teamFactory } from '@shared/testing'; -import { TeamAuthorisableService } from './team-authorisable.service'; import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; +import { TeamAuthorisableService } from './team-authorisable.service'; describe('team authorisable service', () => { let module: TestingModule; diff --git a/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts index a22d99dce55..5c471fb3122 100644 --- a/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts @@ -1,3 +1,4 @@ +import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; import { contextExternalToolEntityFactory } from '@modules/tool/context-external-tool/testing'; @@ -8,9 +9,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; -import { ContextExternalToolRule } from './context-external-tool.rule'; import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; -import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { ContextExternalToolRule } from './context-external-tool.rule'; describe('ContextExternalToolRule', () => { let service: ContextExternalToolRule; diff --git a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts index 4bb2e0d7940..c4eced01902 100644 --- a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts +++ b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts @@ -1,14 +1,14 @@ import { UserLicenseModule } from '@modules/user-license'; import { forwardRef, Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; +import { AuthorizationModule } from '@src/modules/authorization'; import { CommonToolModule } from '../common'; import { ExternalToolModule } from '../external-tool'; import { SchoolExternalToolModule } from '../school-external-tool'; +import { ContextExternalToolRule } from './authorisation/context-external-tool.rule'; import { ContextExternalToolAuthorizableService, ContextExternalToolService, ToolReferenceService } from './service'; import { ContextExternalToolValidationService } from './service/context-external-tool-validation.service'; import { ToolConfigurationStatusService } from './service/tool-configuration-status.service'; -import { ContextExternalToolRule } from './authorisation/context-external-tool.rule'; -import { AuthorizationModule } from '@src/modules/authorization'; @Module({ imports: [ diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts index 251c4c5574b..ee5a057e268 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts @@ -2,12 +2,12 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { ContextExternalToolRepo } from '@shared/repo'; import { legacySchoolDoFactory } from '@shared/testing'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { schoolExternalToolFactory } from '../../school-external-tool/testing'; import { ContextExternalTool } from '../domain'; import { contextExternalToolFactory } from '../testing'; import { ContextExternalToolAuthorizableService } from './context-external-tool-authorizable.service'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; describe('ContextExternalToolAuthorizableService', () => { let module: TestingModule; @@ -24,10 +24,7 @@ describe('ContextExternalToolAuthorizableService', () => { provide: ContextExternalToolRepo, useValue: createMock(), }, - { - provide: AuthorizationInjectionService, - useValue: createMock(), - }, + AuthorizationInjectionService, ], }).compile(); @@ -46,11 +43,7 @@ describe('ContextExternalToolAuthorizableService', () => { describe('constructor', () => { it('should inject itself into the AuthorizationInjectionService', () => { - new ContextExternalToolAuthorizableService(contextExternalToolRepo, injectionService); - expect(injectionService.injectReferenceLoader).toHaveBeenCalledWith( - AuthorizableReferenceType.ContextExternalToolEntity, - service - ); + expect(injectionService.getReferenceLoader(AuthorizableReferenceType.ContextExternalToolEntity)).toBe(service); }); }); diff --git a/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts index ee562198e8e..3b81c335fc8 100644 --- a/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts @@ -5,8 +5,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { setupEntities, userFactory } from '@shared/testing'; -import { ExternalToolRule } from './external-tool.rule'; import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; +import { ExternalToolRule } from './external-tool.rule'; describe(ExternalToolRule.name, () => { let module: TestingModule; @@ -25,10 +25,7 @@ describe(ExternalToolRule.name, () => { provide: AuthorizationHelper, useValue: createMock(), }, - { - provide: AuthorizationInjectionService, - useValue: createMock(), - }, + AuthorizationInjectionService, ], }).compile(); @@ -47,8 +44,7 @@ describe(ExternalToolRule.name, () => { describe('constructor', () => { it('should inject itself into the AuthorizationInjectionService', () => { - new ExternalToolRule(authorizationHelper, injectionService); - expect(injectionService.injectAuthorizationRule).toHaveBeenCalledWith(rule); + expect(injectionService.getAuthorizationRules()).toContain(rule); }); }); diff --git a/apps/server/src/modules/tool/external-tool/external-tool.module.ts b/apps/server/src/modules/tool/external-tool/external-tool.module.ts index 960fcf674ef..4f7e8abd975 100644 --- a/apps/server/src/modules/tool/external-tool/external-tool.module.ts +++ b/apps/server/src/modules/tool/external-tool/external-tool.module.ts @@ -3,6 +3,7 @@ import { OauthProviderServiceModule } from '@modules/oauth-provider'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; +import { AuthorizationModule } from '@src/modules/authorization'; import { InstanceModule } from '../../instance'; import { CommonToolModule } from '../common'; import { ToolContextMapper } from '../common/mapper/tool-context.mapper'; @@ -18,7 +19,6 @@ import { ExternalToolServiceMapper, ExternalToolValidationService, } from './service'; -import { AuthorizationModule } from '@src/modules/authorization'; @Module({ imports: [ diff --git a/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts index de9ae04c774..a3c18229542 100644 --- a/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts @@ -1,3 +1,4 @@ +import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { schoolExternalToolEntityFactory, schoolExternalToolFactory } from '@modules/tool/school-external-tool/testing'; @@ -5,9 +6,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; -import { SchoolExternalToolRule } from './school-external-tool.rule'; import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; -import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { SchoolExternalToolRule } from './school-external-tool.rule'; describe('SchoolExternalToolRule', () => { let service: SchoolExternalToolRule; diff --git a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts index 50469f0e318..e82c64e5326 100644 --- a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts +++ b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts @@ -1,13 +1,13 @@ import { forwardRef, Module } from '@nestjs/common'; +import { AuthorizationModule } from '@src/modules/authorization'; import { CommonToolModule } from '../common'; import { ExternalToolModule } from '../external-tool'; +import { SchoolExternalToolRule } from './authorization/school-external-tool.rule'; import { SchoolExternalToolAuthorizableService, SchoolExternalToolService, SchoolExternalToolValidationService, } from './service'; -import { SchoolExternalToolRule } from './authorization/school-external-tool.rule'; -import { AuthorizationModule } from '@src/modules/authorization'; @Module({ imports: [forwardRef(() => CommonToolModule), forwardRef(() => ExternalToolModule), AuthorizationModule], diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts index b03fec4c875..9fe2d563475 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts @@ -1,12 +1,12 @@ import { Injectable } from '@nestjs/common'; +import { EntityId } from '@shared/domain/types'; +import { SchoolExternalToolRepo } from '@shared/repo'; import { AuthorizableReferenceType, AuthorizationInjectionService, AuthorizationLoaderServiceGeneric, } from '@src/modules/authorization'; import { SchoolExternalTool } from '../domain'; -import { EntityId } from '@shared/domain/types'; -import { SchoolExternalToolRepo } from '@shared/repo'; @Injectable() export class SchoolExternalToolAuthorizableService implements AuthorizationLoaderServiceGeneric { diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts index c7b82d05b03..d83ae11d5b9 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts @@ -2,10 +2,10 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { SchoolExternalToolRepo } from '@shared/repo'; import { legacySchoolDoFactory } from '@shared/testing'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { schoolExternalToolFactory } from '../../school-external-tool/testing'; import { SchoolExternalToolAuthorizableService } from './school-external-tool-authorizable.service'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; describe('SchoolExternalToolAuthorizableService', () => { let module: TestingModule; From 455dfe4299b833352303c90fe146efcce7318c6f Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Mon, 7 Oct 2024 11:40:19 +0200 Subject: [PATCH 18/23] move authorization-reference code into seperate module --- .../api/authorization-reference.controller.ts | 0 .../api/authorization-reference.uc.ts | 3 ++- .../api/dto/authorization-body.params.ts | 4 ++-- .../api/dto/authorization.reponse.ts | 0 .../api/dto/index.ts | 0 .../api/index.ts | 0 .../api/mapper/authorization.response.mapper.ts | 0 .../api/mapper/index.ts | 0 .../api/test/authorization.api.spec.ts | 7 ++++++- .../authorization-reference.api.module.ts | 0 .../authorization-reference.config.ts | 3 +++ .../authorization-reference.module.ts | 5 ++--- .../domain}/authorization-reference.service.spec.ts | 10 ++++++---- .../domain}/authorization-reference.service.ts | 9 ++++++--- .../modules/authorization-reference/domain/index.ts | 2 ++ .../domain}/reference.loader.spec.ts | 3 +-- .../domain}/reference.loader.ts | 9 ++++++--- .../src/modules/authorization-reference/index.ts | 3 +++ .../domain/service/authorization.service.spec.ts | 5 ----- .../src/modules/authorization/domain/service/index.ts | 2 -- .../authorization/domain/service/rule-manager.spec.ts | 1 - apps/server/src/modules/authorization/index.ts | 1 - .../src/modules/learnroom/learnroom-api.module.ts | 2 +- apps/server/src/modules/learnroom/uc/course-copy.uc.ts | 4 ++-- .../src/modules/learnroom/uc/course-export.uc.ts | 4 ++-- apps/server/src/modules/server/server.module.ts | 2 +- apps/server/src/modules/sharing/sharing.module.ts | 2 +- .../video-conference/video-conference.module.ts | 2 +- 28 files changed, 47 insertions(+), 36 deletions(-) rename apps/server/src/modules/{authorization => authorization-reference}/api/authorization-reference.controller.ts (100%) rename apps/server/src/modules/{authorization => authorization-reference}/api/authorization-reference.uc.ts (85%) rename apps/server/src/modules/{authorization => authorization-reference}/api/dto/authorization-body.params.ts (93%) rename apps/server/src/modules/{authorization => authorization-reference}/api/dto/authorization.reponse.ts (100%) rename apps/server/src/modules/{authorization => authorization-reference}/api/dto/index.ts (100%) rename apps/server/src/modules/{authorization => authorization-reference}/api/index.ts (100%) rename apps/server/src/modules/{authorization => authorization-reference}/api/mapper/authorization.response.mapper.ts (100%) rename apps/server/src/modules/{authorization => authorization-reference}/api/mapper/index.ts (100%) rename apps/server/src/modules/{authorization => authorization-reference}/api/test/authorization.api.spec.ts (98%) rename apps/server/src/modules/{authorization => authorization-reference}/authorization-reference.api.module.ts (100%) create mode 100644 apps/server/src/modules/authorization-reference/authorization-reference.config.ts rename apps/server/src/modules/{authorization => authorization-reference}/authorization-reference.module.ts (83%) rename apps/server/src/modules/{authorization/domain/service => authorization-reference/domain}/authorization-reference.service.spec.ts (96%) rename apps/server/src/modules/{authorization/domain/service => authorization-reference/domain}/authorization-reference.service.ts (86%) create mode 100644 apps/server/src/modules/authorization-reference/domain/index.ts rename apps/server/src/modules/{authorization/domain/service => authorization-reference/domain}/reference.loader.spec.ts (97%) rename apps/server/src/modules/{authorization/domain/service => authorization-reference/domain}/reference.loader.ts (90%) create mode 100644 apps/server/src/modules/authorization-reference/index.ts diff --git a/apps/server/src/modules/authorization/api/authorization-reference.controller.ts b/apps/server/src/modules/authorization-reference/api/authorization-reference.controller.ts similarity index 100% rename from apps/server/src/modules/authorization/api/authorization-reference.controller.ts rename to apps/server/src/modules/authorization-reference/api/authorization-reference.controller.ts diff --git a/apps/server/src/modules/authorization/api/authorization-reference.uc.ts b/apps/server/src/modules/authorization-reference/api/authorization-reference.uc.ts similarity index 85% rename from apps/server/src/modules/authorization/api/authorization-reference.uc.ts rename to apps/server/src/modules/authorization-reference/api/authorization-reference.uc.ts index 965efe64c43..551811e5a6b 100644 --- a/apps/server/src/modules/authorization/api/authorization-reference.uc.ts +++ b/apps/server/src/modules/authorization-reference/api/authorization-reference.uc.ts @@ -1,8 +1,9 @@ import { EntityId } from '@shared/domain/types'; import { Injectable } from '@nestjs/common'; -import { AuthorizableReferenceType, AuthorizationContext, AuthorizationReferenceService } from '../domain'; +import { AuthorizationContext, AuthorizableReferenceType } from '@modules/authorization'; import { AuthorizedReponse } from './dto'; import { AuthorizationReponseMapper } from './mapper'; +import { AuthorizationReferenceService } from '../domain'; @Injectable() export class AuthorizationReferenceUc { diff --git a/apps/server/src/modules/authorization/api/dto/authorization-body.params.ts b/apps/server/src/modules/authorization-reference/api/dto/authorization-body.params.ts similarity index 93% rename from apps/server/src/modules/authorization/api/dto/authorization-body.params.ts rename to apps/server/src/modules/authorization-reference/api/dto/authorization-body.params.ts index c2e7b4656ff..4055067fe7f 100644 --- a/apps/server/src/modules/authorization/api/dto/authorization-body.params.ts +++ b/apps/server/src/modules/authorization-reference/api/dto/authorization-body.params.ts @@ -1,8 +1,8 @@ -import { Permission } from '@shared/domain/interface'; +import { Action, AuthorizationContext, AuthorizableReferenceType } from '@modules/authorization'; import { ApiProperty } from '@nestjs/swagger'; +import { Permission } from '@shared/domain/interface'; import { Type } from 'class-transformer'; import { IsArray, IsEnum, IsMongoId, ValidateNested } from 'class-validator'; -import { Action, AuthorizableReferenceType, AuthorizationContext } from '../../domain'; class AuthorizationContextParams implements AuthorizationContext { @IsEnum(Action) diff --git a/apps/server/src/modules/authorization/api/dto/authorization.reponse.ts b/apps/server/src/modules/authorization-reference/api/dto/authorization.reponse.ts similarity index 100% rename from apps/server/src/modules/authorization/api/dto/authorization.reponse.ts rename to apps/server/src/modules/authorization-reference/api/dto/authorization.reponse.ts diff --git a/apps/server/src/modules/authorization/api/dto/index.ts b/apps/server/src/modules/authorization-reference/api/dto/index.ts similarity index 100% rename from apps/server/src/modules/authorization/api/dto/index.ts rename to apps/server/src/modules/authorization-reference/api/dto/index.ts diff --git a/apps/server/src/modules/authorization/api/index.ts b/apps/server/src/modules/authorization-reference/api/index.ts similarity index 100% rename from apps/server/src/modules/authorization/api/index.ts rename to apps/server/src/modules/authorization-reference/api/index.ts diff --git a/apps/server/src/modules/authorization/api/mapper/authorization.response.mapper.ts b/apps/server/src/modules/authorization-reference/api/mapper/authorization.response.mapper.ts similarity index 100% rename from apps/server/src/modules/authorization/api/mapper/authorization.response.mapper.ts rename to apps/server/src/modules/authorization-reference/api/mapper/authorization.response.mapper.ts diff --git a/apps/server/src/modules/authorization/api/mapper/index.ts b/apps/server/src/modules/authorization-reference/api/mapper/index.ts similarity index 100% rename from apps/server/src/modules/authorization/api/mapper/index.ts rename to apps/server/src/modules/authorization-reference/api/mapper/index.ts diff --git a/apps/server/src/modules/authorization/api/test/authorization.api.spec.ts b/apps/server/src/modules/authorization-reference/api/test/authorization.api.spec.ts similarity index 98% rename from apps/server/src/modules/authorization/api/test/authorization.api.spec.ts rename to apps/server/src/modules/authorization-reference/api/test/authorization.api.spec.ts index 068c3ce8480..7fd4f059902 100644 --- a/apps/server/src/modules/authorization/api/test/authorization.api.spec.ts +++ b/apps/server/src/modules/authorization-reference/api/test/authorization.api.spec.ts @@ -4,7 +4,12 @@ import { HttpStatus, INestApplication } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { TestApiClient, UserAndAccountTestFactory } from '@shared/testing'; import { Permission } from '@shared/domain/interface'; -import { Action, AuthorizableReferenceType, AuthorizationContext, AuthorizationContextBuilder } from '../../domain'; +import { + Action, + AuthorizationContext, + AuthorizationContextBuilder, + AuthorizableReferenceType, +} from '@modules/authorization'; import { AuthorizationReponseMapper } from '../mapper'; import { AuthorizationBodyParams } from '../dto'; diff --git a/apps/server/src/modules/authorization/authorization-reference.api.module.ts b/apps/server/src/modules/authorization-reference/authorization-reference.api.module.ts similarity index 100% rename from apps/server/src/modules/authorization/authorization-reference.api.module.ts rename to apps/server/src/modules/authorization-reference/authorization-reference.api.module.ts diff --git a/apps/server/src/modules/authorization-reference/authorization-reference.config.ts b/apps/server/src/modules/authorization-reference/authorization-reference.config.ts new file mode 100644 index 00000000000..8abda97c8d1 --- /dev/null +++ b/apps/server/src/modules/authorization-reference/authorization-reference.config.ts @@ -0,0 +1,3 @@ +import { LoggerConfig } from '@src/core/logger'; + +export interface AuthorizationReferenceConfig extends LoggerConfig {} diff --git a/apps/server/src/modules/authorization/authorization-reference.module.ts b/apps/server/src/modules/authorization-reference/authorization-reference.module.ts similarity index 83% rename from apps/server/src/modules/authorization/authorization-reference.module.ts rename to apps/server/src/modules/authorization-reference/authorization-reference.module.ts index 5a455e44507..817dc754ed8 100644 --- a/apps/server/src/modules/authorization/authorization-reference.module.ts +++ b/apps/server/src/modules/authorization-reference/authorization-reference.module.ts @@ -2,8 +2,8 @@ import { InstanceModule } from '@modules/instance'; import { Module } from '@nestjs/common'; import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from './authorization.module'; -import { AuthorizationHelper, AuthorizationReferenceService, ReferenceLoader } from './domain'; +import { AuthorizationModule } from '@modules/authorization'; +import { AuthorizationReferenceService, ReferenceLoader } from './domain'; /** * This module is part of an intermediate state. In the future it should be replaced by an AuthorizationApiModule. @@ -13,7 +13,6 @@ import { AuthorizationHelper, AuthorizationReferenceService, ReferenceLoader } f @Module({ imports: [AuthorizationModule, LoggerModule, InstanceModule], providers: [ - AuthorizationHelper, ReferenceLoader, UserRepo, CourseRepo, diff --git a/apps/server/src/modules/authorization/domain/service/authorization-reference.service.spec.ts b/apps/server/src/modules/authorization-reference/domain/authorization-reference.service.spec.ts similarity index 96% rename from apps/server/src/modules/authorization/domain/service/authorization-reference.service.spec.ts rename to apps/server/src/modules/authorization-reference/domain/authorization-reference.service.spec.ts index 71b977d1d1b..b0ba329b99a 100644 --- a/apps/server/src/modules/authorization/domain/service/authorization-reference.service.spec.ts +++ b/apps/server/src/modules/authorization-reference/domain/authorization-reference.service.spec.ts @@ -3,11 +3,13 @@ import { NotFoundException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { courseFactory, setupEntities, userFactory } from '@shared/testing'; import { ObjectId } from '@mikro-orm/mongodb'; -import { AuthorizationService } from '@modules/authorization'; -import { AuthorizableReferenceType } from '../type'; +import { + AuthorizableReferenceType, + AuthorizationContextBuilder, + AuthorizationService, + ForbiddenLoggableException, +} from '@modules/authorization'; import { ReferenceLoader } from './reference.loader'; -import { AuthorizationContextBuilder } from '../mapper'; -import { ForbiddenLoggableException } from '../error'; import { AuthorizationReferenceService } from './authorization-reference.service'; describe('AuthorizationReferenceService', () => { diff --git a/apps/server/src/modules/authorization/domain/service/authorization-reference.service.ts b/apps/server/src/modules/authorization-reference/domain/authorization-reference.service.ts similarity index 86% rename from apps/server/src/modules/authorization/domain/service/authorization-reference.service.ts rename to apps/server/src/modules/authorization-reference/domain/authorization-reference.service.ts index 742fcad58a9..4c50986a0c9 100644 --- a/apps/server/src/modules/authorization/domain/service/authorization-reference.service.ts +++ b/apps/server/src/modules/authorization-reference/domain/authorization-reference.service.ts @@ -1,8 +1,11 @@ import { Injectable } from '@nestjs/common'; import { EntityId } from '@shared/domain/types'; -import { ForbiddenLoggableException } from '../error'; -import { AuthorizableReferenceType, AuthorizationContext } from '../type'; -import { AuthorizationService } from './authorization.service'; +import { + AuthorizationContext, + AuthorizationService, + ForbiddenLoggableException, + AuthorizableReferenceType, +} from '@src/modules/authorization'; import { ReferenceLoader } from './reference.loader'; /** diff --git a/apps/server/src/modules/authorization-reference/domain/index.ts b/apps/server/src/modules/authorization-reference/domain/index.ts new file mode 100644 index 00000000000..c41060a9a27 --- /dev/null +++ b/apps/server/src/modules/authorization-reference/domain/index.ts @@ -0,0 +1,2 @@ +export * from './authorization-reference.service'; +export * from './reference.loader'; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts b/apps/server/src/modules/authorization-reference/domain/reference.loader.spec.ts similarity index 97% rename from apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts rename to apps/server/src/modules/authorization-reference/domain/reference.loader.spec.ts index 56003d36e5e..f4c24447d62 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.spec.ts +++ b/apps/server/src/modules/authorization-reference/domain/reference.loader.spec.ts @@ -6,9 +6,8 @@ import { Test, TestingModule } from '@nestjs/testing'; import { EntityId } from '@shared/domain/types'; import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; import { setupEntities, userFactory } from '@shared/testing'; -import { AuthorizableReferenceType } from '../type'; +import { AuthorizationInjectionService, AuthorizableReferenceType } from '@src/modules/authorization'; import { ReferenceLoader } from './reference.loader'; -import { AuthorizationInjectionService } from './authorization-injection.service'; describe('reference.loader', () => { let service: ReferenceLoader; diff --git a/apps/server/src/modules/authorization/domain/service/reference.loader.ts b/apps/server/src/modules/authorization-reference/domain/reference.loader.ts similarity index 90% rename from apps/server/src/modules/authorization/domain/service/reference.loader.ts rename to apps/server/src/modules/authorization-reference/domain/reference.loader.ts index 9be54994466..93243a29339 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization-reference/domain/reference.loader.ts @@ -4,9 +4,12 @@ import { AuthorizableObject } from '@shared/domain/domain-object'; import { BaseDO } from '@shared/domain/domainobject'; import { EntityId } from '@shared/domain/types'; import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; -import { InstanceService } from '../../../instance'; -import { AuthorizableReferenceType, AuthorizationLoaderService } from '../type'; -import { AuthorizationInjectionService } from './authorization-injection.service'; +import { + AuthorizationInjectionService, + AuthorizationLoaderService, + AuthorizableReferenceType, +} from '@src/modules/authorization/domain'; +import { InstanceService } from '../../instance'; @Injectable() export class ReferenceLoader { diff --git a/apps/server/src/modules/authorization-reference/index.ts b/apps/server/src/modules/authorization-reference/index.ts new file mode 100644 index 00000000000..6ebdb7468cd --- /dev/null +++ b/apps/server/src/modules/authorization-reference/index.ts @@ -0,0 +1,3 @@ +export { AuthorizationReferenceModule } from './authorization-reference.module'; +export { AuthorizationReferenceApiModule } from './authorization-reference.api.module'; +export { AuthorizationReferenceService } from './domain/authorization-reference.service'; diff --git a/apps/server/src/modules/authorization/domain/service/authorization.service.spec.ts b/apps/server/src/modules/authorization/domain/service/authorization.service.spec.ts index afe0fd8c5c4..d5d4cf3ed72 100644 --- a/apps/server/src/modules/authorization/domain/service/authorization.service.spec.ts +++ b/apps/server/src/modules/authorization/domain/service/authorization.service.spec.ts @@ -9,7 +9,6 @@ import { AuthorizationContextBuilder } from '../mapper'; import { Rule } from '../type'; import { AuthorizationHelper } from './authorization.helper'; import { AuthorizationService } from './authorization.service'; -import { ReferenceLoader } from './reference.loader'; import { RuleManager } from './rule-manager'; class TestRule implements Rule { @@ -42,10 +41,6 @@ describe('AuthorizationService', () => { provide: RuleManager, useValue: createMock(), }, - { - provide: ReferenceLoader, - useValue: createMock(), - }, { provide: AuthorizationHelper, useValue: createMock(), diff --git a/apps/server/src/modules/authorization/domain/service/index.ts b/apps/server/src/modules/authorization/domain/service/index.ts index a6a242cd682..45e1f203a72 100644 --- a/apps/server/src/modules/authorization/domain/service/index.ts +++ b/apps/server/src/modules/authorization/domain/service/index.ts @@ -1,6 +1,4 @@ export * from './authorization.service'; export * from './authorization.helper'; export * from './rule-manager'; -export * from './authorization-reference.service'; -export * from './reference.loader'; export * from './authorization-injection.service'; diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts index 22526a1c3cc..3213261f612 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.spec.ts @@ -2,7 +2,6 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { InternalServerErrorException, NotImplementedException } from '@nestjs/common'; import { Test } from '@nestjs/testing'; import { courseFactory, setupEntities, userFactory } from '@shared/testing'; -// IMPORTANT: RuleManager has to be imported before the rules to prevent import cycles! import { RuleManager } from '.'; import { AuthorizationContextBuilder } from '../mapper'; import { AuthorizationInjectionService } from './authorization-injection.service'; diff --git a/apps/server/src/modules/authorization/index.ts b/apps/server/src/modules/authorization/index.ts index f7375fe73e9..e9125b5e182 100644 --- a/apps/server/src/modules/authorization/index.ts +++ b/apps/server/src/modules/authorization/index.ts @@ -9,7 +9,6 @@ export { AuthorizationHelper, AuthorizationLoaderService, AuthorizationLoaderServiceGeneric, - AuthorizationReferenceService, AuthorizationService, ForbiddenLoggableException, AuthorizationInjectionService, diff --git a/apps/server/src/modules/learnroom/learnroom-api.module.ts b/apps/server/src/modules/learnroom/learnroom-api.module.ts index 3c565127dab..62cbacd67c5 100644 --- a/apps/server/src/modules/learnroom/learnroom-api.module.ts +++ b/apps/server/src/modules/learnroom/learnroom-api.module.ts @@ -1,5 +1,5 @@ import { AuthorizationModule } from '@modules/authorization'; -import { AuthorizationReferenceModule } from '@modules/authorization/authorization-reference.module'; +import { AuthorizationReferenceModule } from '@modules/authorization-reference'; import { ClassModule } from '@modules/class'; import { CopyHelperModule } from '@modules/copy-helper'; import { GroupModule } from '@modules/group'; diff --git a/apps/server/src/modules/learnroom/uc/course-copy.uc.ts b/apps/server/src/modules/learnroom/uc/course-copy.uc.ts index 995298fa666..76279ce28bf 100644 --- a/apps/server/src/modules/learnroom/uc/course-copy.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course-copy.uc.ts @@ -1,10 +1,10 @@ import { Configuration } from '@hpi-schul-cloud/commons'; -import { AuthorizationContextBuilder } from '@modules/authorization'; -import { AuthorizableReferenceType, AuthorizationReferenceService } from '@modules/authorization/domain'; +import { AuthorizableReferenceType, AuthorizationContextBuilder } from '@modules/authorization'; import { CopyStatus } from '@modules/copy-helper'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; +import { AuthorizationReferenceService } from '@src/modules/authorization-reference'; import { CourseCopyService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/learnroom/uc/course-export.uc.ts b/apps/server/src/modules/learnroom/uc/course-export.uc.ts index 7431a5cf665..2971ad7ba5b 100644 --- a/apps/server/src/modules/learnroom/uc/course-export.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course-export.uc.ts @@ -1,10 +1,10 @@ -import { AuthorizationContextBuilder } from '@modules/authorization'; -import { AuthorizableReferenceType, AuthorizationReferenceService } from '@modules/authorization/domain'; +import { AuthorizationContextBuilder, AuthorizableReferenceType } from '@modules/authorization'; import { CommonCartridgeVersion } from '@modules/common-cartridge'; import { Injectable, NotFoundException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; +import { AuthorizationReferenceService } from '@src/modules/authorization-reference/domain'; import { LearnroomConfig } from '../learnroom.config'; import { CommonCartridgeExportService } from '../service/common-cartridge-export.service'; diff --git a/apps/server/src/modules/server/server.module.ts b/apps/server/src/modules/server/server.module.ts index 904d4bbef8d..b3fad8091ed 100644 --- a/apps/server/src/modules/server/server.module.ts +++ b/apps/server/src/modules/server/server.module.ts @@ -9,7 +9,7 @@ import { MikroOrmModule, MikroOrmModuleSyncOptions } from '@mikro-orm/nestjs'; import { AccountApiModule } from '@modules/account/account-api.module'; import { AlertModule } from '@modules/alert/alert.module'; import { AuthenticationApiModule } from '@modules/authentication/authentication-api.module'; -import { AuthorizationReferenceApiModule } from '@modules/authorization/authorization-reference.api.module'; +import { AuthorizationReferenceApiModule } from '@modules/authorization-reference/authorization-reference.api.module'; import { AuthorizationRulesModule } from '@modules/authorization-rules'; import { BoardApiModule } from '@modules/board/board-api.module'; import { MediaBoardApiModule } from '@modules/board/media-board-api.module'; diff --git a/apps/server/src/modules/sharing/sharing.module.ts b/apps/server/src/modules/sharing/sharing.module.ts index 7474709ac47..71773a87a6f 100644 --- a/apps/server/src/modules/sharing/sharing.module.ts +++ b/apps/server/src/modules/sharing/sharing.module.ts @@ -1,5 +1,5 @@ import { AuthorizationModule } from '@modules/authorization'; -import { AuthorizationReferenceModule } from '@modules/authorization/authorization-reference.module'; +import { AuthorizationReferenceModule } from '@modules/authorization-reference/authorization-reference.module'; import { BoardModule } from '@modules/board'; import { LearnroomModule } from '@modules/learnroom'; import { LessonModule } from '@modules/lesson'; diff --git a/apps/server/src/modules/video-conference/video-conference.module.ts b/apps/server/src/modules/video-conference/video-conference.module.ts index 72c2be9fb6e..769af5e953f 100644 --- a/apps/server/src/modules/video-conference/video-conference.module.ts +++ b/apps/server/src/modules/video-conference/video-conference.module.ts @@ -1,6 +1,6 @@ import { CalendarModule } from '@infra/calendar'; import { AuthorizationModule } from '@modules/authorization'; -import { AuthorizationReferenceModule } from '@modules/authorization/authorization-reference.module'; +import { AuthorizationReferenceModule } from '@modules/authorization-reference/authorization-reference.module'; import { LegacySchoolModule } from '@modules/legacy-school'; import { UserModule } from '@modules/user'; import { HttpModule } from '@nestjs/axios'; From b12a61e19f8b216b63bd9711e542cf89b356c2d2 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Mon, 7 Oct 2024 13:59:31 +0200 Subject: [PATCH 19/23] fix imports in tests --- apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts | 4 ++-- apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts | 4 ++-- .../service/external-tool-authorizable.service.spec.ts | 2 +- .../school-external-tool-authorization.service.spec.ts | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts index 087104ac7af..c9e69293197 100644 --- a/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts @@ -1,12 +1,12 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Configuration } from '@hpi-schul-cloud/commons'; -import { AuthorizationContextBuilder } from '@modules/authorization'; -import { AuthorizableReferenceType, AuthorizationReferenceService } from '@modules/authorization/domain'; +import { AuthorizableReferenceType, AuthorizationContextBuilder } from '@modules/authorization'; import { CopyElementType, CopyStatusEnum } from '@modules/copy-helper'; import { ForbiddenException, InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; import { courseFactory, setupEntities, userFactory } from '@shared/testing'; +import { AuthorizationReferenceService } from '@src/modules/authorization-reference'; import { CourseCopyService } from '../service'; import { CourseCopyUC } from './course-copy.uc'; diff --git a/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts index 626c957b7d0..aea09428d4f 100644 --- a/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts @@ -1,11 +1,11 @@ +import { faker } from '@faker-js/faker'; import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; import { CommonCartridgeVersion } from '@modules/common-cartridge'; import { ForbiddenException, NotFoundException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; -import { faker } from '@faker-js/faker'; -import { AuthorizationReferenceService } from '../../authorization/domain'; +import { AuthorizationReferenceService } from '@src/modules/authorization-reference'; import { LearnroomConfig } from '../learnroom.config'; import { CommonCartridgeExportService } from '../service/common-cartridge-export.service'; import { CourseExportUc } from './course-export.uc'; diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts index f462a5653e7..02db2ea8074 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts @@ -1,9 +1,9 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { ExternalToolRepo } from '@shared/repo'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; import { externalToolFactory } from '../testing'; import { ExternalToolAuthorizableService } from './external-tool-authorizable.service'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; describe(ExternalToolAuthorizableService.name, () => { let module: TestingModule; diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts index d83ae11d5b9..05c6776ca91 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts @@ -3,9 +3,9 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SchoolExternalToolRepo } from '@shared/repo'; import { legacySchoolDoFactory } from '@shared/testing'; import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; -import { SchoolExternalTool } from '../../school-external-tool/domain'; -import { schoolExternalToolFactory } from '../../school-external-tool/testing'; import { SchoolExternalToolAuthorizableService } from './school-external-tool-authorizable.service'; +import { schoolExternalToolFactory } from '../testing'; +import { SchoolExternalTool } from '../domain'; describe('SchoolExternalToolAuthorizableService', () => { let module: TestingModule; From a6bec4b7f0f618b4888d098c321184cad346d510 Mon Sep 17 00:00:00 2001 From: mrikallab <93978883+mrikallab@users.noreply.github.com> Date: Mon, 7 Oct 2024 11:58:42 +0200 Subject: [PATCH 20/23] N21-2191 lti encryption (#5274) --- .../templates/configmap_file_init.yml.j2 | 5 + .../mikro-orm/Migration20240926205656.ts | 58 ++ .../service/external-tool.service.spec.ts | 21 + .../service/external-tool.service.ts | 5 +- .../lti11-tool-launch.strategy.spec.ts | 14 +- .../lti11-tool-launch.strategy.ts | 9 +- .../tool/tool-launch/tool-launch.module.ts | 2 + backup/setup/external-tools.json | 738 ++++++++---------- backup/setup/migrations.json | 47 +- backup/setup/school-external-tools.json | 23 - 10 files changed, 454 insertions(+), 468 deletions(-) create mode 100644 apps/server/src/migrations/mikro-orm/Migration20240926205656.ts diff --git a/ansible/roles/schulcloud-server-init/templates/configmap_file_init.yml.j2 b/ansible/roles/schulcloud-server-init/templates/configmap_file_init.yml.j2 index 43c876279ae..f191ce69ca8 100644 --- a/ansible/roles/schulcloud-server-init/templates/configmap_file_init.yml.j2 +++ b/ansible/roles/schulcloud-server-init/templates/configmap_file_init.yml.j2 @@ -517,6 +517,11 @@ data: # ========== Start of the CTL seed data configuration section. echo "Inserting ctl seed data secrets to external-tools..." + + # Encrypt secrets of external tools that contain an lti11 config. + $CTL_SEED_SECRET_ONLINE_DIA_MATHE=$(node scripts/secret.js -s $AES_KEY -e $CTL_SEED_SECRET_ONLINE_DIA_MATHE) + $CTL_SEED_SECRET_ONLINE_DIA_DEUTSCH=$(node scripts/secret.js -s $AES_KEY -e $CTL_SEED_SECRET_ONLINE_DIA_DEUTSCH) + mongosh $DATABASE__URL --quiet --eval 'db.getCollection("external-tools").updateOne( { "name": "Product Test Onlinediagnose Grundschule - Mathematik", diff --git a/apps/server/src/migrations/mikro-orm/Migration20240926205656.ts b/apps/server/src/migrations/mikro-orm/Migration20240926205656.ts new file mode 100644 index 00000000000..7ffd9be3173 --- /dev/null +++ b/apps/server/src/migrations/mikro-orm/Migration20240926205656.ts @@ -0,0 +1,58 @@ +import { Migration } from '@mikro-orm/migrations-mongodb'; +import CryptoJs from 'crypto-js'; + +// eslint-disable-next-line no-process-env + +export class Migration20240926205656 extends Migration { + async up(): Promise { + // eslint-disable-next-line no-process-env + const { AES_KEY } = process.env; + + if (AES_KEY) { + const tools = await this.driver.aggregate('external-tools', [{ $match: { config_type: { $eq: 'lti11' } } }]); + + for await (const tool of tools) { + // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access + if (tool.config_secret) { + // eslint-disable-next-line no-await-in-loop + await this.driver.nativeUpdate( + 'external-tools', + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access + { _id: tool._id }, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-member-access + { $set: { config_secret: CryptoJs.AES.encrypt(tool.config_secret, AES_KEY).toString() } } + ); + } + } + console.info(`Encrypt field config_secret with AES_KEY of the svs.`); + } else { + console.info(`FAILED: Encrypt field config_secret with AES_KEY of the svs. REASON: AES KEY is not provided.`); + } + } + + async down(): Promise { + // eslint-disable-next-line no-process-env + const { AES_KEY } = process.env; + + if (AES_KEY) { + const tools = await this.driver.aggregate('external-tools', [{ $match: { config_type: { $eq: 'lti11' } } }]); + + for await (const tool of tools) { + if (tool) { + // eslint-disable-next-line no-await-in-loop + await this.driver.nativeUpdate( + 'external-tools', + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access + { _id: tool._id }, + // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-argument,@typescript-eslint/no-unsafe-member-access + { $set: { config_secret: CryptoJs.AES.decrypt(tool.config_secret, AES_KEY).toString(CryptoJs.enc.Utf8) } } + ); + } + } + + console.info(`Rollback: Encrypt field config_secret with AES_KEY of the svs.`); + } else { + console.info(`FAILED: Encrypt field config_secret with AES_KEY of the svs. REASON: AES KEY is not provided.`); + } + } +} diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool.service.spec.ts index ea54061dd37..5e08193da2c 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool.service.spec.ts @@ -510,6 +510,27 @@ describe(ExternalToolService.name, () => { }); describe('updateExternalTool', () => { + describe('when external tool with lti11 config is given', () => { + const setup = () => { + encryptionService.encrypt.mockReturnValue('newEncryptedSecret'); + const changedTool: ExternalTool = externalToolFactory + .withLti11Config({ secret: 'newEncryptedSecret' }) + .build({ name: 'newName' }); + + return { + changedTool, + }; + }; + + it('should call externalToolServiceMapper', async () => { + const { changedTool } = setup(); + + await service.updateExternalTool(changedTool); + + expect(externalToolRepo.save).toHaveBeenLastCalledWith(changedTool); + }); + }); + describe('when external tool with oauthConfig is given', () => { const setup = () => { const existingTool: ExternalTool = externalToolFactory.withOauth2Config().buildWithId(); diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool.service.ts b/apps/server/src/modules/tool/external-tool/service/external-tool.service.ts index 62d1bb39b58..784e64c5643 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool.service.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool.service.ts @@ -42,7 +42,10 @@ export class ExternalToolService { } public async updateExternalTool(toUpdate: ExternalTool): Promise { - // TODO N21-2097 use encryption for secret + if (ExternalTool.isLti11Config(toUpdate.config) && toUpdate.config.secret) { + toUpdate.config.secret = this.encryptionService.encrypt(toUpdate.config.secret); + } + await this.updateOauth2ToolConfig(toUpdate); const externalTool: ExternalTool = await this.externalToolRepo.save(toUpdate); diff --git a/apps/server/src/modules/tool/tool-launch/service/launch-strategy/lti11-tool-launch.strategy.spec.ts b/apps/server/src/modules/tool/tool-launch/service/launch-strategy/lti11-tool-launch.strategy.spec.ts index 05fecd75d56..74b45a6024d 100644 --- a/apps/server/src/modules/tool/tool-launch/service/launch-strategy/lti11-tool-launch.strategy.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/service/launch-strategy/lti11-tool-launch.strategy.spec.ts @@ -1,4 +1,5 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { DefaultEncryptionService, EncryptionService } from '@infra/encryption'; import { ObjectId } from '@mikro-orm/mongodb'; import { PseudonymService } from '@modules/pseudonym/service'; import { UserService } from '@modules/user'; @@ -36,6 +37,7 @@ describe('Lti11ToolLaunchStrategy', () => { let userService: DeepMocked; let pseudonymService: DeepMocked; let lti11EncryptionService: DeepMocked; + let encryptionService: DeepMocked; beforeAll(async () => { module = await Test.createTestingModule({ @@ -77,6 +79,10 @@ describe('Lti11ToolLaunchStrategy', () => { provide: AutoGroupExternalUuidStrategy, useValue: createMock(), }, + { + provide: DefaultEncryptionService, + useValue: createMock(), + }, ], }).compile(); @@ -85,6 +91,7 @@ describe('Lti11ToolLaunchStrategy', () => { userService = module.get(UserService); pseudonymService = module.get(PseudonymService); lti11EncryptionService = module.get(Lti11EncryptionService); + encryptionService = module.get(DefaultEncryptionService); }); afterAll(async () => { @@ -134,10 +141,13 @@ describe('Lti11ToolLaunchStrategy', () => { ], }); + const decrypted = 'decryptedSecret'; + encryptionService.decrypt.mockReturnValue(decrypted); userService.findById.mockResolvedValue(user); return { data, + decrypted, user, mockKey, mockSecret, @@ -148,14 +158,14 @@ describe('Lti11ToolLaunchStrategy', () => { }; it('should contain lti key and secret without location', async () => { - const { data, mockKey, mockSecret } = setup(); + const { data, mockKey, decrypted } = setup(); const result: PropertyData[] = await strategy.buildToolLaunchDataFromConcreteConfig('userId', data); expect(result).toEqual( expect.arrayContaining([ new PropertyData({ name: 'key', value: mockKey }), - new PropertyData({ name: 'secret', value: mockSecret }), + new PropertyData({ name: 'secret', value: decrypted }), ]) ); }); diff --git a/apps/server/src/modules/tool/tool-launch/service/launch-strategy/lti11-tool-launch.strategy.ts b/apps/server/src/modules/tool/tool-launch/service/launch-strategy/lti11-tool-launch.strategy.ts index 948df83c295..c0977743e54 100644 --- a/apps/server/src/modules/tool/tool-launch/service/launch-strategy/lti11-tool-launch.strategy.ts +++ b/apps/server/src/modules/tool/tool-launch/service/launch-strategy/lti11-tool-launch.strategy.ts @@ -1,7 +1,8 @@ +import { DefaultEncryptionService, EncryptionService } from '@infra/encryption'; import { ObjectId } from '@mikro-orm/mongodb'; import { PseudonymService } from '@modules/pseudonym/service'; import { UserService } from '@modules/user'; -import { Injectable, InternalServerErrorException, UnprocessableEntityException } from '@nestjs/common'; +import { Inject, Injectable, InternalServerErrorException, UnprocessableEntityException } from '@nestjs/common'; import { Pseudonym, RoleReference, UserDO } from '@shared/domain/domainobject'; import { RoleName } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; @@ -28,6 +29,7 @@ export class Lti11ToolLaunchStrategy extends AbstractLaunchStrategy { private readonly userService: UserService, private readonly pseudonymService: PseudonymService, private readonly lti11EncryptionService: Lti11EncryptionService, + @Inject(DefaultEncryptionService) private readonly encryptionService: EncryptionService, autoSchoolIdStrategy: AutoSchoolIdStrategy, autoSchoolNumberStrategy: AutoSchoolNumberStrategy, autoContextIdStrategy: AutoContextIdStrategy, @@ -63,10 +65,11 @@ export class Lti11ToolLaunchStrategy extends AbstractLaunchStrategy { const roleNames: RoleName[] = user.roles.map((roleRef: RoleReference): RoleName => roleRef.name); const ltiRoles: LtiRole[] = LtiRoleMapper.mapRolesToLtiRoles(roleNames); + const decrypted = this.encryptionService.decrypt(config.secret); + const additionalProperties: PropertyData[] = [ new PropertyData({ name: 'key', value: config.key }), - // TODO N21-2097 use decryption for secret - new PropertyData({ name: 'secret', value: config.secret }), + new PropertyData({ name: 'secret', value: decrypted }), new PropertyData({ name: 'lti_message_type', value: config.lti_message_type, location: PropertyLocation.BODY }), new PropertyData({ name: 'lti_version', value: 'LTI-1p0', location: PropertyLocation.BODY }), diff --git a/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts b/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts index 15344278bcd..2644dc7f5fc 100644 --- a/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts +++ b/apps/server/src/modules/tool/tool-launch/tool-launch.module.ts @@ -1,3 +1,4 @@ +import { EncryptionModule } from '@infra/encryption'; import { BoardModule } from '@modules/board'; import { LearnroomModule } from '@modules/learnroom'; import { LegacySchoolModule } from '@modules/legacy-school'; @@ -32,6 +33,7 @@ import { BasicToolLaunchStrategy, Lti11ToolLaunchStrategy, OAuth2ToolLaunchStrat LearnroomModule, BoardModule, GroupModule, + EncryptionModule, ], providers: [ ToolLaunchService, diff --git a/backup/setup/external-tools.json b/backup/setup/external-tools.json index 1a88cae3c20..120dfa0f8dd 100644 --- a/backup/setup/external-tools.json +++ b/backup/setup/external-tools.json @@ -4,14 +4,10 @@ "$oid": "644a4593d0a8301e6cf25d85" }, "createdAt": { - "$date": { - "$numberLong": "1682589075592" - } + "$date": "2023-04-27T09:51:15.592Z" }, "updatedAt": { - "$date": { - "$numberLong": "1682589075592" - } + "$date": "2023-04-27T09:51:15.592Z" }, "name": "TestTool", "url": "https://google.de/", @@ -71,88 +67,15 @@ "isDeactivated": false, "restrictToContexts": [] }, - { - "_id": { - "$oid": "647de247cf6a427b9d39e5b9" - }, - "createdAt": { - "$date": { - "$numberLong": "1685971527243" - } - }, - "updatedAt": { - "$date": { - "$numberLong": "1685973728239" - } - }, - "name": "LTI Test Tool", - "url": "https://saltire.lti.app", - "config_type": "lti11", - "config_baseUrl": "https://saltire.lti.app/tool", - "config_key": "12345", - "config_secret": "secret", - "config_lti_message_type": "basic-lti-launch-request", - "config_privacy_permission": "name", - "config_launch_presentation_locale": "de-DE", - "parameters": [ - { - "name": "custom_test", - "displayName": "Custom Test Parameter", - "description": "just a test", - "default": "test", - "scope": "global", - "location": "body", - "type": "string", - "isOptional": false, - "isProtected": false - } - ], - "isHidden": false, - "openNewTab": false, - "version": 1, - "isDeactivated": false, - "restrictToContexts": [] - }, - { - "_id": { - "$oid": "667e4fe648ea6a22a5474359" - }, - "createdAt": { - "$date": { - "$numberLong": "1682589075592" - } - }, - "updatedAt": { - "$date": { - "$numberLong": "1682589075592" - } - }, - "name": "CY Test Tool Course Restriction", - "url": "https://google.de/", - "config_type": "basic", - "config_baseUrl": "https://google.de/", - "parameters": [], - "isHidden": false, - "openNewTab": true, - "version": 1, - "isDeactivated": false, - "restrictToContexts": [ - "course" - ] - }, { "_id": { "$oid": "644a4593d0a8301e6cf25d86" }, "createdAt": { - "$date": { - "$numberLong": "1682589075592" - } + "$date": "2023-04-27T09:51:15.592Z" }, "updatedAt": { - "$date": { - "$numberLong": "1682589075592" - } + "$date": "2023-04-27T09:51:15.592Z" }, "name": "CY Test Tool Board-Element Restriction", "url": "https://google.de/", @@ -169,82 +92,62 @@ }, { "_id": { - "$oid": "667e50f6162707ce02b9ac02" + "$oid": "647de247cf6a427b9d39e5b1" }, "createdAt": { - "$date": { - "$numberLong": "1682589075592" - } + "$date": "2023-11-30T12:37:54.977Z" }, "updatedAt": { - "$date": { - "$numberLong": "1682589075592" - } + "$date": "2023-11-30T15:31:47.749Z" }, - "name": "CY Test Tool Media-Board Restriction", - "url": "https://google.de/", + "name": "CY Test Tool School Scope", "config_type": "basic", - "config_baseUrl": "https://google.de/", - "parameters": [], - "isHidden": false, - "openNewTab": true, - "version": 1, - "isDeactivated": false, - "restrictToContexts": [ - "media-board" - ] - }, - { - "_id": { - "$oid": "667e52a4162707ce02b9ac04" - }, - "createdAt": { - "$date": { - "$numberLong": "1682589075592" - } - }, - "updatedAt": { - "$date": { - "$numberLong": "1682589075592" + "config_baseUrl": "http:google.com", + "parameters": [ + { + "name": "searchparam", + "displayName": "searchparameter", + "description": "", + "scope": "school", + "location": "path", + "type": "string", + "isOptional": false, + "isProtected": false } - }, - "name": "CY Test Tool All Restrictions", - "url": "https://google.de/", - "config_type": "basic", - "config_baseUrl": "https://google.de/", - "parameters": [], + ], "isHidden": false, - "openNewTab": true, + "openNewTab": false, "version": 1, "isDeactivated": false, - "restrictToContexts": [ - "course","board-element","media-board" - ] + "restrictToContexts": [] }, { "_id": { - "$oid": "647de247cf6a427b9d39e5b1" + "$oid": "647de247cf6a427b9d39e5b9" }, "createdAt": { - "$date": { - "$numberLong": "1701347874977" - } + "$date": "2023-06-05T13:25:27.243Z" }, "updatedAt": { - "$date": { - "$numberLong": "1701358307749" - } + "$date": "2023-06-05T14:02:08.239Z" }, - "name": "CY Test Tool School Scope", - "config_type": "basic", - "config_baseUrl": "http:google.com", + "name": "LTI Test Tool", + "url": "https://saltire.lti.app", + "config_type": "lti11", + "config_baseUrl": "https://saltire.lti.app/tool", + "config_key": "12345", + "config_secret": "U2FsdGVkX188+4Kh4t/eADwUS7hh0mwOjCOAIbd64Og=", + "config_lti_message_type": "basic-lti-launch-request", + "config_privacy_permission": "name", + "config_launch_presentation_locale": "de-DE", "parameters": [ { - "name": "searchparam", - "displayName": "searchparameter", - "description": "", - "scope": "school", - "location": "path", + "name": "custom_test", + "displayName": "Custom Test Parameter", + "description": "just a test", + "default": "test", + "scope": "global", + "location": "body", "type": "string", "isOptional": false, "isProtected": false @@ -261,14 +164,10 @@ "$oid": "647de247cf6a427b9d39e5c2" }, "createdAt": { - "$date": { - "$numberLong": "1701348029049" - } + "$date": "2023-11-30T12:40:29.049Z" }, "updatedAt": { - "$date": { - "$numberLong": "1701358325991" - } + "$date": "2023-11-30T15:32:05.991Z" }, "name": "CY Test Tool Context Scope", "config_type": "basic", @@ -296,14 +195,10 @@ "$oid": "647de247cf6a427b9d39e6c3" }, "createdAt": { - "$date": { - "$numberLong": "1701358084733" - } + "$date": "2023-11-30T15:28:04.733Z" }, "updatedAt": { - "$date": { - "$numberLong": "1701358362888" - } + "$date": "2023-11-30T15:32:42.888Z" }, "name": "CY Test Tool deactivated External Tool", "config_type": "basic", @@ -320,14 +215,10 @@ "$oid": "659bf6f049e52dedff83a8f1" }, "createdAt": { - "$date": { - "$numberLong": "1701358084733" - } + "$date": "2023-11-30T15:28:04.733Z" }, "updatedAt": { - "$date": { - "$numberLong": "1701358362888" - } + "$date": "2023-11-30T15:32:42.888Z" }, "name": "CY Test Tool Protected Parameter", "config_type": "basic", @@ -362,235 +253,109 @@ }, { "_id": { - "$oid": "65fc0fcda519d4a3b71193e0" - }, - "createdAt": { - "$date": { - "$numberLong": "1701358084733" - } - }, - "updatedAt": { - "$date": { - "$numberLong": "1701358362888" - } - }, - "name": "CY Test Tool Optional Protected Parameter", - "config_type": "basic", - "config_baseUrl": "https://google.com/search", - "parameters": [ - { - "name": "search", - "displayName": "Suchparameter", - "description": "Danch wird gesucht", - "scope": "context", - "location": "query", - "type": "string", - "isOptional": false, - "isProtected": false - }, - { - "name": "protected", - "displayName": "geschützter Parameter", - "description": "Dieser parameter wird nicht mitkopiert", - "scope": "context", - "location": "query", - "type": "string", - "isOptional": true, - "isProtected": true - } - ], - "isHidden": false, - "openNewTab": false, - "version": 1, - "isDeactivated": false, - "restrictToContexts": [] - }, - { - "_id": { - "$oid": "666829b6ea0c14353cec2056" + "$oid": "65f958bdd8b35469f14032b1" }, + "config_type": "oauth2", + "name": "nextcloud", + "config_baseUrl": "https://nextcloud-nbc.dbildungscloud.dev/", + "config_clientId": "neWZs5MIKnAHUbbuO9TzeClZQF", + "config_skipConsent": true, "createdAt": { - "$date": { - "$numberLong": "1701358084733" - } - }, - "updatedAt": { - "$date": { - "$numberLong": "1701358362888" - } + "$date": "2024-03-19T09:19:57.984Z" }, - "name": "CY Test Tool Hidden", - "config_type": "basic", - "config_baseUrl": "https://google.com/search", - "parameters": [], "isHidden": true, - "openNewTab": false, - "version": 1, - "isDeactivated": false, - "restrictToContexts": [] - }, - { - "_id": { - "$oid": "6667ec1c243527c9139bd799" - }, - "createdAt": { - "$date": { - "$numberLong": "1701358084733" - } - }, - "updatedAt": { - "$date": { - "$numberLong": "1701358362888" - } - }, - "name": "CY Test Tool 1", - "config_type": "basic", - "config_baseUrl": "https://google.com/search", + "logoUrl": "", + "openNewTab": true, "parameters": [], - "isHidden": false, - "openNewTab": false, - "version": 1, - "isDeactivated": false, - "restrictToContexts": [] - }, - { - "_id": { - "$oid": "66682949ea0c14353cec2054" - }, - "createdAt": { - "$date": { - "$numberLong": "1701358084733" - } - }, "updatedAt": { - "$date": { - "$numberLong": "1701358362888" - } + "$date": "2024-03-19T09:19:57.984Z" }, - "name": "CY Test Tool 2", - "config_type": "basic", - "config_baseUrl": "https://google.com/search", - "parameters": [], - "isHidden": false, - "openNewTab": false, + "url": "https://nextcloud-nbc.dbildungscloud.dev/", "version": 1, "isDeactivated": false, "restrictToContexts": [] }, { "_id": { - "$oid": "6667ec58243527c9139bd79b" + "$oid": "65fad93bbe8ce15df1279d9b" }, "createdAt": { - "$date": { - "$numberLong": "1701358084733" - } + "$date": "2024-03-20T12:40:27.057Z" }, "updatedAt": { - "$date": { - "$numberLong": "1701358362888" - } + "$date": "2024-03-25T09:13:39.585Z" }, - "name": "CY Test Tool Optional Parameters", + "name": "OSM Route", + "url": "https://www.openstreetmap.org/", + "logoUrl": "https://wiki.openstreetmap.org/w/images/7/7e/Logo_by_hind_128x128.png?20100124154543", + "logoBase64": "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", "config_type": "basic", - "config_baseUrl": "https://google.com/search", + "config_baseUrl": "https://wiki.openstreetmap.org/w/images/c/c8/Public-images-osm_logo.png", "parameters": [ { - "name": "schoolParam", - "displayName": "school parameter", + "name": "from", + "displayName": "Start", "description": "", - "scope": "school", - "location": "path", + "scope": "context", + "location": "query", "type": "string", - "isOptional": true, + "isOptional": false, "isProtected": false }, { - "name": "contextParam", - "displayName": "context parameter", + "name": "to", + "displayName": "Ziel", "description": "", "scope": "context", "location": "query", "type": "string", - "isOptional": true, + "isOptional": false, "isProtected": false } ], "isHidden": false, - "openNewTab": false, - "version": 1, "isDeactivated": false, + "openNewTab": false, + "version": 3, "restrictToContexts": [] }, { "_id": { - "$oid": "6667ec85243527c9139bd79d" + "$oid": "65fc0fcda519d4a3b71193e0" }, "createdAt": { - "$date": { - "$numberLong": "1701358084733" - } + "$date": "2023-11-30T15:28:04.733Z" }, "updatedAt": { - "$date": { - "$numberLong": "1701358362888" - } + "$date": "2023-11-30T15:32:42.888Z" }, - "name": "CY Test Tool Required Parameters", + "name": "CY Test Tool Optional Protected Parameter", "config_type": "basic", "config_baseUrl": "https://google.com/search", "parameters": [ { - "name": "schoolParam", - "displayName": "school parameter", - "description": "", - "scope": "school", - "location": "path", + "name": "search", + "displayName": "Suchparameter", + "description": "Danch wird gesucht", + "scope": "context", + "location": "query", "type": "string", "isOptional": false, "isProtected": false }, { - "name": "contextParam", - "displayName": "context parameter", - "description": "", + "name": "protected", + "displayName": "geschützter Parameter", + "description": "Dieser parameter wird nicht mitkopiert", "scope": "context", "location": "query", "type": "string", - "isOptional": false, - "isProtected": false - } - ], - "isHidden": false, - "openNewTab": false, - "version": 1, - "isDeactivated": false, - "restrictToContexts": [] - }, - { - "_id": { - "$oid": "65f958bdd8b35469f14032b1" - }, - "config_type": "oauth2", - "name": "nextcloud", - "config_baseUrl": "https://nextcloud-nbc.dbildungscloud.dev/", - "config_clientId": "neWZs5MIKnAHUbbuO9TzeClZQF", - "config_skipConsent": true, - "createdAt": { - "$date": { - "$numberLong": "1710839997984" - } - }, - "isHidden": true, - "logoUrl": "", - "openNewTab": true, - "parameters": [], - "updatedAt": { - "$date": { - "$numberLong": "1710839997984" + "isOptional": true, + "isProtected": true } - }, - "url": "https://nextcloud-nbc.dbildungscloud.dev/", + ], + "isHidden": false, + "openNewTab": false, "version": 1, "isDeactivated": false, "restrictToContexts": [] @@ -600,14 +365,10 @@ "$oid": "65fc0fcde519d4a3b71193e0" }, "createdAt": { - "$date": { - "$numberLong": "1711017933720" - } + "$date": "2024-03-21T10:45:33.720Z" }, "updatedAt": { - "$date": { - "$numberLong": "1711018099651" - } + "$date": "2024-03-21T10:48:19.651Z" }, "name": "Youtube Videoausschnitt", "url": "https://www.youtube.com", @@ -658,14 +419,10 @@ "$oid": "65fc113ce519d4a3b71193e1" }, "createdAt": { - "$date": { - "$numberLong": "1711018300466" - } + "$date": "2024-03-21T10:51:40.466Z" }, "updatedAt": { - "$date": { - "$numberLong": "1711018300466" - } + "$date": "2024-03-21T10:51:40.466Z" }, "name": "Invidious Videoausschnitt", "url": "https://yt.cdaut.de/", @@ -716,14 +473,10 @@ "$oid": "65fc11a5e519d4a3b71193e2" }, "createdAt": { - "$date": { - "$numberLong": "1711018405712" - } + "$date": "2024-03-21T10:53:25.712Z" }, "updatedAt": { - "$date": { - "$numberLong": "1711018405712" - } + "$date": "2024-03-21T10:53:25.712Z" }, "name": "Classtime Session", "url": "https://classtime.com/", @@ -754,14 +507,10 @@ "$oid": "65fc1285e519d4a3b71193e3" }, "createdAt": { - "$date": { - "$numberLong": "1711018629196" - } + "$date": "2024-03-21T10:57:09.196Z" }, "updatedAt": { - "$date": { - "$numberLong": "1711018629196" - } + "$date": "2024-03-21T10:57:09.196Z" }, "name": "Lichtblick-Filmsequenz", "logoUrl": "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAkAAAAFkCAYAAADIT4SLAAAABmJLR0QA/wD/AP+gvaeTAABJrElEQVR42u2dCZgcRdmA4436e98IhISIB4pH2JlZDomIIl4oGECO7G7AiCiGKMfObIAVUBARAVGIyc5CAJEglwKiIAiEI+CBIDcYJJzZKwdJyLn/93X3hs1M9UxXd8/RM+/7PP2gsNPTU11d9XbVV1+NGQMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADCQS/+Lo/GOwWMnvoXaDQAA4C9AwxyNdyzLpt5B7QYAAECAECAAAABAgBAgAAAABIgDAQIAAECAOBAgAAAABIgDAQIAAECAOBAgAAAABIgDAQIAAECAOBAgAAAABIgDAQIAAECAOBAgAAAABIgDAQIAAECAOBAgAAAABIgDAYqJD07tedP4qfndgxxbHXDx2yix+mW7yfNeG/Rebt3e+95GL4+xHfndApbF1tSe5DKuvXeXIPd5y7bzt6G0ECAECDYijf8ntm7PDwc7eidRYnXcERw6+z2B72VH/puNX7fzK4OUxdj23hy1J9H3+ZlA97ktfxKlVQUBWvqbw4ZX/e2CQMey3ukNIx1DP/3K8ItXnza86s7Lhlf/60/Dq267eHj5RccOD57waQQIAQIEqCkFaKup+fHjOvIz5buulU74nnHtPfPlf188rq1nijxTm1FzEaCGEqAV158zHBSVoMTLz/E7D6+649Lh4XVrjb9x/dLFw8svPhYBQoAAAWoeAerufrWIz+nS8a4t8f1P61QdtRcBQoASKj9rF/6z/A/dsGH4xSt+jAAhQIAANYUASYd7YcCOea3GqFCDESAEKGHHqvm/Dfxbh9euGV5yxr4IEAIECFBDC5CU737BnyvneG7zabPeQC1GgBCgpMT8nPIlR2pseOmeqxEgBAgQoIYWII31sRQgvSffphYjQAhQQo4Xr/zJsC3rlw8MD3RlECAECBCghhSgbafNeqecd0MIAbqCWowAIUBJmf66/XfDYRg86XMIEAIECFBDCtD49t4Wa/lxjp5/U4sRIAQoIYdOZ4Vh6LS9ECAECBCghhQgTcwXToDyj1KLESAEKCHHypt67O1HVoMN/ugzCBACBAhQY44ATblg25ACdCu1GAFCgJKSbbnne9b+s+65xwiCRoAAAWpYAZL8P6+U875gL0C9p1CLESAEKCnHzNbhdYsXWgmQZopGgBAgQIAaVoAEOedplgK0buzBsz9MLUaAEKAEHUtnHyZLu9YFSwP05L+HB47bCQFCgAABamgB2uKQOW+Xcy8Mej80YzQ1GAFCgBJ4LP9tdnjDmlUlf+eax+8eHjz582SCRoAAAWp4AXJGgWRERzrdx8rKT3vvHN02gxqMACFASU2KKCu7Xlpw+fD6Zf0vxzuLFK159M7h5ZfkqpL7BwFCgOJmq7bzt9NtCkod+raPACFAJj44tedN0vF2yfc8XPC9L8m/v0GeqS/QCiFACFADHYPdk4YHT9y9tgHaCBACFE/jem25chFJ+iwChACV4/1TLniHCrXuDj/hiLNfR+uDACFADShAdbFCDQFCgOJpXP+DACFAgAAhQAgQAoQANVvjugwBQoAAAUKAECAECAFqGnTKIki5IEAIECBACBAChAAhQA2DiM1EBAgBAgQIAUKAECAEqKkESDrGvREgBAgQIAQIAUKAEKCmEiCRhBkIEAIECBAChAAhQAjQpCZrWM9EgBAgQIAQIAQIAUKAmk2ArkKAECBAgBAgBAgBQoCaTYD+hQAhQIAAIUAIkHkLjJ99bXjVLXMDHVouCBAClKCGdaiBBWg/BAgBQoAQIAQoym7ws6YF/q3rB59pMgEafsWEQ2dvIZ3Nrlu397RLQ/sD+d9ZeRhPFZk4QYNsx7b1HCKbIU4eO6WnVf7dWxtZgLaYcsH7x3bkd5Odrw+S3/5tOd+xUiYnanloJ+QEHUvHLP8+856D576xlnduwoEXvTlouSRUgPYKfOLJ816l2zfIZ/aQe3fo1m35Y+R//8itxz2dUn+P0BVzus3DdpPnvRYBahx0n7vx7b0t8kzur/dZn9lx7T0/lnpwstaDcW35afrM6p542tYhQAgQAtTEAjRuypyUNA4zpWP4kzx0S4PLxcbjKfezIkgiAtr51IsAjZ/as7NNwynn/poGEkvDc48cyy3LYZ0c98k5Thk7Zc4nqySDm+l3iZQeKI38r4Jeq3QM10gnMCuOY/yUuVtVQ4DGtfV8vtS55F7vIB3c8VION8rfr7C4by/J77hJpUI6zg80swDpRqdB7rnUn3PqpT8cd8icsVL/vytt0CVSNk+HaL8G5bhVftdZY9vmfKkaLzL1I0DDr3DEMOCzvlVH/iMIEAKUeAHSRkPfiuQheyJEg1HuGJCO6LcyQrRnJWTIRoDGHjz7w6XO5WROdkd2bpG/Xx9nOUjjdZuOpMX1uydOm/Ua6fgOkLL9iTT2V8r5H/Oka7iWhyZfrIYAyUjcjoWf36Yjv6U3IhdXPd6gMqQirJ1D8wlQT2fAclpVyz5wu8N/9X9SBofLddzu3LN46/RLKtE60q2jqo0sQPI7j7Yolz/U4plAgBCg2NjmkDkflbe3i+TBWlulDvIZfcPQjq4WAjShI/8u0zm009ZykL9ZXeHfv0G+Z46O1ET93dtOm/XOWstOLQVIZWd0PRZRubTC9fgumxFEBKjy6AuLJ7wDVarfK6SeXTBuas/HG02AnBHT4O3fkzpCzhQYApRIAfLiQ86sovgUHit12HzsQbPeV0UBWl84AqXTffLv/1r939+7IGq8VJML0Gq9lyq0MgKWj3u0rlQdEoE/fcIRZ78OAaqlAA2/Qkdk5Hv7a1TPdZTpWo0tagQB0hE0udePBPzta5zQBmKAEKAkCpAGAgd94Cp9aFyNHF06nVNpAZLv6Rv1mffKv/tdBYbLbY7bowTcNrkADbpTlVV78y+sS/fEOYqJAAVHFyJ4U9T1UN83SF24MGpdqLUAybkvDvyb2/LfH1NPIEAIUNC3Jm3QajjqU+rBvqHiI0Ad+Qedv5f8MTV8c9w0kFdGExCgcFNgtT96/1vpIGkEaFN0BaZ81wt1WB/69XlMogA5qyKTFveDACFAtvKjU0513KGcWYUpsP/JcXmd/e51Gr+CACVRgJwOaVGUVW8IkEXdaOvZ151+qcO64L1cJU2AdBWXxQrJ/9VN3A8ChAAFRmIlxrb3zI26EkKOO0U2znXy3bT3HKYNkubO0NVdmgdIjm/p0nc3mFhjXIIvPdYAvGoEQdtOz2ngq/xztrupqORBauvZx/nNsjxWV2DpyhNvufm9EVZgXY4AxS5Az+jKQ8354qYy6M2oaOqIjV6fs7xbhvK9of/nItaT+yu4OggBckdt2yKucNTVfA/qdJWbGkHyPkmqCG279HnW9kv+e4eugpK28jfuyr/84sB1IGL510KAdCGG124FivvRPG9j6hEECAEqWdHb8meEHZ3QvaQ0QZwGyVl/cXf3q73VVUdowGCJxvyhKgVBB3qTc1aptffuotdvNTwvAd06pRUmX9DoFU02o3pbHXDx2/wO7fRtkgqWOpfNESXVQUQB0g7rVOsVOt3dr3QSJGoupPASdCECVKFpL5GUCPLzD31JCZvgUKR5gpP0tD1/fomptw2aRiRxAtSW/3Vi434QIAQoUIfijMqEWuEgb8a9W8dZTzefNusN0ph8Qzua0XFIGgRdYwHSYfXzTXllQpW5NIZB9+B6uQx6fhh3u+ANbzduJujRU5vSQGv9iuF52UXO93DIeK6DEKCK1OGlIYT0nti3vRFRludkJycJYHt+yahYsFtiuM9VFSB9qbUoyz/WXdwPAoQAlUOG+z8WIq/NM9XoDHUVlpd068moohVWgFwJ6z036tubCSc/icSHWIzA3IQAWQvQKp3OiHtJuoqUl0U4xAhUvFvBNLMAeVM0D1jeg9XarqisVLKebjF53uu9xRS360tmkgRIR5stVk/WZ9wPAoQAlZt+Gtee/7tl43FvNZb2Fr5VxdBQfiKE/NwWNvjY4rq+ZnFNS+J+y2p0AZJOf2ol66XNViIvbynS83MEKLbffqpl+S8d1zbn01WvtLG0YVUSIKdf6Jmf+LgfBAgBKj2Ub5XSfNidsqn+RqYxiYaNAK1xOosK70826toWBO48Yx6JavgRIHkDr3THFmLV4GrNU4MARX+mbdJ1aNxdlIUUtW/DqiNA3nZHQdM8TE9E4SFACNAmHZ8TAOts6Be0oj8bLgg3iQLUu381r006ph8EXgnXkf8yAlRHAiToRpjeHms2EnQqAhTxezvy11ll527r+eqYBFMNAfIS4K5riLgfBAgB8q3ozuaYNsP2vV9JduNhJUCTqnltuuzaIoj2KASovgTIkdgpPZ+xFKChuHYQb0YB0v3WqpVDrFkEyNkyRl50g8b9aAxjYgoPAUKARnD3dMkvs2g8Lk5+41G/AuRNo6wKKEAnI0D1J0BeB/UHqxgzyRGFAIX+zddalPVToVJ0NJUA6Q4AgetvMuJ+ECAEyNiBtOWn2eSfqXQq/6YXILdxuy9gh34WAlSvAmQXaO9OISBA1qM/klXbKueP7Ac3pgGopADZxYP2HJm4wkOAEKCXK7vVyq/zG6PxqG8B0n3OAl5fDwJUnwLk3cfbbILtx0+b9RYEyLqMT7LZhiTKZsLNIEAaGB40FUqi4n4QIASokC3bzt/G6i01psR/CFDZxu2qgNc3DwGqXwHytmOoamxdswmQnOdRCwE6aUyDUAkBcsMheh4JOpWYqLgfBAgBMjTQMywaj8cSafsJFCB3b7RAw89XIkB1LECSJsJqM07ZggYBsirfD9kI5vgpF2yLAMXR7kjcT5JfhhEgBMgToJtqlbANASp5fb0IUPIFyOuobrbZhwoBsviutvwx1dp9vdEFSLbWOcSiLGckuvAQIARo4rRZrwm62sh5kGSDQQQIAUKA7BDRONFCgF6y3VC3uUeAgq/+kh3bz0aAzHipN5Y1dNwPAoQAbdIwT5nzSZvEYY2wdBQBQoBqIEBftMtR0/shBCjwb33OYvPgfRAgY1uzmcVGzMmN+0GAEKBNOg6L5e9u/E8jNR4IEAJUHbac2rO5ZSD0ZASoPBMOnb2FTbnKPn4TECDjNOKvmyLuBwFCgDad883/0iLXw5WN1XggQAhQVTurpRYjFT9EgIKMYPfsaSFAKyq923sSBUjqwN4WiTp/0DCFhwAhQNIZXG0xAvQzBAgBQoBCXqNFri3J7n06AhToew6zaL/uH9NghBGgCUec/bqtpubHy3Owq5Rfe+D9H6WvaJQVwAgQAjTyAN3bNFH/CBACVEMB0sDRam010zQCZLV/Ye+fm1iAdGPeuy329So8ntzikDlvb6jCQ4AQIKnY/fXecSBACFCDTIH1WHQ41yNAAaSyvWdus2WwDyNAUQ6Rp7UNE/eDACFABW+lawMPy7f1fB4BQoAQoJD3syP/U4v6dgsCFKhMA0/hx5FgsikFqMFSByBACJCDlwMo+MqUtjmfRoAQIAQodGf9I4vn7S4EKNAL3A3BV9b1/BgBCjUC1Cdt0XsRIASooQRowoEXvdlKgKbMSSFACBACFFqAshYrLv+NAAW47+098y1GgI5HgEIf1zdUADQChABN6Mi/y3IT1E8hQAgQAhTyGjvyR1VrO4zmWQWmgb2B73sWAYpwdOS/jQAhQE07AiQPQBoBQoAQoJD3U0YgLKZr5iNAgabAbrN4hk9oYgFa5S13LzwsclPllzdUIkkEqMmDoGW/IUsB2hUBQoAQoJCddXvvaRadzQ0IUKDf+ReLKbCfNKsA+SZCdPqA3gUWz84dYybPexUChAA1yjL4NRaVfw8ECAFCgELfz3MtrvFqBCjQ77zK4hn+BQJUjI7q6OiOhZx3IUAIUKMI0GKLNOgHIEAIEAIU8hrbey+yGHE9DwEK9DvPt1jOPRcB8h2dPNwmL5DsHN+CACFAyd8M1S49/1EIEAKEAIW8n3Y5a46P2DE2SSLE3hOrNa3YyAKkK7ysMpV35B/cYvK81yNACFDCR4B6rrQIzPw5AoQAIUBhR4CCL9mW0aJvIUCBRtW+ZTGq9gAC5M82B899t/zt800zpYgAIUBSkc+0aED+gAAhQAhQ6M7qucCjFVN6PoMABbjvkp3eov1a1TABvBUQIG+Uci+L8twgbdQXECAEKLlbYbT3TrWo8AsRIAQIAbJnu8N/9X9uhxHsGt8/5YJ3ROwYm0OAbO67jqxN7fkgAlSmT2jLz7Yo06cTu0kqAoQAjZsye3tL438rAoQAIUB2jJ/as7NFrMqiGDrGphAg77c+bTG1OBkBKs17Dp77Rrl/j1iU6UUIEAKUSAHycgGttOg89kKAECAEyPpeTq/mVHNTCZBFcLnEYf0KAQok7DtYpkjZDwFCgJInQO5DdH0z7gyMACFA1UI6oMss6tp0BMjqu4602GPtEQQosFjabN47tE1HfksECAFKnADJEOYRFhX9SRk1eiUChAAhQMGYOG3Wa7SDCFymUv4IUHA0kZ/VnoYHz/4wAhR4duBOi7L9S6I2TEWAECCn8zhkzli7jfGqLwMIEAKUVAGyXKn0VBydSDMJkPd7H7J4lk9BgIKxZdv528jnlwWPX+v5LgKEACVKgLwHycL0ey5BgBAgBCjwVMKl1e6cm0+Aek+wKONn5dgMAQpcf79tIfArErPSDgFCgEZV8g6LSr5OGpAPIUCNKUDagFnkq9kTAfJniykXvF++86VqT880mwBNOHT2FrpFQ0OOVNRYgLzv+YNF//APnfZFgBCgxAjQ5tNmvcEmTkHihn6PADWmAG05tWfzpK3+qFcBkufkHIsVSvNj7BibSoC8l7irbfLXTDjwojcjQAEFsyP/LptEnnL/uxEgBCgxAuQ1Wt1WwYRTer6OADWeAHkyXLUVS40qQDI9uJ183+rgoxJzvoQAhUc36LRJNin7rf0aAbJqj74QtHyd0biOfBoBQoASI0D6RiSVt99Cgl6Qh2JrBKixBMhrWIN23OcjQAbsV9D8I84VNM0oQCFGgTbI798bAbL6vvMsyvdxzYCOAPmw7oUnhlffe33dHKvumNfUAuQ1XEfarQjL37fVARe/DQFqOAF6KklpEepNgHR0weY5Gj81v3vM968pBUiWxH/UKoFfe/7FcVPmpBAgq9Hhhy1CJc5BgBLC+qV9TS9AulmgVNy7bCVo7EGz3ocANZAAdeSvsBCKPRCgl9HOxmoqub1nbgU6xkACNK4jP7ORBChM+cuxNKmpPaotQIpM7U60kEwdZfsiAoQAJUOAxmxcBv2SZW6gZ+N+izVem4w2yQN1uHznA7ryAwGq1AhQ4A5Mjwdqvay4HgRoi8nzXm+5kaQez2uAaQU6xqFavqHXUoC2mzzvtXIf7re8D9Kh9x5b6dFMXR0leaG+qkkDpR62JVGAvBHO4y3K9pmom/siQAhQ1QTIeYuy2yV+o+3rxniaWDHuRkPfIrx8KqtGBTEegwBVSoB6J1ne+4u14wn5Xe/VDlOWJu9TFQFqy38/9lGHjvxuUj8ftO10x7XN+XSFRgaeDHgNNzeaAHl16kNy7iUh2rC7K3FP3Lam9xdu3OTGVX83JlWAVBTl99xi8dJxBQKEACVGgJxOpb3n5yEaEGcFgO59pKta9K3Y/puHX6GjUDI8f6i3h5JfQ3YfAlQZAXKksy3fZ3XvO/J3yPD4ToFG8qbmx4ssf0vzi4zkb5F/XlgVAfICNKV+na5iHTZQ0xlpkCBaue4bwjwnugVNxUbE2vN/D3gd6ysxhVlrAfKmcfdwc5bZ3xt59haIkB+yzcFz3x3muzWVhAq9nOfcEjK6XvNEJVKAvGfYMkv0gQgQApQYAdJ4II1PCNeAvJwZVN8ytbORBuk74zvyX1aZ0HlkfdPyRnb2k/9+lPxzlhw3yd8PBu5EpvZ8HAGKX4AiCvD9ch/Pknv6Pbm/B8iQ/77j2vLT3DQLPZfI7/qvb0B19QSocMnuP1XAdBpEjq9p3Rw3Zfb246fM3UqH7x1hk7qmqR905NFLDLck7HdWKvZmVMfYY3E9q7Wjdn6bLF3WZ3P0EUYQ60GAPAlqCy9BniC25/8l7eBvdIGISo1O9etu6VJerbrNiYjsZG3bpF6cIf/7GovRNx0FOjqpAuSMfIokWpTlEn2eECAEKBkC5I3G2CRzq/YhD/bPEKDKCJCXxXhVNe9n2AYyigBV+dgQdeo2oIAcFtszFiLfV70IkFM3RMAtV4ZV87g3yQLkXcM8C+G7sW4200aAEKCgEiRvONmIb1IVOnpvQYAqI0DeKNBx1RXacMPkCRGgZTpaUI26s+20We+0X8jgO7U5I8kCpOiojXzX4nqsE3qvkixAbl3T/dUCZ4k+EgFCgBIkQN6cr2x8aZcOvaLHKnmT/km4GCMEyCYWyCrYMeIhwvXjMNep01SxdfiVOe6WTMUfqGr96chfEJMAnZV0AVK26chvKd93e/3UiZ4ro04J1YMAeaNsn7fIwv2SPAsfa0gBWtbzveFVd16WyGPlzb1Wv3XJz/cZfunuKwMdtueuRwHyOuW3Sif1K29uvBYNh8Yr9Max0gwBshpNuLfC93W9rpIJu5JsZMpOp0TlXAN1JD4Dupt2LYb9vfsWxwvLVY0gQA7O6iVnenCwhnXi5rhWmtWLALmjxRahEhJzF+VZr1sB4qj9UUkB2hj8NmXOJ3VD1GqJkK5I0qBcfYuLUTAQoKByccict8t3XVuh+3uXLCPfMbZrlVFBCTI+SBrZ62x2CI/5eF6+u6vWWdKlDmWsV/M1sgCNjAbJ6i4J0D/NZhVTxOMlN1amNxNz/E3dCJDmArPJvyTP6MkIEEciBWij9U/t+aBU5jPt5oCDp6nXxleXGlfibQEBChUL1hG00S0bDNyev97NEhvfHljGURBnJZCuPrPa5y7k6KRIovN9tU0MOZot287fRss6zGo+XcWnq0EbTYAKRrSPrtAI5zpNDaHpDiqVCLCeBMh5Me7If8piH8H1lcqDhQAhQNVFl8xrMrj23lOkQZlvswv2qEOHpW+VRuOneq4JR5z9ugo3flt7KxjKHrqzd7WL1Mt4Xfbaoi6ltUXvizTqB0sn9yebuBtphJfrKhDtEOJOlhm0jurSdmfprpty4Z+eZIft4DR78AKNkdFUDtqZjqljdIm7N0V4t5eeovD+LNIMxRr4rsvfIz5bXwv4bF1cL+XjZsDvPVbK4Y9BM2kXxSVKnZL6PUdXnumoaRXu6axg5dy7f9Veit38XoHaVY3jRIA4ki9Ahk7SSWbY3vsVefimy3GCVPhT3QdWM6JKThhZXeJknJaEZVG3tYDa3WftWJ1RD0mP73SwG++x3vPe6bryafyUC7atm+WvhukQnZrQLTKcfC7SCerwvJMHSX6LTpXo/3c7x55DdEWRbLo5oeYxDDFMWWjCPmeaTnavpzaPws10vLUu/NAcVrpprLZfGucidfyXXv042s1vJXmjZCQ8zEgZMAXG0YACBAAAgABxIEAAAAAIEAcCBAAAgABxIEAAAAAIEAcCBAAAgABxIEAAAAAIEAcCBAAAgABxIEAAAAAIEAIEAAAACBACBAAAAMgCAgQAAIAAcSBAAAAAjU7/zNRuHI13DHdPYmNDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ+rvSew3k0rMKj4XdkzajdCAJ9B2z0+amOjyYzexE6QAANCjD0ya+ZjCXOnogm/7rQFf63v5c+kL53x8J+nn5zEnSWQwXHd3pN1O6kAQGczt81FiHs6lDKB0AgEaUn+4xrxzMpm82NP6rh7KtuyJAgAABAECsqGCIPBxZ8SOXnu4rL52tXzA2/O4xHwECBAgAAGJudFNnl5CPWA+/a5DprmyJz62WD74CAQIECAAAGkqAvBEiv88tD/I7ECBAgAAAIFEC1NeV2qHE5y5HgAABAgCA6giQrMQayGXuju9ILyh1HbKM/RT5mw2jr6E/l3p8qDOzNQIECBAAAFRFgJbMTI2r9rWIxGQGcqkT3GvKHLpoRuvrLT6LAAECBAAAyROgiPKEAAECBAAACBACBAgQAAAgQAAIEAAAIEAACBAAAAKEAAEgQAAACBACVA7dx2zJzB236Z+Z2b0/m9l7oCs1eTCX2VP/nf432/M9373juwe70jsPZlNf1nPJqrg9hnItnxjunvTqSv2Gxd2T/m+os/WT+hukHPdxvzf9Oc3PJDuUv6ke7q9eo5MvqjP9eb2+/mzqa0NdLZ8e6tpxbEXvr2zSK+Xy4f6Zqd0kVcNeI/d3oKt1x77cxPchQMF5IZd+T39Xa2rkHmpd0zqn9fv5o7Z/Y6W/XzPKa33pm5mZNPL9Wof0/j7bPfEN9AYACFBNBEiWvXfL9z5RcDxaTQFSaSm+hszdy7onvnMTSZHGWjsl+e/XyPFiiSSOT0v5njzU/Ym3lhIoby+0HjkWlTjXMrmWedpJRi1rbewHujL7SZnlvd+5ocT3rpfjAfnuM/pm7vDB6Pc5PbWojLsy55vKxZHKXPpX8jePlLnG//Z3ZX5aeJ/CCm1/ruWzcn9/7ubCSq8pk+BzkcjYRX259GeCbNkStwCphGm+rKIyzabPiuvZ7M9ljjA8m08MHZv6eNnfdFzrx+RvZ+mzHCBZ6hODufQF8swcEJd4Oxst51JfkvL4rZTT4hLfrfd5vtS376ps0zMAIEDVu45s5peG61hbTQHSN0HTOUY6fh0NkPI6Wv7dkGUW7CcHjmvZrvBtdLAr9U35bw9ZnmudjA59O+wbuHaMco6lIbN5b5DP/25ZNvWO8CN1mR8YzrtwY7lMnvwq+Y4OlZoQ17dEpSnMdS3snrSZjLbNENH7X4Rs5/8KIgVxjwBJx/43w+dejKsjl3P921SnSwnf4LET3yLleWkZcS11vCgydI6+lIQWNx2xy6UftP1uR5SkDtI7ACBATS9ASzp3flt/Z0uLdI73R+gcn17a3fp2t2Fufb807jdFONeGwa7MVwK/BYu4aYLJMqNVNsez+mYfowDd45X/7tLx/Cfita2WurST5TXtJ597KqayWS0d6JSqClA21Wb6nI6kRB79ybZ+yCdT/El+n1me3eVd3qhdHOW5RqcbbUc4dVQu8nfLyKQ+O/QSAAhQswrQOhGffeWfK/2npqTzzKZf0Ost1aBqWTtTAl3p53z+ZpX3354p8X3ekflf0LggmU7Y3P0dvufr96Z75stxhychq8tN/Qwck94iDgFSGRSh+7E33WaUCmcUzb3GJ8qXTfqh4e7tXmsxwlFqFO5FrzzukuNWHeUJMIK2VuOEqiVAzpSs6Zq60tfGMPpzonGURMTIKNs6splN3xxAMFbJMRhghGiVzTOs08267U6pKV1vyvAWiQP6k9zb+0o9tzIldmHUqU0AQIASOwJUeE1y/F6DKFUsCjui/q7U10sMu+sIzEDBv1vgxFhk0x/R6Z+NHYn8bwmqToscXO0vVME7WRGmG0eP4DgxR/Ib9G3d2JGJXGmAqMZk+ImJ/LcrYxoBMp37MSmT4/s60xML38L1/zuBrNn0Db4dl9yHwNck3zPqs8v1d+k045LOlvF+nZ9OaXp1brnPNTxvG9gbJQhaynWOqa7qtGdEATKM5GTu9v0NMjJZou6fptvdFE7N6XSZBkI7U5/u7xj1/GSuCDzSecSer/Mk3vT9QyIzWZO065SuxJB93xOy4rqYzXyHngIAAWpuAepK/zlITIK+hXpvmaXiDB7X1TBlG3XpgEWQLvYbUbLo5DWu5lYVg9GiFaxcUrt5I11FU3EqaTEL0LM6dRNkBZ0TsOw71ZG6NOg1Le5snaD3Q8rme7bBt9J5b+U3giTXlquWAOm0n1kEM98P+0w6q+4szyn/7Y+G5+a5vmxqW6vvlrg7+exxNpLvBcybnrW/6bRz2e+UgHKfl5elcQTYAwAClEQBWidL0w+3GQoXaWkv0cn32Cy71bdW0yiMTh0Ffju2lJ7i8nWWxhs6l8y58QlQaraOBticz5vyWFYqsLrS5aPPinzfCsM1PGr3LEZbBu8jYgsijP6cbjOq5KxolED0WizjlxG7XXym0+6wGYnzpov7DS8bJ9NbACBAzShAx9n+nsVHt7zXZxQpVFyGaSWOThNVt66k/2EayYpFgLLpoyJ01JeYRqequZxZvu/MqM9RVAGS2JvOUqsYrYRQZN+Ju7Kov84qQ9P3F0wVV6j87zCN3AQZ+Skqx670NNMoVtSXCABIiAAtnpn+gMZaRDosEgHWswCFXXbus+rqknDnylxhijOpZl3xVpIVlY9fHJGNAOlUUujrEnkynbPSSRI3GTmQPEDG3yWpDqolQJ50rzV03j+y/T0a/2X7e7xpq+K2pMIiOpRt3dXnxeWHYc6nAfSGWL1h29WFAJBQAYrjsImBaEgBMq/2ujzk6MsFprw3Va4rBxjLuCDHUbUFKI7riiyH7jSl6TmYXi0B8urctabpQNuVTDKy92tTIHOp6SRv+qjq90GXqxu+d4XtdGqB7M82tAPH0GMAIEAIULARoKfjEiAva3NNBcjZwsA00iJv4LUUIC/hXfHUi6wgq1bZLJrR+nrbfDkVEaBs+hvm6wieS0dXAHppHYry4gQYOVlnCJr+aWUFyPiicUmkc+Yyh5pi9+gxABAgBKgJBchJVGgsn/QXazoC5LP0upoC5HX+Jnn5eTUFSK/DtOWDrpAKLrrO1izFz3OALNs+y9DX2iTutEFXl/nk7/luRKnKGM57Gz0GAAKEACFACFAdCpA3CnSW4TwDQZND+kwpPRMkCNjLp2NMQKhtTdS8RMW/1dmTbzjueB1NjWBK/kmPAdAMAuTsCt66Y5TDZgUGAoQAIUDxCJC3AWlxeWQzXy33WXdPNNNS9vTPApWDm4zwiRL5r17S/eR02lK/K7oApX9h/q12eYeKRpbcnEBFK8HoMQCaQIBYBt/cAqQdmW53oInoNAeSdoB+Wxw0mwDpSIiuMHOyUevu9lr3ZLfxehEgdyQm9c8wySElseXexjgvi01e9W8Dbhi8TMtNnq8vB93Wpfjep+f6vMDdK//t7yOHl0Zi0x3t3TinwVFHuS1OnqfHAECAEKAGECBdGaSZrd3A2dQJXmdym3fdgXfxblQBcvLaSOC3rv6R882S4y9ehu81gaeCayVAur2KYV8tTRxZus5m5hk+94Dt9w91tn5SE0HabLCrwdL6DFo+Y9dVagqfESAABAgBajABkg611ds24Pk4OoZGEqChzszWTr6j6LvS11SAlna3vt2Zbio+31S/z3ibqr4Y1/Jv3VbE2+R2hVXsYC71By2LgM/YnVUTINkDkB4DAAFCgBIoQDqlFfKNeYW3E/sdjSpAmjPGq4OrLctmjTPSoTuL15EAufUvdakhluevJcrxQFPw8kBXy5ZRnu3nu3d8t5es8mGbctVFFOWSqfoKkJS7iNSpcR26YW4cMUsAgAAhQFUWII3lKRPjoPlbHnE2s5TOQ3+zswmqdH4jSfQaNQjamQY0b4K5yQogmR66Ufc+098gz82XNNB2ZKd6L/i3vgTIvJzdV2jkv11jI0zhnnPZs8uty4PBpp0yc0olcdR7Ysw+LVmxad0BAAFqcgHq72xp8ZkO2aBbbGhnHmS7gkYUII2J0T3VzNs+pP+um9oG6UzrUYC8zUmfCjKltaRz57cZR7+y6Y5KPO+6ak7rjbfkfkWZ2JsjfetkLnOxz95yH6F1BwAEqIkFSFfX+HTwutT5czbX0IgC5FOuG+S5ONpm+4h6FCCvbH5skIP/FJVDNvMtU9B0lO0kgqLxSlrePs+Ls6mp/o1P+3WyeQPYzCRadwBAgJpYgHTzSvPohn1m3kYTIJ0KMq7oCpjzJgkCpJsam1b0iRRvv+l3p28ylMPvqtoGiGwZp+GcwOhMu/Ee+mz9wb5dAIAANbkA+SxrvivMNTSaADn5jYo/t7LcUvEkCZBXD28ttT+Xt4v8OkO9/3K126Nnuye+Qb77v4bpyLnGOinJVn0E72padwBAgJpagDR4t7BzSJ2MAOmquNRFhs/dEuYa6lqAJI7HcN6nR1ZY+eQM6hsJ8K428t3H2QRjy3+/x3D9y8OILAAgQAhQAwiQxrCYpnh05AMB8llBFHLap54FyMvvs6x4r75M2h1FkZV/xXtp/bJWbZLUkf0M5XBPiedsurFO5TLH0sIDAALUjALk1ymXWFXTXAJkyiGTuaLRBMiv/uhIoAbJG9MjyG7oNRMg04hVV/rPvuXmxg6Z9i97geXwAIAANaEAedew0tCZ/CZkXTmgsYKgjckLHwl3f5yA6roVICf/TvHeYLdrMLQhfuYxmxVwsbcF5v29ZpV8diVpos8KsltqNZUHAAhQTQXIb/lsEwnQQ4bPDdgsb9YcQfKZE/32wNKdvRM6AnSeT/6fnQOP/EgcTX82fbDGzJinYVJnBy6b41q2i7P+BagLq/TcBjE6Icq90JVnEUaqMvrcGxIi7ld2BC6bvs98D9I3aTbqsNe0LJt6h27rQW8BgADVpQDJKERn1M6sQQXoPJ9psD+XkyBndZArLCX3DNO37yQKkF+KADkW9s3c4YOlPuusVnJjVf5dei+w4JmU3X3IjOc4K45nTPZ/yxr23Xq8MAeSZsYOc34v8eIz3nnu0KnWoVzLJ8ptZ+F+1pmKm2qcyhK5DCIgnkAu8dtsVcRuRpCEn869kABqqddf87YTEVHMfIfeAgABqk8ByqX399lY8XHnLVd29dYYllKdYkMKkPtG7dc5vyD/PF2CYfdWURzqavl0f1fq67IqqEuE8mbD0mi/HeKXqQTpVJiWcX+u5bNJECBvZKvf5zet0izFOrqjSfV0A1knc7ETcOt0ii+aM2sbc9icq2XslI0cmnXZdxTDkI1Zs3hrvI4uSx85h2ZSthYgd8n4utLbT6RuD3sf+nLpz/icd8jdgy5zhrPiTGN8ulKTVXg0AaIz5eXWRb9rmmF5DctL/MblKv+6t5deh45eutvEZPbV75Gy/rX8zb+cLUM2/dwd9BYACFBdClBfbuL7jEPnAbLgNrIAeddxeQw7YT/jdS6BNrT0m3qst0zQsgLq+1HLxttmZLqzT1igHeLT3ygh8jcF+k6pq+Hqkc+mrRFXCHr17PT4d2BPXWobjySilzKlf4h4rNapMHoMAASo7gTIa4DPC9CQLW86AZJAcBnZ+WfYhl+nYEYaf5k2OizI5/zkox43Q9UNN0N3jF3pazV42TmP7D8VcDf5H5aog7uXGGl7+ZCNTsPVSR3p8N+BfVn3xHeGvQ+LZrS+Xpeelx7NCXxs0Hqno2JhrkXrq5yjN1BZlj7W6X5j5aZEAQABqqkAaQMsjeZvSzRmOkJ03vDkya9qJgEaVTZnOVM7wRr+IR3RWNLZMn70efRtXKYNTik52qZTDMekt0iKAHn17zsWHfdazTRs2m9KpxDL7HT+sK7IKiMph/pMsW2cptKd6MOUlU6d+Qdsp6+K41nW+CgvuPqW8lNuxbvVa16ioWzrrnFcy9CxqY97G64ut7yORbolSthyBoAEoMnQnDn5giNosGCMArSTDL9PG31oRxCq0ZPAS30T9aYkeqRD/pE07geVG8J2dgYvuAY9wk43SMd8YPFvstuAdOO1SYdQdF0++yOV4oVc+j06zeHtoq15cB7RZc+687kzVSar6ZwYkzJv3irI3lYSp2kZ69YKKhGFwlRUJse1fsxUxlHq21DXjmNN57QdzVjYPWkzDYz26s1tcjwgxxNOTIhKncSw6AhKuZWF3g7zB2m980aXztRYl/7OlpYgAcGKrlpyRMiV1h4nVkviZ8LWxSAvPaWm5kJ/lwTaO7FL2cxMCcK+ULfl8EYjtVwf0W1Z5Lhef6eTZsFHnKPijE5JDJe7eWrmCvnOBd41POFcjwSr633XOj0yogcAAAANhAqZQYD6w043AQAAANQ1OgUmQduLDaM/v6B0AAAAoCHxlp8XTX9prAylAwAAAI0pQBLrYhCgOykZAAAAaEi8vcY2GFZ/7U/pAAAAQGMKkHmT0UVsFgoAAACNKT/uru/rDZu1Hk3pAAAAQMPh7FifS/3NtL2JJi2khAAAAKDh0CSEcWY4BwAAAKg5ui+YZtgu/Pea2NDNfGzc6uGu4e5Jr6b0AAAAIJkClEs/6knNU3Jc424CmrnCmPDQPVawuScAAAAkFmcvP7tNPtdWYs8vAAAAgOoJUC6dtZCfleT8AQAQnj9q+zfqrumFx0BX646UTnVZnt3lXaZ7ocuXKR3wQ1d4DWQz3xK5eaiE+Gjyw7/0ZVPbUmLJRaY1Dy1qq3OZfSkZgBC8kEu/x9Rg6qqRJP6exd2T/m8gl5rhxEBk02fJKpcvJ+XaNYjV3HllDqWmQhD6cplP9Xdlvq8bm0rdmaUB0INdqcMGulq2pHQaQYCMkns9JQPQ5AI01JnZWq59YdHv6cqcjwABAAIEAA0pQD5p/t2jM/15BAgAECAAaDgBGrUUuPj35FKnIkAAgAABQCMK0FN+AjTYlfkxAgQACBAANKIA/d5PgPqyma8iQACAAAFAwwnQ4pnpD8i1LzH8nuuGx4x5BQIEAAgQADScACn9Xa3vl+s/U3e+lvw5V0mSuO8mZb+jehYgSSdwTEFuooN4egAQIKd96Mp8pSg/UXf6zdwxQIAg0QKkCRqLryl1O3cMAAFyrytzY1Hc5bETt+KOAQIEiRagvq7UDggQAALkh4y4P44AAQIEDSdAA13pfRAgAATIhLMVSy69GgECBAgaUIAyP2hGARru3u61g9nMTpJgc5oT1yD/1K0mqKmAAI1uH1q2NKYeQYAAAYLEC5DsqdZMArS0u/Xtsp/WT+V39hdvq5L+DTUVEKBR7VY2tQsCBAhQABZ2T9pM9+xaMnPHbfS7dfg0qWXXl5v4viWdLeNHfov+tloJkI5WLD665b0j17Okc+e3xdbAyWq6JAvQ8LSJr9H7o2kStHykYX6L/z1Nf0Z+37O+GcUjPhfPH7X9G0fukQaX10sZaX0ZuS5dSRklbYSzIfEx6S30XFontfwrfo+P2PN1y7onvlN/g97nOJ7HyNckZfh8947v1nLQ8ohjVVQUAVKxH91exXVfdEUoAgQIkI/wSAe+r7dP13/lWF/wXfr/n5G/uaI/m2rTDiJKgyPnmVd0ZNMdcTRm8ht2lvOdIcfdcs0rDeW2XhqDx+Sfl8nUyXeGunYcG4cAabkUddTH7LS5s8t3Nv07bwuQtYbPrnRWZ8gUlnZKNp20Bj5LYOMU3UJEvuMFw7n7jWVdcPTnMudWu04/2z3xDXrPpdwuMm6O6x59cvxFjh+O7JQ+kM18S/7/Ot/95NwtVaZYdVjS6cnnpmqdkONpwznXyPGklNPF/V2pr6vIRnuWC+9Barbp3g/lWj4h33uiHPPlWGq4ruXasUp93r/cS4q+CMizcbiUzR98fqOW6QI5jhvq/sRbY3n5mLnDB+V80+W43Kv/fvftCTku0fqg9aKS9U5f7DRdxEBX6k8yUvic8ZqcZ0lXTGW65W8ytqLpI0DXmNrd/q70XoO51Nlyb/7u3c/Cz22Q67lPjl+oNAZuCyWtSF82ta3WV3nGcnKeu3zK/pogbYTeS3piaCgBWjSj9fWSgyfrdTTDQQ9pRBfL9MP3w4wMuQJkOK9M4UQSH/cN50Gb3zHSwAxm0zdLo/jlKAI0+vOOhHWlry3XUZsaXs3VEaRxcxpG+9/qdzxVrbqsdU63PJHvHLS8xrVSpn82yLnWx5e0To50IEPZ1l2DjqjI358mx4uW17JQpSPUKI6ONBjOOdTZ+smCEa47La9pvo7kFH5f/8zMh1XcfOTb7xiQ43OhnsXJk1+lAurJVJi6OCTPwfFxj0ipTMo9u9pUfwK0d4+LpBwd9MXPJED6cjlaRt2XNGNC2NLPQC59epB2V37rlTG2D8MaZ0dPDA0jQN6y6UeiPRiZG23fFuMWIB3Clc/fGs+Dnrlb5SXcCFAmLY3O9nLcFPE61kpZfCOAAA0nTYCGjk19PKSkmo4HdGRI33JHv6HLqNubgkyrDHS2fqHUNFqgTkE6NZ3WiUOAdLRQp2LkheTC0HLblb5XBdOtI9u91ouRWhPmXCqVA9lUq9XIh5ZpNv2fmO7vPVomkUeFRaQ8yV0X+ZrkBUVk8giVPPsRoMwZWjc1Aawcq6Jch9aRAAJ0FQIECJABHRb1mR4afazyNi5dWaYT+Ls+2LUQIImDSNmOXgU4rgsjQDKFNce0zDTkMaD3vJEEaOC4lu2MAcsj9UimJb1Gu8cbdr+71KiFZg8Pey1S/78XoENcoeXiiECp+i+jCuU6xAACtMGTwydjkPjuZdnUO0KMIJnOdX/QUV6VlYDitsxnqscodFFicvSzmm0+zmdF60O52D2jAOmUm/9UbwjZzeyHAAECZNso5FJ7lOio/62dQ+GblzvKkprh+8YsMS7VFiCdMvCJi9jYqbtTLfJ7JbhRYwu0MdcgQ1diJOYplz7HG0kY1aGlDgglQMXHi9qR69Scfmak0dTr0LgjiQ36ZsmRq670Sf4CNOaVRSntXVktPM+ThX9nPCQ2pJL1VwOafeJOtCE/X0dx/D7n/bal5piV1B729T9zaImO+jaN5VKBKJxGcuJBfOqb1JmTo44AFY4KuNMu6Z/Iub+0uLN1gtZbnTpxpF+miLxpqmFj3FfxKMyQ1Ke81m0VUZ0q02Bk+buPOOVRYtRGp+OCl+0mcSYb5Pr/oc+glN/uIzFcm4zMyLWoyOrflejozw9T59y4xtTtZV7wevX51PiWkZFsfZlbMjM1TqcAtZx1enz0vdGppTBTYMbYHpXUbPooHXXW0T8dtdOXGw2878+1fFb++3kl2uoHS0p+NrN3QftwnbEP6UqfEqSN0KB7emJItAB5je+QadpFhna7yu3HpY2EF0RpGAkqH7sSlwB5b7hP+b25a+CxTaCqBka6nUrm/nJBmAEE6MmgwZxaFt40hek8/7Xr2E0xF/WxCsybgigKRu/Ppg8ONGLjCIhRoB622UNuoKt1R58pIZXVQ8sFu3p72N1jkrHB3A4fjShAI6Mu/9POq9zIi7MSzI17KlUXV2qMX7ngem8RxI1Rn0udHvKC8U+06TCd50BEyGdUbp2KkrV0uws6/MqlV2Uy6Lk09kfrqjO93ZWaHFGAVsnU4s/1pSxQ29TV8mm/UfjRcWPlp8QcCWIVGDSnADnikU3/1fQmIg1Xe+DzSMyDcVhZhqurJUDeyiFT4/JspZPglRCgNbrawjYmRKdPnKF+49t38Ea6XgVIR758ppFOtJIoWY3jEwA9JWi9VWEyBlDLCEXQ69CRGJ/Yud9HFiAZpbGZTnaEw7+jvVO/K+i5dIWRaWRMn/Wg59Brj7JCVH7/kT7TjOfYnMeb4jeOuujijSoI/0O+K64Cis8m53NH/AyjN6nvIUCAAAVqFNJ7+TyUp9tej7O80tCpBZknjipAXvD2BuOIwszUbpW+FyUEaHqEBnO68Zyd6c8nXYBkZPBA0zSNzZL/Ub/xMsOIyRXBPutM4Zpi2Kyn/7zpiaJR1CBBu74CJDFk1uWheWvMdXHBSDC0ZUd7n+Fci6pVV5yXAYOkqnAGPodMrZn2vBoJRK7SiKdJgOaHzdtU4j7PQoAAAQr0UDpBpUVxMmHf2MxDzOUbmKgC5Bfcp3PZVenQ/QQomzokdIMpq22i5rOpWwHKpS+IK67DvN9Zelm56SJvJZApfm1B2CSfpviSIDLlJ0BhRya8oOJQo1GG33Sp4VxLqltfUiebyse0xN88Opw+2C9mJkr+phgE6PqI53zGMDJ2FQIECFAZvIRqhsj+dGfoB9JdRmw9DRZFgLwYjLWmaYxSq6bqXYC8FTSRVjrVrQC5QaSFI4XfifNNuFy2Zv8pkcy+YX+XF7NSeM7LaiBAiwzP4bUhBfM3hmtbXWVh3j9KvIsGsvuM9E2r1m+okADdaWgz/4oAAQJU/uE5zjQfbhNjUoiTSr94Kmpl2bfxCAKkb9g+WZgvqloDXQEB8lZJmd5af5h0ATJ1BhrkG+Zc3khO8fRnmSBZja0xlO1glBEBcz3I3F8DAXrCNpVDCan7lSm2rZr1RaexfZ6FsokZ3QSDxkSHSyqdYbryAmQMUr8DAQIEqPwD+RfbZZRhG99yW0tEEiA3k2vkrQ/qTYA8mTSVyVGJHwFytx0p/F3fCCVAbiBz8f2XVWLW12DYmsDqWkSeDEuUVwVZvYUAlRoxNG/aqdOf5a/fyUA9HPe9rpMRoOsRIECALAXIS5q3vFRq9vAPZXEchAaIVkqA/PIQ2eyRgwBVfQRoQRyBx+5ITsuWpnLSlVm+owI+04ua1yfyb3P3krLqUBCgkAIUQJplavWX1dw0GgECqHMB8jLwmt6KTov8oGfTNxiSwn2pEgKkOYh8fsfSKLtiI0AV7gwk4Dku+dYcLMYg6BL3X/doM8eEpA6L+ttMq438kjoiQJUXIL9cRtL574kAIUDQhAKkG0P6iINmIZ61yeHsWj56F2DJHqqSM3LIUk7d+mLkMG3mVykB0n22/HaSrmoDjQBZSnLqEFP8TZgtDnw2eLysdKeeafffBbug/hftgi2dzqb1/46C+l+0p1O5XbMRoGI0samb3kKys/uM4gQTIGfH+eKy7WxpQYAQIGjGESDz0uGKHZUSoL6ZmUl+e5EhQPUrQM6WC4ZMtpoB2+Y8XkbcDYYM5AeWGaU8qpr1HwHyeW6ko9WRGGcfNs2ELPmbvASgSwNuRBpEgIzbg+hWIggQAgTNKEDu3kfV6gDW6JL7yowAZfb0+dxfq3kvEKAQZZZLnW1ahSjTHd8OJD+axsEQb+PkdpGVYSW/29kPrmr1f4nu6YQAjWQAd0bfrnFG/KLvxB5EgFaYPlvuniBAAA0qQJpzxSe9/D+Kh/wjHgFWaoQVIGPeITeX0c0IUH0LkBe/tcgnGPkKnaIwxfFo3idPYEx7Ia0PkinbZx8ybwo43vofJBN6owuQW5edjWOXhRCdtT6iG0mAgiZRRIAAGkyAdHdrn6mqo2vxW0IHQbtTIKZG818IUH0LkHP/JJGdz0a8I6kMdGPPW+S43Evb8GipzlL3wQpWLk5nPBzXUvyoNLIALelsGV9qZ/nRMWBeHNVcp2OW4Pa+zvRE3b7D9zkPJkB9YaYlESCARh0BkpicWm4dEZcAeTuCmxrThQhQ/QuQWxd3+Gg5sQlwrAg6deaOIknMiXHkMPg5EKDyeEkIn/WRW10td5yOkJXbeieiAD3o006mESAECJpQgPyXwadmJ0mAfDJPOx2ibqKIANW/AHlC8nWfbL3ljvW691G5ZeZF98tnGbyMjOYQoPgEyCfZ6no5Z7ZcnFaMAnS9z8veXggQAgRNKEDe9gGmGIp7kiRAXiPwX594pu0RoPoXIInvOKlAYh+WqayL/XfvduJI/iLlcLxOr4T5Ts1M7vMCcCkCFI8AyVTWzlG3colFgLLpn/lM95+MACFA0IQC5D08d5gCDiVL7psSJUBOnqLabnSIAIUc+cllugqu767R9U/3anI2u82mPzLUmdk6zrrpMzXzLAIUjwDJ35xnys8VZmQ2kgCZE2VqoP2NCBACBE0qQIbOx2t0U19PkgD5BXTLCMIfEKA6XgbvZvcdPe21WiWneh1SarZxh/FjUx9HgGIRoAfDPM9xC5AuvXdWkhV/fmWp7VIQIIAGFiBNBOYTP3NbkgRIGzFT9l3tXMNOkTS2AAVvICvbIWy6RYFualvV0aeZmd3NG2xmzkeAYhGgFYbNS4+stgB513JdrVe91qUAZVPHIEDQlALkPEBdqT/5zNN/LikC5L7JpC+oZVB3vQqQjIL9zdAJ3Vvruuu9la+LkgU6pjpnWiG0uj/b+iEEKLwA6TSXz+7tNRGgvmzmqz7t3KIw2680igBp/TL3IdWt/wA1ESBNNmccBcqmX9DYi6QIkK4C8hnm1jf6/ZpVgHSFlHGj0O4xr6xl3TUFyMqU7Lk1GIXa16djfLiasXDNMgKke3qFG61L7RZFgLR9kan9f/rEAs1rWgHKpaaE2boIoG4FSHNr2D1E5lgIaRjuXzwz/YFIYtO93WurIUDO78imf+G3M7z8xj1C/4YAy3Xrdwosc0Y9jPAVXZc5g/fT5fLBVES83Q1NjdnEo26XELT+N+QqsFz6McPnngpaJi+PUqT38s0ELQHOlqNI632eqZ+FfSnQOjTcPenVSRQgzcFUT+lQAIJXfhm69Rlm/o3NefRN1y9ZmGbp1ey6uhon6Pn0b70362uCPuBxCJDzvf4ZZ9fpcmvdfiH4G2Nrymn8pfGVOfG3JFKAZPTL5xz/0d22fX+/TGHIdhNfqdRIkQYa+9Tda5dnd3lXVZ+jY9Jb+Haw0mFLR36QVc4a3eLD3e3+VnnD/nXTjgBJLJVPeopzytUrff5UWIxTuBGW1JfYAkVl6k+alNOi3dzc21T3wf5s+uAkCpBm2dZ7aVwNnEt/puR3Sy45psqgZnjSsMyYFTeXOkCnsDTWQo9yS0+XzEyN0zfwEg1Nn3QSeXngO1QMNIBag4/1c852BjIUrXk15O/mawzFaPHQhqIaAuT8Dk2775N5dmRjSmmY52in5m6m2bKldrj6e/SBl6mZwzUXjE4BFnxuahIFyJNbv521n9IORK6x1Vlmnm3d1dmoUspH/v0zzoooubeVqLuuJPte10q3M0qfIx3gqeWPdFY+M13roDbIYZZZe1PBS/zrTeZ/KgL6XOn2DFrvnfov9U2fB7nW/XW0TbdyKIht6g8y4tGQI0DZ9Bf9yzN1+2BX6puaj0k7YT2cdAeyj5snKQsDJsJ8Wr9H9/UaaetKtjMq9hJsXyqxphsXmZox0NW640g7p6sTtY7o/Xc28XVftDbYiEw9CpB3jt/7rKJ9SZ9B555I+6DthK4Qdvfhy9zt/c2p9MRQu1Egnyynho64teybqzzk8rcPxL8bdmpGtQTI6fTdeKAnYv0NMk2SRAHyphBOCf27QwatBhp+N+8GH8fxvHbaKhVW9SaX+dSI+MV5aNbpZhQg75m+M4YyXKOrteSfTwb5+2XdE99Z8rpESP1yh0U41pb73noVIN0SpHBBgkXbfju9MNQMyeOwZ6BGWN62gpxPYzB02D7ktgSFx3LtfIMEk8YpQO5IkK4ykpGc6L9hgwYSlxvqrWcBGj5iz9d5IxNhOu9jKlV3nd+XTd9XIQly32Cz6U6ba9LRQPnsZTF9/2K9V0Gmzxp1KwxdTq1CGqEc7+jrSu3gjSh1BvmMjtAFam9E7uXvX4zhXj8k59onqSNAznm60j8K+dsX0AtDbUeBZEsAn3w+G+N4dBjT5pzO9JArEGtDPBQLdbftcm9ElRSglxvvls9qMGuI37BSt2MI0pjWuwB5HdFbpDP9o8WI131adpWuuxovU2ZKIvohjbv1dcl0oNe5bLD/zsz9OuppE9DdyLvB6zSzxkNZibcIu4xM7L1JG+Fu33N5mc8u0tgUi+diKy9j9aoQdWu+TAcdGHTKtZ4FyBuRPVpfGgL+9kH9e5vYOIDKdSSOsMiKn2z6r+7bviSZkyBEzZIcNPDXhEqMxr+4eXa0Yd90aas3TyyrPTJXaCyGCoPKjO33eAI0q+iIaQm7Tos5b5ASZOvFvhSOcA3qcK429jrPbbsE2olfMFy/rrIIe806cmMskwgruPpmZiZ5wakLCzr3Vd4o0ZkafBrmHoZqwHWbgk0DkJc69cyNwdoQkwSt0+mtcNfXsqW7c7zzMvCwoYNY6caDyH+XEQVJrvjhMN/jjTwV3Wtd/h2qM3PiNArOJYsZQnaMBxSfK1hw9yZ1T2LsvPiyhwriBIe96a3r9BktF4isKwg1ZYITJ6Z11s1lNktHYRZ2T9osrIhrWynnm+sENRff51VevfytjKYfFiZjuTfKUniPp0cUoOlFbY5kdw7/PGp9T5/iCOimZbDB25vvEo2ftFkUA9BQaOXXKaawjU09oG9tGwPEa5wTp2ZlIJKjv7/aS89Hyt/pxAoCWjXY0uY8eu3aaGveEi8Z5jqflUcXxHXtOjLn1hu75dxQIETyklFudWUt0VW2SW/n4qrr1FYAgNhGfpyRgE1GUoJON5Z8k3enrlYYJKifUgcAAIDayU8uc6ghTuek+OTKHNAZZSoYAAAAIDReXqL+Ajl5sVRSRlvceLhiAbJdFg8AAAAQCxJcOS3ulTCFaFI8RoAAAACgbtDVgpXeBNUnJcFySh8AAABqI0Bd6XsrvfGiBFj/wLSxKaUPAAAAtRGgXPqR4mR/qX/GdX4vb9VThkzoh1H6AAAAUBMkH89NPtma94l6bi/j8AJTZnLdbJPSBwAAgJogMnJcqX27NPmc7Tk1K6+I1U+cLV8Mm1RqBmJKHgAAAGqGLIPf3N3uwnfbihedrQ0kL5DmC5ItHb4i20vsvvGQbUr6s+mDnf3v3C09Hi21BYb+LaUOAAAANUc3ufTbsiLGY1HYfbQAAAAAKoK3ZcWiCohPv4wOHUXMDwAAANQlzm732dQhIi13aKxOBOkZ7M+lL9SRJXapBgAAgMSgu4Lrju6SE2iG/PNskZrfy0jODfLP2yR79N8lSPpvEhd0rcQFzdPEiRow3ZfNfHVxZ+sE3VmeEgQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACoEf8PC5vzecNZxDkAAAAASUVORK5CYII==", @@ -791,14 +540,10 @@ "$oid": "65fc1488e519d4a3b71193e4" }, "createdAt": { - "$date": { - "$numberLong": "1711019144780" - } + "$date": "2024-03-21T11:05:44.780Z" }, "updatedAt": { - "$date": { - "$numberLong": "1711019144780" - } + "$date": "2024-03-21T11:05:44.780Z" }, "name": "Product Test Onlinediagnose Grundschule - Deutsch", "url": "https://onlinediagnose.westermann.de/", @@ -863,14 +608,10 @@ "$oid": "65fc15b5e519d4a3b71193e5" }, "createdAt": { - "$date": { - "$numberLong": "1711019445098" - } + "$date": "2024-03-21T11:10:45.098Z" }, "updatedAt": { - "$date": { - "$numberLong": "1711019445098" - } + "$date": "2024-03-21T11:10:45.098Z" }, "name": "Product Test Onlinediagnose Grundschule - Mathematik", "url": "https://onlinediagnose.westermann.de/", @@ -935,14 +676,10 @@ "$oid": "65fd9736cb3d21d77bee50a6" }, "createdAt": { - "$date": { - "$numberLong": "1711118134160" - } + "$date": "2024-03-22T14:35:34.160Z" }, "updatedAt": { - "$date": { - "$numberLong": "1711358224688" - } + "$date": "2024-03-25T09:17:04.688Z" }, "name": "OpenStreetMap", "url": "https://www.openstreetmap.org/", @@ -993,40 +730,49 @@ }, { "_id": { - "$oid": "65fad93bbe8ce15df1279d9b" + "$oid": "65fd9dabcb3d21d77bee50ae" }, "createdAt": { - "$date": { - "$numberLong": "1710938427057" - } + "$date": "2024-03-22T15:03:07.052Z" }, "updatedAt": { - "$date": { - "$numberLong": "1711358019585" - } + "$date": "2024-03-22T15:03:07.052Z" }, - "name": "OSM Route", - "url": "https://www.openstreetmap.org/", - "logoUrl": "https://wiki.openstreetmap.org/w/images/7/7e/Logo_by_hind_128x128.png?20100124154543", - "logoBase64": "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", + "name": "Übersetzer", + "url": "https://translate.google.com/", + "logoUrl": "https://cdn1.iconfinder.com/data/icons/google-s-logo/150/Google_Icons-09-512.png", + "logoBase64": "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", "config_type": "basic", - "config_baseUrl": "https://wiki.openstreetmap.org/w/images/c/c8/Public-images-osm_logo.png", + "config_baseUrl": "https://translate.google.com/", "parameters": [ { - "name": "from", - "displayName": "Start", - "description": "", - "scope": "context", + "name": "op", + "displayName": "Operation", + "description": "Operation der Anwendung", + "default": "op", + "scope": "global", "location": "query", "type": "string", "isOptional": false, "isProtected": false }, { - "name": "to", - "displayName": "Ziel", - "description": "", - "scope": "context", + "name": "sl", + "displayName": "Quell-Sprache", + "description": "geben Sie die Quell-Sprache ein", + "default": "de", + "scope": "global", + "location": "query", + "type": "string", + "isOptional": false, + "isProtected": false + }, + { + "name": "tl", + "displayName": "Ziel-Sprache", + "description": "Geben Sie die Ziel-Sprache ein", + "default": "en", + "scope": "global", "location": "query", "type": "string", "isOptional": false, @@ -1035,59 +781,100 @@ ], "isHidden": false, "isDeactivated": false, + "openNewTab": true, + "version": 1, + "restrictToContexts": [] + }, + { + "_id": { + "$oid": "6667ec1c243527c9139bd799" + }, + "createdAt": { + "$date": "2023-11-30T15:28:04.733Z" + }, + "updatedAt": { + "$date": "2023-11-30T15:32:42.888Z" + }, + "name": "CY Test Tool 1", + "config_type": "basic", + "config_baseUrl": "https://google.com/search", + "parameters": [], + "isHidden": false, "openNewTab": false, - "version": 3, + "version": 1, + "isDeactivated": false, "restrictToContexts": [] }, { "_id": { - "$oid": "65fd9dabcb3d21d77bee50ae" + "$oid": "6667ec58243527c9139bd79b" }, "createdAt": { - "$date": { - "$numberLong": "1711119787052" - } + "$date": "2023-11-30T15:28:04.733Z" }, "updatedAt": { - "$date": { - "$numberLong": "1711119787052" - } + "$date": "2023-11-30T15:32:42.888Z" }, - "name": "Übersetzer", - "url": "https://translate.google.com/", - "logoUrl": "https://cdn1.iconfinder.com/data/icons/google-s-logo/150/Google_Icons-09-512.png", - "logoBase64": "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", + "name": "CY Test Tool Optional Parameters", "config_type": "basic", - "config_baseUrl": "https://translate.google.com/", + "config_baseUrl": "https://google.com/search", "parameters": [ { - "name": "op", - "displayName": "Operation", - "description": "Operation der Anwendung", - "default": "op", - "scope": "global", - "location": "query", + "name": "schoolParam", + "displayName": "school parameter", + "description": "", + "scope": "school", + "location": "path", "type": "string", - "isOptional": false, + "isOptional": true, "isProtected": false }, { - "name": "sl", - "displayName": "Quell-Sprache", - "description": "geben Sie die Quell-Sprache ein", - "default": "de", - "scope": "global", + "name": "contextParam", + "displayName": "context parameter", + "description": "", + "scope": "context", "location": "query", "type": "string", + "isOptional": true, + "isProtected": false + } + ], + "isHidden": false, + "openNewTab": false, + "version": 1, + "isDeactivated": false, + "restrictToContexts": [] + }, + { + "_id": { + "$oid": "6667ec85243527c9139bd79d" + }, + "createdAt": { + "$date": "2023-11-30T15:28:04.733Z" + }, + "updatedAt": { + "$date": "2023-11-30T15:32:42.888Z" + }, + "name": "CY Test Tool Required Parameters", + "config_type": "basic", + "config_baseUrl": "https://google.com/search", + "parameters": [ + { + "name": "schoolParam", + "displayName": "school parameter", + "description": "", + "scope": "school", + "location": "path", + "type": "string", "isOptional": false, "isProtected": false }, { - "name": "tl", - "displayName": "Ziel-Sprache", - "description": "Geben Sie die Ziel-Sprache ein", - "default": "en", - "scope": "global", + "name": "contextParam", + "displayName": "context parameter", + "description": "", + "scope": "context", "location": "query", "type": "string", "isOptional": false, @@ -1095,9 +882,120 @@ } ], "isHidden": false, + "openNewTab": false, + "version": 1, "isDeactivated": false, - "openNewTab": true, + "restrictToContexts": [] + }, + { + "_id": { + "$oid": "66682949ea0c14353cec2054" + }, + "createdAt": { + "$date": "2023-11-30T15:28:04.733Z" + }, + "updatedAt": { + "$date": "2023-11-30T15:32:42.888Z" + }, + "name": "CY Test Tool 2", + "config_type": "basic", + "config_baseUrl": "https://google.com/search", + "parameters": [], + "isHidden": false, + "openNewTab": false, + "version": 1, + "isDeactivated": false, + "restrictToContexts": [] + }, + { + "_id": { + "$oid": "666829b6ea0c14353cec2056" + }, + "createdAt": { + "$date": "2023-11-30T15:28:04.733Z" + }, + "updatedAt": { + "$date": "2023-11-30T15:32:42.888Z" + }, + "name": "CY Test Tool Hidden", + "config_type": "basic", + "config_baseUrl": "https://google.com/search", + "parameters": [], + "isHidden": true, + "openNewTab": false, "version": 1, + "isDeactivated": false, "restrictToContexts": [] + }, + { + "_id": { + "$oid": "667e4fe648ea6a22a5474359" + }, + "createdAt": { + "$date": "2023-04-27T09:51:15.592Z" + }, + "updatedAt": { + "$date": "2023-04-27T09:51:15.592Z" + }, + "name": "CY Test Tool Course Restriction", + "url": "https://google.de/", + "config_type": "basic", + "config_baseUrl": "https://google.de/", + "parameters": [], + "isHidden": false, + "openNewTab": true, + "version": 1, + "isDeactivated": false, + "restrictToContexts": [ + "course" + ] + }, + { + "_id": { + "$oid": "667e50f6162707ce02b9ac02" + }, + "createdAt": { + "$date": "2023-04-27T09:51:15.592Z" + }, + "updatedAt": { + "$date": "2023-04-27T09:51:15.592Z" + }, + "name": "CY Test Tool Media-Board Restriction", + "url": "https://google.de/", + "config_type": "basic", + "config_baseUrl": "https://google.de/", + "parameters": [], + "isHidden": false, + "openNewTab": true, + "version": 1, + "isDeactivated": false, + "restrictToContexts": [ + "media-board" + ] + }, + { + "_id": { + "$oid": "667e52a4162707ce02b9ac04" + }, + "createdAt": { + "$date": "2023-04-27T09:51:15.592Z" + }, + "updatedAt": { + "$date": "2023-04-27T09:51:15.592Z" + }, + "name": "CY Test Tool All Restrictions", + "url": "https://google.de/", + "config_type": "basic", + "config_baseUrl": "https://google.de/", + "parameters": [], + "isHidden": false, + "openNewTab": true, + "version": 1, + "isDeactivated": false, + "restrictToContexts": [ + "course", + "board-element", + "media-board" + ] } ] diff --git a/backup/setup/migrations.json b/backup/setup/migrations.json index 2a7feac0983..c09fc0c4799 100644 --- a/backup/setup/migrations.json +++ b/backup/setup/migrations.json @@ -107,6 +107,15 @@ "$date": "2024-05-17T14:00:42.414Z" } }, + { + "_id": { + "$oid": "6655e94f06722f2a434c135f" + }, + "name": "Migration20240528140356", + "created_at": { + "$date": "2024-05-28T14:25:19.577Z" + } + }, { "_id": { "$oid": "6656f4835290f6d36be31830" @@ -116,6 +125,15 @@ "$date": "2024-05-29T09:25:23.454Z" } }, + { + "_id": { + "$oid": "6668485aadfd9c4d7be91ca3" + }, + "name": "Migration20240611081033", + "created_at": { + "$date": "2024-06-11T12:51:38.379Z" + } + }, { "_id": { "$oid": "66684c3db14698848e23c0c2" @@ -152,24 +170,6 @@ "$date": "2024-06-12T12:26:01.665Z" } }, - { - "_id": { - "$oid": "6655e94f06722f2a434c135f" - }, - "name": "Migration20240528140356", - "created_at": { - "$date": "2024-05-28T14:25:19.577Z" - } - }, - { - "_id": { - "$oid": "6668485aadfd9c4d7be91ca3" - }, - "name": "Migration20240611081033", - "created_at": { - "$date": "2024-06-11T12:51:38.379Z" - } - }, { "_id": { "$oid": "667e611e207a39b02c306406" @@ -185,7 +185,7 @@ }, "name": "Migration20240719115036", "created_at": { - "$date": "2024-07-24T014:50:10.278Z" + "$date": "1970-01-01T00:00:00Z" } }, { @@ -214,5 +214,14 @@ "created_at": { "$date": "2024-08-23T15:25:05.360Z" } + }, + { + "_id": { + "$oid": "66fda9462a63b5749b3a64c9" + }, + "name": "Migration20240926205656", + "created_at": { + "$date": "2024-10-02T20:12:54.209Z" + } } ] diff --git a/backup/setup/school-external-tools.json b/backup/setup/school-external-tools.json index b9909e5a5c5..0f87303d154 100644 --- a/backup/setup/school-external-tools.json +++ b/backup/setup/school-external-tools.json @@ -50,29 +50,6 @@ "schoolParameters": [], "isDeactivated": false }, - { - "_id": { - "$oid": "65fd74c4d1c1ddf3bb2b05de" - }, - "createdAt": { - "$date": { - "$numberLong": "1711109316850" - } - }, - "updatedAt": { - "$date": { - "$numberLong": "1711109316850" - } - }, - "tool": { - "$oid": "65fd44ba09e6ffd0bae3b8d3" - }, - "school": { - "$oid": "5f2987e020834114b8efd6f8" - }, - "schoolParameters": [], - "isDeactivated": false - }, { "_id": { "$oid": "65fd9882cb3d21d77bee50a7" From f441161da1d2f33bc3bae0f803d866b5742aa47e Mon Sep 17 00:00:00 2001 From: Steliyan Dinkov <133751031+sdinkov@users.noreply.github.com> Date: Mon, 7 Oct 2024 13:10:21 +0200 Subject: [PATCH 21/23] N21 2202 fix shd school data cannot be updated (#5261) * update school rule * migration: Migration20240925165112 --------- Co-authored-by: Alexander Weber <103171324+alweber-cap@users.noreply.github.com> --- .../mikro-orm/Migration20240925165112.ts | 37 ++++++++++++++++++ .../rules/school.rule.spec.ts | 39 +++++++++++++++++-- .../authorization-rules/rules/school.rule.ts | 11 ++++-- .../domain/interface/permission.enum.ts | 1 + backup/setup/migrations.json | 9 +++++ backup/setup/roles.json | 3 +- 6 files changed, 91 insertions(+), 9 deletions(-) create mode 100644 apps/server/src/migrations/mikro-orm/Migration20240925165112.ts diff --git a/apps/server/src/migrations/mikro-orm/Migration20240925165112.ts b/apps/server/src/migrations/mikro-orm/Migration20240925165112.ts new file mode 100644 index 00000000000..144aad8cfd3 --- /dev/null +++ b/apps/server/src/migrations/mikro-orm/Migration20240925165112.ts @@ -0,0 +1,37 @@ +import { Migration } from '@mikro-orm/migrations-mongodb'; + +export class Migration20240925165112 extends Migration { + async up(): Promise { + const adminRoleUpdate = await this.getCollection('roles').updateOne( + { name: 'superhero' }, + { + $addToSet: { + permissions: { + $each: ['SCHOOL_EDIT_ALL'], + }, + }, + } + ); + + if (adminRoleUpdate.modifiedCount > 0) { + console.info('Permission SCHOOL_EDIT_ALL added to role superhero.'); + } + } + + async down(): Promise { + const adminRoleUpdate = await this.getCollection('roles').updateOne( + { name: 'superhero' }, + { + $pull: { + permissions: { + $in: ['SCHOOL_EDIT_ALL'], + }, + }, + } + ); + + if (adminRoleUpdate.modifiedCount > 0) { + console.info('Rollback: Permission SCHOOL_EDIT_ALL added to role superhero.'); + } + } +} diff --git a/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts index 319a0b86790..574d2dfd1f8 100644 --- a/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts @@ -1,3 +1,4 @@ +import { Permission } from '@shared/domain/interface/permission.enum'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { schoolFactory } from '@modules/school/testing/school.factory'; import { Test, TestingModule } from '@nestjs/testing'; @@ -13,11 +14,12 @@ describe('SchoolRule', () => { let rule: SchoolRule; let authorizationHelper: DeepMocked; let injectionService: AuthorizationInjectionService; + let module: TestingModule; beforeAll(async () => { await setupEntities(); - const module: TestingModule = await Test.createTestingModule({ + module = await Test.createTestingModule({ providers: [ SchoolRule, { provide: AuthorizationHelper, useValue: createMock() }, @@ -33,10 +35,19 @@ describe('SchoolRule', () => { const setupSchoolAndUser = () => { const school = schoolFactory.build(); const user = userFactory.build({ school: schoolEntityFactory.buildWithId(undefined, school.id) }); + const superUser = userFactory.asSuperhero([Permission.SCHOOL_EDIT_ALL]).build(); - return { school, user }; + return { school, user, superUser }; }; + afterEach(() => { + jest.clearAllMocks(); + }); + + afterAll(async () => { + await module.close(); + }); + describe('constructor', () => { it('should inject into AuthorizationInjectionService', () => { expect(injectionService.getAuthorizationRules()).toContain(rule); @@ -103,7 +114,7 @@ describe('SchoolRule', () => { const { user, school } = setupSchoolAndUser(); const context = AuthorizationContextBuilder.read([]); - authorizationHelper.hasAllPermissions.mockReturnValueOnce(false); + authorizationHelper.hasAllPermissions.mockReturnValue(false); return { user, school, context }; }; @@ -123,7 +134,7 @@ describe('SchoolRule', () => { const someOtherSchool = schoolFactory.build(); const context = AuthorizationContextBuilder.read([]); - authorizationHelper.hasAllPermissions.mockReturnValueOnce(true); + authorizationHelper.hasAllPermissions.mockReturnValueOnce(false); return { user, someOtherSchool, context }; }; @@ -136,5 +147,25 @@ describe('SchoolRule', () => { expect(result).toBe(false); }); }); + + describe('when the user has super powers', () => { + const setup = () => { + const { superUser } = setupSchoolAndUser(); + const someOtherSchool = schoolFactory.build(); + const context = AuthorizationContextBuilder.read([]); + + authorizationHelper.hasAllPermissions.mockReturnValueOnce(true); + + return { superUser, someOtherSchool, context }; + }; + + it('should return true', () => { + const { superUser, someOtherSchool, context } = setup(); + + const result = rule.hasPermission(superUser, someOtherSchool, context); + + expect(result).toBe(true); + }); + }); }); }); diff --git a/apps/server/src/modules/authorization-rules/rules/school.rule.ts b/apps/server/src/modules/authorization-rules/rules/school.rule.ts index 2bc70f85a5e..5018fc9c4f1 100644 --- a/apps/server/src/modules/authorization-rules/rules/school.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/school.rule.ts @@ -6,6 +6,7 @@ import { AuthorizationInjectionService, Rule, } from '@src/modules/authorization'; +import { Permission } from '@shared/domain/interface/permission.enum'; import { School } from '@src/modules/school/domain/do'; @Injectable() @@ -24,11 +25,13 @@ export class SchoolRule implements Rule { } public hasPermission(user: User, school: School, context: AuthorizationContext): boolean { - const hasRequiredPermissions = this.authorizationHelper.hasAllPermissions(user, context.requiredPermissions); - + let hasPermission = false; const isUsersSchool = user.school.id === school.id; - - const hasPermission = hasRequiredPermissions && isUsersSchool; + if (isUsersSchool) { + hasPermission = this.authorizationHelper.hasAllPermissions(user, context.requiredPermissions); + } else { + hasPermission = this.authorizationHelper.hasAllPermissions(user, [Permission.SCHOOL_EDIT_ALL]); + } return hasPermission; } diff --git a/apps/server/src/shared/domain/interface/permission.enum.ts b/apps/server/src/shared/domain/interface/permission.enum.ts index 70d57507164..24b1536a4d2 100644 --- a/apps/server/src/shared/domain/interface/permission.enum.ts +++ b/apps/server/src/shared/domain/interface/permission.enum.ts @@ -103,6 +103,7 @@ export enum Permission { SCHOOL_CHAT_MANAGE = 'SCHOOL_CHAT_MANAGE', SCHOOL_CREATE = 'SCHOOL_CREATE', SCHOOL_EDIT = 'SCHOOL_EDIT', + SCHOOL_EDIT_ALL = 'SCHOOL_EDIT_ALL', SCHOOL_LOGO_MANAGE = 'SCHOOL_LOGO_MANAGE', SCHOOL_NEWS_EDIT = 'SCHOOL_NEWS_EDIT', SCHOOL_PERMISSION_CHANGE = 'SCHOOL_PERMISSION_CHANGE', diff --git a/backup/setup/migrations.json b/backup/setup/migrations.json index c09fc0c4799..225324cd518 100644 --- a/backup/setup/migrations.json +++ b/backup/setup/migrations.json @@ -215,6 +215,15 @@ "$date": "2024-08-23T15:25:05.360Z" } }, + { + "_id": { + "$oid": "66f440bf0dbeeb6747a4242c" + }, + "name": "Migration20240925165112", + "created_at": { + "$date": "2024-09-25T16:56:31.889Z" + } + }, { "_id": { "$oid": "66fda9462a63b5749b3a64c9" diff --git a/backup/setup/roles.json b/backup/setup/roles.json index 94454494dba..01fc0562cce 100644 --- a/backup/setup/roles.json +++ b/backup/setup/roles.json @@ -203,7 +203,8 @@ "ACCOUNT_DELETE", "USER_LOGIN_MIGRATION_FORCE", "USER_LOGIN_MIGRATION_ROLLBACK", - "INSTANCE_VIEW" + "INSTANCE_VIEW", + "SCHOOL_EDIT_ALL" ], "__v": 2 }, From 728262ed7576a2914f8599bb9b841ad3cf8d73dd Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Fri, 18 Oct 2024 11:45:54 +0200 Subject: [PATCH 22/23] review comments --- .../domain/authorization-reference.service.ts | 2 +- .../domain/reference.loader.spec.ts | 2 +- .../domain/reference.loader.ts | 12 ++++++------ .../authorization-rules.config.ts | 5 ++--- .../authorization-rules.module.ts | 2 +- .../rules/course-group.rule.spec.ts | 2 +- .../authorization-rules/rules/course-group.rule.ts | 6 +++--- .../authorization-rules/rules/course.rule.spec.ts | 2 +- .../authorization-rules/rules/course.rule.ts | 10 +++++----- .../authorization-rules/rules/group.rule.spec.ts | 14 +++++++------- .../authorization-rules/rules/group.rule.ts | 9 ++------- .../rules/instance.rule.spec.ts | 10 +++++----- .../authorization-rules/rules/instance.rule.ts | 10 +++++----- .../rules/legacy-school.rule.spec.ts | 2 +- .../rules/legacy-school.rule.ts | 7 +------ .../authorization-rules/rules/lesson.rule.spec.ts | 14 +++++++------- .../authorization-rules/rules/lesson.rule.ts | 6 +++--- .../rules/school-system-options.rule.spec.ts | 10 +++++----- .../rules/school-system-options.rule.ts | 7 +------ .../authorization-rules/rules/school.rule.spec.ts | 10 +++++----- .../authorization-rules/rules/school.rule.ts | 9 ++------- .../rules/submission.rule.spec.ts | 12 ++++++------ .../authorization-rules/rules/submission.rule.ts | 2 +- .../authorization-rules/rules/system.rule.spec.ts | 10 +++++----- .../authorization-rules/rules/system.rule.ts | 8 ++++---- .../authorization-rules/rules/task.rule.spec.ts | 2 +- .../modules/authorization-rules/rules/task.rule.ts | 6 +++--- .../authorization-rules/rules/team.rule.spec.ts | 8 ++++---- .../modules/authorization-rules/rules/team.rule.ts | 7 +------ .../rules/user-login-migration.rule.spec.ts | 10 +++++----- .../rules/user-login-migration.rule.ts | 7 +------ .../authorization-rules/rules/user.rule.spec.ts | 2 +- .../modules/authorization-rules/rules/user.rule.ts | 7 +------ .../authorization/domain/service/rule-manager.ts | 2 +- .../modules/learnroom/uc/course-copy.uc.spec.ts | 2 +- .../src/modules/learnroom/uc/course-copy.uc.ts | 2 +- .../modules/learnroom/uc/course-export.uc.spec.ts | 2 +- .../src/modules/learnroom/uc/course-export.uc.ts | 4 ++-- .../modules/lesson/service/lesson.service.spec.ts | 2 +- .../src/modules/lesson/service/lesson.service.ts | 2 +- .../service/team-authorisable.service.spec.ts | 2 +- .../teams/service/team-authorisable.service.ts | 8 ++++---- apps/server/src/modules/teams/teams.module.ts | 4 ++-- .../context-external-tool.rule.spec.ts | 2 +- .../authorisation/context-external-tool.rule.ts | 7 +------ .../context-external-tool.module.ts | 2 +- ...text-external-tool-authorizable.service.spec.ts | 2 +- .../authorization/external-tool.rule.spec.ts | 2 +- .../authorization/external-tool.rule.ts | 7 +------ .../tool/external-tool/external-tool.module.ts | 2 +- .../external-tool-authorizable.service.spec.ts | 2 +- .../school-external-tool.rule.spec.ts | 2 +- .../authorization/school-external-tool.rule.ts | 7 +------ .../school-external-tool.module.ts | 2 +- .../school-external-tool-authorizable.service.ts | 8 ++++---- ...ool-external-tool-authorization.service.spec.ts | 6 +++--- 56 files changed, 131 insertions(+), 182 deletions(-) diff --git a/apps/server/src/modules/authorization-reference/domain/authorization-reference.service.ts b/apps/server/src/modules/authorization-reference/domain/authorization-reference.service.ts index 4c50986a0c9..8ab8843ba2c 100644 --- a/apps/server/src/modules/authorization-reference/domain/authorization-reference.service.ts +++ b/apps/server/src/modules/authorization-reference/domain/authorization-reference.service.ts @@ -5,7 +5,7 @@ import { AuthorizationService, ForbiddenLoggableException, AuthorizableReferenceType, -} from '@src/modules/authorization'; +} from '@modules/authorization'; import { ReferenceLoader } from './reference.loader'; /** diff --git a/apps/server/src/modules/authorization-reference/domain/reference.loader.spec.ts b/apps/server/src/modules/authorization-reference/domain/reference.loader.spec.ts index f4c24447d62..eac81888b35 100644 --- a/apps/server/src/modules/authorization-reference/domain/reference.loader.spec.ts +++ b/apps/server/src/modules/authorization-reference/domain/reference.loader.spec.ts @@ -1,12 +1,12 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@modules/authorization'; import { InstanceService } from '@modules/instance'; import { NotImplementedException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { EntityId } from '@shared/domain/types'; import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; import { setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationInjectionService, AuthorizableReferenceType } from '@src/modules/authorization'; import { ReferenceLoader } from './reference.loader'; describe('reference.loader', () => { diff --git a/apps/server/src/modules/authorization-reference/domain/reference.loader.ts b/apps/server/src/modules/authorization-reference/domain/reference.loader.ts index 93243a29339..c2bd031baac 100644 --- a/apps/server/src/modules/authorization-reference/domain/reference.loader.ts +++ b/apps/server/src/modules/authorization-reference/domain/reference.loader.ts @@ -1,15 +1,15 @@ // TODO fix modules circular dependency +import { + AuthorizableReferenceType, + AuthorizationInjectionService, + AuthorizationLoaderService, +} from '@modules/authorization'; +import { InstanceService } from '@modules/instance'; import { Injectable, NotImplementedException } from '@nestjs/common'; import { AuthorizableObject } from '@shared/domain/domain-object'; import { BaseDO } from '@shared/domain/domainobject'; import { EntityId } from '@shared/domain/types'; import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; -import { - AuthorizationInjectionService, - AuthorizationLoaderService, - AuthorizableReferenceType, -} from '@src/modules/authorization/domain'; -import { InstanceService } from '../../instance'; @Injectable() export class ReferenceLoader { diff --git a/apps/server/src/modules/authorization-rules/authorization-rules.config.ts b/apps/server/src/modules/authorization-rules/authorization-rules.config.ts index d01b979a80a..536a4e4fbac 100644 --- a/apps/server/src/modules/authorization-rules/authorization-rules.config.ts +++ b/apps/server/src/modules/authorization-rules/authorization-rules.config.ts @@ -1,3 +1,2 @@ -import { LoggerConfig } from '@src/core/logger'; - -export interface AuthorizationRulesConfig extends LoggerConfig {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface AuthorizationRulesConfig {} diff --git a/apps/server/src/modules/authorization-rules/authorization-rules.module.ts b/apps/server/src/modules/authorization-rules/authorization-rules.module.ts index 2c6134ddb2d..8289aa5c7ca 100644 --- a/apps/server/src/modules/authorization-rules/authorization-rules.module.ts +++ b/apps/server/src/modules/authorization-rules/authorization-rules.module.ts @@ -18,7 +18,7 @@ import { } from './rules'; @Module({ - imports: [AuthorizationModule /* FeathersModule */ /* LoggerModule */], + imports: [AuthorizationModule], providers: [ // rules CourseGroupRule, diff --git a/apps/server/src/modules/authorization-rules/rules/course-group.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/course-group.rule.spec.ts index 6dce5a62991..1c197d3b6e2 100644 --- a/apps/server/src/modules/authorization-rules/rules/course-group.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/course-group.rule.spec.ts @@ -1,8 +1,8 @@ +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@modules/authorization'; import { Test, TestingModule } from '@nestjs/testing'; import { CourseGroup, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory, courseGroupFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; diff --git a/apps/server/src/modules/authorization-rules/rules/course-group.rule.ts b/apps/server/src/modules/authorization-rules/rules/course-group.rule.ts index 224cc259c0a..37fe4a7dc84 100644 --- a/apps/server/src/modules/authorization-rules/rules/course-group.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/course-group.rule.ts @@ -1,12 +1,12 @@ -import { Injectable } from '@nestjs/common'; -import { CourseGroup, User } from '@shared/domain/entity'; import { Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { Injectable } from '@nestjs/common'; +import { CourseGroup, User } from '@shared/domain/entity'; import { CourseRule } from './course.rule'; @Injectable() diff --git a/apps/server/src/modules/authorization-rules/rules/course.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/course.rule.spec.ts index 935aabd6661..97a29a0841c 100644 --- a/apps/server/src/modules/authorization-rules/rules/course.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/course.rule.spec.ts @@ -1,9 +1,9 @@ +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@modules/authorization'; import { courseFactory } from '@modules/learnroom/testing'; import { Test, TestingModule } from '@nestjs/testing'; import { Course, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { courseFactory as courseEntityFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { CourseRule } from './course.rule'; describe('CourseRule', () => { diff --git a/apps/server/src/modules/authorization-rules/rules/course.rule.ts b/apps/server/src/modules/authorization-rules/rules/course.rule.ts index e0890d2feb4..e0ee021c997 100644 --- a/apps/server/src/modules/authorization-rules/rules/course.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/course.rule.ts @@ -1,14 +1,14 @@ -import { Course } from '@modules/learnroom/domain'; -import { Injectable } from '@nestjs/common'; -import { Course as CourseEntity, User } from '@shared/domain/entity'; -import { Permission } from '@shared/domain/interface'; import { Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { Course } from '@modules/learnroom/domain'; +import { Injectable } from '@nestjs/common'; +import { Course as CourseEntity, User } from '@shared/domain/entity'; +import { Permission } from '@shared/domain/interface'; @Injectable() export class CourseRule implements Rule { diff --git a/apps/server/src/modules/authorization-rules/rules/group.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/group.rule.spec.ts index 50afff12426..37af3eccf6a 100644 --- a/apps/server/src/modules/authorization-rules/rules/group.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/group.rule.spec.ts @@ -1,16 +1,16 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { Test, TestingModule } from '@nestjs/testing'; -import { Role, SchoolEntity, User } from '@shared/domain/entity'; -import { Permission } from '@shared/domain/interface'; -import { groupFactory, roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; +import { ObjectId } from '@mikro-orm/mongodb'; import { Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, -} from '@src/modules/authorization'; -import { Group } from '@src/modules/group'; -import { ObjectId } from '@mikro-orm/mongodb'; +} from '@modules/authorization'; +import { Group } from '@modules/group'; +import { Test, TestingModule } from '@nestjs/testing'; +import { Role, SchoolEntity, User } from '@shared/domain/entity'; +import { Permission } from '@shared/domain/interface'; +import { groupFactory, roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; import { GroupRule } from './group.rule'; describe('GroupRule', () => { diff --git a/apps/server/src/modules/authorization-rules/rules/group.rule.ts b/apps/server/src/modules/authorization-rules/rules/group.rule.ts index da220685dd4..dc9c388c49b 100644 --- a/apps/server/src/modules/authorization-rules/rules/group.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/group.rule.ts @@ -1,12 +1,7 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; +import { Group } from '@modules/group'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; -import { Group } from '@src/modules/group'; @Injectable() export class GroupRule implements Rule { diff --git a/apps/server/src/modules/authorization-rules/rules/instance.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/instance.rule.spec.ts index 96540919f6e..de5e1147041 100644 --- a/apps/server/src/modules/authorization-rules/rules/instance.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/instance.rule.spec.ts @@ -1,14 +1,14 @@ import { DeepMocked } from '@golevelup/ts-jest'; -import { instanceFactory } from '@modules/instance/testing'; -import { Test, TestingModule } from '@nestjs/testing'; -import { Permission } from '@shared/domain/interface'; -import { setupEntities, userFactory } from '@shared/testing'; import { Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { instanceFactory } from '@modules/instance/testing'; +import { Test, TestingModule } from '@nestjs/testing'; +import { Permission } from '@shared/domain/interface'; +import { setupEntities, userFactory } from '@shared/testing'; import { InstanceRule } from './instance.rule'; describe(InstanceRule.name, () => { diff --git a/apps/server/src/modules/authorization-rules/rules/instance.rule.ts b/apps/server/src/modules/authorization-rules/rules/instance.rule.ts index 21c215b6e58..83d02c3c0b5 100644 --- a/apps/server/src/modules/authorization-rules/rules/instance.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/instance.rule.ts @@ -1,14 +1,14 @@ -import { Instance } from '@modules/instance'; -import { Injectable } from '@nestjs/common'; -import { User } from '@shared/domain/entity'; -import { RoleName } from '@shared/domain/interface'; import { Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { Instance } from '@modules/instance'; +import { Injectable } from '@nestjs/common'; +import { User } from '@shared/domain/entity'; +import { RoleName } from '@shared/domain/interface'; @Injectable() export class InstanceRule implements Rule { diff --git a/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts index a747e5cd13b..4760a35955d 100644 --- a/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.spec.ts @@ -1,8 +1,8 @@ import { ObjectId } from '@mikro-orm/mongodb'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@modules/authorization'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; import { legacySchoolDoFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { LegacySchoolRule } from './legacy-school.rule'; describe('LegacySchoolRule', () => { diff --git a/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts index b2121ebfa1a..791331fbce7 100644 --- a/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/legacy-school.rule.ts @@ -1,12 +1,7 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { LegacySchoolDo } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; /** * @deprecated because it uses the deprecated LegacySchoolDo. diff --git a/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts index bc26aad6626..338c4490e47 100644 --- a/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/lesson.rule.spec.ts @@ -1,4 +1,11 @@ import { DeepPartial } from '@mikro-orm/core'; +import { + Action, + AuthorizationContext, + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@modules/authorization'; import { NotImplementedException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { LessonEntity, User } from '@shared/domain/entity'; @@ -11,13 +18,6 @@ import { setupEntities, userFactory, } from '@shared/testing'; -import { - Action, - AuthorizationContext, - AuthorizationContextBuilder, - AuthorizationHelper, - AuthorizationInjectionService, -} from '@src/modules/authorization'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; diff --git a/apps/server/src/modules/authorization-rules/rules/lesson.rule.ts b/apps/server/src/modules/authorization-rules/rules/lesson.rule.ts index 71e7ad2d3e0..5fc1565fe01 100644 --- a/apps/server/src/modules/authorization-rules/rules/lesson.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/lesson.rule.ts @@ -1,12 +1,12 @@ -import { Injectable, NotImplementedException } from '@nestjs/common'; -import { Course, CourseGroup, LessonEntity, User } from '@shared/domain/entity'; import { Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { Injectable, NotImplementedException } from '@nestjs/common'; +import { Course, CourseGroup, LessonEntity, User } from '@shared/domain/entity'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; diff --git a/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.spec.ts index 6ac394ce455..49964341624 100644 --- a/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.spec.ts @@ -1,5 +1,10 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; +import { + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@modules/authorization'; import { SchoolSystemOptions } from '@modules/legacy-school'; import { SystemEntity } from '@modules/system/entity'; import { Test, TestingModule } from '@nestjs/testing'; @@ -12,11 +17,6 @@ import { systemEntityFactory, userFactory, } from '@shared/testing'; -import { - AuthorizationContextBuilder, - AuthorizationHelper, - AuthorizationInjectionService, -} from '@src/modules/authorization'; import { SchoolSystemOptionsRule } from './school-system-options.rule'; describe(SchoolSystemOptionsRule.name, () => { diff --git a/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.ts b/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.ts index d598e686881..10d04f634f6 100644 --- a/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/school-system-options.rule.ts @@ -1,12 +1,7 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { SchoolSystemOptions } from '@modules/legacy-school'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; @Injectable() export class SchoolSystemOptionsRule implements Rule { diff --git a/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts index 574d2dfd1f8..f3df85dc95b 100644 --- a/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/school.rule.spec.ts @@ -1,13 +1,13 @@ -import { Permission } from '@shared/domain/interface/permission.enum'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; -import { schoolFactory } from '@modules/school/testing/school.factory'; -import { Test, TestingModule } from '@nestjs/testing'; -import { schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; import { AuthorizationContextBuilder, AuthorizationHelper, AuthorizationInjectionService, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { schoolFactory } from '@modules/school/testing/school.factory'; +import { Test, TestingModule } from '@nestjs/testing'; +import { Permission } from '@shared/domain/interface/permission.enum'; +import { schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; import { SchoolRule } from './school.rule'; describe('SchoolRule', () => { diff --git a/apps/server/src/modules/authorization-rules/rules/school.rule.ts b/apps/server/src/modules/authorization-rules/rules/school.rule.ts index 5018fc9c4f1..5e0bf03e758 100644 --- a/apps/server/src/modules/authorization-rules/rules/school.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/school.rule.ts @@ -1,13 +1,8 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; +import { School } from '@modules/school'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; import { Permission } from '@shared/domain/interface/permission.enum'; -import { School } from '@src/modules/school/domain/do'; @Injectable() export class SchoolRule implements Rule { diff --git a/apps/server/src/modules/authorization-rules/rules/submission.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/submission.rule.spec.ts index f7905c5ce5e..2e38fee4b5e 100644 --- a/apps/server/src/modules/authorization-rules/rules/submission.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/submission.rule.spec.ts @@ -1,3 +1,9 @@ +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@modules/authorization'; import { NotImplementedException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; @@ -10,12 +16,6 @@ import { taskFactory, userFactory, } from '@shared/testing'; -import { - Action, - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, -} from '@src/modules/authorization'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; diff --git a/apps/server/src/modules/authorization-rules/rules/submission.rule.ts b/apps/server/src/modules/authorization-rules/rules/submission.rule.ts index 1492dd7db8a..f8d258860f3 100644 --- a/apps/server/src/modules/authorization-rules/rules/submission.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/submission.rule.ts @@ -6,7 +6,7 @@ import { AuthorizationHelper, AuthorizationInjectionService, Rule, -} from '@src/modules/authorization'; +} from '@modules/authorization'; import { TaskRule } from './task.rule'; @Injectable() diff --git a/apps/server/src/modules/authorization-rules/rules/system.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/system.rule.spec.ts index ad3482934de..6b9d2b72935 100644 --- a/apps/server/src/modules/authorization-rules/rules/system.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/system.rule.spec.ts @@ -1,4 +1,9 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@modules/authorization'; import { System } from '@modules/system'; import { SystemEntity } from '@modules/system/entity'; import { systemFactory } from '@modules/system/testing'; @@ -6,11 +11,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { SchoolEntity, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { schoolEntityFactory, setupEntities, systemEntityFactory, userFactory } from '@shared/testing'; -import { - AuthorizationContextBuilder, - AuthorizationHelper, - AuthorizationInjectionService, -} from '@src/modules/authorization'; import { SystemRule } from './system.rule'; describe(SystemRule.name, () => { diff --git a/apps/server/src/modules/authorization-rules/rules/system.rule.ts b/apps/server/src/modules/authorization-rules/rules/system.rule.ts index f9fa9502a3f..e12ea4fed7e 100644 --- a/apps/server/src/modules/authorization-rules/rules/system.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/system.rule.ts @@ -1,13 +1,13 @@ -import { System } from '@modules/system'; -import { Injectable } from '@nestjs/common'; -import { User } from '@shared/domain/entity'; import { Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { System } from '@modules/system'; +import { Injectable } from '@nestjs/common'; +import { User } from '@shared/domain/entity'; @Injectable() export class SystemRule implements Rule { diff --git a/apps/server/src/modules/authorization-rules/rules/task.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/task.rule.spec.ts index 13683e54851..844c11ceeec 100644 --- a/apps/server/src/modules/authorization-rules/rules/task.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/task.rule.spec.ts @@ -1,8 +1,8 @@ import { DeepPartial } from '@mikro-orm/core'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@modules/authorization'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission, RoleName } from '@shared/domain/interface'; import { courseFactory, lessonFactory, roleFactory, setupEntities, taskFactory, userFactory } from '@shared/testing'; -import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; diff --git a/apps/server/src/modules/authorization-rules/rules/task.rule.ts b/apps/server/src/modules/authorization-rules/rules/task.rule.ts index e59b379e422..dbe7650d8b8 100644 --- a/apps/server/src/modules/authorization-rules/rules/task.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/task.rule.ts @@ -1,12 +1,12 @@ -import { Injectable } from '@nestjs/common'; -import { Task, User } from '@shared/domain/entity'; import { Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { Injectable } from '@nestjs/common'; +import { Task, User } from '@shared/domain/entity'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; diff --git a/apps/server/src/modules/authorization-rules/rules/team.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/team.rule.spec.ts index 226e3d2f53d..4a7026c2789 100644 --- a/apps/server/src/modules/authorization-rules/rules/team.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/team.rule.spec.ts @@ -1,11 +1,11 @@ -import { Test, TestingModule } from '@nestjs/testing'; -import { Permission } from '@shared/domain/interface'; -import { roleFactory, setupEntities, teamFactory, userFactory } from '@shared/testing'; import { AuthorizationContextBuilder, AuthorizationHelper, AuthorizationInjectionService, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { Test, TestingModule } from '@nestjs/testing'; +import { Permission } from '@shared/domain/interface'; +import { roleFactory, setupEntities, teamFactory, userFactory } from '@shared/testing'; import { TeamRule } from './team.rule'; describe('TeamRule', () => { diff --git a/apps/server/src/modules/authorization-rules/rules/team.rule.ts b/apps/server/src/modules/authorization-rules/rules/team.rule.ts index f24f4646bc8..3042b56facd 100644 --- a/apps/server/src/modules/authorization-rules/rules/team.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/team.rule.ts @@ -1,11 +1,6 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { TeamEntity, TeamUserEntity, User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; @Injectable() export class TeamRule implements Rule { diff --git a/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.spec.ts index d21895fe194..4640e1ee176 100644 --- a/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.spec.ts @@ -1,15 +1,15 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; -import { Test, TestingModule } from '@nestjs/testing'; -import { UserLoginMigrationDO } from '@shared/domain/domainobject'; -import { Permission } from '@shared/domain/interface'; -import { schoolEntityFactory, setupEntities, userFactory, userLoginMigrationDOFactory } from '@shared/testing'; import { Action, AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { Test, TestingModule } from '@nestjs/testing'; +import { UserLoginMigrationDO } from '@shared/domain/domainobject'; +import { Permission } from '@shared/domain/interface'; +import { schoolEntityFactory, setupEntities, userFactory, userLoginMigrationDOFactory } from '@shared/testing'; import { UserLoginMigrationRule } from './user-login-migration.rule'; describe('UserLoginMigrationRule', () => { diff --git a/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.ts b/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.ts index cfc509662ff..f866571382c 100644 --- a/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/user-login-migration.rule.ts @@ -1,12 +1,7 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { UserLoginMigrationDO } from '@shared/domain/domainobject'; import { User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; @Injectable() export class UserLoginMigrationRule implements Rule { diff --git a/apps/server/src/modules/authorization-rules/rules/user.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/user.rule.spec.ts index 05785246050..62722af6ac9 100644 --- a/apps/server/src/modules/authorization-rules/rules/user.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/user.rule.spec.ts @@ -1,8 +1,8 @@ +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@modules/authorization'; import { Test, TestingModule } from '@nestjs/testing'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { UserRule } from './user.rule'; describe('UserRule', () => { diff --git a/apps/server/src/modules/authorization-rules/rules/user.rule.ts b/apps/server/src/modules/authorization-rules/rules/user.rule.ts index cdb7d5f1c94..195b39b50cd 100644 --- a/apps/server/src/modules/authorization-rules/rules/user.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/user.rule.ts @@ -1,11 +1,6 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; @Injectable() export class UserRule implements Rule { diff --git a/apps/server/src/modules/authorization/domain/service/rule-manager.ts b/apps/server/src/modules/authorization/domain/service/rule-manager.ts index 43d9bb6b40f..8dafe2c3ec4 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -10,7 +10,7 @@ export class RuleManager { constructor(private readonly authorizationInjectionService: AuthorizationInjectionService) {} public selectRule(user: User, object: AuthorizableObject | BaseDO, context: AuthorizationContext): Rule { - const rules = [...this.authorizationInjectionService.getAuthorizationRules()]; + const rules = this.authorizationInjectionService.getAuthorizationRules(); const selectedRules = rules.filter((rule) => rule.isApplicable(user, object, context)); const rule = this.matchSingleRule(selectedRules); diff --git a/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts index c9e69293197..826f1804bf3 100644 --- a/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts @@ -1,12 +1,12 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Configuration } from '@hpi-schul-cloud/commons'; import { AuthorizableReferenceType, AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationReferenceService } from '@modules/authorization-reference'; import { CopyElementType, CopyStatusEnum } from '@modules/copy-helper'; import { ForbiddenException, InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; import { courseFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationReferenceService } from '@src/modules/authorization-reference'; import { CourseCopyService } from '../service'; import { CourseCopyUC } from './course-copy.uc'; diff --git a/apps/server/src/modules/learnroom/uc/course-copy.uc.ts b/apps/server/src/modules/learnroom/uc/course-copy.uc.ts index 76279ce28bf..5d6bf898748 100644 --- a/apps/server/src/modules/learnroom/uc/course-copy.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course-copy.uc.ts @@ -1,10 +1,10 @@ import { Configuration } from '@hpi-schul-cloud/commons'; import { AuthorizableReferenceType, AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationReferenceService } from '@modules/authorization-reference'; import { CopyStatus } from '@modules/copy-helper'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; -import { AuthorizationReferenceService } from '@src/modules/authorization-reference'; import { CourseCopyService } from '../service'; @Injectable() diff --git a/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts b/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts index aea09428d4f..e8258de334c 100644 --- a/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts +++ b/apps/server/src/modules/learnroom/uc/course-export.uc.spec.ts @@ -1,11 +1,11 @@ import { faker } from '@faker-js/faker'; import { DeepMocked, createMock } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; +import { AuthorizationReferenceService } from '@modules/authorization-reference'; import { CommonCartridgeVersion } from '@modules/common-cartridge'; import { ForbiddenException, NotFoundException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Test, TestingModule } from '@nestjs/testing'; -import { AuthorizationReferenceService } from '@src/modules/authorization-reference'; import { LearnroomConfig } from '../learnroom.config'; import { CommonCartridgeExportService } from '../service/common-cartridge-export.service'; import { CourseExportUc } from './course-export.uc'; diff --git a/apps/server/src/modules/learnroom/uc/course-export.uc.ts b/apps/server/src/modules/learnroom/uc/course-export.uc.ts index 2971ad7ba5b..93b79f6d1ed 100644 --- a/apps/server/src/modules/learnroom/uc/course-export.uc.ts +++ b/apps/server/src/modules/learnroom/uc/course-export.uc.ts @@ -1,10 +1,10 @@ -import { AuthorizationContextBuilder, AuthorizableReferenceType } from '@modules/authorization'; +import { AuthorizableReferenceType, AuthorizationContextBuilder } from '@modules/authorization'; +import { AuthorizationReferenceService } from '@modules/authorization-reference'; import { CommonCartridgeVersion } from '@modules/common-cartridge'; import { Injectable, NotFoundException } from '@nestjs/common'; import { ConfigService } from '@nestjs/config'; import { Permission } from '@shared/domain/interface'; import { EntityId } from '@shared/domain/types'; -import { AuthorizationReferenceService } from '@src/modules/authorization-reference/domain'; import { LearnroomConfig } from '../learnroom.config'; import { CommonCartridgeExportService } from '../service/common-cartridge-export.service'; diff --git a/apps/server/src/modules/lesson/service/lesson.service.spec.ts b/apps/server/src/modules/lesson/service/lesson.service.spec.ts index 058707135a0..95aa50bc708 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.spec.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.spec.ts @@ -1,6 +1,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { MikroORM } from '@mikro-orm/core'; import { ObjectId } from '@mikro-orm/mongodb'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@modules/authorization'; import { DataDeletedEvent, DomainDeletionReportBuilder, @@ -15,7 +16,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { ComponentProperties, ComponentType } from '@shared/domain/entity'; import { lessonFactory, setupEntities } from '@shared/testing'; import { Logger } from '@src/core/logger'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; import { LessonRepo } from '../repository'; import { LessonService } from './lesson.service'; diff --git a/apps/server/src/modules/lesson/service/lesson.service.ts b/apps/server/src/modules/lesson/service/lesson.service.ts index 9fa84210ddc..ec33d569174 100644 --- a/apps/server/src/modules/lesson/service/lesson.service.ts +++ b/apps/server/src/modules/lesson/service/lesson.service.ts @@ -21,7 +21,7 @@ import { AuthorizableReferenceType, AuthorizationInjectionService, AuthorizationLoaderService, -} from '@src/modules/authorization'; +} from '@modules/authorization'; import { LessonRepo } from '../repository'; @Injectable() diff --git a/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts b/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts index ab992e81d78..1c6f8923a41 100644 --- a/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts +++ b/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts @@ -1,8 +1,8 @@ import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@modules/authorization'; import { Test, TestingModule } from '@nestjs/testing'; import { TeamsRepo } from '@shared/repo'; import { setupEntities, teamFactory } from '@shared/testing'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; import { TeamAuthorisableService } from './team-authorisable.service'; describe('team authorisable service', () => { diff --git a/apps/server/src/modules/teams/service/team-authorisable.service.ts b/apps/server/src/modules/teams/service/team-authorisable.service.ts index 5c86d638ef4..05061d3f83c 100644 --- a/apps/server/src/modules/teams/service/team-authorisable.service.ts +++ b/apps/server/src/modules/teams/service/team-authorisable.service.ts @@ -1,11 +1,11 @@ -import { Injectable } from '@nestjs/common'; -import { TeamEntity } from '@shared/domain/entity'; -import { TeamsRepo } from '@shared/repo'; import { AuthorizableReferenceType, AuthorizationInjectionService, AuthorizationLoaderServiceGeneric, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { Injectable } from '@nestjs/common'; +import { TeamEntity } from '@shared/domain/entity'; +import { TeamsRepo } from '@shared/repo'; @Injectable() export class TeamAuthorisableService implements AuthorizationLoaderServiceGeneric { diff --git a/apps/server/src/modules/teams/teams.module.ts b/apps/server/src/modules/teams/teams.module.ts index 39a6f29395d..ea9530a7881 100644 --- a/apps/server/src/modules/teams/teams.module.ts +++ b/apps/server/src/modules/teams/teams.module.ts @@ -1,9 +1,9 @@ +import { AuthorizationModule } from '@modules/authorization'; import { Module } from '@nestjs/common'; +import { CqrsModule } from '@nestjs/cqrs'; import { TeamsRepo } from '@shared/repo'; import { LoggerModule } from '@src/core/logger'; -import { CqrsModule } from '@nestjs/cqrs'; import { TeamAuthorisableService, TeamService } from './service'; -import { AuthorizationModule } from '../authorization'; @Module({ imports: [CqrsModule, LoggerModule, AuthorizationModule], diff --git a/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts index 5c471fb3122..01b7b97140e 100644 --- a/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.spec.ts @@ -1,4 +1,5 @@ import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@modules/authorization'; import { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; import { contextExternalToolEntityFactory } from '@modules/tool/context-external-tool/testing'; @@ -9,7 +10,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { ContextExternalToolRule } from './context-external-tool.rule'; describe('ContextExternalToolRule', () => { diff --git a/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.ts b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.ts index 9dc7128152a..cb552fefaa4 100644 --- a/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.ts +++ b/apps/server/src/modules/tool/context-external-tool/authorisation/context-external-tool.rule.ts @@ -1,13 +1,8 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { ContextExternalTool } from '@modules/tool/context-external-tool/domain'; import { ContextExternalToolEntity } from '@modules/tool/context-external-tool/entity'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; @Injectable() export class ContextExternalToolRule implements Rule { diff --git a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts index c4eced01902..e447ea3e2b9 100644 --- a/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts +++ b/apps/server/src/modules/tool/context-external-tool/context-external-tool.module.ts @@ -1,7 +1,7 @@ +import { AuthorizationModule } from '@modules/authorization'; import { UserLicenseModule } from '@modules/user-license'; import { forwardRef, Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; import { CommonToolModule } from '../common'; import { ExternalToolModule } from '../external-tool'; import { SchoolExternalToolModule } from '../school-external-tool'; diff --git a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts index ee5a057e268..72170d073da 100644 --- a/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts +++ b/apps/server/src/modules/tool/context-external-tool/service/context-external-tool-authorizable.service.spec.ts @@ -1,8 +1,8 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@modules/authorization'; import { Test, TestingModule } from '@nestjs/testing'; import { ContextExternalToolRepo } from '@shared/repo'; import { legacySchoolDoFactory } from '@shared/testing'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; import { SchoolExternalTool } from '../../school-external-tool/domain'; import { schoolExternalToolFactory } from '../../school-external-tool/testing'; import { ContextExternalTool } from '../domain'; diff --git a/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts index 3b81c335fc8..d31b9dd09d5 100644 --- a/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.spec.ts @@ -1,11 +1,11 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@modules/authorization'; import { ExternalTool } from '@modules/tool/external-tool/domain'; import { externalToolFactory } from '@modules/tool/external-tool/testing'; import { Test, TestingModule } from '@nestjs/testing'; import { User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { ExternalToolRule } from './external-tool.rule'; describe(ExternalToolRule.name, () => { diff --git a/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.ts b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.ts index 51c32f1905d..1ae116d7c1f 100644 --- a/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.ts +++ b/apps/server/src/modules/tool/external-tool/authorization/external-tool.rule.ts @@ -1,12 +1,7 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { ExternalTool } from '@modules/tool/external-tool/domain'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; @Injectable() export class ExternalToolRule implements Rule { diff --git a/apps/server/src/modules/tool/external-tool/external-tool.module.ts b/apps/server/src/modules/tool/external-tool/external-tool.module.ts index 4f7e8abd975..dfa73439c1f 100644 --- a/apps/server/src/modules/tool/external-tool/external-tool.module.ts +++ b/apps/server/src/modules/tool/external-tool/external-tool.module.ts @@ -1,9 +1,9 @@ import { EncryptionModule } from '@infra/encryption'; +import { AuthorizationModule } from '@modules/authorization'; import { OauthProviderServiceModule } from '@modules/oauth-provider'; import { HttpModule } from '@nestjs/axios'; import { Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; -import { AuthorizationModule } from '@src/modules/authorization'; import { InstanceModule } from '../../instance'; import { CommonToolModule } from '../common'; import { ToolContextMapper } from '../common/mapper/tool-context.mapper'; diff --git a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts index 02db2ea8074..4a43a0c7ca5 100644 --- a/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts +++ b/apps/server/src/modules/tool/external-tool/service/external-tool-authorizable.service.spec.ts @@ -1,7 +1,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@modules/authorization'; import { Test, TestingModule } from '@nestjs/testing'; import { ExternalToolRepo } from '@shared/repo'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; import { externalToolFactory } from '../testing'; import { ExternalToolAuthorizableService } from './external-tool-authorizable.service'; diff --git a/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts index a3c18229542..42c22849882 100644 --- a/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.spec.ts @@ -1,4 +1,5 @@ import { DeepMocked, createMock } from '@golevelup/ts-jest'; +import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@modules/authorization'; import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { schoolExternalToolEntityFactory, schoolExternalToolFactory } from '@modules/tool/school-external-tool/testing'; @@ -6,7 +7,6 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Role, User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { roleFactory, schoolEntityFactory, setupEntities, userFactory } from '@shared/testing'; -import { Action, AuthorizationHelper, AuthorizationInjectionService } from '@src/modules/authorization'; import { SchoolExternalToolRule } from './school-external-tool.rule'; describe('SchoolExternalToolRule', () => { diff --git a/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.ts b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.ts index 79d256138ce..8073b4714a1 100644 --- a/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.ts +++ b/apps/server/src/modules/tool/school-external-tool/authorization/school-external-tool.rule.ts @@ -1,13 +1,8 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { SchoolExternalTool } from '@modules/tool/school-external-tool/domain'; import { SchoolExternalToolEntity } from '@modules/tool/school-external-tool/entity'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { - AuthorizationContext, - AuthorizationHelper, - AuthorizationInjectionService, - Rule, -} from '@src/modules/authorization'; @Injectable() export class SchoolExternalToolRule implements Rule { diff --git a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts index e82c64e5326..2762cdeda73 100644 --- a/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts +++ b/apps/server/src/modules/tool/school-external-tool/school-external-tool.module.ts @@ -1,5 +1,5 @@ +import { AuthorizationModule } from '@modules/authorization'; import { forwardRef, Module } from '@nestjs/common'; -import { AuthorizationModule } from '@src/modules/authorization'; import { CommonToolModule } from '../common'; import { ExternalToolModule } from '../external-tool'; import { SchoolExternalToolRule } from './authorization/school-external-tool.rule'; diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts index 9fe2d563475..39de9ffa293 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts @@ -1,11 +1,11 @@ -import { Injectable } from '@nestjs/common'; -import { EntityId } from '@shared/domain/types'; -import { SchoolExternalToolRepo } from '@shared/repo'; import { AuthorizableReferenceType, AuthorizationInjectionService, AuthorizationLoaderServiceGeneric, -} from '@src/modules/authorization'; +} from '@modules/authorization'; +import { Injectable } from '@nestjs/common'; +import { EntityId } from '@shared/domain/types'; +import { SchoolExternalToolRepo } from '@shared/repo'; import { SchoolExternalTool } from '../domain'; @Injectable() diff --git a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts index 05c6776ca91..bb14eed177d 100644 --- a/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorization.service.spec.ts @@ -1,11 +1,11 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@modules/authorization'; import { Test, TestingModule } from '@nestjs/testing'; import { SchoolExternalToolRepo } from '@shared/repo'; import { legacySchoolDoFactory } from '@shared/testing'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; -import { SchoolExternalToolAuthorizableService } from './school-external-tool-authorizable.service'; -import { schoolExternalToolFactory } from '../testing'; import { SchoolExternalTool } from '../domain'; +import { schoolExternalToolFactory } from '../testing'; +import { SchoolExternalToolAuthorizableService } from './school-external-tool-authorizable.service'; describe('SchoolExternalToolAuthorizableService', () => { let module: TestingModule; From bcef9f17815b7d43bf70ec4027b94146085b05d5 Mon Sep 17 00:00:00 2001 From: Thomas Feldtkeller Date: Tue, 22 Oct 2024 10:12:52 +0200 Subject: [PATCH 23/23] more review fixes --- .../api/dto/authorization-body.params.ts | 2 +- .../src/modules/board/authorisation/board-node.rule.spec.ts | 2 +- .../board/service/board-node-authorizable.service.spec.ts | 2 +- .../src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/server/src/modules/authorization-reference/api/dto/authorization-body.params.ts b/apps/server/src/modules/authorization-reference/api/dto/authorization-body.params.ts index 10f0796d5f7..c411aefbd53 100644 --- a/apps/server/src/modules/authorization-reference/api/dto/authorization-body.params.ts +++ b/apps/server/src/modules/authorization-reference/api/dto/authorization-body.params.ts @@ -1,6 +1,6 @@ import { ApiProperty } from '@nestjs/swagger'; import { Permission } from '@shared/domain/interface'; -import { Action, AuthorizableReferenceType, AuthorizationContext } from '@src/modules/authorization'; +import { Action, AuthorizableReferenceType, AuthorizationContext } from '@modules/authorization'; import { Type } from 'class-transformer'; import { IsArray, IsEnum, IsMongoId, ValidateNested } from 'class-validator'; diff --git a/apps/server/src/modules/board/authorisation/board-node.rule.spec.ts b/apps/server/src/modules/board/authorisation/board-node.rule.spec.ts index faad700109c..2c580152b53 100644 --- a/apps/server/src/modules/board/authorisation/board-node.rule.spec.ts +++ b/apps/server/src/modules/board/authorisation/board-node.rule.spec.ts @@ -3,7 +3,7 @@ import { BoardNodeAuthorizable, BoardRoles } from '@modules/board'; import { Test, TestingModule } from '@nestjs/testing'; import { Permission } from '@shared/domain/interface'; import { roleFactory, setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationHelper, AuthorizationInjectionService, Action } from '@src/modules/authorization'; +import { AuthorizationHelper, AuthorizationInjectionService, Action } from '@modules/authorization'; import { BoardNodeRule } from './board-node.rule'; import { columnBoardFactory, drawingElementFactory, fileElementFactory, submissionItemFactory } from '../testing'; diff --git a/apps/server/src/modules/board/service/board-node-authorizable.service.spec.ts b/apps/server/src/modules/board/service/board-node-authorizable.service.spec.ts index 59d736dd1ac..10d768d1c73 100644 --- a/apps/server/src/modules/board/service/board-node-authorizable.service.spec.ts +++ b/apps/server/src/modules/board/service/board-node-authorizable.service.spec.ts @@ -1,7 +1,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { Test, TestingModule } from '@nestjs/testing'; import { setupEntities } from '@shared/testing'; -import { AuthorizableReferenceType, AuthorizationInjectionService } from '@src/modules/authorization'; +import { AuthorizableReferenceType, AuthorizationInjectionService } from '@modules/authorization'; import { columnBoardFactory, columnFactory } from '../testing'; import { BoardNodeAuthorizable, BoardRoles, UserWithBoardRoles } from '../domain'; import { BoardNodeRepo } from '../repo'; diff --git a/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts b/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts index 1274f3fb69b..671c6ad8b7a 100644 --- a/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts +++ b/apps/server/src/modules/tool/tool-launch/uc/tool-launch.uc.spec.ts @@ -6,7 +6,7 @@ import { Test, TestingModule } from '@nestjs/testing'; import { User } from '@shared/domain/entity'; import { Permission } from '@shared/domain/interface'; import { setupEntities, userFactory } from '@shared/testing'; -import { AuthorizationContextBuilder, AuthorizationService } from '@src/modules/authorization'; +import { AuthorizationContextBuilder, AuthorizationService } from '@modules/authorization'; import { ToolContextType } from '../../common/enum'; import { ToolPermissionHelper } from '../../common/uc/tool-permission-helper'; import { ContextExternalTool, ContextExternalToolLaunchable } from '../../context-external-tool/domain';