SearchGuard.php 1.81 KB
<?php

namespace FootyRoom\Support;

use Illuminate\Http\Request;
use FootyRoom\User\BanCreator;
use FootyRoom\Repositories\BanRepository;
use Illuminate\Cache\RateLimiter;
use Illuminate\Support\Str;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;

class SearchGuard
{
    /** @var int */
    protected const LIMIT = 200;

    protected const LIMIT_AWS = 1;

    /** @var int */
    protected const RESET_INTERVAL_MINUTES = 1440;

    /** @var int */
    protected const BAN_SECONDS = 43200;

    /** @var string */
    protected const KEY_PREFIX = 'search_';

    /** @var \FootyRoom\Repositories\BanRepository */
    protected $banRepo;

    /** @var \FootyRoom\User\BanCreator */
    protected $banCreator;

    /** @var \Illuminate\Cache\RateLimiter */
    protected $rateLimiter;

    public function __construct(
        BanRepository $banRepo,
        BanCreator $banCreator,
        RateLimiter $rateLimiter
    ) {
        $this->banRepo = $banRepo;
        $this->banCreator = $banCreator;
        $this->rateLimiter = $rateLimiter;
    }

    public function check(Request $request): void
    {
        $ip = $request->ip();
        $key = self::KEY_PREFIX.$ip;

        $this->rateLimiter->hit($key, self::RESET_INTERVAL_MINUTES);

        $ban = $this->banRepo->findActive('ip', $ip, 'search');

        if ($ban) {
            throw new TooManyRequestsHttpException();
        }

        if ($this->rateLimiter->tooManyAttempts($key, $this->getLimit($ip))) {
            $this->banCreator->create('ip', $ip, 'search', self::BAN_SECONDS, 0);

            throw new TooManyRequestsHttpException();
        }
    }

    protected function getLimit(string $ip): int
    {
        if (Str::endsWith(gethostbyaddr($ip), 'amazonaws.com')) {
            return self::LIMIT_AWS;
        }

        return self::LIMIT;
    }
}