import { Injectable, CanActivate, ExecutionContext, HttpException, } from '@nestjs/common'; import { Request } from 'express'; import { AuditAction } from 'src/modules/objects/audit/audit.enum'; import { AuditService } from 'src/modules/objects/audit/audit.service'; import { IPLimitService } from 'src/modules/utility/services/iplimit.service'; @Injectable() export class LoginAntispamGuard implements CanActivate { constructor(private iplimit: IPLimitService, private audit: AuditService) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); if (['GET', 'OPTIONS'].includes(request.method)) return true; const known = this.iplimit.getAddressLimit(request.ip); if (known && known.attempts >= 3) { if (known.attempts >= 5) { let reported = false; if (!known.reported) { reported = true; await this.audit.insertAudit( AuditAction.THROTTLE, `antispam-guard ${known.attempts} attempts`, undefined, request.ip, request.header('user-agent'), ); } const limitMinutes = known.attempts > 10 ? 30 : 10; // Half-Hour this.iplimit.limitUntil(request.ip, limitMinutes * 60 * 1000, reported); await new Promise((resolve) => setTimeout(resolve, known.attempts * 1000), ); throw new HttpException( `Too Many Requests. Try again in ${limitMinutes} minutes.`, 429, ); } await new Promise((resolve) => setTimeout(resolve, 1000)); } this.iplimit.limitUntil(request.ip, 30 * 1000); // 30 seconds return true; } }