app/Plugin/TwoFactorAuthCustomer42/EventListener/CustomerTwoFactorAuthListener.php line 206

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of EC-CUBE
  4.  *
  5.  * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6.  *
  7.  * http://www.ec-cube.co.jp/
  8.  *
  9.  * For the full copyright and license information, please view the LICENSE
  10.  * file that was distributed with this source code.
  11.  */
  12. namespace Plugin\TwoFactorAuthCustomer42\EventListener;
  13. use Eccube\Common\EccubeConfig;
  14. use Eccube\Entity\BaseInfo;
  15. use Eccube\Entity\Customer;
  16. use Eccube\Entity\Master\CustomerStatus;
  17. use Eccube\Repository\BaseInfoRepository;
  18. use Eccube\Request\Context;
  19. use Plugin\TwoFactorAuthCustomer42\Repository\TwoFactorAuthTypeRepository;
  20. use Plugin\TwoFactorAuthCustomer42\Repository\TwoFactorAuthCustomerCookieRepository;
  21. use Plugin\TwoFactorAuthCustomer42\Service\CustomerTwoFactorAuthService;
  22. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  23. use Symfony\Component\HttpFoundation\RedirectResponse;
  24. use Symfony\Component\HttpFoundation\Session\Session;
  25. use Symfony\Component\HttpFoundation\Session\SessionInterface;
  26. use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
  27. use Symfony\Component\HttpKernel\KernelEvents;
  28. use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
  29. use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
  30. use Symfony\Component\Security\Http\Event\LogoutEvent;
  31. use Symfony\Contracts\EventDispatcher\Event;
  32. class CustomerTwoFactorAuthListener implements EventSubscriberInterface
  33. {
  34.     /**
  35.      * @var EccubeConfig
  36.      */
  37.     protected $eccubeConfig;
  38.     /**
  39.      * @var Context
  40.      */
  41.     protected $requestContext;
  42.     /**
  43.      * @var UrlGeneratorInterface
  44.      */
  45.     protected $router;
  46.     /**
  47.      * @var CustomerTwoFactorAuthService
  48.      */
  49.     protected $customerTwoFactorAuthService;
  50.     /**
  51.      * @var TwoFactorAuthTypeRepository
  52.      */
  53.     protected TwoFactorAuthTypeRepository $twoFactorAuthTypeRepository;
  54.     /**
  55.      * @var TwoFactorAuthCustomerCookieRepository
  56.      */
  57.     protected TwoFactorAuthCustomerCookieRepository $twoFactorAuthCustomerCookieRepository;
  58.     /**
  59.      * @var BaseInfo|object|null
  60.      */
  61.     protected $baseInfo;
  62.     /**
  63.      * @var Session
  64.      */
  65.     protected $session;
  66.     /**
  67.      * 通常(ログイン・マイページ)ルート.
  68.      */
  69.     protected $default_routes;
  70.     /**
  71.      * 重要操作ルート.
  72.      */
  73.     protected $include_routes;
  74.     /**
  75.      * @param Context $requestContext
  76.      * @param UrlGeneratorInterface $router
  77.      * @param CustomerTwoFactorAuthService $customerTwoFactorAuthService
  78.      * @param TwoFactorAuthTypeRepository $twoFactorAuthTypeRepository
  79.      * @param TwoFactorAuthCustomerCookieRepository $twoFactorAuthCustomerCookieRepository
  80.      * @param BaseInfoRepository $baseInfoRepository
  81.      * @param SessionInterface $session
  82.      */
  83.     public function __construct(
  84.         Context $requestContext,
  85.         UrlGeneratorInterface $router,
  86.         CustomerTwoFactorAuthService $customerTwoFactorAuthService,
  87.         TwoFactorAuthTypeRepository $twoFactorAuthTypeRepository,
  88.         TwoFactorAuthCustomerCookieRepository $twoFactorAuthCustomerCookieRepository,
  89.         BaseInfoRepository $baseInfoRepository,
  90.         SessionInterface $session
  91.     ) {
  92.         $this->requestContext $requestContext;
  93.         $this->router $router;
  94.         $this->customerTwoFactorAuthService $customerTwoFactorAuthService;
  95.         $this->baseInfo $baseInfoRepository->find(1);
  96.         $this->twoFactorAuthTypeRepository $twoFactorAuthTypeRepository;
  97.         $this->twoFactorAuthCustomerCookieRepository $twoFactorAuthCustomerCookieRepository;
  98.         $this->session $session;
  99.         $this->default_routes $this->customerTwoFactorAuthService->getDefaultAuthRoutes();
  100.         $this->include_routes $this->customerTwoFactorAuthService->getIncludeRoutes();
  101.     }
  102.     /**
  103.      * @return array
  104.      */
  105.     public static function getSubscribedEvents(): array
  106.     {
  107.         return [
  108.             KernelEvents::CONTROLLER_ARGUMENTS => ['onKernelController'7],
  109.             LoginSuccessEvent::class => ['onLoginSuccess'],
  110.             LogoutEvent::class => 'logoutEvent',
  111.         ];
  112.     }
  113.     /**
  114.      * リクエスト受信時イベントハンドラ.
  115.      *
  116.      * @param ControllerArgumentsEvent $event
  117.      */
  118.     public function onKernelController(ControllerArgumentsEvent $event)
  119.     {
  120.         if (!$event->isMainRequest()) {
  121.             // サブリクエストの場合、処理なし
  122.             return;
  123.         }
  124.         if ($this->requestContext->isAdmin()) {
  125.             // バックエンドURLの場合、処理なし
  126.             return;
  127.         }
  128.         if (!$this->baseInfo->isTwoFactorAuthUse()) {
  129.             // 2段階認証使用しない場合は処理なし
  130.             return;
  131.         }
  132.         $route $event->getRequest()->attributes->get('_route');
  133.         $uri $event->getRequest()->getRequestUri();
  134.         $Customer $this->requestContext->getCurrentUser();
  135.         if ($Customer instanceof Customer) {
  136.             if ($Customer->getStatus()->getId() !== CustomerStatus::REGULAR) {
  137.                 // ログインしていない場合、処理なし
  138.                 return;
  139.             }
  140.             if (!$this->isDefaultRoute($route$uri) && !$this->isIncludeRoute($route$uri)) {
  141.                 // 重要操作指定ではなく、マイページ系列ではない場合、処理なし
  142.                 return;
  143.             }
  144.             $this->multiFactorAuth($event$Customer$route);
  145.         }
  146.     }
  147.     /**
  148.      * ログイン完了 イベントハンドラ.
  149.      *
  150.      * @param LoginSuccessEvent $event
  151.      *
  152.      * @return RedirectResponse|void
  153.      */
  154.     public function onLoginSuccess(LoginSuccessEvent $event)
  155.     {
  156.         if ($this->requestContext->isAdmin()) {
  157.             // バックエンドURLの場合処理なし
  158.             return;
  159.         }
  160.         if (!$this->baseInfo->isTwoFactorAuthUse()) {
  161.             // 2段階認証使用しない場合は処理なし
  162.             return;
  163.         }
  164.         if ($this->requestContext->getCurrentUser() === null) {
  165.             // ログインしていない場合は処理なし
  166.             return;
  167.         }
  168.         if ($this->requestContext->getCurrentUser()->getTwoFactorAuthType() !== null &&
  169.             $this->requestContext->getCurrentUser()->getTwoFactorAuthType()->isDisabled()) {
  170.             // ユーザーが選択した2段階認証方式は無効になっている場合、ログアウトさせる。
  171.             return new RedirectResponse($this->router->generate('logout'), 302);
  172.         }
  173.         $this->multiFactorAuth(
  174.             $event,
  175.             $this->requestContext->getCurrentUser(),
  176.             $event->getRequest()->attributes->get('_route'));
  177.     }
  178.     /**
  179.      * ログアウトする前に全ての2FA認証クッキーを消す
  180.      *
  181.      * @param LogoutEvent $logoutEvent ログアウトイベント
  182.      *
  183.      * @return void
  184.      */
  185.     public function logoutEvent(LogoutEvent $logoutEvent)
  186.     {
  187.         $this->customerTwoFactorAuthService->clear2AuthCookies($logoutEvent->getRequest(), $logoutEvent->getResponse());
  188.         $Customer $this->requestContext->getCurrentUser();
  189.         if ($Customer !== null) {
  190.             $this->twoFactorAuthCustomerCookieRepository->deleteByCustomer($Customer);
  191.         }
  192.     }
  193.     /**
  194.      * ルート・URIが個別認証対象かチェック.
  195.      *
  196.      * @param string $route
  197.      * @param string $uri
  198.      *
  199.      * @return bool
  200.      */
  201.     private function isDefaultRoute(string $routestring $uri): bool
  202.     {
  203.         return $this->isTargetRoute($this->default_routes$route$uri);
  204.     }
  205.     /**
  206.      * ルート・URIが対象であるかチェック.
  207.      *
  208.      * @param array $targetRoutes
  209.      * @param string $route
  210.      * @param string $uri
  211.      *
  212.      * @return bool
  213.      */
  214.     private function isTargetRoute(array $targetRoutesstring $routestring $uri): bool
  215.     {
  216.         // ルートで認証
  217.         if (in_array($route$targetRoutes)) {
  218.             return true;
  219.         }
  220.         // URIで認証
  221.         foreach ($targetRoutes as $r) {
  222.             if ($r != '' && $r !== '/' && strpos($uri$r) === 0) {
  223.                 return true;
  224.             }
  225.         }
  226.         return false;
  227.     }
  228.     /**
  229.      * ルート・URIが個別認証対象かチェック.
  230.      *
  231.      * @param string $route
  232.      * @param string $uri
  233.      *
  234.      * @return bool
  235.      */
  236.     private function isIncludeRoute(string $routestring $uri): bool
  237.     {
  238.         return $this->isTargetRoute($this->include_routes$route$uri);
  239.     }
  240.     /**
  241.      * 多要素認証.
  242.      *
  243.      * @param Event $event
  244.      * @param Customer $Customer
  245.      * @param string $route
  246.      *
  247.      * @return mixed
  248.      */
  249.     private function multiFactorAuth($event$Customer$route)
  250.     {
  251.         if (!$this->baseInfo->isTwoFactorAuthUse()) {
  252.             // MFA無効の場合処理なし
  253.             return;
  254.         }
  255.         if (count($this->twoFactorAuthTypeRepository->findBy(['isDisabled' => false])) == 0) {
  256.             // 2段階認証プラグインが有効化されていない場合処理なし
  257.             return;
  258.         }
  259.         // [会員] ログイン時2段階認証状態
  260.         $is_auth $this->customerTwoFactorAuthService->isAuthed($Customer$route);
  261.         if (!$is_auth) {
  262.             log_info('[2段階認証] 実施');
  263.             if ($Customer->getTwoFactorAuthType() === null) {
  264.                 // 2段階認証未設定
  265.                 $this->selectAuthType($event$route);
  266.             } else {
  267.                 // 2段階認証設定済み
  268.                 $this->auth($event$Customer$route);
  269.             }
  270.         }
  271.     }
  272.     /**
  273.      * 多要素認証方式設定画面へリダイレクト.
  274.      *
  275.      * @param Event $event
  276.      * @param string|null $route
  277.      */
  278.     private function selectAuthType($event, ?string $route)
  279.     {
  280.         // [会員] 2段階認証が未設定の場合
  281.         // コールバックURLをセッションへ設定
  282.         $this->setCallbackRoute($route);
  283.         // 2段階認証選択画面へリダイレクト
  284.         $url $this->router->generate('plg_customer_2fa_auth_type_select', [], UrlGeneratorInterface::ABSOLUTE_PATH);
  285.         if ($event instanceof ControllerArgumentsEvent) {
  286.             $event->setController(function () use ($url) {
  287.                 return new RedirectResponse($url302);
  288.             });
  289.         } else {
  290.             $event->setResponse(new RedirectResponse($url302));
  291.         }
  292.     }
  293.     /**
  294.      * コールバックルートをセッションへ設定.
  295.      *
  296.      * @param string|null $route
  297.      */
  298.     private function setCallbackRoute(?string $route)
  299.     {
  300.         if ($route) {
  301.             $this->session->set(CustomerTwoFactorAuthService::SESSION_CALL_BACK_URL$route);
  302.         }
  303.     }
  304.     /**
  305.      * 2段階認証のディスパッチ.
  306.      *
  307.      * @param Event $event
  308.      * @param Customer $Customer
  309.      * @param string|null $route
  310.      */
  311.     private function auth($eventCustomer $Customer, ?string $route)
  312.     {
  313.         // コールバックURLをセッションへ設定
  314.         $this->setCallbackRoute($route);
  315.         // 選択された多要素認証方式で指定されているルートへリダイレクト
  316.         if ($Customer->getTwoFactorAuthType() !== null && $Customer->getTwoFactorAuthType()->isDisabled()) {
  317.             // ユーザーが選択した2段階認証方式は無効になっている場合、ログアウトさせる。
  318.             $event->setController(function () {
  319.                 return new RedirectResponse($this->router->generate('logout'), 302);
  320.             });
  321.             return;
  322.         }
  323.         $url $this->router->generate($Customer->getTwoFactorAuthType()->getRoute());
  324.         if ($event instanceof ControllerArgumentsEvent) {
  325.             $event->setController(function () use ($url) {
  326.                 return new RedirectResponse($url302);
  327.             });
  328.         } else {
  329.             $event->setResponse(new RedirectResponse($url302));
  330.         }
  331.     }
  332. }