diff --git a/apps/server/src/apps/server.app.ts b/apps/server/src/apps/server.app.ts index 2409153fc89..f0fa2e4779e 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 { ContextExternalToolService } from '@src/modules/tool/context-external-tool'; import { CollaborativeStorageUc } from '@modules/collaborative-storage/uc/collaborative-storage.uc'; 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 97% 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 474a3eeb865..c411aefbd53 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 { ApiProperty } from '@nestjs/swagger'; import { Permission } from '@shared/domain/interface'; +import { Action, AuthorizableReferenceType, AuthorizationContext } from '@modules/authorization'; 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 50% rename from apps/server/src/modules/authorization/authorization-reference.module.ts rename to apps/server/src/modules/authorization-reference/authorization-reference.module.ts index 08a73a33aaf..817dc754ed8 100644 --- a/apps/server/src/modules/authorization/authorization-reference.module.ts +++ b/apps/server/src/modules/authorization-reference/authorization-reference.module.ts @@ -1,20 +1,9 @@ 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 { 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 { TeamsModule } from '../teams'; +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. @@ -22,10 +11,8 @@ 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, LoggerModule, InstanceModule], providers: [ - AuthorizationHelper, ReferenceLoader, UserRepo, CourseRepo, @@ -33,7 +20,6 @@ import { TeamsModule } from '../teams'; TaskRepo, LegacySchoolRepo, SubmissionRepo, - SchoolExternalToolRepo, AuthorizationReferenceService, ], exports: [AuthorizationReferenceService], 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..8ab8843ba2c 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 '@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 63% 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 5a7dd6516f4..eac81888b35 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 @@ -1,25 +1,13 @@ 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 { 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'; describe('reference.loader', () => { let service: ReferenceLoader; @@ -29,12 +17,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 +51,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 +69,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 +142,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 +149,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, 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 53% 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 43aecf0f1e3..c2bd031baac 100644 --- a/apps/server/src/modules/authorization/domain/service/reference.loader.ts +++ b/apps/server/src/modules/authorization-reference/domain/reference.loader.ts @@ -1,26 +1,15 @@ // 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'; +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, - SchoolExternalToolRepo, - SubmissionRepo, - TaskRepo, - UserRepo, -} from '@shared/repo'; -import { InstanceService } from '../../../instance'; -import { AuthorizableReferenceType, AuthorizationLoaderService } from '../type'; -import { AuthorizationInjectionService } from './authorization-injection.service'; +import { CourseGroupRepo, CourseRepo, LegacySchoolRepo, SubmissionRepo, TaskRepo, UserRepo } from '@shared/repo'; @Injectable() export class ReferenceLoader { @@ -30,12 +19,7 @@ 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 schoolExternalToolRepo: SchoolExternalToolRepo, - private readonly contextExternalToolAuthorizableService: ContextExternalToolAuthorizableService, - private readonly externalToolAuthorizableService: ExternalToolAuthorizableService, private readonly instanceService: InstanceService, private readonly authorizationInjectionService: AuthorizationInjectionService ) { @@ -45,15 +29,7 @@ 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.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-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-rules/authorization-rules.config.ts b/apps/server/src/modules/authorization-rules/authorization-rules.config.ts new file mode 100644 index 00000000000..536a4e4fbac --- /dev/null +++ b/apps/server/src/modules/authorization-rules/authorization-rules.config.ts @@ -0,0 +1,2 @@ +// 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 new file mode 100644 index 00000000000..8289aa5c7ca --- /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], + 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 91% 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 62c14baa138..1c197d3b6e2 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 @@ -1,15 +1,15 @@ +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 { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; -import { Action } from '../type'; -import { AuthorizationHelper } from '../service/authorization.helper'; describe('CourseGroupRule', () => { let service: CourseGroupRule; let authorizationHelper: AuthorizationHelper; + let injectionService: AuthorizationInjectionService; let courseRule: CourseRule; let user: User; let entity: CourseGroup; @@ -21,11 +21,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 +40,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-rules/rules/course-group.rule.ts similarity index 70% 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 cd21258a802..37fe4a7dc84 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,12 +1,23 @@ +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@modules/authorization'; 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'; @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-rules/rules/course.rule.spec.ts similarity index 91% 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 46d616cf45b..97a29a0841c 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 @@ -1,16 +1,16 @@ +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 { 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; @@ -21,11 +21,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 +42,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-rules/rules/course.rule.ts similarity index 78% 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 c97afb098fa..e0ee021c997 100644 --- a/apps/server/src/modules/authorization/domain/rules/course.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/course.rule.ts @@ -1,13 +1,23 @@ +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} 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'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '../type'; @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-rules/rules/group.rule.spec.ts similarity index 91% 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 index dcc1f39b753..37af3eccf6a 100644 --- a/apps/server/src/modules/authorization/domain/rules/group.rule.spec.ts +++ b/apps/server/src/modules/authorization-rules/rules/group.rule.spec.ts @@ -1,16 +1,22 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { ObjectId } from '@mikro-orm/mongodb'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, +} 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 { Action, AuthorizationContext, AuthorizationHelper } from '@src/modules/authorization'; -import { Group } from '@src/modules/group'; -import { ObjectId } from '@mikro-orm/mongodb'; 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-rules/rules/group.rule.ts similarity index 62% 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 4c07f3e060f..dc9c388c49b 100644 --- a/apps/server/src/modules/authorization/domain/rules/group.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/group.rule.ts @@ -1,12 +1,16 @@ +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 { Group } from '@src/modules/group'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; @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/index.ts b/apps/server/src/modules/authorization-rules/rules/index.ts similarity index 82% rename from apps/server/src/modules/authorization/domain/rules/index.ts rename to apps/server/src/modules/authorization-rules/rules/index.ts index 437d8d2d6df..773f74611e2 100644 --- a/apps/server/src/modules/authorization/domain/rules/index.ts +++ b/apps/server/src/modules/authorization-rules/rules/index.ts @@ -2,13 +2,11 @@ * 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 './course-group.rule'; 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'; @@ -18,4 +16,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/rules/instance.rule.spec.ts b/apps/server/src/modules/authorization-rules/rules/instance.rule.spec.ts similarity index 89% 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 d2168b5d7c3..de5e1147041 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 @@ -1,15 +1,20 @@ import { DeepMocked } from '@golevelup/ts-jest'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, +} 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 { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext } from '../type'; import { InstanceRule } from './instance.rule'; describe(InstanceRule.name, () => { let module: TestingModule; let rule: InstanceRule; + let injectionService: AuthorizationInjectionService; let authorizationHelper: DeepMocked; @@ -17,17 +22,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-rules/rules/instance.rule.ts similarity index 72% 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 c40fed63eac..83d02c3c0b5 100644 --- a/apps/server/src/modules/authorization/domain/rules/instance.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/instance.rule.ts @@ -1,13 +1,23 @@ +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} 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'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext, Rule } from '../type'; @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-rules/rules/legacy-school.rule.spec.ts similarity index 79% 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 732c6625ce5..4760a35955d 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 @@ -1,14 +1,14 @@ +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 { roleFactory, legacySchoolDoFactory, setupEntities, userFactory } from '@shared/testing'; -import { ObjectId } from '@mikro-orm/mongodb'; -import { Action } from '../type'; -import { AuthorizationHelper } from '../service/authorization.helper'; +import { legacySchoolDoFactory, roleFactory, setupEntities, userFactory } from '@shared/testing'; import { LegacySchoolRule } from './legacy-school.rule'; 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 +17,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 +36,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-rules/rules/legacy-school.rule.ts similarity index 68% 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 fe8eb466fbd..791331fbce7 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,15 +1,19 @@ +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 { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; /** * @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-rules/rules/lesson.rule.spec.ts similarity index 92% 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 433d4b26db6..338c4490e47 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 @@ -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,9 +18,6 @@ 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'; @@ -25,6 +29,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 +38,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 +53,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-rules/rules/lesson.rule.ts similarity index 89% 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 48e0cfa92e6..5fc1565fe01 100644 --- a/apps/server/src/modules/authorization/domain/rules/lesson.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/lesson.rule.ts @@ -1,7 +1,12 @@ +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@modules/authorization'; 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 { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; @@ -10,8 +15,11 @@ 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-rules/rules/school-system-options.rule.spec.ts similarity index 93% 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 1c7e93c3ad4..49964341624 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 @@ -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,13 +17,12 @@ import { systemEntityFactory, userFactory, } from '@shared/testing'; -import { AuthorizationContextBuilder } from '../mapper'; -import { AuthorizationHelper } from '../service/authorization.helper'; import { SchoolSystemOptionsRule } from './school-system-options.rule'; describe(SchoolSystemOptionsRule.name, () => { let module: TestingModule; let rule: SchoolSystemOptionsRule; + let injectionService: AuthorizationInjectionService; let authorizationHelper: DeepMocked; @@ -32,11 +36,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 +53,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-rules/rules/school-system-options.rule.ts similarity index 72% 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 475697553db..10d04f634f6 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,12 +1,16 @@ +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 { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; @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-rules/rules/school.rule.spec.ts similarity index 87% 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 cf82db40d32..f3df85dc95b 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 @@ -1,26 +1,35 @@ -import { Permission } from '@shared/domain/interface/permission.enum'; import { createMock, DeepMocked } from '@golevelup/ts-jest'; +import { + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} 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 { AuthorizationContextBuilder } from '../mapper'; -import { AuthorizationHelper } from '../service/authorization.helper'; import { SchoolRule } from './school.rule'; describe('SchoolRule', () => { let rule: SchoolRule; let authorizationHelper: DeepMocked; + let injectionService: AuthorizationInjectionService; let module: TestingModule; beforeAll(async () => { await setupEntities(); module = 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 = () => { @@ -39,6 +48,12 @@ describe('SchoolRule', () => { await module.close(); }); + 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-rules/rules/school.rule.ts similarity index 68% 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 ad87a582294..5e0bf03e758 100644 --- a/apps/server/src/modules/authorization/domain/rules/school.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/school.rule.ts @@ -1,13 +1,17 @@ +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 { Permission } from '@shared/domain/interface/permission.enum'; -import { School } from '@src/modules/school/domain/do'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; @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-rules/rules/submission.rule.spec.ts similarity index 96% 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 041d583a13f..2e38fee4b5e 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 @@ -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,8 +16,6 @@ import { taskFactory, userFactory, } from '@shared/testing'; -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'; @@ -27,21 +31,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-rules/rules/submission.rule.ts similarity index 85% 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 fda0bcca7ad..f8d258860f3 100644 --- a/apps/server/src/modules/authorization/domain/rules/submission.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/submission.rule.ts @@ -1,12 +1,23 @@ 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 '@modules/authorization'; import { TaskRule } from './task.rule'; @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-rules/rules/system.rule.spec.ts similarity index 94% 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 b947ac31f68..6b9d2b72935 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 @@ -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,13 +11,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 { SystemRule } from './system.rule'; describe(SystemRule.name, () => { let module: TestingModule; let rule: SystemRule; + let injectionService: AuthorizationInjectionService; let authorizationHelper: DeepMocked; @@ -26,11 +30,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 +47,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-rules/rules/system.rule.ts similarity index 76% 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 253224097b6..e12ea4fed7e 100644 --- a/apps/server/src/modules/authorization/domain/rules/system.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/system.rule.ts @@ -1,12 +1,22 @@ +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@modules/authorization'; 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'; @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-rules/rules/task.rule.spec.ts similarity index 93% 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 aa32062c7af..844c11ceeec 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 @@ -1,9 +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 { AuthorizationHelper } from '../service/authorization.helper'; -import { Action } from '../type'; import { CourseGroupRule } from './course-group.rule'; import { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; @@ -14,6 +13,7 @@ describe('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 +22,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-rules/rules/task.rule.ts similarity index 84% 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 f24dee67740..dbe7650d8b8 100644 --- a/apps/server/src/modules/authorization/domain/rules/task.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/task.rule.ts @@ -1,7 +1,12 @@ +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, + Rule, +} from '@modules/authorization'; 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 { CourseRule } from './course.rule'; import { LessonRule } from './lesson.rule'; @@ -10,8 +15,11 @@ 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-rules/rules/team.rule.spec.ts similarity index 89% 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 d331adb80f9..4a7026c2789 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,12 +1,16 @@ +import { + AuthorizationContextBuilder, + AuthorizationHelper, + AuthorizationInjectionService, +} from '@modules/authorization'; 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 { TeamRule } from './team.rule'; 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 +22,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-rules/rules/team.rule.ts similarity index 67% 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 48cca110e2a..3042b56facd 100644 --- a/apps/server/src/modules/authorization/domain/rules/team.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/team.rule.ts @@ -1,11 +1,15 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { TeamEntity, TeamUserEntity, User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; @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-rules/rules/user-login-migration.rule.spec.ts similarity index 91% 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 6ee893fc9d2..4640e1ee176 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 @@ -1,16 +1,21 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest'; import { ObjectId } from '@mikro-orm/mongodb'; +import { + Action, + AuthorizationContext, + AuthorizationHelper, + AuthorizationInjectionService, +} 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 { AuthorizationHelper } from '../service/authorization.helper'; -import { Action, AuthorizationContext } from '../type'; import { UserLoginMigrationRule } from './user-login-migration.rule'; describe('UserLoginMigrationRule', () => { let module: TestingModule; let rule: UserLoginMigrationRule; + let injectionService: AuthorizationInjectionService; let authorizationHelper: DeepMocked; @@ -24,17 +29,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-rules/rules/user-login-migration.rule.ts similarity index 67% 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 05ca647e7ab..f866571382c 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,12 +1,16 @@ +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 { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; @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-rules/rules/user.rule.spec.ts similarity index 83% 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 85492348f75..62722af6ac9 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 @@ -1,9 +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 } from '../type'; -import { AuthorizationHelper } from '../service/authorization.helper'; import { UserRule } from './user.rule'; describe('UserRule', () => { @@ -12,6 +11,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 +20,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 +33,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-rules/rules/user.rule.ts similarity index 62% 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 66ea8e44052..195b39b50cd 100644 --- a/apps/server/src/modules/authorization/domain/rules/user.rule.ts +++ b/apps/server/src/modules/authorization-rules/rules/user.rule.ts @@ -1,11 +1,15 @@ +import { AuthorizationContext, AuthorizationHelper, AuthorizationInjectionService, Rule } from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { User } from '@shared/domain/entity'; -import { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; @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/authorization.module.ts b/apps/server/src/modules/authorization/authorization.module.ts index 289b7209f49..73e6ad197ca 100644 --- a/apps/server/src/modules/authorization/authorization.module.ts +++ b/apps/server/src/modules/authorization/authorization.module.ts @@ -3,25 +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 { - ContextExternalToolRule, - CourseGroupRule, - CourseRule, - ExternalToolRule, - GroupRule, - InstanceRule, - LegacySchoolRule, - LessonRule, - SchoolExternalToolRule, - SchoolRule, - SchoolSystemOptionsRule, - SubmissionRule, - SystemRule, - TaskRule, - TeamRule, - UserLoginMigrationRule, - UserRule, -} from './domain/rules'; import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; @Module({ @@ -34,31 +15,7 @@ import { FeathersAuthorizationService, FeathersAuthProvider } from './feathers'; UserRepo, RuleManager, AuthorizationHelper, - // rules - ContextExternalToolRule, - CourseGroupRule, - CourseRule, - GroupRule, - LessonRule, - SchoolRule, - SchoolExternalToolRule, - SubmissionRule, - TaskRule, - TeamRule, - UserRule, - UserLoginMigrationRule, - LegacySchoolRule, - SystemRule, - SchoolSystemOptionsRule, - ExternalToolRule, - 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/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 9f4da3b753b..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,50 +2,13 @@ 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 { - 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 +17,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 +115,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); - }); - }); }); 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..8dafe2c3ec4 100644 --- a/apps/server/src/modules/authorization/domain/service/rule-manager.ts +++ b/apps/server/src/modules/authorization/domain/service/rule-manager.ts @@ -2,71 +2,15 @@ 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 { - 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 type { AuthorizationContext, Rule } from '../type'; import { AuthorizationInjectionService } from './authorization-injection.service'; @Injectable() export class RuleManager { - constructor( - contextExternalToolRule: ContextExternalToolRule, - courseGroupRule: CourseGroupRule, - courseRule: CourseRule, - groupRule: GroupRule, - legaySchoolRule: LegacySchoolRule, - lessonRule: LessonRule, - schoolExternalToolRule: SchoolExternalToolRule, - schoolRule: SchoolRule, - schoolSystemOptionsRule: SchoolSystemOptionsRule, - submissionRule: SubmissionRule, - systemRule: SystemRule, - taskRule: TaskRule, - teamRule: TeamRule, - userLoginMigrationRule: UserLoginMigrationRule, - userRule: UserRule, - externalToolRule: ExternalToolRule, - instanceRule: InstanceRule, - private readonly authorizationInjectionService: AuthorizationInjectionService - ) { - this.authorizationInjectionService.injectAuthorizationRule(contextExternalToolRule); - this.authorizationInjectionService.injectAuthorizationRule(courseGroupRule); - this.authorizationInjectionService.injectAuthorizationRule(courseRule); - 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); - this.authorizationInjectionService.injectAuthorizationRule(systemRule); - this.authorizationInjectionService.injectAuthorizationRule(taskRule); - this.authorizationInjectionService.injectAuthorizationRule(teamRule); - this.authorizationInjectionService.injectAuthorizationRule(userLoginMigrationRule); - this.authorizationInjectionService.injectAuthorizationRule(userRule); - this.authorizationInjectionService.injectAuthorizationRule(externalToolRule); - this.authorizationInjectionService.injectAuthorizationRule(instanceRule); - } + 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/authorization/index.ts b/apps/server/src/modules/authorization/index.ts index 115cfb338e3..e9125b5e182 100644 --- a/apps/server/src/modules/authorization/index.ts +++ b/apps/server/src/modules/authorization/index.ts @@ -9,13 +9,10 @@ export { AuthorizationHelper, AuthorizationLoaderService, AuthorizationLoaderServiceGeneric, - AuthorizationReferenceService, AuthorizationService, 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/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/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.spec.ts b/apps/server/src/modules/learnroom/uc/course-copy.uc.spec.ts index 087104ac7af..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,7 +1,7 @@ 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 { AuthorizationReferenceService } from '@modules/authorization-reference'; import { CopyElementType, CopyStatusEnum } from '@modules/copy-helper'; import { ForbiddenException, InternalServerErrorException } from '@nestjs/common'; import { Test, TestingModule } from '@nestjs/testing'; 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..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,6 +1,6 @@ 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 { AuthorizationReferenceService } from '@modules/authorization-reference'; import { CopyStatus } from '@modules/copy-helper'; import { Injectable, InternalServerErrorException } from '@nestjs/common'; import { Permission } from '@shared/domain/interface'; 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..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 { faker } from '@faker-js/faker'; -import { AuthorizationReferenceService } from '../../authorization/domain'; 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 7431a5cf665..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,5 +1,5 @@ -import { AuthorizationContextBuilder } from '@modules/authorization'; -import { AuthorizableReferenceType, AuthorizationReferenceService } from '@modules/authorization/domain'; +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'; 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..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, @@ -23,6 +24,7 @@ describe('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..ec33d569174 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 '@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() diff --git a/apps/server/src/modules/server/server.module.ts b/apps/server/src/modules/server/server.module.ts index af457631d66..b3fad8091ed 100644 --- a/apps/server/src/modules/server/server.module.ts +++ b/apps/server/src/modules/server/server.module.ts @@ -9,7 +9,8 @@ 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'; import { CollaborativeStorageModule } from '@modules/collaborative-storage'; @@ -56,6 +57,7 @@ const serverModules = [ AuthenticationApiModule, AuthGuardModule, AuthorizationReferenceApiModule, + AuthorizationRulesModule, AccountApiModule, CollaborativeStorageModule, OauthApiModule, 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/teams/service/team-authorisable.service.spec.ts b/apps/server/src/modules/teams/service/team-authorisable.service.spec.ts index 3321d6ce818..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,5 +1,6 @@ -import { Test, TestingModule } from '@nestjs/testing'; 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 { TeamAuthorisableService } from './team-authorisable.service'; @@ -7,6 +8,7 @@ import { TeamAuthorisableService } from './team-authorisable.service'; 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..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,17 @@ +import { + AuthorizableReferenceType, + AuthorizationInjectionService, + AuthorizationLoaderServiceGeneric, +} from '@modules/authorization'; import { Injectable } from '@nestjs/common'; import { TeamEntity } from '@shared/domain/entity'; import { TeamsRepo } from '@shared/repo'; -import { 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..ea9530a7881 100644 --- a/apps/server/src/modules/teams/teams.module.ts +++ b/apps/server/src/modules/teams/teams.module.ts @@ -1,11 +1,12 @@ +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'; @Module({ - imports: [CqrsModule, LoggerModule], + imports: [CqrsModule, LoggerModule, AuthorizationModule], providers: [TeamService, TeamsRepo, TeamAuthorisableService], exports: [TeamService, TeamAuthorisableService], }) 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 65% 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..01b7b97140e 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 @@ -1,3 +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'; @@ -8,55 +10,68 @@ 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'; 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/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..cb552fefaa4 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 @@ -1,13 +1,17 @@ +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 { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; @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..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,9 +1,11 @@ +import { AuthorizationModule } from '@modules/authorization'; import { UserLicenseModule } from '@modules/user-license'; import { forwardRef, Module } from '@nestjs/common'; import { LoggerModule } from '@src/core/logger'; 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'; @@ -15,6 +17,7 @@ import { ToolConfigurationStatusService } from './service/tool-configuration-sta SchoolExternalToolModule, LoggerModule, UserLicenseModule, + AuthorizationModule, ], providers: [ ContextExternalToolService, @@ -22,11 +25,11 @@ import { ToolConfigurationStatusService } from './service/tool-configuration-sta ContextExternalToolAuthorizableService, ToolReferenceService, ToolConfigurationStatusService, + ContextExternalToolRule, ], exports: [ ContextExternalToolService, ContextExternalToolValidationService, - ContextExternalToolAuthorizableService, ToolReferenceService, ToolConfigurationStatusService, ], 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..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,4 +1,5 @@ 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'; @@ -13,6 +14,7 @@ describe('ContextExternalToolAuthorizableService', () => { let service: ContextExternalToolAuthorizableService; let contextExternalToolRepo: DeepMocked; + let injectionService: DeepMocked; beforeAll(async () => { module = await Test.createTestingModule({ @@ -22,11 +24,13 @@ describe('ContextExternalToolAuthorizableService', () => { provide: ContextExternalToolRepo, useValue: createMock(), }, + AuthorizationInjectionService, ], }).compile(); service = module.get(ContextExternalToolAuthorizableService); contextExternalToolRepo = module.get(ContextExternalToolRepo); + injectionService = module.get(AuthorizationInjectionService); }); afterAll(async () => { @@ -37,6 +41,12 @@ describe('ContextExternalToolAuthorizableService', () => { jest.resetAllMocks(); }); + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + expect(injectionService.getReferenceLoader(AuthorizableReferenceType.ContextExternalToolEntity)).toBe(service); + }); + }); + describe('findById', () => { describe('when id is given', () => { const setup = () => { 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); 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 88% 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..d31b9dd09d5 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 @@ -1,12 +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 { AuthorizationHelper } from '../service/authorization.helper'; -import { Action } from '../type'; import { ExternalToolRule } from './external-tool.rule'; describe(ExternalToolRule.name, () => { @@ -14,6 +13,7 @@ describe(ExternalToolRule.name, () => { let rule: ExternalToolRule; let authorizationHelper: DeepMocked; + let injectionService: DeepMocked; beforeAll(async () => { await setupEntities(); @@ -25,11 +25,13 @@ describe(ExternalToolRule.name, () => { provide: AuthorizationHelper, useValue: createMock(), }, + AuthorizationInjectionService, ], }).compile(); rule = module.get(ExternalToolRule); authorizationHelper = module.get(AuthorizationHelper); + injectionService = module.get(AuthorizationInjectionService); }); beforeEach(() => { @@ -40,6 +42,12 @@ describe(ExternalToolRule.name, () => { await module.close(); }); + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + expect(injectionService.getAuthorizationRules()).toContain(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 67% 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..1ae116d7c1f 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,16 @@ +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 { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; @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..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,4 +1,5 @@ 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'; @@ -20,7 +21,15 @@ import { } from './service'; @Module({ - imports: [CommonToolModule, LoggerModule, OauthProviderServiceModule, EncryptionModule, HttpModule, InstanceModule], + imports: [ + CommonToolModule, + LoggerModule, + OauthProviderServiceModule, + EncryptionModule, + HttpModule, + InstanceModule, + AuthorizationModule, + ], providers: [ ExternalToolService, ExternalToolServiceMapper, @@ -40,7 +49,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..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,4 +1,5 @@ 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 { externalToolFactory } from '../testing'; @@ -9,6 +10,7 @@ describe(ExternalToolAuthorizableService.name, () => { let service: ExternalToolAuthorizableService; let externalToolRepo: DeepMocked; + let authorizationInjectionService: DeepMocked; beforeAll(async () => { module = await Test.createTestingModule({ @@ -18,11 +20,13 @@ describe(ExternalToolAuthorizableService.name, () => { provide: ExternalToolRepo, useValue: createMock(), }, + AuthorizationInjectionService, ], }).compile(); service = module.get(ExternalToolAuthorizableService); externalToolRepo = module.get(ExternalToolRepo); + authorizationInjectionService = module.get(AuthorizationInjectionService); }); afterAll(async () => { @@ -33,6 +37,12 @@ describe(ExternalToolAuthorizableService.name, () => { jest.resetAllMocks(); }); + describe('constructor', () => { + it('should inject itself into the AuthorizationInjectionService', () => { + expect(authorizationInjectionService.getReferenceLoader(AuthorizableReferenceType.ExternalTool)).toEqual(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); 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..42c22849882 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 @@ -1,3 +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'; @@ -5,23 +7,30 @@ 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'; 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 77% 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..8073b4714a1 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 @@ -1,13 +1,17 @@ +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 { AuthorizationHelper } from '../service/authorization.helper'; -import { AuthorizationContext, Rule } from '../type'; @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..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,11 +1,22 @@ +import { AuthorizationModule } from '@modules/authorization'; import { forwardRef, Module } from '@nestjs/common'; import { CommonToolModule } from '../common'; import { ExternalToolModule } from '../external-tool'; -import { SchoolExternalToolService, SchoolExternalToolValidationService } from './service'; +import { SchoolExternalToolRule } from './authorization/school-external-tool.rule'; +import { + SchoolExternalToolAuthorizableService, + SchoolExternalToolService, + SchoolExternalToolValidationService, +} from './service'; @Module({ - imports: [forwardRef(() => CommonToolModule), forwardRef(() => ExternalToolModule)], - providers: [SchoolExternalToolService, SchoolExternalToolValidationService], + imports: [forwardRef(() => CommonToolModule), forwardRef(() => ExternalToolModule), AuthorizationModule], + 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..39de9ffa293 --- /dev/null +++ b/apps/server/src/modules/tool/school-external-tool/service/school-external-tool-authorizable.service.ts @@ -0,0 +1,25 @@ +import { + AuthorizableReferenceType, + AuthorizationInjectionService, + AuthorizationLoaderServiceGeneric, +} from '@modules/authorization'; +import { Injectable } from '@nestjs/common'; +import { EntityId } from '@shared/domain/types'; +import { SchoolExternalToolRepo } from '@shared/repo'; +import { SchoolExternalTool } from '../domain'; + +@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..bb14eed177d --- /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 { AuthorizableReferenceType, AuthorizationInjectionService } from '@modules/authorization'; +import { Test, TestingModule } from '@nestjs/testing'; +import { SchoolExternalToolRepo } from '@shared/repo'; +import { legacySchoolDoFactory } from '@shared/testing'; +import { SchoolExternalTool } from '../domain'; +import { schoolExternalToolFactory } from '../testing'; +import { SchoolExternalToolAuthorizableService } from './school-external-tool-authorizable.service'; + +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); + }); + }); + }); +}); 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'; 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'; 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();