Init Gitea
This commit is contained in:
410
EventListener/CustomerPasskeysAuthListener.php
Executable file
410
EventListener/CustomerPasskeysAuthListener.php
Executable file
@@ -0,0 +1,410 @@
|
||||
<?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 Plugin\Passkeys\EventListener;
|
||||
|
||||
use Eccube\Common\EccubeConfig;
|
||||
use Eccube\Entity\BaseInfo;
|
||||
use Eccube\Entity\Customer;
|
||||
use Eccube\Entity\Master\CustomerStatus;
|
||||
use Eccube\Repository\BaseInfoRepository;
|
||||
use Eccube\Request\Context;
|
||||
use Plugin\Passkeys\Repository\PasskeysAuthTypeRepository;
|
||||
use Plugin\Passkeys\Repository\PasskeysAuthCustomerCookieRepository;
|
||||
use Plugin\Passkeys\Service\CustomerPasskeysAuthService;
|
||||
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
||||
use Symfony\Component\HttpFoundation\RedirectResponse;
|
||||
use Symfony\Component\HttpFoundation\Session\Session;
|
||||
use Symfony\Component\HttpFoundation\Session\SessionInterface;
|
||||
use Symfony\Component\HttpKernel\Event\RequestEvent;
|
||||
use Symfony\Component\HttpKernel\Event\ControllerArgumentsEvent;
|
||||
use Symfony\Component\HttpKernel\KernelEvents;
|
||||
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
|
||||
use Symfony\Component\Security\Http\Event\LoginSuccessEvent;
|
||||
use Symfony\Component\Security\Http\Event\LogoutEvent;
|
||||
use Symfony\Contracts\EventDispatcher\Event;
|
||||
|
||||
class CustomerPasskeysAuthListener implements EventSubscriberInterface
|
||||
{
|
||||
/**
|
||||
* @var EccubeConfig
|
||||
*/
|
||||
protected $eccubeConfig;
|
||||
/**
|
||||
* @var Context
|
||||
*/
|
||||
protected $requestContext;
|
||||
/**
|
||||
* @var UrlGeneratorInterface
|
||||
*/
|
||||
protected $router;
|
||||
/**
|
||||
* @var CustomerPasskeysAuthService
|
||||
*/
|
||||
protected $customerPasskeysAuthService;
|
||||
/**
|
||||
* @var PasskeysAuthTypeRepository
|
||||
*/
|
||||
protected PasskeysAuthTypeRepository $PasskeysAuthTypeRepository;
|
||||
/**
|
||||
* @var PasskeysAuthCustomerCookieRepository
|
||||
*/
|
||||
protected PasskeysAuthCustomerCookieRepository $PasskeysAuthCustomerCookieRepository;
|
||||
/**
|
||||
* @var BaseInfo|object|null
|
||||
*/
|
||||
protected $baseInfo;
|
||||
/**
|
||||
* @var Session
|
||||
*/
|
||||
protected $session;
|
||||
/**
|
||||
* 通常(ログイン・マイページ)ルート.
|
||||
*/
|
||||
protected $default_routes;
|
||||
/**
|
||||
* 重要操作ルート.
|
||||
*/
|
||||
protected $include_routes;
|
||||
|
||||
/**
|
||||
* @param Context $requestContext
|
||||
* @param UrlGeneratorInterface $router
|
||||
* @param CustomerPasskeysAuthService $customerPasskeysAuthService
|
||||
* @param PasskeysAuthTypeRepository $PasskeysAuthTypeRepository
|
||||
* @param PasskeysAuthCustomerCookieRepository $PasskeysAuthCustomerCookieRepository
|
||||
* @param BaseInfoRepository $baseInfoRepository
|
||||
* @param SessionInterface $session
|
||||
*/
|
||||
public function __construct(
|
||||
Context $requestContext,
|
||||
UrlGeneratorInterface $router,
|
||||
CustomerPasskeysAuthService $customerPasskeysAuthService,
|
||||
PasskeysAuthTypeRepository $PasskeysAuthTypeRepository,
|
||||
PasskeysAuthCustomerCookieRepository $PasskeysAuthCustomerCookieRepository,
|
||||
BaseInfoRepository $baseInfoRepository,
|
||||
SessionInterface $session
|
||||
) {
|
||||
$this->requestContext = $requestContext;
|
||||
$this->router = $router;
|
||||
$this->customerPasskeysAuthService = $customerPasskeysAuthService;
|
||||
$this->baseInfo = $baseInfoRepository->find(1);
|
||||
$this->PasskeysAuthTypeRepository = $PasskeysAuthTypeRepository;
|
||||
$this->PasskeysAuthCustomerCookieRepository = $PasskeysAuthCustomerCookieRepository;
|
||||
$this->session = $session;
|
||||
|
||||
$this->default_routes = $this->customerPasskeysAuthService->getDefaultAuthRoutes();
|
||||
$this->include_routes = $this->customerPasskeysAuthService->getIncludeRoutes();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getSubscribedEvents(): array
|
||||
{
|
||||
return [
|
||||
KernelEvents::CONTROLLER_ARGUMENTS => ['onKernelController', 7],
|
||||
KernelEvents::REQUEST => 'onKernelRequest',
|
||||
LoginSuccessEvent::class => ['onLoginSuccess'],
|
||||
LogoutEvent::class => 'logoutEvent',
|
||||
];
|
||||
}
|
||||
|
||||
public function onKernelRequest(RequestEvent $event)
|
||||
{
|
||||
log_info('pk:onKernelRequest');
|
||||
|
||||
if (!$event->isMainRequest()) {
|
||||
log_info('pk:onKernelRequest-NotMainRequest');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->requestContext->isAdmin()) {
|
||||
log_info('pk:onKernelRequest-isAdmin');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->baseInfo->isPasskeysUse()) {
|
||||
log_info('pk:onKernelRequest-NoPK');
|
||||
return;
|
||||
}
|
||||
|
||||
$route = $event->getRequest()->attributes->get('_route');
|
||||
if($route !== 'mypage_login' && $route !== 'plg_customer_passkey_page'){
|
||||
$this->setCallbackRoute($route);
|
||||
log_info('pk:onKernelRequest set:'.$route);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* リクエスト受信時イベントハンドラ.
|
||||
*
|
||||
* @param ControllerArgumentsEvent $event
|
||||
*/
|
||||
public function onKernelController(ControllerArgumentsEvent $event)
|
||||
{
|
||||
//log_info('pk:onKernelController');
|
||||
|
||||
if (!$event->isMainRequest()) {
|
||||
// サブリクエストの場合、処理なし
|
||||
log_info('pk:onKernelController-NotMainRequest');
|
||||
return;
|
||||
}
|
||||
|
||||
if ($this->requestContext->isAdmin()) {
|
||||
// バックエンドURLの場合、処理なし
|
||||
log_info('pk:onKernelController-isAdmin');
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->baseInfo->isPasskeysUse()) {
|
||||
log_info('pk:onKernelControllerNoPK');
|
||||
return;
|
||||
}
|
||||
|
||||
$route = $event->getRequest()->attributes->get('_route');
|
||||
$uri = $event->getRequest()->getRequestUri();
|
||||
|
||||
if (!$this->isDefaultRoute($route, $uri) && !$this->isIncludeRoute($route, $uri)) {
|
||||
// 重要操作指定ではなく、マイページ系列ではない場合、処理なし
|
||||
return;
|
||||
}
|
||||
|
||||
$Customer = $this->requestContext->getCurrentUser();
|
||||
log_info('pk:onKernelController2:'.$Customer);
|
||||
|
||||
//TODO: may try passkeys before form login in the future.
|
||||
//$this->passkeyProcess($event, $Customer, $route);
|
||||
|
||||
if ($Customer instanceof Customer) {
|
||||
if(!$Customer->isEnablePasskeys()){
|
||||
log_info('pk:onKernelController:passkey disabled by user');
|
||||
return;
|
||||
}else{//for debug
|
||||
log_info('pk:onKernelController:passkey enabled by user');
|
||||
}
|
||||
|
||||
|
||||
if ($Customer->getStatus()->getId() !== CustomerStatus::REGULAR) {
|
||||
// ログインしていない場合、処理なし
|
||||
return;
|
||||
}
|
||||
|
||||
if (!$this->isDefaultRoute($route, $uri) && !$this->isIncludeRoute($route, $uri)) {
|
||||
// 重要操作指定ではなく、マイページ系列ではない場合、処理なし
|
||||
return;
|
||||
}
|
||||
|
||||
$this->passkeyProcess($event, $Customer, $route);
|
||||
|
||||
}else{
|
||||
log_info('pk:onKernelController:no customer obj yet');
|
||||
//TODO: Jump to original URL before login page.
|
||||
//$referer = $event->getRequest()->headers->get('referer');
|
||||
//$this->setCallbackRoute($uri);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ログイン完了 イベントハンドラ.
|
||||
*
|
||||
* @param LoginSuccessEvent $event
|
||||
*
|
||||
* @return RedirectResponse|void
|
||||
*/
|
||||
public function onLoginSuccess(LoginSuccessEvent $event)
|
||||
{
|
||||
//log_info('pk:onLoginSuccess1');
|
||||
if ($this->requestContext->isAdmin()) {
|
||||
// バックエンドURLの場合処理なし
|
||||
return;
|
||||
}
|
||||
//log_info('pk:onLoginSuccess2');
|
||||
if (!$this->baseInfo->isPasskeysUse()) {
|
||||
// Return if non Passkeys
|
||||
return;
|
||||
}
|
||||
//log_info('pk:onLoginSuccess3');
|
||||
if ($this->requestContext->getCurrentUser() === null) {
|
||||
// ログインしていない場合は処理なし
|
||||
return;
|
||||
}
|
||||
|
||||
log_info('pk:onLoginSuccess5');
|
||||
|
||||
$Customer = $this->requestContext->getCurrentUser();
|
||||
|
||||
if(!$Customer->isEnablePasskeys()){
|
||||
log_info('pk:onKernelController:passkey disabled by user');
|
||||
return;
|
||||
}else{//for debug
|
||||
log_info('pk:onKernelController:passkey enabled by user');
|
||||
}
|
||||
|
||||
$this->passkeyProcess(
|
||||
$event,
|
||||
$Customer,
|
||||
$event->getRequest()->attributes->get('_route'));
|
||||
}
|
||||
|
||||
/**
|
||||
* ログアウトする前に全ての2FA認証クッキーを消す
|
||||
*
|
||||
* @param LogoutEvent $logoutEvent ログアウトイベント
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function logoutEvent(LogoutEvent $logoutEvent)
|
||||
{
|
||||
$this->customerPasskeysAuthService->clearPKAuthCookies($logoutEvent->getRequest(), $logoutEvent->getResponse());
|
||||
$Customer = $this->requestContext->getCurrentUser();
|
||||
if ($Customer !== null) {
|
||||
$this->PasskeysAuthCustomerCookieRepository->deleteByCustomer($Customer);
|
||||
}
|
||||
$this->session->remove(CustomerPasskeysAuthService::SESSION_CALL_BACK_URL);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* ルート・URIが個別認証対象かチェック.
|
||||
*
|
||||
* @param string $route
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isDefaultRoute(string $route, string $uri): bool
|
||||
{
|
||||
return $this->isTargetRoute($this->default_routes, $route, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* ルート・URIが対象であるかチェック.
|
||||
*
|
||||
* @param array $targetRoutes
|
||||
* @param string $route
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isTargetRoute(array $targetRoutes, string $route, string $uri): bool
|
||||
{
|
||||
// ルートで認証
|
||||
if (in_array($route, $targetRoutes)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// URIで認証
|
||||
foreach ($targetRoutes as $r) {
|
||||
if ($r != '' && $r !== '/' && strpos($uri, $r) === 0) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* ルート・URIが個別認証対象かチェック.
|
||||
*
|
||||
* @param string $route
|
||||
* @param string $uri
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
private function isIncludeRoute(string $route, string $uri): bool
|
||||
{
|
||||
return $this->isTargetRoute($this->include_routes, $route, $uri);
|
||||
}
|
||||
|
||||
/**
|
||||
* Passkey authentication
|
||||
*
|
||||
* @param Event $event
|
||||
* @param Customer $Customer
|
||||
* @param string $route
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
private function passkeyProcess($event, $Customer, $route)
|
||||
{
|
||||
log_info('pk:passkeyProcess1');
|
||||
if (!$this->baseInfo->isPasskeysUse()) {
|
||||
return;
|
||||
}
|
||||
|
||||
//log_info('pk:passkeyProcess2');
|
||||
$is_auth = $this->customerPasskeysAuthService->isAuthed($Customer, $route);
|
||||
|
||||
if ($is_auth) {
|
||||
$my_route = $this->session->get(CustomerPasskeysAuthService::SESSION_CALL_BACK_URL);
|
||||
log_info('pk:passkey auth done:'.$route.'|'.$my_route);
|
||||
if($my_route !== null && $my_route !== $route){
|
||||
//$this->session->remove(CustomerPasskeysAuthService::SESSION_CALL_BACK_URL);
|
||||
$my_url = $this->router->generate($my_route, [], UrlGeneratorInterface::ABSOLUTE_PATH);
|
||||
if ($event instanceof ControllerArgumentsEvent) {
|
||||
log_info('pk:passkey auth done1:'.$my_url.'|||');
|
||||
$event->setController(function () use ($my_url) {
|
||||
return new RedirectResponse($my_url, $status = 302);
|
||||
});
|
||||
} else {
|
||||
log_info('pk:passkey auth done2:'.$my_url.'|||');
|
||||
$event->setResponse(new RedirectResponse($my_url, $status = 302));
|
||||
}
|
||||
}
|
||||
else{
|
||||
log_info('pk:passkey auth done3: remove SESSION_CALL_BACK_URL');
|
||||
$this->session->remove(CustomerPasskeysAuthService::SESSION_CALL_BACK_URL);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
//$this->setCallbackRoute($route);
|
||||
|
||||
log_info('pk:passkeyProcess3');
|
||||
|
||||
$url = $this->router->generate('plg_customer_passkey_page', [], UrlGeneratorInterface::ABSOLUTE_PATH);
|
||||
|
||||
//TODO: may try passkeys before form ligin in the future.
|
||||
/*if($Customer !== null && $Customer->getStatus()->getId() === CustomerStatus::REGULAR){
|
||||
$url.= '?pkreg=1';
|
||||
}*/
|
||||
|
||||
log_info('pk:plg_customer_passkey_page_process:'.$url.'|||');
|
||||
|
||||
if ($event instanceof ControllerArgumentsEvent) {
|
||||
log_info('pk:setController:'.$url.'|||');
|
||||
$event->setController(function () use ($url) {
|
||||
return new RedirectResponse($url, $status = 302);
|
||||
});
|
||||
} else {
|
||||
log_info('pk:setResponse:'.$url.'|||');
|
||||
$event->setResponse(new RedirectResponse($url, $status = 302));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* コールバックルートをセッションへ設定.
|
||||
*
|
||||
* @param string|null $route
|
||||
*/
|
||||
private function setCallbackRoute(?string $route)
|
||||
{
|
||||
log_info('pk:setCallbackRoute:'.$route);
|
||||
if ($route) {
|
||||
log_info('pk:setCallbackRoute1:'.($this->session !=null ).'|'.CustomerPasskeysAuthService::SESSION_CALL_BACK_URL);
|
||||
$this->session->set(CustomerPasskeysAuthService::SESSION_CALL_BACK_URL, $route);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user