feat: add rate limit : a user can update 25 domains per day

This commit is contained in:
Maël Gangloff 2024-07-21 14:56:10 +02:00
parent b45bbe63f5
commit 43c4c9a33d
No known key found for this signature in database
GPG Key ID: 11FDC81C24A7F629
8 changed files with 200 additions and 18 deletions

6
.env
View File

@ -50,3 +50,9 @@ JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=827c9f8cce8bb82e75b2aec4a14a61f572ac28c7a8531f08dcdf1652573a7049 JWT_PASSPHRASE=827c9f8cce8bb82e75b2aec4a14a61f572ac28c7a8531f08dcdf1652573a7049
###< lexik/jwt-authentication-bundle ### ###< lexik/jwt-authentication-bundle ###
###> symfony/lock ###
# Choose one of the stores below
# postgresql+advisory://db_user:db_password@localhost/db_name
LOCK_DSN=flock
###< symfony/lock ###

View File

@ -46,6 +46,7 @@
"symfony/framework-bundle": "7.1.*", "symfony/framework-bundle": "7.1.*",
"symfony/http-client": "7.1.*", "symfony/http-client": "7.1.*",
"symfony/intl": "7.1.*", "symfony/intl": "7.1.*",
"symfony/lock": "7.1.*",
"symfony/mailer": "7.1.*", "symfony/mailer": "7.1.*",
"symfony/mime": "7.1.*", "symfony/mime": "7.1.*",
"symfony/monolog-bundle": "^3.0", "symfony/monolog-bundle": "^3.0",
@ -53,6 +54,7 @@
"symfony/process": "7.1.*", "symfony/process": "7.1.*",
"symfony/property-access": "7.1.*", "symfony/property-access": "7.1.*",
"symfony/property-info": "7.1.*", "symfony/property-info": "7.1.*",
"symfony/rate-limiter": "7.1.*",
"symfony/runtime": "7.1.*", "symfony/runtime": "7.1.*",
"symfony/scheduler": "7.1.*", "symfony/scheduler": "7.1.*",
"symfony/security-bundle": "7.1.*", "symfony/security-bundle": "7.1.*",

150
composer.lock generated
View File

@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically" "This file is @generated automatically"
], ],
"content-hash": "2e69cb333159db20699449d20ab40c4a", "content-hash": "a8eb6290221266a2746c56c3cfa4c5d7",
"packages": [ "packages": [
{ {
"name": "api-platform/core", "name": "api-platform/core",
@ -5056,6 +5056,84 @@
], ],
"time": "2024-05-31T14:57:53+00:00" "time": "2024-05-31T14:57:53+00:00"
}, },
{
"name": "symfony/lock",
"version": "v7.1.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/lock.git",
"reference": "1f8c941f1270dee046e09a826bcdd3b2ebada45e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/lock/zipball/1f8c941f1270dee046e09a826bcdd3b2ebada45e",
"reference": "1f8c941f1270dee046e09a826bcdd3b2ebada45e",
"shasum": ""
},
"require": {
"php": ">=8.2",
"psr/log": "^1|^2|^3"
},
"conflict": {
"doctrine/dbal": "<3.6",
"symfony/cache": "<6.4"
},
"require-dev": {
"doctrine/dbal": "^3.6|^4",
"predis/predis": "^1.1|^2.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\Lock\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Jérémy Derussé",
"email": "jeremy@derusse.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Creates and manages locks, a mechanism to provide exclusive access to a shared resource",
"homepage": "https://symfony.com",
"keywords": [
"cas",
"flock",
"locking",
"mutex",
"redlock",
"semaphore"
],
"support": {
"source": "https://github.com/symfony/lock/tree/v7.1.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
},
{ {
"name": "symfony/mailer", "name": "symfony/mailer",
"version": "v7.1.2", "version": "v7.1.2",
@ -6465,6 +6543,76 @@
], ],
"time": "2024-06-26T07:21:35+00:00" "time": "2024-06-26T07:21:35+00:00"
}, },
{
"name": "symfony/rate-limiter",
"version": "v7.1.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/rate-limiter.git",
"reference": "f1fbc60e7fed63f1c77bbf8601170cc80fddd95a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/rate-limiter/zipball/f1fbc60e7fed63f1c77bbf8601170cc80fddd95a",
"reference": "f1fbc60e7fed63f1c77bbf8601170cc80fddd95a",
"shasum": ""
},
"require": {
"php": ">=8.2",
"symfony/options-resolver": "^6.4|^7.0"
},
"require-dev": {
"psr/cache": "^1.0|^2.0|^3.0",
"symfony/lock": "^6.4|^7.0"
},
"type": "library",
"autoload": {
"psr-4": {
"Symfony\\Component\\RateLimiter\\": ""
},
"exclude-from-classmap": [
"/Tests/"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Wouter de Jong",
"email": "wouter@wouterj.nl"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Provides a Token Bucket implementation to rate limit input and output in your application",
"homepage": "https://symfony.com",
"keywords": [
"limiter",
"rate-limiter"
],
"support": {
"source": "https://github.com/symfony/rate-limiter/tree/v7.1.1"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-05-31T14:57:53+00:00"
},
{ {
"name": "symfony/routing", "name": "symfony/routing",
"version": "v7.1.1", "version": "v7.1.1",

View File

@ -8,6 +8,11 @@ framework:
#esi: true #esi: true
#fragments: true #fragments: true
rate_limiter:
authenticated_api:
policy: 'sliding_window'
limit: 25
interval: '1 day'
when@test: when@test:
framework: framework:

View File

@ -0,0 +1,2 @@
framework:
lock: '%env(LOCK_DSN)%'

View File

@ -8,12 +8,15 @@ use App\Service\RDAPService;
use DateTimeImmutable; use DateTimeImmutable;
use Exception; use Exception;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
use Symfony\Component\RateLimiter\RateLimiterFactory;
class DomainRefreshController extends AbstractController class DomainRefreshController extends AbstractController
{ {
public function __construct(private readonly DomainRepository $domainRepository, public function __construct(private readonly DomainRepository $domainRepository,
private readonly RDAPService $RDAPService) private readonly RDAPService $RDAPService,
private readonly RateLimiterFactory $authenticatedApiLimiter)
{ {
} }
@ -27,7 +30,11 @@ class DomainRefreshController extends AbstractController
if ($domain === null || if ($domain === null ||
$domain->getUpdatedAt()->diff(new DateTimeImmutable('now'))->days >= 7) { $domain->getUpdatedAt()->diff(new DateTimeImmutable('now'))->days >= 7) {
//TODO : Domain search rate limit here, before the RDAP request $limiter = $this->authenticatedApiLimiter->create($this->getUser()->getUserIdentifier());
if (false === $limiter->consume()->isAccepted()) {
throw new TooManyRequestsHttpException();
}
$domain = $this->RDAPService->registerDomain($ldhName); $domain = $this->RDAPService->registerDomain($ldhName);
} }
return $domain; return $domain;

View File

@ -148,6 +148,18 @@
"src/Kernel.php" "src/Kernel.php"
] ]
}, },
"symfony/lock": {
"version": "7.1",
"recipe": {
"repo": "github.com/symfony/recipes",
"branch": "main",
"version": "5.2",
"ref": "8e937ff2b4735d110af1770f242c1107fdab4c8e"
},
"files": [
"config/packages/lock.yaml"
]
},
"symfony/mailer": { "symfony/mailer": {
"version": "7.1", "version": "7.1",
"recipe": { "recipe": {