feat(module-grants): Proxmox, Handelsware und DKV mit Freigabestufe Verwalten
- Proxmox-Schreibwege und Handelsware-Einstellungen auf @ModuleManage umgestellt - DKV-Fleet: ganze Klasse Verwalten-Stufe, Benutzen allein bleibt ohne Zugriff - Metadaten-Test belegt umgestellte und bewusst Administratoren vorbehaltene Handler - Webseiten (Proxmox, Handelsware, Widget) folgen canManage, DKV-Zugriffsseite erklärt die Stufe Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -15,8 +15,7 @@ import {
|
||||
UseInterceptors,
|
||||
} from '@nestjs/common';
|
||||
import { FileInterceptor } from '@nestjs/platform-express';
|
||||
import { Role } from '@prisma/client';
|
||||
import { Roles } from '../auth/decorators/roles.decorator';
|
||||
import { ModuleManage } from '../module-registry/module.guard';
|
||||
import type {
|
||||
AuthenticatedRequest,
|
||||
UploadedFileLike,
|
||||
@@ -29,11 +28,17 @@ import { DkvHistoryQueryDto } from './dto/dkv-history.dto';
|
||||
import { CreateVehicleDto, UpdateVehicleDto } from './dto/dkv-vehicle.dto';
|
||||
|
||||
/**
|
||||
* DkvController — all /dkv/* routes, ADMIN-only (V4).
|
||||
* DkvController — all /dkv/* routes, manager level (V4, 261002-icv).
|
||||
*
|
||||
* Every handler carries @Roles(Role.ADMIN, Role.SUPER_ADMIN).
|
||||
* Global JwtAuthGuard enforces JWT authentication; RolesGuard enforces the
|
||||
* @Roles decorator. No route is publicly accessible.
|
||||
* The whole module is Verwalten-level: `@ModuleManage('dkv-fleet')` on the
|
||||
* class replaces the former per-handler @Roles(ADMIN, SUPER_ADMIN). Access is
|
||||
* therefore limited to administrators and to users with the grant level
|
||||
* "Verwalten" (MANAGE) on the dkv-fleet module. Users with only "Benutzen"
|
||||
* (USE) keep getting 403 exactly as before — nothing was widened. The class
|
||||
* guard additionally requires the dkv-fleet module to be active for the
|
||||
* tenant (the web page already required that).
|
||||
* Global JwtAuthGuard enforces JWT authentication; ModuleGuard enforces the
|
||||
* grant level. No route is publicly accessible.
|
||||
*
|
||||
* Tenant extraction: `req.tenantId` set by TenantGuard (runs after auth guards).
|
||||
* All operations are scoped to the authenticated tenant's data.
|
||||
@@ -52,6 +57,7 @@ import { CreateVehicleDto, UpdateVehicleDto } from './dto/dkv-vehicle.dto';
|
||||
* POST /dkv/vehicles/import — bulk-import from CSV upload
|
||||
*/
|
||||
@Controller('dkv')
|
||||
@ModuleManage('dkv-fleet')
|
||||
export class DkvController {
|
||||
constructor(
|
||||
private readonly dkvService: DkvService,
|
||||
@@ -62,7 +68,6 @@ export class DkvController {
|
||||
|
||||
/** GET /dkv/config — returns module config with username + hasPassword. 404 when not yet configured. */
|
||||
@Get('config')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async getConfig(@Req() req: AuthenticatedRequest) {
|
||||
const tenantId = this._requireTenant(req);
|
||||
const config = await this.dkvService.getConfigForApi(tenantId);
|
||||
@@ -79,7 +84,6 @@ export class DkvController {
|
||||
* or stops the cron job if isActive is false.
|
||||
*/
|
||||
@Put('config')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async saveConfig(@Req() req: AuthenticatedRequest, @Body() dto: DkvConfigDto) {
|
||||
const tenantId = this._requireTenant(req);
|
||||
const result = await this.dkvService.saveConfig(tenantId, dto);
|
||||
@@ -98,7 +102,6 @@ export class DkvController {
|
||||
|
||||
/** POST /dkv/check-now — immediately run the inbox processing pipeline. */
|
||||
@Post('check-now')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async checkNow(@Req() req: AuthenticatedRequest) {
|
||||
const tenantId = this._requireTenant(req);
|
||||
return this.dkvService.checkNow(tenantId);
|
||||
@@ -109,7 +112,6 @@ export class DkvController {
|
||||
* Used by the InboxConfigForm "Verbindung testen" button before saving.
|
||||
*/
|
||||
@Post('test-connection')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async testConnection(@Req() req: AuthenticatedRequest, @Body() dto: DkvConfigDto) {
|
||||
const tenantId = this._requireTenant(req);
|
||||
return this.dkvService.testConnection(tenantId, dto);
|
||||
@@ -122,7 +124,6 @@ export class DkvController {
|
||||
* T-07-06: pagination parameters validated by DkvHistoryQueryDto.
|
||||
*/
|
||||
@Get('history')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async getHistory(@Req() req: AuthenticatedRequest, @Query() query: DkvHistoryQueryDto) {
|
||||
const tenantId = this._requireTenant(req);
|
||||
const page = query.page ?? 1;
|
||||
@@ -140,7 +141,6 @@ export class DkvController {
|
||||
* containing path separators or non-whitelisted characters is rejected.
|
||||
*/
|
||||
@Get('exports/:filename')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async downloadExport(
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Param('filename') filename: string,
|
||||
@@ -168,7 +168,6 @@ export class DkvController {
|
||||
|
||||
/** GET /dkv/vehicles — list all vehicle master records for this tenant. */
|
||||
@Get('vehicles')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async listVehicles(@Req() req: AuthenticatedRequest) {
|
||||
const tenantId = this._requireTenant(req);
|
||||
return this.dkvService.listVehicles(tenantId);
|
||||
@@ -176,7 +175,6 @@ export class DkvController {
|
||||
|
||||
/** POST /dkv/vehicles — create a new vehicle master record. */
|
||||
@Post('vehicles')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async createVehicle(@Req() req: AuthenticatedRequest, @Body() dto: CreateVehicleDto) {
|
||||
const tenantId = this._requireTenant(req);
|
||||
return this.dkvService.createVehicle(tenantId, dto);
|
||||
@@ -184,7 +182,6 @@ export class DkvController {
|
||||
|
||||
/** PUT /dkv/vehicles/:id — update an existing vehicle master record. */
|
||||
@Put('vehicles/:id')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async updateVehicle(
|
||||
@Req() req: AuthenticatedRequest,
|
||||
@Param('id') id: string,
|
||||
@@ -196,7 +193,6 @@ export class DkvController {
|
||||
|
||||
/** DELETE /dkv/vehicles/:id — delete a vehicle master record. */
|
||||
@Delete('vehicles/:id')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
async deleteVehicle(@Req() req: AuthenticatedRequest, @Param('id') id: string) {
|
||||
const tenantId = this._requireTenant(req);
|
||||
return this.dkvService.deleteVehicle(tenantId, id);
|
||||
@@ -213,7 +209,6 @@ export class DkvController {
|
||||
* The controller reads `file.buffer.toString('utf-8')` and passes to DkvService.
|
||||
*/
|
||||
@Post('vehicles/import')
|
||||
@Roles(Role.ADMIN, Role.SUPER_ADMIN)
|
||||
@UseInterceptors(FileInterceptor('file', {
|
||||
limits: { fileSize: 5 * 1024 * 1024 }, // 5 MB — generous for any realistic vehicle list (WR-05)
|
||||
}))
|
||||
|
||||
Reference in New Issue
Block a user