Skip to content

Commit

Permalink
Merge branch 'release-0101' into SPSH-1622
Browse files Browse the repository at this point in the history
  • Loading branch information
DPDS93CT committed Dec 13, 2024
2 parents 40621e8 + 7c955d4 commit 4a82f13
Showing 17 changed files with 411 additions and 29 deletions.
5 changes: 4 additions & 1 deletion src/console/console.module.spec.ts
Original file line number Diff line number Diff line change
@@ -2,13 +2,16 @@ import { Test, TestingModule } from '@nestjs/testing';
import { ConsoleModule } from './console.module.js';
import { DbConsole } from './db.console.js';
import { DbInitConsole } from './db-init.console.js';
import { LoggingTestModule } from '../../test/utils/index.js';
import { PersonModule } from '../modules/person/person.module.js';
import { KeycloakAdministrationModule } from '../modules/keycloak-administration/keycloak-administration.module.js';

describe('ConsoleModule', () => {
let module: TestingModule;

beforeAll(async () => {
module = await Test.createTestingModule({
imports: [ConsoleModule],
imports: [PersonModule, KeycloakAdministrationModule, ConsoleModule, LoggingTestModule],
}).compile();
});

2 changes: 0 additions & 2 deletions src/console/console.module.ts
Original file line number Diff line number Diff line change
@@ -11,7 +11,6 @@ import { DbConsole } from './db.console.js';
import { DbInitConsole } from './db-init.console.js';
import { LoggerModule } from '../core/logging/logger.module.js';
import { KeycloakAdministrationModule } from '../modules/keycloak-administration/keycloak-administration.module.js';
import { UsernameGeneratorService } from '../modules/person/domain/username-generator.service.js';
import { KeycloakConfigModule } from '../modules/keycloak-administration/keycloak-config.module.js';
import { OrganisationModule } from '../modules/organisation/organisation.module.js';
import { RolleModule } from '../modules/rolle/rolle.module.js';
@@ -93,7 +92,6 @@ import { KeycloakConsoleModule } from './keycloak/keycloak-console.module.js';
DbInitMigrationConsole,
DbCreateMigrationConsole,
DbApplyMigrationConsole,
UsernameGeneratorService,
DbSeedDataGeneratorConsole,
],
})
Original file line number Diff line number Diff line change
@@ -25,6 +25,7 @@ import { DBiamPersonenkontextService } from '../../modules/personenkontext/domai
import { DbSeedReferenceRepo } from './repo/db-seed-reference.repo.js';
import { PersonenKontextModule } from '../../modules/personenkontext/personenkontext.module.js';
import { LdapClient } from '../../core/ldap/domain/ldap-client.js';
import { OxUserBlacklistRepo } from '../../modules/person/persistence/ox-user-blacklist.repo.js';

describe('DbSeedConsoleMockedDbSeedRepo', () => {
let module: TestingModule;
@@ -50,6 +51,7 @@ describe('DbSeedConsoleMockedDbSeedRepo', () => {
providers: [
UsernameGeneratorService,
DBiamPersonenkontextRepo,
OxUserBlacklistRepo,
DbSeedConsole,
DbSeedService,
DBiamPersonenkontextService,
3 changes: 2 additions & 1 deletion src/console/dbseed/db-seed.console.integration-spec.ts
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ import { RolleModule } from '../../modules/rolle/rolle.module.js';
import { ServiceProviderModule } from '../../modules/service-provider/service-provider.module.js';
import { DbSeedModule } from './db-seed.module.js';
import { PersonenKontextModule } from '../../modules/personenkontext/personenkontext.module.js';
import { OxUserBlacklistRepo } from '../../modules/person/persistence/ox-user-blacklist.repo.js';

describe('DbSeedConsoleIntegration', () => {
let module: TestingModule;
@@ -41,7 +42,7 @@ describe('DbSeedConsoleIntegration', () => {
ServiceProviderModule,
PersonenKontextModule,
],
providers: [UsernameGeneratorService, DBiamPersonenkontextRepo],
providers: [UsernameGeneratorService, DBiamPersonenkontextRepo, OxUserBlacklistRepo],
})
.overrideModule(KeycloakConfigModule)
.useModule(KeycloakConfigTestModule.forRoot({ isKeycloakRequired: true }))
Original file line number Diff line number Diff line change
@@ -21,6 +21,7 @@ import { PersonModule } from '../../../modules/person/person.module.js';
import { DbSeedModule } from '../db-seed.module.js';
import { PersonenKontextModule } from '../../../modules/personenkontext/personenkontext.module.js';
import { VornameForPersonWithTrailingSpaceError } from '../../../modules/person/domain/vorname-with-trailing-space.error.js';
import { OxUserBlacklistRepo } from '../../../modules/person/persistence/ox-user-blacklist.repo.js';

describe('DbSeedServiceIntegration', () => {
let module: TestingModule;
@@ -42,7 +43,7 @@ describe('DbSeedServiceIntegration', () => {
LoggingTestModule,
PersonenKontextModule,
],
providers: [UsernameGeneratorService, DBiamPersonenkontextRepo],
providers: [UsernameGeneratorService, DBiamPersonenkontextRepo, OxUserBlacklistRepo],
})
.overrideModule(KeycloakConfigModule)
.useModule(KeycloakConfigTestModule.forRoot({ isKeycloakRequired: true }))
2 changes: 2 additions & 0 deletions src/modules/email/persistence/email.repo.spec.ts
Original file line number Diff line number Diff line change
@@ -27,6 +27,7 @@ import { PersonAlreadyHasEnabledEmailAddressError } from '../error/person-alread
import { UserLockRepository } from '../../keycloak-administration/repository/user-lock.repository.js';
import { PersonEmailResponse } from '../../person/api/person-email-response.js';
import { generatePassword } from '../../../shared/util/password-generator.js';
import { OxUserBlacklistRepo } from '../../person/persistence/ox-user-blacklist.repo.js';
import { OrganisationID, PersonID } from '../../../shared/types/aggregate-ids.types.js';

describe('EmailRepo', () => {
@@ -45,6 +46,7 @@ describe('EmailRepo', () => {
imports: [ConfigTestModule, DatabaseTestModule.forRoot({ isDatabaseRequired: true })],
providers: [
UsernameGeneratorService,
OxUserBlacklistRepo,
EmailRepo,
EmailFactory,
PersonFactory,
27 changes: 27 additions & 0 deletions src/modules/person/domain/ox-user-blacklist-entry.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import { OXEmail, OXUserName } from '../../../shared/types/ox-ids.types.js';

export class OxUserBlacklistEntry<WasPersisted extends boolean> {
public constructor(
public id: Persisted<string, WasPersisted>,
public readonly createdAt: Persisted<Date, WasPersisted>,
public readonly updatedAt: Persisted<Date, WasPersisted>,
public email: OXEmail,
public name: string,
public username: OXUserName,
) {}

public static construct(
id: string,
createdAt: Date,
updatedAt: Date,
email: OXEmail,
name: string,
username: OXUserName,
): OxUserBlacklistEntry<true> {
return new OxUserBlacklistEntry(id, createdAt, updatedAt, email, name, username);
}

public static createNew(email: OXEmail, name: string, username: OXUserName): OxUserBlacklistEntry<false> {
return new OxUserBlacklistEntry(undefined, undefined, undefined, email, name, username);
}
}
Original file line number Diff line number Diff line change
@@ -18,11 +18,14 @@ import {
ConfigTestModule,
} from '../../../../test/utils/index.js';
import { MikroORM } from '@mikro-orm/core';
import { OxUserBlacklistRepo } from '../persistence/ox-user-blacklist.repo.js';
import { ClassLogger } from '../../../core/logging/class-logger.js';

describe('The UsernameGenerator Service', () => {
describe('UsernameGeneratorService', () => {
let module: TestingModule;
let service: UsernameGeneratorService;
let kcUserService: DeepMocked<KeycloakUserService>;
let loggerMock: DeepMocked<ClassLogger>;
let em: EntityManager;
let orm: MikroORM;

@@ -31,12 +34,21 @@ describe('The UsernameGenerator Service', () => {
imports: [ConfigTestModule, DatabaseTestModule.forRoot({ isDatabaseRequired: true })],
providers: [
UsernameGeneratorService,
{ provide: KeycloakUserService, useValue: createMock<KeycloakUserService>() },
OxUserBlacklistRepo,
{
provide: KeycloakUserService,
useValue: createMock<KeycloakUserService>(),
},
{
provide: ClassLogger,
useValue: createMock<ClassLogger>(),
},
],
}).compile();
orm = module.get(MikroORM);
service = module.get(UsernameGeneratorService);
kcUserService = module.get(KeycloakUserService);
loggerMock = module.get(ClassLogger);
em = module.get(EntityManager);

await DatabaseTestModule.setupDatabase(orm);
@@ -127,6 +139,7 @@ describe('The UsernameGenerator Service', () => {
.mockResolvedValueOnce({ ok: true, value: createMock<User<true>>() })
.mockResolvedValueOnce({ ok: false, error: new EntityNotFoundError('Not found') });
const generatedUsername: Result<string, DomainError> = await service.generateUsername('Max', 'Meyer');
expect(loggerMock.info).toHaveBeenLastCalledWith(`Next Available Username Is:mmeyer1`);
expect(generatedUsername).toEqual({ ok: true, value: 'mmeyer1' });
});

@@ -137,6 +150,7 @@ describe('The UsernameGenerator Service', () => {
} else return Promise.resolve({ ok: false, error: new EntityNotFoundError('Not found') });
});
const generatedUsername: Result<string, DomainError> = await service.generateUsername('Max', 'Meyer');
expect(loggerMock.info).toHaveBeenLastCalledWith(`Next Available Username Is:mmeyer2`);
expect(generatedUsername).toEqual({ ok: true, value: 'mmeyer2' });
});

@@ -148,6 +162,7 @@ describe('The UsernameGenerator Service', () => {
.mockResolvedValueOnce({ ok: false, error: new EntityNotFoundError('Not found') })
.mockResolvedValueOnce({ ok: true, value: createMock<User<true>>() });
const generatedUsername: Result<string, DomainError> = await service.generateUsername('Renate', 'Bergmann');
expect(loggerMock.info).toHaveBeenLastCalledWith(`Next Available Username Is:rbergmann3`);
expect(generatedUsername).toEqual({ ok: true, value: 'rbergmann3' });
});

@@ -156,6 +171,7 @@ describe('The UsernameGenerator Service', () => {
await expect(service.generateUsername('Maximilian', 'Mustermann')).rejects.toStrictEqual(
new KeycloakClientError('Could not reach'),
);
expect(loggerMock.info).toHaveBeenCalledTimes(0);
});

it('should return error if username can not be generated (cleaned names are of length 0)', async () => {
@@ -199,6 +215,8 @@ describe('The UsernameGenerator Service', () => {
const generatedUsername: Result<string, DomainError> = await service.generateUsername('Max', 'Meyer');

// Assert: The generated username should have the counter appended
expect(loggerMock.info).toHaveBeenLastCalledWith(`Next Available Username Is:mmeyer1`);

expect(generatedUsername).toEqual({ ok: true, value: 'mmeyer1' });
});
});
37 changes: 15 additions & 22 deletions src/modules/person/domain/username-generator.service.ts
Original file line number Diff line number Diff line change
@@ -8,14 +8,16 @@ import {
InvalidAttributeLengthError,
} from '../../../shared/error/index.js';
import { isDIN91379A, toDIN91379SearchForm } from '../../../shared/util/din-91379-validation.js';
import { OxUserBlacklistEntity } from '../persistence/ox-user-blacklist.entity.js';
import { EntityManager } from '@mikro-orm/postgresql';
import { OxUserBlacklistRepo } from '../persistence/ox-user-blacklist.repo.js';
import { OxUserBlacklistEntry } from './ox-user-blacklist-entry.js';
import { ClassLogger } from '../../../core/logging/class-logger.js';

@Injectable()
export class UsernameGeneratorService {
public constructor(
private readonly em: EntityManager,
private readonly logger: ClassLogger,
private kcUserService: KeycloakUserService,
private oxUserBlacklistRepo: OxUserBlacklistRepo,
) {}

public async generateUsername(firstname: string, lastname: string): Promise<Result<string, DomainError>> {
@@ -80,22 +82,17 @@ export class UsernameGeneratorService {
while (await this.usernameExists(calculatedUsername + counter)) {
counter = counter + 1;
}
/* eslint-disable no-await-in-loop */
return calculatedUsername + counter;
}

public async findByOxUsername(username: string): Promise<OxUserBlacklistEntity | null> {
const person: Option<OxUserBlacklistEntity> = await this.em.findOne(OxUserBlacklistEntity, {
username: username,
});
if (person) {
return person;
}
this.logger.info(`Next Available Username Is:${calculatedUsername + counter}`);

return null;
return calculatedUsername + counter;
}

public async usernameExists(username: string): Promise<boolean> {
/**
* This method can throw errors e.g. if Keycloak search fails.
* @param username
* @private
*/
private async usernameExists(username: string): Promise<boolean> {
// Check Keycloak
const searchResult: Result<User<true>, DomainError> = await this.kcUserService.findOne({ username });
if (searchResult.ok) {
@@ -105,12 +102,8 @@ export class UsernameGeneratorService {
}

// Check OX Blacklist for the username. If it exists then return true.
const oxUser: OxUserBlacklistEntity | null = await this.findByOxUsername(username);

if (oxUser) {
return true; // Username exists in the blacklist
}
const oxUser: Option<OxUserBlacklistEntry<true>> = await this.oxUserBlacklistRepo.findByOxUsername(username);

return false; // Username is available
return !!oxUser;
}
}
Loading

0 comments on commit 4a82f13

Please sign in to comment.