<?php
/*
* This file is part of EC-CUBE
*
* Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
*
* http://www.ec-cube.co.jp/
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Eccube\EventListener;
use Eccube\Common\EccubeConfig;
use Eccube\Entity\Customer;
use Eccube\Entity\Member;
use Eccube\Request\Context;
use Psr\Container\ContainerInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ControllerEvent;
use Symfony\Component\HttpKernel\Exception\TooManyRequestsHttpException;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\RateLimiter\RateLimiterFactory;
use Symfony\Component\Security\Core\User\UserInterface;
class RateLimiterListener implements EventSubscriberInterface
{
private ContainerInterface $locator;
private EccubeConfig $eccubeConfig;
private Context $requestContext;
public function __construct(ContainerInterface $locator, EccubeConfig $eccubeConfig, Context $requestContext)
{
$this->locator = $locator;
$this->eccubeConfig = $eccubeConfig;
$this->requestContext = $requestContext;
}
public function onController(ControllerEvent $event)
{
if (!$event->isMainRequest()) {
return;
}
$request = $event->getRequest();
$route = $request->attributes->get('_route');
$limiterConfigs = $this->eccubeConfig['eccube_rate_limiter_configs'];
if (!isset($limiterConfigs[$route])) {
return;
}
$method = $request->getMethod();
foreach ($limiterConfigs[$route] as $id => $config) {
$methods = array_filter($config['method'], fn ($m) => $m === $method);
if (empty($methods)) {
// http methodが不一致であればスキップ
continue;
}
if (!empty($config['params'])) {
$matchParams = array_filter($config['params'], function ($value, $key) use ($request) {
return $request->get($key) === $value;
}, ARRAY_FILTER_USE_BOTH);
if (count($config['params']) !== count($matchParams)) {
// パラメータが不一致であればスキップ
continue;
}
}
$limiterId = 'limiter.'.$id;
if (!$this->locator->has($limiterId)) {
continue;
}
/** @var RateLimiterFactory $factory */
$factory = $this->locator->get($limiterId);
if (in_array('customer', $config['type']) || in_array('user', $config['type'])) {
$User = $this->requestContext->getCurrentUser();
if ($User instanceof UserInterface) {
$limiter = $factory->create($User->getId());
if (!$limiter->consume()->isAccepted()) {
throw new TooManyRequestsHttpException();
}
}
}
if (in_array('ip', $config['type'])) {
$limiter = $factory->create($request->getClientIp());
if (!$limiter->consume()->isAccepted()) {
throw new TooManyRequestsHttpException();
}
}
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents()
{
return [
KernelEvents::CONTROLLER => ['onController', 0],
];
}
}