<?php
declare(strict_types=1);
namespace Iwv\IwvTwoFactorAuthentication\Subscriber;
use Exception;
use League\OAuth2\Server\Exception\OAuthServerException;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Core\PlatformRequest;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\KernelEvents;
use Iwv\IwvTwoFactorAuthentication\Service\TwoFactorAdaptors\GoogleAuthenticatorAdaptor;
use Iwv\IwvTwoFactorAuthentication\Service\TwoFactorAdaptors\YubicoAuthenticatorAdaptor;
use Iwv\IwvTwoFactorAuthentication\Service\StoreFrontCookieHelperValidator;
use Iwv\IwvTwoFactorAuthentication\Service\SessionHelpers\StoreFrontSessionTokenHelper;
class StoreFrontSubscriber implements EventSubscriberInterface
{
/**
* @var EntityRepositoryInterface
*/
private $userRepository;
/**
* @var EntityRepositoryInterface
*/
private $customerRepository;
/**
* @var StoreFrontSessionTokenHelper
*/
private $storeFrontSessionTokenHelper;
/**
* @var StoreFrontCookieHelperValidator
*/
private $storeFrontCookieHelperValidator;
/**
* @var GoogleAuthenticatorAdaptor
*/
private $googleAuthenticatorAdaptor;
/**
* @var YubicoAuthenticatorAdaptor
*/
private $yubicoAuthenticatorAdaptor;
public function __construct(
EntityRepositoryInterface $userRepository,
EntityRepositoryInterface $customerRepository,
StoreFrontCookieHelperValidator $storeFrontCookieHelperValidator,
StoreFrontSessionTokenHelper $storeFrontSessionTokenHelper,
GoogleAuthenticatorAdaptor $googleAuthenticatorAdaptor,
YubicoAuthenticatorAdaptor $yubicoAuthenticatorAdaptor
) {
$this->userRepository = $userRepository;
$this->customerRepository = $customerRepository;
$this->storeFrontCookieHelperValidator = $storeFrontCookieHelperValidator;
$this->storeFrontSessionTokenHelper = $storeFrontSessionTokenHelper;
$this->googleAuthenticatorAdaptor = $googleAuthenticatorAdaptor;
$this->yubicoAuthenticatorAdaptor = $yubicoAuthenticatorAdaptor;
}
public static function getSubscribedEvents()
{
return [
KernelEvents::RESPONSE => 'onResponse',
];
}
public function onResponse(ResponseEvent $event): void
{
/** @var \Symfony\Component\HttpFoundation\Request $request */
$request = $event->getRequest();
/** @var \Symfony\Component\HttpFoundation\JsonResponse $response */
$response = $event->getResponse();
/* return on invalid requests */
if (!in_array($event->getResponse()->getStatusCode(), [200, 204])) {
return;
}
/* $event->getContext() does not exists */
$context = $request->attributes->get(PlatformRequest::ATTRIBUTE_CONTEXT_OBJECT);
/* update cookie request on profiles change */
if ($request->attributes->get('_route') === 'frontend.account.iwvtwofactor.yubico' ||
$request->attributes->get('_route') === 'frontend.account.iwvtwofactor.yubico.disable' ||
$request->attributes->get('_route') === 'frontend.account.iwvtwofactor.otp' ||
$request->attributes->get('_route') === 'frontend.account.iwvtwofactor.otp.disable'
)
{
/** @var SalesChannelContext $salesChannelContext */
$salesChannelContext = $request->attributes->get('sw-sales-channel-context');
$customerId = $salesChannelContext->getCustomer()->getId();
if ($customerId) {
$customerDB = $context->scope(Context::SYSTEM_SCOPE, fn(Context $systemContext) => $this->customerRepository->search(new Criteria([$customerId]), $systemContext)->first());
$customFields = $customerDB->getCustomFields();
$twoFaParams = $customFields['iwvTwoFactor'] ?? [];
$response = !empty($twoFaParams) ? $this->storeFrontCookieHelperValidator->addTwoFaCookie($response, $customerDB) : $this->storeFrontCookieHelperValidator->removeTwoFaCookie($response);
$event->setResponse($response);
}
return;
}
// if(!$request->attributes->get('_loginRequired')){
// return;
// }
$salesChannelContext = $request->attributes->get('sw-sales-channel-context');
if(!$salesChannelContext){
return;
}
$customerId = null;
if($salesChannelContext->getCustomer()){
$customerId = $salesChannelContext->getCustomer()->getId();
}
if(!$customerId){
return;
}
/** @var CustomerEntity $customerDB */
$customerDB = $context->scope(Context::SYSTEM_SCOPE, fn(Context $systemContext) => $this->customerRepository->search((new Criteria())->addFilter(new EqualsFilter('id', $customerId)), $systemContext)->first());
if (!$customerDB || !($customFields = $customerDB->getCustomFields()) || !isset($customFields['iwvTwoFactor']) || empty($customFields['iwvTwoFactor'])){
return;
}
try {
$response = $this->storeFrontCookieHelperValidator->addTwoFaCookie($response, $customerDB);
$response = $this->storeFrontSessionTokenHelper->addTwoFaSessionCookie($context, $response, $customerDB);
$event->setResponse($response);
} catch (\Exception $ex) {
throw new OAuthServerException('OTP is required', $ex->getCode(), 'iwv-request-otp', 401, array_keys($customFields['iwvTwoFactor']) ?? $customFields['iwvTwoFactor']);
}
}
}