Skip to content

Commit

Permalink
feat: creating rate limiting (#3961)
Browse files Browse the repository at this point in the history
  • Loading branch information
YazeedLoonat committed Mar 19, 2024
1 parent 7bfd41b commit fe803b5
Show file tree
Hide file tree
Showing 20 changed files with 78 additions and 15 deletions.
4 changes: 4 additions & 0 deletions api/.env.template
Original file line number Diff line number Diff line change
Expand Up @@ -44,3 +44,7 @@ CORS_ORIGINS=["http://localhost:3000", "http://localhost:3001"]
CORS_REGEX=["test1", "test2"]
# controls the repetition of the temp file clearing cron job
TEMP_FILE_CLEAR_CRON_STRING=0 * * *
# how long we maintain our request time outs (60 * 60 * 1000 ms)
THROTTLE_TTL=3600000
# how many requests before we throttle
THROTTLE_LIMIT=100
3 changes: 2 additions & 1 deletion api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,12 @@
"@nestjs/platform-express": "^10.3.2",
"@nestjs/schedule": "^4.0.1",
"@nestjs/swagger": "~7.1.12",
"@nestjs/throttler": "^5.1.2",
"@prisma/client": "^5.0.0",
"@sendgrid/mail": "7.7.0",
"@turf/boolean-point-in-polygon": "6.5.0",
"@turf/buffer": "6.5.0",
"@turf/helpers": "6.5.0",
"@turf/boolean-point-in-polygon": "6.5.0",
"@turf/points-within-polygon": "6.5.0",
"@types/archiver": "^6.0.2",
"archiver": "^6.0.1",
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/ami-chart.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,13 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('/amiCharts')
@ApiTags('amiCharts')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@PermissionTypeDecorator('amiChart')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
@ApiExtraModels(AmiChartQueryParams)
export class AmiChartController {
constructor(private readonly AmiChartService: AmiChartService) {}
Expand Down
2 changes: 2 additions & 0 deletions api/src/controllers/app.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,10 @@ import { PermissionAction } from '../decorators/permission-action.decorator';
import { permissionActions } from '../enums/permissions/permission-actions-enum';
import { AdminOrJurisdictionalAdminGuard } from '../guards/admin-or-jurisdiction-admin.guard';
import { AppService } from '../services/app.service';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller()
@UseGuards(ThrottleGuard)
@ApiExtraModels(SuccessDTO)
@ApiTags('root')
export class AppController {
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/application-flagged-set.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,12 @@ import { mapTo } from '../utilities/mapTo';
import { OptionalAuthGuard } from '../guards/optional.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('/applicationFlaggedSets')
@ApiExtraModels(SuccessDTO)
@ApiTags('applicationFlaggedSets')
@UseGuards(OptionalAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard, PermissionGuard)
@PermissionTypeDecorator('applicationFlaggedSet')
@UsePipes(
new ValidationPipe({
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/application.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ import { ApplicationCsvExporterService } from '../services/application-csv-expor
import { ApplicationCsvQueryParams } from '../dtos/applications/application-csv-query-params.dto';
import { MostRecentApplicationQueryParams } from '../dtos/applications/most-recent-application-query-params.dto';
import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('applications')
@ApiTags('applications')
Expand All @@ -59,7 +60,7 @@ import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
}),
)
@ApiExtraModels(IdDTO, AddressInput, BooleanInput, TextInput)
@UseGuards(OptionalAuthGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard)
@PermissionTypeDecorator('application')
@UseInterceptors(ActivityLogInterceptor)
export class ApplicationController {
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/asset.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { CreatePresignedUploadMetadataResponse } from '../dtos/assets/create-pre
import { CreatePresignedUploadMetadata } from '../dtos/assets/create-presigned-upload-meta.dto';
import { AssetService } from '../services/asset.service';
import { defaultValidationPipeOptions } from '../utilities/default-validation-pipe-options';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('assets')
@ApiTags('assets')
Expand All @@ -28,7 +29,7 @@ import { defaultValidationPipeOptions } from '../utilities/default-validation-pi
CreatePresignedUploadMetadataResponse,
)
@PermissionTypeDecorator('asset')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class AssetController {
constructor(private readonly assetService: AssetService) {}

Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/auth.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { User } from '../dtos/users/user.dto';
import { RequestSingleUseCode } from '../dtos/single-use-code/request-single-use-code.dto';
import { LoginViaSingleUseCode } from '../dtos/auth/login-single-use-code.dto';
import { SingleUseCodeAuthGuard } from '../guards/single-use-code.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('auth')
@ApiTags('auth')
Expand All @@ -43,7 +44,7 @@ export class AuthController {
@ApiOperation({ summary: 'Login', operationId: 'login' })
@ApiOkResponse({ type: SuccessDTO })
@ApiBody({ type: Login })
@UseGuards(MfaAuthGuard)
@UseGuards(ThrottleGuard, MfaAuthGuard)
async login(
@Request() req: ExpressRequest,
@Response({ passthrough: true }) res: ExpressResponse,
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/jurisdiction.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,14 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { OptionalAuthGuard } from '../guards/optional.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('jurisdictions')
@ApiTags('jurisdictions')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@ApiExtraModels(JurisdictionCreate, JurisdictionUpdate, IdDTO)
@PermissionTypeDecorator('jurisdiction')
@UseGuards(OptionalAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard, PermissionGuard)
export class JurisdictionController {
constructor(private readonly jurisdictionService: JurisdictionService) {}

Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/listing.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ import { ListingCsvExporterService } from '../services/listing-csv-export.servic
import { ListingCsvQueryParams } from '../dtos/listings/listing-csv-query-params.dto';
import { PermissionGuard } from '../guards/permission.guard';
import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('listings')
@ApiTags('listings')
Expand All @@ -63,7 +64,7 @@ import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
PaginationAllowsAllQueryParams,
IdDTO,
)
@UseGuards(OptionalAuthGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard)
@PermissionTypeDecorator('listing')
@ActivityLogMetadata([{ targetPropertyName: 'status', propertyPath: 'status' }])
@UseInterceptors(ActivityLogInterceptor)
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/map-layer.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@ import { defaultValidationPipeOptions } from '../utilities/default-validation-pi
import { OptionalAuthGuard } from '../guards/optional.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('/mapLayers')
@ApiTags('mapLayers')
@UseGuards(OptionalAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard, PermissionGuard)
@PermissionTypeDecorator('mapLayers')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
export class MapLayersController {
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/multiselect-question.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import { OptionalAuthGuard } from '../guards/optional.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { AdminOrJurisdictionalAdminGuard } from '../guards/admin-or-jurisdiction-admin.guard';
import { ActivityLogInterceptor } from '../interceptors/activity-log.interceptor';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('multiselectQuestions')
@ApiTags('multiselectQuestions')
Expand All @@ -46,7 +47,7 @@ import { ActivityLogInterceptor } from '../interceptors/activity-log.interceptor
IdDTO,
)
@PermissionTypeDecorator('multiselectQuestion')
@UseGuards(OptionalAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, OptionalAuthGuard, PermissionGuard)
export class MultiselectQuestionController {
constructor(
private readonly multiselectQuestionService: MultiselectQuestionService,
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/reserved-community-type.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,14 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('reservedCommunityTypes')
@ApiTags('reservedCommunityTypes')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@ApiExtraModels(ReservedCommunityTypeQueryParams)
@PermissionTypeDecorator('reservedCommunityType')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class ReservedCommunityTypeController {
constructor(
private readonly ReservedCommunityTypeService: ReservedCommunityTypeService,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('unitAccessibilityPriorityTypes')
@ApiTags('unitAccessibilityPriorityTypes')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@ApiExtraModels(IdDTO)
@PermissionTypeDecorator('unitAccessibilityPriorityType')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class UnitAccessibilityPriorityTypeController {
constructor(
private readonly unitAccessibilityPriorityTypeService: UnitAccessibilityPriorityTypeService,
Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/unit-rent-type.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('unitRentTypes')
@ApiTags('unitRentTypes')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@ApiExtraModels(UnitRentTypeCreate, UnitRentTypeUpdate, IdDTO)
@PermissionTypeDecorator('unitRentType')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class UnitRentTypeController {
constructor(private readonly unitRentTypeService: UnitRentTypeService) {}

Expand Down
3 changes: 2 additions & 1 deletion api/src/controllers/unit-type.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,13 @@ import { SuccessDTO } from '../dtos/shared/success.dto';
import { PermissionTypeDecorator } from '../decorators/permission-type.decorator';
import { JwtAuthGuard } from '../guards/jwt.guard';
import { PermissionGuard } from '../guards/permission.guard';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('unitTypes')
@ApiTags('unitTypes')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
@PermissionTypeDecorator('unitType')
@UseGuards(JwtAuthGuard, PermissionGuard)
@UseGuards(ThrottleGuard, JwtAuthGuard, PermissionGuard)
export class UnitTypeController {
constructor(private readonly unitTypeService: UnitTypeService) {}

Expand Down
2 changes: 2 additions & 0 deletions api/src/controllers/user.controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,10 @@ import { PermissionTypeDecorator } from '../decorators/permission-type.decorator
import { UserFilterParams } from '../dtos/users/user-filter-params.dto';
import { UserCsvExporterService } from '../services/user-csv-export.service';
import { ExportLogInterceptor } from '../interceptors/export-log.interceptor';
import { ThrottleGuard } from '../guards/throttler.guard';

@Controller('user')
@UseGuards(ThrottleGuard)
@ApiTags('user')
@PermissionTypeDecorator('user')
@UsePipes(new ValidationPipe(defaultValidationPipeOptions))
Expand Down
19 changes: 19 additions & 0 deletions api/src/guards/throttler.guard.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { ThrottlerGuard } from '@nestjs/throttler';
import { ExecutionContext, Injectable } from '@nestjs/common';
import { ThrottlerLimitDetail } from '@nestjs/throttler/dist/throttler.guard.interface';

@Injectable()
export class ThrottleGuard extends ThrottlerGuard {
protected async getTracker(req: Record<string, any>): Promise<string> {
console.log('7:', req.ips.length ? req.ips : req.ip);
return req.ips.length ? req.ips[0] : req.ip;
}

protected async throwThrottlingException(
context: ExecutionContext,
throttlerLimitDetail: ThrottlerLimitDetail,
): Promise<void> {
console.error(`IP Address: ${throttlerLimitDetail.tracker} was throttled`);
await super.throwThrottlingException(context, throttlerLimitDetail);
}
}
19 changes: 18 additions & 1 deletion api/src/modules/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@ import { UserModule } from './user.module';
import { AuthModule } from './auth.module';
import { ApplicationFlaggedSetModule } from './application-flagged-set.module';
import { MapLayerModule } from './map-layer.module';
import { APP_GUARD } from '@nestjs/core';
import { ThrottlerModule } from '@nestjs/throttler';
import { ThrottleGuard } from '../guards/throttler.guard';

@Module({
imports: [
Expand All @@ -35,9 +38,23 @@ import { MapLayerModule } from './map-layer.module';
AuthModule,
ApplicationFlaggedSetModule,
MapLayerModule,
ThrottlerModule.forRoot([
{
ttl: Number(process.env.THROTTLE_TTL),
limit: Number(process.env.THROTTLE_LIMIT),
},
]),
],
controllers: [AppController],
providers: [AppService, Logger, SchedulerRegistry],
providers: [
AppService,
Logger,
SchedulerRegistry,
{
provide: APP_GUARD,
useClass: ThrottleGuard,
},
],
exports: [
ListingModule,
AmiChartModule,
Expand Down
5 changes: 5 additions & 0 deletions api/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,11 @@
dependencies:
tslib "2.6.2"

"@nestjs/throttler@^5.1.2":
version "5.1.2"
resolved "https://registry.yarnpkg.com/@nestjs/throttler/-/throttler-5.1.2.tgz#dc65634153c8b887329b1cc6061db2e556517dcb"
integrity sha512-60MqhSLYUqWOgc38P6C6f76JIpf6mVjly7gpuPBCKtVd0p5e8Fq855j7bJuO4/v25vgaOo1OdVs0U1qtgYioGw==

"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5"
Expand Down

0 comments on commit fe803b5

Please sign in to comment.