feat(15-03): ModuleGrantsController und Einbindung in GroupsModule
- GET /module-grants/matrix, GET /module-grants/users/:userId, POST /module-grants, DELETE /module-grants — alle vier rollengeschützt (RolesGuard + Roles ADMIN/SUPER_ADMIN) - matrix vor users/:userId deklariert (Beschattungsfehler-Vermeidung) - GroupsModule bindet ModuleGrantsController/-Service ein; kein Import von ModuleRegistryModule nötig, da der Service nur PrismaService braucht
This commit is contained in:
@@ -1,17 +1,24 @@
|
|||||||
import { Module } from '@nestjs/common';
|
import { Module } from '@nestjs/common';
|
||||||
import { GroupsController } from './groups.controller';
|
import { GroupsController } from './groups.controller';
|
||||||
import { GroupsService } from './groups.service';
|
import { GroupsService } from './groups.service';
|
||||||
|
import { ModuleGrantsController } from './module-grants.controller';
|
||||||
|
import { ModuleGrantsService } from './module-grants.service';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* NestJS-Modul für die Gruppenverwaltung (PERM-01, PERM-06).
|
* NestJS-Modul für die Gruppenverwaltung (PERM-01, PERM-06) und die
|
||||||
|
* Schreibseite der Modul-Freigaben (PERM-03, Plan 15-03).
|
||||||
*
|
*
|
||||||
* Exportiert GroupsService, damit UserModule (Plan 15-02 Task 2) darüber
|
* Exportiert GroupsService, damit UserModule (Plan 15-02 Task 2) darüber
|
||||||
* addUserToDefaultGroup aufrufen kann — der einzige Codepfad für die
|
* addUserToDefaultGroup aufrufen kann — der einzige Codepfad für die
|
||||||
* automatische Standardgruppen-Mitgliedschaft (D-11/D-12).
|
* automatische Standardgruppen-Mitgliedschaft (D-11/D-12).
|
||||||
|
*
|
||||||
|
* ModuleGrantsService injiziert ausschließlich PrismaService, deshalb ist
|
||||||
|
* kein Import von ModuleRegistryModule nötig — anders als
|
||||||
|
* ModuleAccessService (15-01/15-05), das hier nicht verwendet wird.
|
||||||
*/
|
*/
|
||||||
@Module({
|
@Module({
|
||||||
controllers: [GroupsController],
|
controllers: [GroupsController, ModuleGrantsController],
|
||||||
providers: [GroupsService],
|
providers: [GroupsService, ModuleGrantsService],
|
||||||
exports: [GroupsService],
|
exports: [GroupsService, ModuleGrantsService],
|
||||||
})
|
})
|
||||||
export class GroupsModule {}
|
export class GroupsModule {}
|
||||||
|
|||||||
@@ -0,0 +1,88 @@
|
|||||||
|
import {
|
||||||
|
Body,
|
||||||
|
Controller,
|
||||||
|
Delete,
|
||||||
|
ForbiddenException,
|
||||||
|
Get,
|
||||||
|
Param,
|
||||||
|
Post,
|
||||||
|
Req,
|
||||||
|
UseGuards,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Role } from '@prisma/client';
|
||||||
|
import { Request } from 'express';
|
||||||
|
import { Roles } from '../auth/decorators/roles.decorator';
|
||||||
|
import { RolesGuard } from '../auth/guards/roles.guard';
|
||||||
|
import { CreateModuleGrantDto } from './dto/create-module-grant.dto';
|
||||||
|
import { ModuleGrantsService } from './module-grants.service';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* REST-Controller für die Schreibseite der Modul-Freigaben (PERM-03).
|
||||||
|
*
|
||||||
|
* tenantId kommt ausschließlich aus dem JWT (req.tenantId ?? req.user?.tenantId),
|
||||||
|
* niemals aus Body/Params (T-03-04) — identisch zum Muster in
|
||||||
|
* GroupsController/ModuleRegistryController. Jede Route ist rollengeschützt.
|
||||||
|
*
|
||||||
|
* Statische Segmente stehen vor Parameter-Routen: `matrix` ist vor
|
||||||
|
* `users/:userId` deklariert. Dieses Projekt hat den Beschattungsfehler
|
||||||
|
* schon einmal gehabt und Unit-Tests fangen ihn nicht.
|
||||||
|
*/
|
||||||
|
@Controller('module-grants')
|
||||||
|
export class ModuleGrantsController {
|
||||||
|
constructor(private readonly moduleGrantsService: ModuleGrantsService) {}
|
||||||
|
|
||||||
|
private getTenantId(req: Request): string {
|
||||||
|
const tenantId = (req as any).tenantId ?? (req as any).user?.tenantId;
|
||||||
|
if (!tenantId) {
|
||||||
|
throw new ForbiddenException('No tenant context');
|
||||||
|
}
|
||||||
|
return tenantId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /module-grants/matrix
|
||||||
|
* Module × Gruppen mit den bestehenden Gruppen-Grants (D-15).
|
||||||
|
*/
|
||||||
|
@Get('matrix')
|
||||||
|
@UseGuards(RolesGuard)
|
||||||
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||||
|
async matrix(@Req() req: Request) {
|
||||||
|
return this.moduleGrantsService.getMatrix(this.getTenantId(req));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GET /module-grants/users/:userId
|
||||||
|
* Je aktivem Modul: über welche Gruppen der Benutzer erbt, und ob ein
|
||||||
|
* Direkt-Grant besteht (D-16).
|
||||||
|
*/
|
||||||
|
@Get('users/:userId')
|
||||||
|
@UseGuards(RolesGuard)
|
||||||
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||||
|
async userAccess(@Param('userId') userId: string, @Req() req: Request) {
|
||||||
|
return this.moduleGrantsService.getUserAccess(this.getTenantId(req), userId);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POST /module-grants
|
||||||
|
* Legt einen Grant für eine Gruppe oder einen Benutzer an.
|
||||||
|
*/
|
||||||
|
@Post()
|
||||||
|
@UseGuards(RolesGuard)
|
||||||
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||||
|
async create(@Body() dto: CreateModuleGrantDto, @Req() req: Request) {
|
||||||
|
return this.moduleGrantsService.grant(this.getTenantId(req), dto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* DELETE /module-grants
|
||||||
|
* Entzieht einen Grant. Ziel im Body, weil die Kombination aus drei
|
||||||
|
* Feldern besteht und nicht sinnvoll in einen Pfadparameter passt.
|
||||||
|
*/
|
||||||
|
@Delete()
|
||||||
|
@UseGuards(RolesGuard)
|
||||||
|
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||||
|
async remove(@Body() dto: CreateModuleGrantDto, @Req() req: Request) {
|
||||||
|
await this.moduleGrantsService.revoke(this.getTenantId(req), dto);
|
||||||
|
return { success: true };
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user