fix(16): WR-01 name lock in GroupsService.update() also checks ldapDn
A legacy binding from plan 15-06 has ldapDn set but ldapObjectGuid stays null until the first syncBoundGroupsForTenant() run backfills it. Until then, GroupsService.update() let a direct PATCH rename through even though GroupFormModal.tsx already treats the same group as AD-bound (isImported = ldapDn != null) — the D-03 name lock was only a UI convention for that window, not the backend invariant the 16-02 summary claimed.
This commit is contained in:
@@ -409,6 +409,28 @@ describe('GroupsService', () => {
|
|||||||
expect(result.name).toBe('Neu');
|
expect(result.name).toBe('Neu');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('lehnt name fuer eine Alt-Bindung (ldapDn gesetzt, ldapObjectGuid noch null) mit BadRequestException ab (WR-01, 16-REVIEW.md)', async () => {
|
||||||
|
// Eine Gruppe aus der alten Plan-15-06-Bindung: ldapDn ist gesetzt,
|
||||||
|
// aber der neue Rekonziliations-Schritt (syncBoundGroupsForTenant,
|
||||||
|
// Legacy-Backfill) hat sie noch nicht durchlaufen — ldapObjectGuid
|
||||||
|
// ist deshalb noch null. Ohne die ldapDn-Bedingung wuerde dieser
|
||||||
|
// direkte PATCH-Aufruf durchgehen, obwohl GroupFormModal.tsx dieselbe
|
||||||
|
// Gruppe bereits als importiert sperrt (isImported = ldapDn != null).
|
||||||
|
const prisma = makeFakePrisma();
|
||||||
|
const service = new GroupsService(prisma as any);
|
||||||
|
const group = await service.create('t1', { name: 'Vertrieb' });
|
||||||
|
await (prisma as any).group.update({
|
||||||
|
where: { id: group.id },
|
||||||
|
data: { ldapDn: 'cn=Vertrieb,dc=example,dc=com', ldapObjectGuid: null },
|
||||||
|
});
|
||||||
|
|
||||||
|
await expect(
|
||||||
|
service.update('t1', group.id, { name: 'Umbenannt' }),
|
||||||
|
).rejects.toBeInstanceOf(BadRequestException);
|
||||||
|
const list = await service.listForTenant('t1');
|
||||||
|
expect(list.find((g) => g.id === group.id)!.name).toBe('Vertrieb');
|
||||||
|
});
|
||||||
|
|
||||||
it('setzt internalName auf einer importierten Gruppe, name bleibt unangetastet', async () => {
|
it('setzt internalName auf einer importierten Gruppe, name bleibt unangetastet', async () => {
|
||||||
const prisma = makeFakePrisma();
|
const prisma = makeFakePrisma();
|
||||||
const service = new GroupsService(prisma as any);
|
const service = new GroupsService(prisma as any);
|
||||||
|
|||||||
@@ -107,10 +107,16 @@ export class GroupsService {
|
|||||||
* setzen.
|
* setzen.
|
||||||
*
|
*
|
||||||
* Namenssperre (D-03/D-07): trägt die geladene Gruppe einen gesetzten
|
* Namenssperre (D-03/D-07): trägt die geladene Gruppe einen gesetzten
|
||||||
* ldapObjectGuid, ist sie aus dem Verzeichnis importiert — ein `name`
|
* ldapObjectGuid ODER ldapDn, ist sie aus dem Verzeichnis importiert —
|
||||||
* im Request wird dann mit BadRequestException abgelehnt, BEVOR die
|
* ein `name` im Request wird dann mit BadRequestException abgelehnt,
|
||||||
* Leerstring-Prüfung läuft. Das ist eine Backend-Invariante, kein UI-
|
* BEVOR die Leerstring-Prüfung läuft. Das ldapDn-Kriterium deckt eine
|
||||||
* Feld-Disable: ein direkter API-Aufruf kommt an ihr nicht vorbei.
|
* Alt-Bindung aus Plan 15-06 ab, die den neuen Rekonziliations-Schritt
|
||||||
|
* (syncBoundGroupsForTenant, Legacy-Backfill) noch nicht durchlaufen hat
|
||||||
|
* und deshalb noch keinen ldapObjectGuid trägt — ohne diese zweite
|
||||||
|
* Bedingung wäre die Sperre bis zum ersten Sync-Lauf nur eine
|
||||||
|
* UI-Konvention (WR-01, 16-REVIEW.md). Das ist eine Backend-Invariante,
|
||||||
|
* kein UI-Feld-Disable: ein direkter API-Aufruf kommt an ihr nicht
|
||||||
|
* vorbei.
|
||||||
*
|
*
|
||||||
* internalName (D-04) läuft unabhängig von dieser Sperre — jede Gruppe,
|
* internalName (D-04) läuft unabhängig von dieser Sperre — jede Gruppe,
|
||||||
* importiert oder lokal, darf ihn setzen. undefined lässt die Spalte
|
* importiert oder lokal, darf ihn setzen. undefined lässt die Spalte
|
||||||
@@ -132,7 +138,7 @@ export class GroupsService {
|
|||||||
} = {};
|
} = {};
|
||||||
|
|
||||||
if (data.name !== undefined) {
|
if (data.name !== undefined) {
|
||||||
if (existing.ldapObjectGuid) {
|
if (existing.ldapObjectGuid || existing.ldapDn) {
|
||||||
throw new BadRequestException(
|
throw new BadRequestException(
|
||||||
'Der Name einer aus dem Verzeichnis übernommenen Gruppe wird dort gepflegt und kann hier nicht geändert werden',
|
'Der Name einer aus dem Verzeichnis übernommenen Gruppe wird dort gepflegt und kann hier nicht geändert werden',
|
||||||
);
|
);
|
||||||
|
|||||||
Reference in New Issue
Block a user