fix(auth): stop class guards shadowing route permission keys

Nest runs class and method guards together, so a class-level view key
ANDs with every action key below it. Staff granted only an action were
denied before their key was ever checked: OCC could not fulfil wagon
transfers, dispatchers could not create a yard, and track staff could not
assign first/last-mile vehicles. Reads now carry the view key themselves,
and the warehouses baseline lists every key its routes use.
This commit is contained in:
Nathnael
2026-08-07 07:40:50 +00:00
parent 2a0810e734
commit 87e2edcbde
4 changed files with 26 additions and 5 deletions

View File

@@ -41,7 +41,9 @@ const toInt = (value?: string): number | undefined => {
*/
@ApiTags('wagon-transfer-requests')
@Controller('wagon-transfer-requests')
@WagonTransferView()
// No class-level key: Nest stacks class and method guards, so a class-level
// `transfer_view` would AND with every action key below and lock out the OCC
// staff granted only `transfer_fulfill`. Reads carry the view key themselves.
export class WagonTransferRequestsController {
constructor(private readonly service: WagonTransferRequestsService) {}
@@ -56,6 +58,7 @@ export class WagonTransferRequestsController {
}
@Get()
@WagonTransferView()
@ApiOperation({
summary:
'Transfer desk list — paginated, filterable by status (comma-separated), yards and wagon type',
@@ -84,6 +87,7 @@ export class WagonTransferRequestsController {
// matches in declaration order, so `/history` would otherwise be captured by
// the `:id` param route (and rejected by ParseUUIDPipe).
@Get('history')
@WagonTransferView()
@ApiQuery({ name: 'page', required: false })
@ApiQuery({ name: 'pageSize', required: false })
@ApiOperation({
@@ -129,6 +133,7 @@ export class WagonTransferRequestsController {
}
@Get(':id')
@WagonTransferView()
@ApiOperation({ summary: 'Get one transfer request' })
findOne(@Param('id', ParseUUIDPipe) id: string) {
return this.service.findById(id);