library-ts/node/users/requirements/security/NotTooManyRequests.ts

71 lines
2.0 KiB
TypeScript

import { FastifyReply, FastifyRequest } from "fastify";
import { RequestRequirement } from "../RequestRequirement";
import { UserManagementServer } from "../../UserManagementServer";
import { MapList } from "../../../../browser/tools/MapList";
import { DateHelper } from "../../../../browser/date/DateHelper";
import { Duration } from "../../../../browser/date/Duration";
import { DateMath } from "../../../../browser/date/DateMath";
export class NotTooManyRequests extends RequestRequirement
{
_trackedIPs = new Map<string,number[]>();
_maxRequests = 50;
_duration = Duration.fromMinutes( 2 );
_watchList = new Set<string>();
_blockList = new Map<string,number>()
_blockDuration = Duration.fromHours( 4 );
async handle( request:FastifyRequest, reply:FastifyReply ):Promise<boolean>
{
if ( this._blockList.has( request.ip ) )
{
let blockTime = this._blockList.get( request.ip );
if ( ! DateMath.isExpired( new Date( blockTime + this._blockDuration ) ) )
{
return Promise.resolve( false );
}
}
let valid = this.updateIP( request.ip );
return Promise.resolve( valid );
}
async updateIP( ip:string ):Promise<boolean>
{
MapList.add( this._trackedIPs, ip, DateHelper.nowMS() );
MapList.shiftToSize( this._trackedIPs, ip, this._maxRequests );
let lastRelevantDate = DateMath.addSeconds( DateHelper.now(), -this._duration );
let timeStamps = this._trackedIPs.get( ip );
let filteredStamps = timeStamps.filter( ts => DateMath.isAfter( lastRelevantDate, new Date( ts ) ) );
if ( filteredStamps.length < this._maxRequests )
{
this._trackedIPs.set( ip, filteredStamps );
return Promise.resolve( true );
}
this._trackedIPs.delete( ip );
if ( this._watchList.has( ip ) )
{
this._blockList.set( ip, DateHelper.nowMS() );
this._watchList.delete( ip );
}
else
{
this._watchList.add( ip );
}
return Promise.resolve( false );
}
}