Skip to content

Commit

Permalink
N21-1505 Prevent deletion of global systems (#4574)
Browse files Browse the repository at this point in the history
  • Loading branch information
MarvinOehlerkingCap authored Nov 24, 2023
1 parent 25eead8 commit b90f460
Show file tree
Hide file tree
Showing 91 changed files with 1,761 additions and 678 deletions.
19 changes: 11 additions & 8 deletions apps/server/src/apps/server.app.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,34 @@
/* istanbul ignore file */
import { Mail, MailService } from '@infra/mail';
// application imports
/* eslint-disable no-console */
import { MikroORM } from '@mikro-orm/core';
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { enableOpenApiDocs } from '@shared/controller/swagger';
import { Mail, MailService } from '@infra/mail';
import { LegacyLogger, Logger } from '@src/core/logger';
import { AccountService } from '@modules/account';
import { TeamService } from '@modules/teams/service/team.service';
import { AccountValidationService } from '@modules/account/services/account.validation.service';

Check warning on line 7 in apps/server/src/apps/server.app.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'@modules/account/services/account.validation.service' import is restricted from being used by a pattern. Do not deep import from a module
import { AccountUc } from '@modules/account/uc/account.uc';

Check warning on line 8 in apps/server/src/apps/server.app.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'@modules/account/uc/account.uc' import is restricted from being used by a pattern. Do not deep import from a module
import { SystemRule } from '@modules/authorization/domain/rules';

Check warning on line 9 in apps/server/src/apps/server.app.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'@modules/authorization/domain/rules' import is restricted from being used by a pattern. Do not deep import from a module
import { CollaborativeStorageUc } from '@modules/collaborative-storage/uc/collaborative-storage.uc';

Check warning on line 10 in apps/server/src/apps/server.app.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'@modules/collaborative-storage/uc/collaborative-storage.uc' import is restricted from being used by a pattern. Do not deep import from a module
import { GroupService } from '@modules/group';
import { FeathersRosterService } from '@modules/pseudonym';
import { RocketChatService } from '@modules/rocketchat';
import { ServerModule } from '@modules/server';
import { TeamService } from '@modules/teams/service/team.service';

Check warning on line 15 in apps/server/src/apps/server.app.ts

View workflow job for this annotation

GitHub Actions / nest_lint

'@modules/teams/service/team.service' import is restricted from being used by a pattern. Do not deep import from a module
import { NestFactory } from '@nestjs/core';
import { ExpressAdapter } from '@nestjs/platform-express';
import { enableOpenApiDocs } from '@shared/controller/swagger';
import { LegacyLogger, Logger } from '@src/core/logger';
import express from 'express';
import { join } from 'path';

// register source-map-support for debugging
import { install as sourceMapInstall } from 'source-map-support';
import { FeathersRosterService } from '@modules/pseudonym';
import legacyAppPromise = require('../../../../src/app');

import { AppStartLoggable } from './helpers/app-start-loggable';
import {
addPrometheusMetricsMiddlewaresIfEnabled,
createAndStartPrometheusMetricsAppIfEnabled,
} from './helpers/prometheus-metrics';
import legacyAppPromise = require('../../../../src/app');

async function bootstrap() {
sourceMapInstall();
Expand Down Expand Up @@ -85,6 +86,8 @@ async function bootstrap() {
feathersExpress.services['nest-feathers-roster-service'] = nestApp.get(FeathersRosterService);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
feathersExpress.services['nest-group-service'] = nestApp.get(GroupService);
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment,@typescript-eslint/no-unsafe-member-access
feathersExpress.services['nest-system-rule'] = nestApp.get(SystemRule);
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-assignment
feathersExpress.services['nest-orm'] = orm;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { createMock } from '@golevelup/ts-jest';
import { MongoMemoryDatabaseModule } from '@infra/database';
import KeycloakAdminClient from '@keycloak/keycloak-admin-client-cjs/keycloak-admin-client-cjs-index';
import AuthenticationExecutionExportRepresentation from '@keycloak/keycloak-admin-client/lib/defs/authenticationExecutionExportRepresentation';
import AuthenticationFlowRepresentation from '@keycloak/keycloak-admin-client/lib/defs/authenticationFlowRepresentation';
import { LegacySystemService } from '@modules/system';
import { ConfigModule } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { MongoMemoryDatabaseModule } from '@infra/database';
import { SystemRepo } from '@shared/repo/system/system.repo';
import { systemFactory } from '@shared/testing/factory';
import { LegacySystemRepo } from '@shared/repo';
import { systemEntityFactory } from '@shared/testing/factory';
import { LoggerModule } from '@src/core/logger';
import { SystemService } from '@modules/system/service/system.service';
import { v1 } from 'uuid';
import { KeycloakAdministrationService } from '../../keycloak-administration/service/keycloak-administration.service';
import { KeycloakConfigurationModule } from '../keycloak-configuration.module';
Expand All @@ -17,15 +17,15 @@ import { KeycloakConfigurationService } from './keycloak-configuration.service';
describe('KeycloakConfigurationService Integration', () => {
let module: TestingModule;
let keycloak: KeycloakAdminClient;
let systemRepo: SystemRepo;
let systemRepo: LegacySystemRepo;
let keycloakAdministrationService: KeycloakAdministrationService;
let keycloakConfigurationService: KeycloakConfigurationService;
let isKeycloakAvailable = false;

const testRealm = `test-realm-${v1().toString()}`;
const flowAlias = 'Direct Broker Flow';
const systemServiceMock = createMock<SystemService>();
const systems = systemFactory.withOidcConfig().buildList(1);
const systemServiceMock = createMock<LegacySystemService>();
const systems = systemEntityFactory.withOidcConfig().buildList(1);

beforeAll(async () => {
module = await Test.createTestingModule({
Expand All @@ -38,9 +38,9 @@ describe('KeycloakConfigurationService Integration', () => {
validationOptions: { infer: true },
}),
],
providers: [SystemRepo],
providers: [LegacySystemRepo],
}).compile();
systemRepo = module.get(SystemRepo);
systemRepo = module.get(LegacySystemRepo);
keycloakAdministrationService = module.get(KeycloakAdministrationService);
keycloakConfigurationService = module.get(KeycloakConfigurationService);
isKeycloakAvailable = await keycloakAdministrationService.testKcConnection();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { HttpService } from '@nestjs/axios';
import { ConfigService } from '@nestjs/config';
import { Test, TestingModule } from '@nestjs/testing';
import { SystemEntity, SystemTypeEnum } from '@shared/domain';
import { systemFactory } from '@shared/testing';
import { systemEntityFactory } from '@shared/testing';
import { AxiosResponse } from 'axios';
import { of } from 'rxjs';
import { v1 } from 'uuid';
Expand Down Expand Up @@ -63,7 +63,9 @@ describe('KeycloakConfigurationService Unit', () => {
};
};

const systems: SystemEntity[] = systemFactory.withOidcConfig().buildListWithId(1, { type: SystemTypeEnum.OIDC });
const systems: SystemEntity[] = systemEntityFactory
.withOidcConfig()
.buildListWithId(1, { type: SystemTypeEnum.OIDC });
const oidcSystems = SystemOidcMapper.mapFromEntitiesToDtos(systems);
const idps: IdentityProviderRepresentation[] = [
{
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { DefaultEncryptionService, EncryptionService } from '@infra/encryption';
import { OauthConfigDto } from '@modules/system/service';
import { OauthConfigDto } from '@modules/system/service/dto';
import { HttpService } from '@nestjs/axios';
import { Inject, Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
Expand Down
4 changes: 2 additions & 2 deletions apps/server/src/modules/account/account.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { IdentityManagementModule } from '@infra/identity-management';
import { Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PermissionService } from '@shared/domain';
import { SystemRepo, UserRepo } from '@shared/repo';
import { LegacySystemRepo, UserRepo } from '@shared/repo';
import { LoggerModule } from '@src/core/logger/logger.module';
import { ServerConfig } from '../server/server.config';
import { AccountIdmToDtoMapper, AccountIdmToDtoMapperDb, AccountIdmToDtoMapperIdm } from './mapper';
Expand All @@ -24,7 +24,7 @@ function accountIdmToDtoMapperFactory(configService: ConfigService<ServerConfig,
imports: [IdentityManagementModule, LoggerModule],
providers: [
UserRepo,
SystemRepo,
LegacySystemRepo,
PermissionService,
AccountRepo,
AccountServiceDb,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Test, TestingModule } from '@nestjs/testing';
import { EntityNotFoundError } from '@shared/common';
import { Account, EntityId, Permission, Role, RoleName, User } from '@shared/domain';
import { UserRepo } from '@shared/repo';
import { accountFactory, setupEntities, systemFactory, userFactory } from '@shared/testing';
import { accountFactory, setupEntities, systemEntityFactory, userFactory } from '@shared/testing';
import { ObjectId } from 'bson';
import { AccountRepo } from '../repo/account.repo';
import { AccountValidationService } from './account.validation.service';
Expand Down Expand Up @@ -133,8 +133,8 @@ describe('AccountValidationService', () => {
mockOtherTeacherAccount = accountFactory.buildWithId({
userId: mockOtherTeacherUser.id,
});
const externalSystemA = systemFactory.buildWithId();
const externalSystemB = systemFactory.buildWithId();
const externalSystemA = systemEntityFactory.buildWithId();
const externalSystemB = systemEntityFactory.buildWithId();
mockExternalUserAccount = accountFactory.buildWithId({
userId: mockExternalUser.id,
username: 'unique.within@system',
Expand Down
12 changes: 6 additions & 6 deletions apps/server/src/modules/account/uc/account.uc.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
User,
} from '@shared/domain';
import { UserRepo } from '@shared/repo';
import { accountFactory, schoolFactory, setupEntities, systemFactory, userFactory } from '@shared/testing';
import { accountFactory, schoolFactory, setupEntities, systemEntityFactory, userFactory } from '@shared/testing';
import { BruteForcePrevention } from '@src/imports-from-feathers';
import { ObjectId } from 'bson';
import {
Expand Down Expand Up @@ -431,7 +431,7 @@ describe('AccountUc', () => {
userId: mockUnknownRoleUser.id,
password: defaultPasswordHash,
});
const externalSystem = systemFactory.buildWithId();
const externalSystem = systemEntityFactory.buildWithId();
mockExternalUserAccount = accountFactory.buildWithId({
userId: mockExternalUser.id,
password: defaultPasswordHash,
Expand All @@ -440,25 +440,25 @@ describe('AccountUc', () => {
mockAccountWithoutUser = accountFactory.buildWithId({
userId: undefined,
password: defaultPasswordHash,
systemId: systemFactory.buildWithId().id,
systemId: systemEntityFactory.buildWithId().id,
});
mockAccountWithSystemId = accountFactory.withSystemId(new ObjectId(10)).build();
mockAccountWithLastFailedLogin = accountFactory.buildWithId({
userId: undefined,
password: defaultPasswordHash,
systemId: systemFactory.buildWithId().id,
systemId: systemEntityFactory.buildWithId().id,
lasttriedFailedLogin: new Date(),
});
mockAccountWithOldLastFailedLogin = accountFactory.buildWithId({
userId: undefined,
password: defaultPasswordHash,
systemId: systemFactory.buildWithId().id,
systemId: systemEntityFactory.buildWithId().id,
lasttriedFailedLogin: new Date(new Date().getTime() - LOGIN_BLOCK_TIME - 1),
});
mockAccountWithNoLastFailedLogin = accountFactory.buildWithId({
userId: undefined,
password: defaultPasswordHash,
systemId: systemFactory.buildWithId().id,
systemId: systemEntityFactory.buildWithId().id,
lasttriedFailedLogin: undefined,
});

Expand Down
12 changes: 6 additions & 6 deletions apps/server/src/modules/authentication/authentication.module.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import { Module } from '@nestjs/common';
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { CacheWrapperModule } from '@infra/cache';
import { IdentityManagementModule } from '@infra/identity-management';
import { LegacySchoolRepo, SystemRepo, UserRepo } from '@shared/repo';
import { LoggerModule } from '@src/core/logger';
import { AccountModule } from '@modules/account';
import { OauthModule } from '@modules/oauth/oauth.module';
import { RoleModule } from '@modules/role';
import { SystemModule } from '@modules/system';
import { Module } from '@nestjs/common';
import { JwtModule, JwtModuleOptions } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { LegacySchoolRepo, LegacySystemRepo, UserRepo } from '@shared/repo';
import { LoggerModule } from '@src/core/logger';
import { Algorithm, SignOptions } from 'jsonwebtoken';
import { jwtConstants } from './constants';
import { AuthenticationService } from './services/authentication.service';
Expand Down Expand Up @@ -69,7 +69,7 @@ const jwtModuleOptions: JwtModuleOptions = {
JwtStrategy,
JwtValidationAdapter,
UserRepo,
SystemRepo,
LegacySystemRepo,
LegacySchoolRepo,
LocalStrategy,
AuthenticationService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { ServerTestModule } from '@modules/server/server.module';
import { HttpStatus, INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { Account, RoleName, SchoolEntity, SystemEntity, User } from '@shared/domain';
import { accountFactory, roleFactory, schoolFactory, systemFactory, userFactory } from '@shared/testing';
import { accountFactory, roleFactory, schoolFactory, systemEntityFactory, userFactory } from '@shared/testing';
import axios from 'axios';
import MockAdapter from 'axios-mock-adapter';
import crypto, { KeyPairKeyObjectResult } from 'crypto';
Expand Down Expand Up @@ -149,7 +149,7 @@ describe('Login Controller (api)', () => {
describe('when user login succeeds', () => {
const setup = async () => {
const schoolExternalId = 'mockSchoolExternalId';
const system: SystemEntity = systemFactory.withLdapConfig().buildWithId({});
const system: SystemEntity = systemEntityFactory.withLdapConfig().buildWithId({});
const school: SchoolEntity = schoolFactory.buildWithId({ systems: [system], externalId: schoolExternalId });
const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] });

Expand Down Expand Up @@ -200,7 +200,7 @@ describe('Login Controller (api)', () => {
describe('when user login fails', () => {
const setup = async () => {
const schoolExternalId = 'mockSchoolExternalId';
const system: SystemEntity = systemFactory.withLdapConfig().buildWithId({});
const system: SystemEntity = systemEntityFactory.withLdapConfig().buildWithId({});
const school: SchoolEntity = schoolFactory.buildWithId({ systems: [system], externalId: schoolExternalId });
const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] });

Expand Down Expand Up @@ -238,7 +238,7 @@ describe('Login Controller (api)', () => {
describe('when logging in as a user of the Central LDAP of Brandenburg', () => {
const setup = async () => {
const officialSchoolNumber = '01234';
const system: SystemEntity = systemFactory.withLdapConfig().buildWithId({});
const system: SystemEntity = systemEntityFactory.withLdapConfig().buildWithId({});
const school: SchoolEntity = schoolFactory.buildWithId({
systems: [system],
externalId: officialSchoolNumber,
Expand Down Expand Up @@ -301,7 +301,7 @@ describe('Login Controller (api)', () => {
const schoolExternalId = 'schoolExternalId';
const userExternalId = 'userExternalId';

const system = systemFactory.withOauthConfig().buildWithId({});
const system = systemEntityFactory.withOauthConfig().buildWithId({});
const school = schoolFactory.buildWithId({ systems: [system], externalId: schoolExternalId });
const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] });
const user = userFactory.buildWithId({ school, roles: [studentRoles], externalId: userExternalId });
Expand Down Expand Up @@ -391,7 +391,7 @@ describe('Login Controller (api)', () => {
const schoolExternalId = 'schoolExternalId';
const userExternalId = 'userExternalId';

const system = systemFactory.withOauthConfig().buildWithId({});
const system = systemEntityFactory.withOauthConfig().buildWithId({});
const school = schoolFactory.buildWithId({ systems: [system], externalId: schoolExternalId });
const studentRoles = roleFactory.build({ name: RoleName.STUDENT, permissions: [] });
const user = userFactory.buildWithId({ school, roles: [studentRoles], externalId: userExternalId });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { createMock } from '@golevelup/ts-jest';
import { UnauthorizedException } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import { SystemEntity } from '@shared/domain';
import { systemFactory } from '@shared/testing';
import { systemEntityFactory } from '@shared/testing';
import { LegacyLogger } from '@src/core/logger';
import { LdapService } from './ldap.service';

Expand Down Expand Up @@ -59,7 +59,7 @@ describe('LdapService', () => {
describe('checkLdapCredentials', () => {
describe('when credentials are correct', () => {
it('should login successfully', async () => {
const system: SystemEntity = systemFactory.withLdapConfig().buildWithId();
const system: SystemEntity = systemEntityFactory.withLdapConfig().buildWithId();
await expect(
ldapService.checkLdapCredentials(system, 'connectSucceeds', 'mockPassword')
).resolves.not.toThrow();
Expand All @@ -68,7 +68,7 @@ describe('LdapService', () => {

describe('when no ldap config is provided', () => {
it('should throw error', async () => {
const system: SystemEntity = systemFactory.buildWithId();
const system: SystemEntity = systemEntityFactory.buildWithId();
await expect(ldapService.checkLdapCredentials(system, 'mockUsername', 'mockPassword')).rejects.toThrow(
new Error(`no LDAP config found in system ${system.id}`)
);
Expand All @@ -77,7 +77,7 @@ describe('LdapService', () => {

describe('when user is not authorized', () => {
it('should throw unauthorized error', async () => {
const system: SystemEntity = systemFactory.withLdapConfig().buildWithId();
const system: SystemEntity = systemEntityFactory.withLdapConfig().buildWithId();
await expect(ldapService.checkLdapCredentials(system, 'mockUsername', 'mockPassword')).rejects.toThrow(
new UnauthorizedException('User could not authenticate')
);
Expand All @@ -86,7 +86,7 @@ describe('LdapService', () => {

describe('when connected flag is not set', () => {
it('should throw unauthorized error', async () => {
const system: SystemEntity = systemFactory.withLdapConfig().buildWithId();
const system: SystemEntity = systemEntityFactory.withLdapConfig().buildWithId();
await expect(ldapService.checkLdapCredentials(system, 'connectWithoutFlag', 'mockPassword')).rejects.toThrow(
new UnauthorizedException('User could not authenticate')
);
Expand Down
Loading

0 comments on commit b90f460

Please sign in to comment.