-
Notifications
You must be signed in to change notification settings - Fork 312
Expand file tree
/
Copy pathadmin-pentest-credits.controller.ts
More file actions
75 lines (70 loc) · 2.47 KB
/
admin-pentest-credits.controller.ts
File metadata and controls
75 lines (70 loc) · 2.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
import {
BadRequestException,
Body,
Controller,
Get,
Param,
Post,
UseGuards,
UseInterceptors,
} from '@nestjs/common';
import { ApiExcludeController, ApiOperation, ApiTags } from '@nestjs/swagger';
import { Throttle } from '@nestjs/throttler';
import { IsInt, IsOptional, IsString, Max, Min } from 'class-validator';
import { PlatformAdminGuard } from '../auth/platform-admin.guard';
import { PentestCreditsService } from '../security-penetration-tests/pentest-credits.service';
import { AdminAuditLogInterceptor } from './admin-audit-log.interceptor';
/**
* Request body for granting credits via the admin panel. `amount` is capped
* at 1000 to prevent typo-induced runaway grants — admins can submit
* multiple times if a larger pool is genuinely needed.
*/
class GrantPentestCreditsDto {
@IsInt()
@Min(1)
@Max(1000)
amount!: number;
/**
* Free-form note. Persisted on the audit log entry as `data.note` so
* support / compliance can reconstruct *why* a grant happened.
*/
@IsOptional()
@IsString()
note?: string;
}
@ApiExcludeController()
@ApiTags('Admin - Pentest Credits')
@Controller({ path: 'admin/organizations', version: '1' })
@UseGuards(PlatformAdminGuard)
@UseInterceptors(AdminAuditLogInterceptor)
@Throttle({ default: { ttl: 60_000, limit: 30 } })
export class AdminPentestCreditsController {
constructor(private readonly credits: PentestCreditsService) {}
@Get(':orgId/pentest-credits')
@ApiOperation({
summary: 'Get pentest credit wallet status for any organization',
})
async getStatus(@Param('orgId') orgId: string) {
return this.credits.getStatus(orgId);
}
// POST /:orgId/pentest-credits (no `/grant` suffix). The
// AdminAuditLogInterceptor's URL parser treats the segment after the
// resource as an entity id; if we used `/grant`, the audit log
// would record `entityId: "grant"` which is meaningless and breaks
// the admin audit trail. Keeping the route shape standard
// (`:orgId/<resource>`) lets the interceptor produce correct metadata.
@Post(':orgId/pentest-credits')
@ApiOperation({
summary: 'Grant pentest credits to an organization (platform admin)',
})
async grant(
@Param('orgId') orgId: string,
@Body() body: GrantPentestCreditsDto,
) {
if (!Number.isInteger(body.amount) || body.amount < 1) {
throw new BadRequestException('amount must be a positive integer');
}
await this.credits.grant(orgId, body.amount, 'manual');
return this.credits.getStatus(orgId);
}
}