Skip to content

Commit

Permalink
Merge branch 'main' into SPSH-1035
Browse files Browse the repository at this point in the history
  • Loading branch information
YoussefBouch authored Sep 9, 2024
2 parents 3c5d4e1 + 4bf3c8c commit 3ade453
Show file tree
Hide file tree
Showing 27 changed files with 519 additions and 782 deletions.
30 changes: 27 additions & 3 deletions src/core/ldap/domain/ldap-client.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { LdapClient } from './ldap-client.js';
import { Client, Entry, SearchResult } from 'ldapts';
import { KennungRequiredForSchuleError } from '../../../modules/organisation/specification/error/kennung-required-for-schule.error.js';
import { PersonID } from '../../../shared/types/aggregate-ids.types.js';

describe('LDAP Client Service', () => {
let app: INestApplication;
Expand Down Expand Up @@ -199,6 +200,29 @@ describe('LDAP Client Service', () => {
expect(result.ok).toBeTruthy();
});

it('when called with extra entryUUID should return truthy result', async () => {
ldapClientMock.getClient.mockImplementation(() => {
clientMock.bind.mockResolvedValue();
clientMock.add.mockResolvedValueOnce();
clientMock.search.mockResolvedValueOnce(
createMock<SearchResult>({ searchEntries: [createMock<Entry>()] }),
); //mock existsSchule
clientMock.search.mockResolvedValueOnce(createMock<SearchResult>()); //mock existsLehrer

return clientMock;
});
const testLehrer: PersonData = {
id: faker.string.uuid(),
vorname: faker.person.firstName(),
familienname: faker.person.lastName(),
referrer: faker.lorem.word(),
ldapEntryUUID: faker.string.uuid(),
};
const result: Result<PersonData> = await ldapClientService.createLehrer(testLehrer, organisation);

expect(result.ok).toBeTruthy();
});

it('when called with valid person and an organisation without kennung should return error result', async () => {
ldapClientMock.getClient.mockImplementation(() => {
clientMock.bind.mockResolvedValue();
Expand Down Expand Up @@ -337,7 +361,7 @@ describe('LDAP Client Service', () => {
return clientMock;
});

const result: Result<PersonData> = await ldapClientService.deleteLehrerByPersonId(person);
const result: Result<PersonID> = await ldapClientService.deleteLehrerByPersonId(person.id);

expect(result.ok).toBeTruthy();
});
Expand All @@ -354,7 +378,7 @@ describe('LDAP Client Service', () => {
return clientMock;
});

const result: Result<PersonData> = await ldapClientService.deleteLehrerByPersonId(person);
const result: Result<PersonID> = await ldapClientService.deleteLehrerByPersonId(person.id);

expect(result.ok).toBeFalsy();
});
Expand All @@ -365,7 +389,7 @@ describe('LDAP Client Service', () => {
clientMock.add.mockResolvedValueOnce();
return clientMock;
});
const result: Result<PersonData> = await ldapClientService.deleteLehrerByPersonId(person);
const result: Result<PersonID> = await ldapClientService.deleteLehrerByPersonId(person.id);

expect(result.ok).toBeFalsy();
});
Expand Down
22 changes: 16 additions & 6 deletions src/core/ldap/domain/ldap-client.service.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { Injectable } from '@nestjs/common';
import { ClassLogger } from '../../logging/class-logger.js';
import { Client, SearchResult } from 'ldapts';
import { Client, Control, SearchResult } from 'ldapts';
import { LdapEntityType, LdapOrganisationEntry, LdapPersonEntry, LdapRoleEntry } from './ldap.types.js';
import { KennungRequiredForSchuleError } from '../../../modules/organisation/specification/error/kennung-required-for-schule.error.js';
import { LdapClient } from './ldap-client.js';
import { LdapInstanceConfig } from '../ldap-instance-config.js';
import { UsernameRequiredError } from '../../../modules/person/domain/username-required.error.js';
import { Mutex } from 'async-mutex';
import { LdapSearchError } from '../error/ldap-search.error.js';
import { PersonID } from '../../../shared/types/aggregate-ids.types.js';

export type PersonData = {
vorname: string;
familienname: string;
id: string;
referrer?: string;
ldapEntryUUID?: string;
};

type OrganisationData = {
Expand Down Expand Up @@ -153,7 +155,15 @@ export class LdapClientService {
mail: [`${person.referrer}@schule-sh.de`],
objectclass: ['inetOrgPerson'],
};
await client.add(lehrerUid, entry);

const controls: Control[] = [];
if (person.ldapEntryUUID) {
const relaxRulesControlOID: string = '1.3.6.1.4.1.4203.666.5.12';
entry.entryUUID = person.ldapEntryUUID;
controls.push(new Control(relaxRulesControlOID));
}

await client.add(lehrerUid, entry, controls);
this.logger.info(`LDAP: Successfully created lehrer ${lehrerUid}`);

return { ok: true, value: person };
Expand All @@ -164,7 +174,7 @@ export class LdapClientService {
return `uid=${referrer},cn=lehrer,ou=${orgaKennung},ou=oeffentlicheSchulen,dc=schule-sh,dc=de`;
}

public async deleteLehrerByPersonId(person: PersonData): Promise<Result<PersonData>> {
public async deleteLehrerByPersonId(personId: PersonID): Promise<Result<PersonID>> {
return this.mutex.runExclusive(async () => {
this.logger.info('LDAP: deleteLehrer');
const client: Client = this.ldapClient.getClient();
Expand All @@ -173,7 +183,7 @@ export class LdapClientService {

const searchResultLehrer: SearchResult = await client.search(`ou=oeffentlicheSchulen,dc=schule-sh,dc=de`, {
scope: 'sub',
filter: `(employeeNumber=${person.id})`,
filter: `(employeeNumber=${personId})`,
});
if (!searchResultLehrer.searchEntries[0]) {
return {
Expand All @@ -182,9 +192,9 @@ export class LdapClientService {
};
}
await client.del(searchResultLehrer.searchEntries[0].dn);
this.logger.info(`LDAP: Successfully deleted lehrer by personId:${person.id}`);
this.logger.info(`LDAP: Successfully deleted lehrer by personId:${personId}`);

return { ok: true, value: person };
return { ok: true, value: personId };
});
}

Expand Down
41 changes: 19 additions & 22 deletions src/core/ldap/domain/ldap-event-handler.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import { GlobalValidationPipe } from '../../../shared/validation/global-validati
import { LdapModule } from '../ldap.module.js';
import { LdapEventHandler } from './ldap-event-handler.js';
import { createMock, DeepMocked } from '@golevelup/ts-jest';
import { LdapClientService, PersonData } from './ldap-client.service.js';
import { LdapClientService } from './ldap-client.service.js';
import { SchuleCreatedEvent } from '../../../shared/events/schule-created.event.js';
import { PersonRepository } from '../../../modules/person/persistence/person.repository.js';
import { RolleRepo } from '../../../modules/rolle/repo/rolle.repo.js';
Expand All @@ -25,9 +25,11 @@ import { DBiamPersonenkontextRepo } from '../../../modules/personenkontext/persi
import { PersonenkontextFactory } from '../../../modules/personenkontext/domain/personenkontext.factory.js';
import { PersonenkontextUpdatedEvent } from '../../../shared/events/personenkontext-updated.event.js';
import { ClassLogger } from '../../logging/class-logger.js';
import { PersonenkontextDeletedEvent } from '../../../shared/events/personenkontext-deleted.event.js';
import { KennungRequiredForSchuleError } from '../../../modules/organisation/specification/error/kennung-required-for-schule.error.js';
import { RootDirectChildrenType } from '../../../modules/organisation/domain/organisation.enums.js';
import { PersonID } from '../../../shared/types/aggregate-ids.types.js';
import { PersonDeletedEvent } from '../../../shared/events/person-deleted.event.js';
import { LdapSearchError } from '../error/ldap-search.error.js';
import { LdapEntityType } from './ldap.types.js';

describe('LDAP Event Handler', () => {
let app: INestApplication;
Expand Down Expand Up @@ -195,36 +197,31 @@ describe('LDAP Event Handler', () => {
});
});

describe('handlePersonenkontextDeletedEvent', () => {
describe('when calling LdapClientService.deleteLehrer is successful', () => {
describe('handlePersonDeletedEvent', () => {
describe('when calling LdapClientService.deleteLehrerByPersonId is successful', () => {
it('should NOT log errors', async () => {
const deletionResult: Result<PersonData> = {
const deletionResult: Result<PersonID> = {
ok: true,
value: {
vorname: faker.person.firstName(),
familienname: faker.person.lastName(),
id: faker.string.uuid(),
referrer: faker.internet.userName(),
},
value: faker.string.uuid(),
};
ldapClientServiceMock.deleteLehrer.mockResolvedValueOnce(deletionResult);
ldapClientServiceMock.deleteLehrerByPersonId.mockResolvedValueOnce(deletionResult);

await ldapEventHandler.handlePersonenkontextDeletedEvent(createMock<PersonenkontextDeletedEvent>());
await ldapEventHandler.handlePersonDeletedEvent(createMock<PersonDeletedEvent>());

expect(loggerMock.error).toHaveBeenCalledTimes(0);
});
});

describe('when calling LdapClientService.deleteLehrer is return error', () => {
describe('when calling LdapClientService.deleteLehrerByPersonId is return error', () => {
it('should log errors', async () => {
const error: KennungRequiredForSchuleError = new KennungRequiredForSchuleError();
const deletionResult: Result<PersonData> = {
const error: LdapSearchError = new LdapSearchError(LdapEntityType.LEHRER);
const deletionResult: Result<PersonID> = {
ok: false,
error: error,
};
ldapClientServiceMock.deleteLehrer.mockResolvedValueOnce(deletionResult);
ldapClientServiceMock.deleteLehrerByPersonId.mockResolvedValueOnce(deletionResult);

await ldapEventHandler.handlePersonenkontextDeletedEvent(createMock<PersonenkontextDeletedEvent>());
await ldapEventHandler.handlePersonDeletedEvent(createMock<PersonDeletedEvent>());

expect(loggerMock.error).toHaveBeenCalledTimes(1);
expect(loggerMock.error).toHaveBeenCalledWith(error.message);
Expand Down Expand Up @@ -296,7 +293,7 @@ describe('LDAP Event Handler', () => {

await ldapEventHandler.handlePersonenkontextUpdatedEvent(event);

expect(ldapClientServiceMock.deleteLehrerByPersonId).toHaveBeenCalledTimes(1);
expect(ldapClientServiceMock.deleteLehrer).toHaveBeenCalledTimes(1);
});

describe('when ldap client fails', () => {
Expand Down Expand Up @@ -348,14 +345,14 @@ describe('LDAP Event Handler', () => {
],
[],
);
ldapClientServiceMock.deleteLehrerByPersonId.mockResolvedValueOnce({
ldapClientServiceMock.deleteLehrer.mockResolvedValueOnce({
ok: false,
error: new Error('Error'),
});

await ldapEventHandler.handlePersonenkontextUpdatedEvent(event);

expect(ldapClientServiceMock.deleteLehrerByPersonId).toHaveBeenCalledTimes(1);
expect(ldapClientServiceMock.deleteLehrer).toHaveBeenCalledTimes(1);
});
});
});
21 changes: 9 additions & 12 deletions src/core/ldap/domain/ldap-event-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,9 @@ import { ClassLogger } from '../../logging/class-logger.js';
import { RollenArt } from '../../../modules/rolle/domain/rolle.enums.js';
import { SchuleDeletedEvent } from '../../../shared/events/schule-deleted.event.js';
import { PersonenkontextUpdatedEvent } from '../../../shared/events/personenkontext-updated.event.js';
import { PersonenkontextDeletedEvent } from '../../../shared/events/personenkontext-deleted.event.js';
import { PersonenkontextEventKontextData } from '../../../shared/events/personenkontext-event.types.js';
import { PersonDeletedEvent } from '../../../shared/events/person-deleted.event.js';
import { PersonID } from '../../../shared/types/aggregate-ids.types.js';

@Injectable()
export class LdapEventHandler {
Expand Down Expand Up @@ -51,14 +52,10 @@ export class LdapEventHandler {
}
}

@EventHandler(PersonenkontextDeletedEvent)
public async handlePersonenkontextDeletedEvent(event: PersonenkontextDeletedEvent): Promise<void> {
this.logger.info(
`Received PersonenkontextDeletedEvent, personId:${event.personData.id}, orgaId:${event.kontextData.orgaId}, rolleId:${event.kontextData.rolleId}`,
);
const deletionResult: Result<PersonData> = await this.ldapClientService.deleteLehrer(event.personData, {
kennung: event.kontextData.orgaKennung,
});
@EventHandler(PersonDeletedEvent)
public async handlePersonDeletedEvent(event: PersonDeletedEvent): Promise<void> {
this.logger.info(`Received PersonenkontextDeletedEvent, personId:${event.personId}`);
const deletionResult: Result<PersonID> = await this.ldapClientService.deleteLehrerByPersonId(event.personId);
if (!deletionResult.ok) {
this.logger.error(deletionResult.error.message);
}
Expand All @@ -76,9 +73,9 @@ export class LdapEventHandler {
.filter((pk: PersonenkontextEventKontextData) => pk.rolle === RollenArt.LEHR)
.map(async (pk: PersonenkontextEventKontextData) => {
this.logger.info(`Call LdapClientService because rollenArt is LEHR, pkId: ${pk.id}`);
const deletionResult: Result<PersonData> = await this.ldapClientService.deleteLehrerByPersonId(
event.person,
);
const deletionResult: Result<PersonData> = await this.ldapClientService.deleteLehrer(event.person, {
kennung: pk.orgaKennung,
});
if (!deletionResult.ok) {
this.logger.error(deletionResult.error.message);
}
Expand Down
1 change: 1 addition & 0 deletions src/core/ldap/domain/ldap.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ export type LdapPersonEntry = {
employeeNumber: string;
mail: string[];
objectclass: string[];
entryUUID?: string;
};

export type LdapOrganisationEntry = {
Expand Down
Loading

0 comments on commit 3ade453

Please sign in to comment.