add ratelimits

This commit is contained in:
rubikscraft 2022-09-17 19:46:53 +02:00 committed by Rubikscraft
parent 78ff25034c
commit 7b6ffb5010
12 changed files with 62 additions and 33 deletions

View File

@ -6,7 +6,10 @@ import { PicsurThrottlerGuard } from './throttler/PicsurThrottler.guard';
import { ZodValidationPipe } from './validate/zod-validator.pipe';
@Module({
imports: [ThrottlerModule.forRoot()],
imports: [ThrottlerModule.forRoot({
ttl: 60,
limit: 60,
})],
providers: [
PicsurThrottlerGuard,
MainExceptionFilter,

View File

@ -5,6 +5,6 @@ import { Fail, FT } from 'picsur-shared/dist/types';
@Injectable()
export class PicsurThrottlerGuard extends ThrottlerGuard {
protected override throwThrottlingException(context: ExecutionContext): void {
throw Fail(FT.Permission, undefined, 'Too many requests');
throw Fail(FT.RateLimit);
}
}

View File

@ -1,4 +1,5 @@
import { Body, Controller, Post } from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import {
ApiKeyCreateResponse,
ApiKeyDeleteRequest,
@ -8,14 +9,14 @@ import {
ApiKeyListRequest,
ApiKeyListResponse,
ApiKeyUpdateRequest,
ApiKeyUpdateResponse,
ApiKeyUpdateResponse
} from 'picsur-shared/dist/dto/api/apikeys.dto';
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
import { ThrowIfFailed } from 'picsur-shared/dist/types';
import { ApiKeyDbService } from '../../../collections/apikey-db/apikey-db.service';
import {
HasPermission,
RequiredPermissions,
RequiredPermissions
} from '../../../decorators/permissions.decorator';
import { ReqUserID } from '../../../decorators/request-user.decorator';
import { Returns } from '../../../decorators/returns.decorator';
@ -53,6 +54,7 @@ export class ApiKeysController {
@Post('create')
@Returns(ApiKeyCreateResponse)
@Throttle(10)
async createApiKey(
@ReqUserID() userID: string,
): Promise<ApiKeyCreateResponse> {

View File

@ -1,22 +1,18 @@
import { Controller, Get, Request, Response } from '@nestjs/common';
import type { FastifyReply } from 'fastify';
import { UserInfoResponse } from 'picsur-shared/dist/dto/api/user-manage.dto';
import { Controller } from '@nestjs/common';
import { NoPermissions } from '../../../decorators/permissions.decorator';
import { Returns } from '../../../decorators/returns.decorator';
import type AuthFastifyRequest from '../../../models/interfaces/authrequest.dto';
@Controller('api/experiment')
@NoPermissions()
export class ExperimentController {
constructor() {}
@Get()
@Returns(UserInfoResponse)
async testRoute(
@Request() req: AuthFastifyRequest,
@Response({ passthrough: true }) res: FastifyReply,
): Promise<UserInfoResponse> {
res.header('Location', '/error/delete-success');
res.code(302);
return req.user;
}
// @Get()
// @Returns(UserInfoResponse)
// async testRoute(
// @Request() req: AuthFastifyRequest,
// @Response({ passthrough: true }) res: FastifyReply,
// ): Promise<UserInfoResponse> {
// res.header('Location', '/error/delete-success');
// res.code(302);
// return req.user;
// }
}

View File

@ -1,9 +1,10 @@
import { Body, Controller, Get, Logger, Param, Post } from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import {
GetPreferenceResponse,
MultiplePreferencesResponse,
UpdatePreferenceRequest,
UpdatePreferenceResponse,
UpdatePreferenceResponse
} from 'picsur-shared/dist/dto/api/pref.dto';
import { ThrowIfFailed } from 'picsur-shared/dist/types';
import { SysPreferenceDbService } from '../../../collections/preference-db/sys-preference-db.service';
@ -20,6 +21,7 @@ export class SysPrefController {
@Get()
@Returns(MultiplePreferencesResponse)
@Throttle(20)
async getAllSysPrefs(): Promise<MultiplePreferencesResponse> {
const prefs = ThrowIfFailed(await this.prefService.getAllPreferences());
@ -39,6 +41,7 @@ export class SysPrefController {
@Post(':key')
@Returns(UpdatePreferenceResponse)
@Throttle(30)
async setSysPref(
@Param('key') key: string,
@Body() body: UpdatePreferenceRequest,

View File

@ -1,9 +1,10 @@
import { Body, Controller, Get, Logger, Param, Post } from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import {
GetPreferenceResponse,
MultiplePreferencesResponse,
UpdatePreferenceRequest,
UpdatePreferenceResponse,
UpdatePreferenceResponse
} from 'picsur-shared/dist/dto/api/pref.dto';
import { ThrowIfFailed } from 'picsur-shared/dist/types';
import { UsrPreferenceDbService } from '../../../collections/preference-db/usr-preference-db.service';
@ -21,7 +22,8 @@ export class UsrPrefController {
@Get()
@Returns(MultiplePreferencesResponse)
async getAllSysPrefs(
@Throttle(20)
async getAllUsrPrefs(
@ReqUserID() userid: string,
): Promise<MultiplePreferencesResponse> {
const prefs = ThrowIfFailed(
@ -36,7 +38,7 @@ export class UsrPrefController {
@Get(':key')
@Returns(GetPreferenceResponse)
async getSysPref(
async getUsrPref(
@Param('key') key: string,
@ReqUserID() userid: string,
): Promise<GetPreferenceResponse> {
@ -49,7 +51,8 @@ export class UsrPrefController {
@Post(':key')
@Returns(UpdatePreferenceResponse)
async setSysPref(
@Throttle(30)
async setUsrPref(
@Param('key') key: string,
@ReqUserID() userid: string,
@Body() body: UpdatePreferenceRequest,

View File

@ -1,4 +1,5 @@
import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import {
RoleCreateRequest,
RoleCreateResponse,
@ -9,7 +10,7 @@ import {
RoleListResponse,
RoleUpdateRequest,
RoleUpdateResponse,
SpecialRolesResponse,
SpecialRolesResponse
} from 'picsur-shared/dist/dto/api/roles.dto';
import { Fail, FT, ThrowIfFailed } from 'picsur-shared/dist/types';
import { RoleDbService } from '../../../collections/role-db/role-db.service';
@ -21,7 +22,7 @@ import {
DefaultRolesList,
ImmutableRolesList,
SoulBoundRolesList,
UndeletableRolesList,
UndeletableRolesList
} from '../../../models/constants/roles.const';
import { isPermissionsArray } from '../../../models/validators/permissions.validator';
@ -56,6 +57,7 @@ export class RolesController {
@Post('update')
@Returns(RoleUpdateResponse)
@Throttle(20)
async updateRole(
@Body() body: RoleUpdateRequest,
): Promise<RoleUpdateResponse> {
@ -73,6 +75,7 @@ export class RolesController {
@Post('create')
@Returns(RoleCreateResponse)
@Throttle(10)
async createRole(
@Body() role: RoleCreateRequest,
): Promise<RoleCreateResponse> {

View File

@ -1,4 +1,5 @@
import { Controller, Logger, Post, Req, Res } from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import type { FastifyReply, FastifyRequest } from 'fastify';
import { Fail, FT, ThrowIfFailed } from 'picsur-shared/dist/types';
import { UsageConfigService } from '../../../config/late/usage.config.service';
@ -14,6 +15,7 @@ export class UsageController {
@Post(['report', 'report/*'])
@ReturnsAnything()
@Throttle(120)
async deleteRole(
@Req() req: FastifyRequest,
@Res({

View File

@ -1,4 +1,5 @@
import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import {
GetSpecialUsersResponse,
UserCreateRequest,
@ -10,7 +11,7 @@ import {
UserListRequest,
UserListResponse,
UserUpdateRequest,
UserUpdateResponse,
UserUpdateResponse
} from 'picsur-shared/dist/dto/api/user-manage.dto';
import { ThrowIfFailed } from 'picsur-shared/dist/types';
import { UserDbService } from '../../../collections/user-db/user-db.service';
@ -20,7 +21,7 @@ import { Permission } from '../../../models/constants/permissions.const';
import {
ImmutableUsersList,
LockedLoginUsersList,
UndeletableUsersList,
UndeletableUsersList
} from '../../../models/constants/special-users.const';
import { EUserBackend2EUser } from '../../../models/transformers/user.transformer';
@ -46,6 +47,7 @@ export class UserAdminController {
@Post('create')
@Returns(UserCreateResponse)
@Throttle(10)
async register(
@Body() create: UserCreateRequest,
): Promise<UserCreateResponse> {
@ -78,6 +80,7 @@ export class UserAdminController {
@Post('update')
@Returns(UserUpdateResponse)
@Throttle(20)
async setPermissions(
@Body() body: UserUpdateRequest,
): Promise<UserUpdateResponse> {

View File

@ -1,4 +1,5 @@
import { Body, Controller, Get, Logger, Post } from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import {
UserCheckNameRequest,
UserCheckNameResponse,
@ -6,7 +7,7 @@ import {
UserMePermissionsResponse,
UserMeResponse,
UserRegisterRequest,
UserRegisterResponse,
UserRegisterResponse
} from 'picsur-shared/dist/dto/api/user.dto';
import type { EUser } from 'picsur-shared/dist/entities/user.entity';
import { ThrowIfFailed } from 'picsur-shared/dist/types';
@ -14,7 +15,7 @@ import { UserDbService } from '../../../collections/user-db/user-db.service';
import {
NoPermissions,
RequiredPermissions,
UseLocalAuth,
UseLocalAuth
} from '../../../decorators/permissions.decorator';
import { ReqUser, ReqUserID } from '../../../decorators/request-user.decorator';
import { Returns } from '../../../decorators/returns.decorator';
@ -34,6 +35,7 @@ export class UserController {
@Post('login')
@Returns(UserLoginResponse)
@UseLocalAuth(Permission.UserLogin)
@Throttle(30, 300)
async login(@ReqUser() user: EUser): Promise<UserLoginResponse> {
const jwt_token = ThrowIfFailed(await this.authService.createToken(user));
@ -43,6 +45,7 @@ export class UserController {
@Post('register')
@Returns(UserRegisterResponse)
@RequiredPermissions(Permission.UserRegister)
@Throttle(5, 300)
async register(
@Body() register: UserRegisterRequest,
): Promise<UserRegisterResponse> {
@ -56,6 +59,7 @@ export class UserController {
@Post('checkname')
@Returns(UserCheckNameResponse)
@RequiredPermissions(Permission.UserRegister)
@Throttle(20)
async checkName(
@Body() checkName: UserCheckNameRequest,
): Promise<UserCheckNameResponse> {
@ -67,6 +71,7 @@ export class UserController {
@Get('me')
@Returns(UserMeResponse)
@RequiredPermissions(Permission.UserKeepLogin)
@Throttle(10)
async me(@ReqUserID() userid: string): Promise<UserMeResponse> {
const backenduser = ThrowIfFailed(await this.usersService.findOne(userid));
@ -81,6 +86,7 @@ export class UserController {
@Get('me/permissions')
@Returns(UserMePermissionsResponse)
@NoPermissions()
@Throttle(20)
async refresh(
@ReqUserID() userid: string,
): Promise<UserMePermissionsResponse> {

View File

@ -5,8 +5,9 @@ import {
Logger,
Param,
Post,
Res,
Res
} from '@nestjs/common';
import { Throttle } from '@nestjs/throttler';
import type { FastifyReply } from 'fastify';
import {
ImageDeleteRequest,
@ -17,14 +18,14 @@ import {
ImageListResponse,
ImageUpdateRequest,
ImageUpdateResponse,
ImageUploadResponse,
ImageUploadResponse
} from 'picsur-shared/dist/dto/api/image-manage.dto';
import { Permission } from 'picsur-shared/dist/dto/permissions.enum';
import { HasFailed, ThrowIfFailed } from 'picsur-shared/dist/types';
import { MultiPart } from '../../decorators/multipart/multipart.decorator';
import {
HasPermission,
RequiredPermissions,
RequiredPermissions
} from '../../decorators/permissions.decorator';
import { ReqUserID } from '../../decorators/request-user.decorator';
import { Returns } from '../../decorators/returns.decorator';
@ -39,6 +40,7 @@ export class ImageManageController {
@Post('upload')
@Returns(ImageUploadResponse)
@Throttle(20)
async uploadImage(
@MultiPart() multipart: ImageUploadDto,
@ReqUserID() userid: string,

View File

@ -10,6 +10,7 @@ export enum FT {
SysValidation = 'sysvalidation',
UsrValidation = 'usrvalidation',
Permission = 'permission',
RateLimit = 'ratelimit',
NotFound = 'notfound',
RouteNotFound = 'routenotfound',
Conflict = 'conflict',
@ -63,6 +64,11 @@ const FTProps: {
code: 403,
message: 'Permission denied',
},
[FT.RateLimit]: {
important: false,
code: 429,
message: 'Rate limit exceeded',
},
[FT.NotFound]: {
important: false,
code: 404,