Files
ApiCore/ApiCore-Backend/src/honey-batches/honey-batches.controller.ts
T
Nick SzaunigandClaude Opus 5 b8809eeaa2 fix: restore the authorization decorators three new routes displaced
Inserting the new PATCH handlers directly above an existing route put them
between that route's @RequirePermission decorator and its method, so the
decorator bound to the new handler instead:

- DELETE /documents/:id lost documents:delete
- POST /honey-batches/:id/measurements lost honey-batches:measure
- POST /queens/:id/evaluations lost queens:evaluate

and each new PATCH ended up carrying two @RequirePermission decorators, of
which only one takes effect.

This did not open anything up. PermissionsGuard fails closed by decorator
absence: a non-public route with neither @RequirePermission nor
@AnyAuthenticatedUser is denied outright, so the three routes would have
returned 403 for every caller including an administrator. The effect was a
broken endpoint, not a bypass.

Each decorator is back on the route it belongs to, and the duplicates are
gone. test/authorization-coverage.e2e-spec.ts asserts exactly this contract
across every route and is the check that should run before a route change
ships.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-22 02:12:56 +02:00

144 lines
4.1 KiB
TypeScript

import {
Body,
Controller,
Delete,
Get,
Param,
Patch,
Post,
Put,
Query,
} from '@nestjs/common';
import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
import { HoneyBatchStatus } from '../../generated/prisma';
import { RequirePermission } from '../auth/decorators/require-permission.decorator';
import { CurrentUser } from '../auth/decorators/current-user.decorator';
import type { AuthenticatedUser } from '../auth/types/authenticated-user';
import { CreateHoneyBatchDto } from './dto/create-honey-batch.dto';
import { UpdateHoneyBatchDto } from './dto/update-honey-batch.dto';
import { CreateHoneyMeasurementDto } from './dto/create-honey-measurement.dto';
import { UpdateHoneyMeasurementDto } from './dto/update-honey-measurement.dto';
import { RelocateHoneyBatchDto } from './dto/relocate-honey-batch.dto';
import { SetHoneyBatchContainerAllocationDto } from './dto/set-honey-batch-container-allocation.dto';
import { HoneyBatchesService } from './honey-batches.service';
@ApiTags('honey-batches')
@ApiBearerAuth()
@Controller('honey-batches')
export class HoneyBatchesController {
constructor(private readonly honeyBatchesService: HoneyBatchesService) {}
@RequirePermission('honey-batches:read')
@Get()
findAll(@Query('status') status?: HoneyBatchStatus) {
return this.honeyBatchesService.findAll({ status });
}
@RequirePermission('honey-batches:read')
@Get(':id')
findOne(@Param('id') id: string) {
return this.honeyBatchesService.findById(id);
}
@RequirePermission('honey-batches:read')
@Get(':id/traceability')
traceability(@Param('id') id: string) {
return this.honeyBatchesService.traceability(id);
}
@RequirePermission('honey-batches:read')
@Get(':id/relocations')
findRelocations(@Param('id') id: string) {
return this.honeyBatchesService.findRelocations(id);
}
@RequirePermission('honey-batches:read')
@Get(':id/containers')
listContainerAllocations(@Param('id') id: string) {
return this.honeyBatchesService.listContainerAllocations(id);
}
@RequirePermission('honey-batches:relocate')
@Put(':id/containers/:containerId')
setContainerAllocation(
@Param('id') id: string,
@Param('containerId') containerId: string,
@Body() dto: SetHoneyBatchContainerAllocationDto,
@CurrentUser() actor: AuthenticatedUser,
) {
return this.honeyBatchesService.setContainerAllocation(
id,
containerId,
dto,
actor.id,
);
}
@RequirePermission('honey-batches:relocate')
@Delete(':id/containers/:containerId')
removeContainerAllocation(
@Param('id') id: string,
@Param('containerId') containerId: string,
@CurrentUser() actor: AuthenticatedUser,
) {
return this.honeyBatchesService.removeContainerAllocation(
id,
containerId,
actor.id,
);
}
@RequirePermission('honey-batches:relocate')
@Post(':id/relocate')
relocate(
@Param('id') id: string,
@Body() dto: RelocateHoneyBatchDto,
@CurrentUser() actor: AuthenticatedUser,
) {
return this.honeyBatchesService.relocate(id, dto, actor.id);
}
@RequirePermission('honey-batches:create')
@Post()
create(
@Body() dto: CreateHoneyBatchDto,
@CurrentUser() actor: AuthenticatedUser,
) {
return this.honeyBatchesService.create(dto, actor.id);
}
@RequirePermission('honey-batches:update')
@Patch(':id')
update(
@Param('id') id: string,
@Body() dto: UpdateHoneyBatchDto,
@CurrentUser() actor: AuthenticatedUser,
) {
return this.honeyBatchesService.update(id, dto, actor.id);
}
@RequirePermission('honey-batches:measure')
@Patch('measurements/:measurementId')
updateMeasurement(
@Param('measurementId') measurementId: string,
@Body() dto: UpdateHoneyMeasurementDto,
@CurrentUser() actor: AuthenticatedUser,
) {
return this.honeyBatchesService.updateMeasurement(
measurementId,
dto,
actor.id,
);
}
@RequirePermission('honey-batches:measure')
@Post(':id/measurements')
addMeasurement(
@Param('id') id: string,
@Body() dto: CreateHoneyMeasurementDto,
@CurrentUser() actor: AuthenticatedUser,
) {
return this.honeyBatchesService.addMeasurement(id, dto, actor.id);
}
}