feat(module-grants): Stufe Verwalten in Freigaben-Matrix und Benutzerdetails, Doku und Changelog
- Matrix und Benutzerdetails liefern und zeigen die Stufe, Auswahlfeld Benutzen/Verwalten - Gruppen mit Verwalten sind in den Benutzerdetails markiert - Administrationshandbuch, Anwenderanleitung und Changelog beschreiben die Stufen Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -495,7 +495,18 @@ describe('ModuleGrantsService.getMatrix', () => {
|
||||
|
||||
expect(matrix.modules.map((m: any) => m.id)).toEqual(['mod-a', 'mod-b']);
|
||||
expect(matrix.groups.map((g: any) => g.name)).toEqual(['Alpha', 'Zeta']);
|
||||
expect(matrix.grants).toEqual([{ moduleId: 'mod-a', groupId: 'g1' }]);
|
||||
expect(matrix.grants).toEqual([{ moduleId: 'mod-a', groupId: 'g1', level: 'USE' }]);
|
||||
});
|
||||
|
||||
it('261002-icv: jedes Grant-Element trägt die Stufe', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
seedBase(prisma);
|
||||
const service = new ModuleGrantsService(prisma as any);
|
||||
await service.grant('t1', { moduleId: 'mod-1', groupId: 'g1', level: 'MANAGE' });
|
||||
|
||||
const matrix = await service.getMatrix('t1');
|
||||
|
||||
expect(matrix.grants).toEqual([{ moduleId: 'mod-1', groupId: 'g1', level: 'MANAGE' }]);
|
||||
});
|
||||
|
||||
it('empty: ein Mandant ohne Gruppen liefert eine leere Gruppenliste und wirft nicht', async () => {
|
||||
@@ -552,11 +563,32 @@ describe('ModuleGrantsService.getUserAccess', () => {
|
||||
module: { id: 'mod-1', category: 'ops', name: 'Modul Eins' },
|
||||
viaGroups: ['Gruppe A'],
|
||||
direct: false,
|
||||
directLevel: null,
|
||||
manageViaGroups: [],
|
||||
},
|
||||
]);
|
||||
expect(result.groups).toEqual([{ id: 'g1', name: 'Gruppe A', source: 'MANUAL' }]);
|
||||
});
|
||||
|
||||
it('261002-icv: directLevel zeigt die Stufe der Direkt-Freigabe, manageViaGroups nennt Gruppen mit Verwalten', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
seedBase(prisma);
|
||||
prisma.__seedGroup({ id: 'g2', tenantId: 't1', name: 'Gruppe B' });
|
||||
prisma.__seedMembership('g1', 'u1');
|
||||
prisma.__seedMembership('g2', 'u1');
|
||||
const service = new ModuleGrantsService(prisma as any);
|
||||
await service.grant('t1', { moduleId: 'mod-1', groupId: 'g1' });
|
||||
await service.grant('t1', { moduleId: 'mod-1', groupId: 'g2', level: 'MANAGE' });
|
||||
await service.grant('t1', { moduleId: 'mod-1', userId: 'u1', level: 'MANAGE' });
|
||||
|
||||
const row = (await service.getUserAccess('t1', 'u1')).modules[0];
|
||||
|
||||
expect(row.direct).toBe(true);
|
||||
expect(row.directLevel).toBe('MANAGE');
|
||||
expect([...row.viaGroups].sort()).toEqual(['Gruppe A', 'Gruppe B']);
|
||||
expect(row.manageViaGroups).toEqual(['Gruppe B']);
|
||||
});
|
||||
|
||||
it('adjacency: ein Direkt-Grant UND ein Gruppen-Grant auf dasselbe Modul erscheinen gleichzeitig, keiner verdrängt den anderen', async () => {
|
||||
const prisma = makeFakePrisma();
|
||||
seedBase(prisma);
|
||||
|
||||
@@ -226,7 +226,7 @@ export class ModuleGrantsService {
|
||||
}),
|
||||
tenantPrisma.moduleGrant.findMany({
|
||||
where: { tenantId, groupId: { not: null } },
|
||||
select: { moduleId: true, groupId: true },
|
||||
select: { moduleId: true, groupId: true, level: true },
|
||||
}),
|
||||
]);
|
||||
|
||||
@@ -242,6 +242,7 @@ export class ModuleGrantsService {
|
||||
grants: groupGrants.map((g) => ({
|
||||
moduleId: g.moduleId,
|
||||
groupId: g.groupId,
|
||||
level: g.level ?? ModuleGrantLevel.USE,
|
||||
})),
|
||||
};
|
||||
}
|
||||
@@ -255,7 +256,8 @@ export class ModuleGrantsService {
|
||||
* dadurch sichtbar. `modules` beantwortet je aktivem Modul die andere
|
||||
* Frage (welche Gruppe gewährt dieses Modul, und besteht zusätzlich ein
|
||||
* Direkt-Grant) und behält dafür je Eintrag exakt die Form
|
||||
* { module, viaGroups, direct }.
|
||||
* { module, viaGroups, direct }; seit 261002-icv kommen `directLevel` und
|
||||
* `manageViaGroups` hinzu (Anzeige der Freigabestufe).
|
||||
*
|
||||
* Anzeigename mit Fallback (D-04, UI-SPEC Surface Contract 6): beide
|
||||
* Projektionsstellen (viaGroups-Namen, groups[].name) liefern
|
||||
@@ -282,7 +284,7 @@ export class ModuleGrantsService {
|
||||
}),
|
||||
tenantPrisma.moduleGrant.findMany({
|
||||
where: { tenantId, userId },
|
||||
select: { moduleId: true },
|
||||
select: { moduleId: true, level: true },
|
||||
}),
|
||||
// Mandantengebunden seit 260909-jts (Aufgabe 3): der Kontext wird
|
||||
// über denselben tenantPrisma wie die drei Abfragen oben gesetzt —
|
||||
@@ -299,13 +301,23 @@ export class ModuleGrantsService {
|
||||
}),
|
||||
]);
|
||||
|
||||
const directModuleIds = new Set(directGrants.map((g) => g.moduleId));
|
||||
const directLevelByModule = new Map<string, ModuleGrantLevel>();
|
||||
for (const g of directGrants) {
|
||||
directLevelByModule.set(g.moduleId, g.level ?? ModuleGrantLevel.USE);
|
||||
}
|
||||
const groupNamesByModule = new Map<string, string[]>();
|
||||
const manageGroupNamesByModule = new Map<string, string[]>();
|
||||
for (const g of groupGrants) {
|
||||
if (!g.group) continue;
|
||||
const displayName = g.group.internalName ?? g.group.name;
|
||||
const names = groupNamesByModule.get(g.moduleId) ?? [];
|
||||
names.push(g.group.internalName ?? g.group.name);
|
||||
names.push(displayName);
|
||||
groupNamesByModule.set(g.moduleId, names);
|
||||
if (g.level === ModuleGrantLevel.MANAGE) {
|
||||
const manageNames = manageGroupNamesByModule.get(g.moduleId) ?? [];
|
||||
manageNames.push(displayName);
|
||||
manageGroupNamesByModule.set(g.moduleId, manageNames);
|
||||
}
|
||||
}
|
||||
|
||||
const modules = activations
|
||||
@@ -328,7 +340,11 @@ export class ModuleGrantsService {
|
||||
modules: modules.map((module) => ({
|
||||
module,
|
||||
viaGroups: groupNamesByModule.get(module.id) ?? [],
|
||||
direct: directModuleIds.has(module.id),
|
||||
direct: directLevelByModule.has(module.id),
|
||||
// 261002-icv: Stufe der Direkt-Freigabe (null ohne Direkt-Grant) und
|
||||
// die Gruppen, die Verwalten gewähren (Teilmenge von viaGroups).
|
||||
directLevel: directLevelByModule.get(module.id) ?? null,
|
||||
manageViaGroups: manageGroupNamesByModule.get(module.id) ?? [],
|
||||
})),
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user