<?php
declare(strict_types=1);
namespace Nextag\Login\Controller;
use Shopware\Core\Checkout\Customer\CustomerEntity;
use Shopware\Storefront\Controller\StorefrontController;
use Shopware\Core\Checkout\Customer\Exception\BadCredentialsException;
use Shopware\Core\Checkout\Customer\Exception\CustomerNotFoundByHashException;
use Shopware\Core\Checkout\Customer\Exception\CustomerNotFoundException;
use Shopware\Core\Checkout\Customer\Exception\CustomerRecoveryHashExpiredException;
use Shopware\Core\Checkout\Customer\Exception\InactiveCustomerException;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLoginRoute;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractLogoutRoute;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractResetPasswordRoute;
use Shopware\Core\Checkout\Customer\SalesChannel\AbstractSendPasswordRecoveryMailRoute;
use Shopware\Core\Content\Category\Exception\CategoryNotFoundException;
use Shopware\Core\Framework\Context;
use Shopware\Core\Framework\DataAbstractionLayer\EntityRepositoryInterface;
use Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Criteria;
use Shopware\Core\Framework\DataAbstractionLayer\Search\Filter\EqualsFilter;
use Shopware\Core\Framework\Routing\Annotation\RouteScope;
use Shopware\Core\Framework\Routing\Exception\MissingRequestParameterException;
use Shopware\Core\Framework\Validation\DataBag\RequestDataBag;
use Shopware\Core\Framework\Validation\Exception\ConstraintViolationException;
use Shopware\Core\System\SalesChannel\SalesChannelContext;
use Shopware\Core\System\SystemConfig\SystemConfigService;
use Shopware\Storefront\Framework\Routing\RequestTransformer;
use Shopware\Storefront\Page\Account\Login\AccountLoginPageLoader;
use Symfony\Component\HttpFoundation\Cookie;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Shopware\Core\Checkout\Cart\SalesChannel\CartService;
use Symfony\Component\HttpKernel\Exception\UnauthorizedHttpException;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Session\Session;
/**
* @RouteScope(scopes={"storefront"})
*/
class AuthController extends StorefrontController
{
/**
* @var EntityRepositoryInterface
*/
private $customerRepository;
/**
* @var AccountLoginPageLoader
*/
private $loginPageLoader;
/**
* @var EntityRepositoryInterface
*/
private $customerRecoveryRepository;
/**
* @var AbstractSendPasswordRecoveryMailRoute
*/
private $sendPasswordRecoveryMailRoute;
/**
* @var AbstractResetPasswordRoute
*/
private $resetPasswordRoute;
/**
* @var AbstractLoginRoute
*/
private $loginRoute;
/**
* @var AbstractLogoutRoute
*/
private $logoutRoute;
/**
* @var SystemConfigService
*/
private $systemConfig;
private $cartService;
private $session;
private $log;
public function __construct(
AccountLoginPageLoader $loginPageLoader,
EntityRepositoryInterface $customerRecoveryRepository,
AbstractSendPasswordRecoveryMailRoute $sendPasswordRecoveryMailRoute,
AbstractResetPasswordRoute $resetPasswordRoute,
AbstractLoginRoute $loginRoute,
SystemConfigService $systemConfig,
AbstractLogoutRoute $logoutRoute,
CartService $cartService,
EntityRepositoryInterface $customerRepository,
Session $session,
\Nextag\Login\Helper\Log $log
) {
$this->loginPageLoader = $loginPageLoader;
$this->customerRecoveryRepository = $customerRecoveryRepository;
$this->sendPasswordRecoveryMailRoute = $sendPasswordRecoveryMailRoute;
$this->resetPasswordRoute = $resetPasswordRoute;
$this->loginRoute = $loginRoute;
$this->logoutRoute = $logoutRoute;
$this->systemConfig = $systemConfig;
$this->cartService = $cartService;
$this->customerRepository = $customerRepository;
$this->session = $session;
$this->log = $log;
}
/**
* @Route("/account/login", name="frontend.account.login.page", methods={"GET"})
*/
public function loginPage(Request $request, RequestDataBag $data, SalesChannelContext $context): Response
{
/** @var string $redirect */
$redirect = $request->get('redirectTo', 'frontend.account.home.page');
// if ($context->getCustomer() && $context->getCustomer()->getGuest() == false) {
// return $this->createActionResponse($request);
// }
$page = $this->loginPageLoader->load($request, $context);
return $this->renderStorefront('@Storefront/storefront/page/account/register/index.html.twig', [
'redirectTo' => $redirect,
'redirectParameters' => $request->get('redirectParameters', json_encode([])),
'page' => $page,
'loginError' => (bool) $request->get('loginError'),
'errorSnippet' => $request->get('errorSnippet'),
'data' => $data,
]);
}
/**
* @Route("/account/logout", name="frontend.account.logout.page", methods={"GET"})
*/
public function logout(Request $request, SalesChannelContext $context, RequestDataBag $dataBag): Response
{
if ($context->getCustomer() === null) {
return $this->redirectToRoute('frontend.account.login.page');
}
$this->session->set("pos", "");
try {
$this->logoutRoute->logout($context, $dataBag);
$this->addFlash(self::SUCCESS, $this->trans('account.logoutSucceeded'));
$parameters = [];
} catch (ConstraintViolationException $formViolations) {
$parameters = ['formViolations' => $formViolations];
}
return $this->redirectToRoute('frontend.account.login.page', $parameters);
}
/**
* @Route("/account/login", name="frontend.account.login", methods={"POST"}, defaults={"XmlHttpRequest"=true})
*/
public function login(Request $request, RequestDataBag $data, SalesChannelContext $context): Response
{
if ($context->getCustomer() && $context->getCustomer()->getGuest() == false) {
return $this->createActionResponse($request);
}
$errorSnippet = null;
// try {
// $token = $this->loginRoute->login($data, $context)->getToken();
// if (!empty($token)) {
// $this->addCartErrors($this->cartService->getCart($token, $context));
// return $this->createActionResponse($request);
// }
// } catch (BadCredentialsException | UnauthorizedHttpException | InactiveCustomerException $e) {
// if ($e instanceof InactiveCustomerException) {
// $errorSnippet = $e->getSnippetKey();
// }
// }
// $data->set('password', null);
// return $this->forwardToRoute(
// 'frontend.account.login.page',
// [
// 'loginError' => true,
// 'errorSnippet' => $errorSnippet ?? null,
// ]
// );
try {
// if ($data->get("password") == "test1234" && $request->get("sw-sales-channel-absolute-base-url") != "https://martel.dev5") {
// throw new BadCredentialsException();
// }
$email = $data->get('email', $data->get('username'));
$password = $data->get('password');
$token = $this->loginRoute->login($data, $context)->getToken();
if (!empty($token)) {
$this->log->info("Login von: " . $email ." erfolgreich. Token: " . $token, false);
$this->addCartErrors($this->cartService->getCart($token, $context));
$loginReferer = $data->get("loginReferer");
if ($loginReferer && $loginReferer != "") {
if (strpos($loginReferer, "/account/login")) {
$loginReferer = explode("/account/login", $loginReferer)[0];
}
if (strpos($loginReferer, "/account/recover/password")) {
return $this->redirectToRoute('frontend.account.login.page');
}
$timestamp = date('YmdGis');
if (strpos($loginReferer, "?")) {
$response = new RedirectResponse($loginReferer . "&cache=" . $timestamp, 302);
} else {
$response = new RedirectResponse($loginReferer . "?cache=" . $timestamp, 302);
}
return $response;
//return $this->createActionResponse($request);
} else {
return $this->createActionResponse($request);
}
}
} catch (BadCredentialsException | UnauthorizedHttpException | InactiveCustomerException $e) {
//if ($e instanceof InactiveCustomerException) {
// $errorSnippet = $e->getSnippetKey();
//}
$this->log->info("Login von: " . $email . " fehlgeschlagen mit Fehlermeldung: " . $e->getMessage(), false);
}
$data->set('password', null);
if ($errorSnippet == null) {
$this->log->info("Login von: " . $email ." fehlgeschlagen", false);
} else {
$this->log->info("Login von: " . $email . " fehlgeschlagen: " . json_encode($errorSnippet), false);
}
return $this->forwardToRoute(
'frontend.account.login.page',
[
'loginError' => true,
'errorSnippet' => $errorSnippet ?? null,
]
);
}
/**
* @Route("/account/recover", name="frontend.account.recover.page", methods={"GET"})
*
* @throws CategoryNotFoundException
* @throws InconsistentCriteriaIdsException
* @throws MissingRequestParameterException
*/
public function recoverAccountForm(Request $request, SalesChannelContext $context): Response
{
$page = $this->loginPageLoader->load($request, $context);
return $this->renderStorefront('@Storefront/storefront/page/account/profile/recover-password.html.twig', [
'page' => $page,
]);
}
/**
* @Route("/account/emailexists", name="frontend.account.emailexists", methods={"GET"})
*
*/
public function checkEmail(Request $request, SalesChannelContext $context): JsonResponse
{
$email = $request->query->get("email");
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter("guest",false));
$criteria->addFilter(new EqualsFilter("email",$email));
$customer = $this->customerRepository->search($criteria,$context->getContext())->first();
if ($customer) {
$response = new JsonResponse(
[
'result' => true,
'email' => $email
],
200
);
} else {
$response = new JsonResponse(
[
'result' => false,
'email' => ''
],
200
);
}
return $response;
}
/**
* @param string $loginName
* @param Context $context
* @return string
* @throws CustomerNotFoundException
* @throws \Shopware\Core\Framework\DataAbstractionLayer\Exception\InconsistentCriteriaIdsException
*/
private function getCustomerByLoginNameRecovery(string $loginName, SalesChannelContext $context)
{
$customField = $this->systemConfig->get("NextagLogin.config.customField");
$customer = null;
if ($customField != null && $customField != "") {
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('guest', 0));
$criteria->addFilter(new EqualsFilter('customFields.' . $customField, $loginName));
$criteria->setLimit(1);
/** @var CustomerEntity $customer */
$customer = $this->customerRepository->search($criteria, $context->getContext())->first();
}
if (!$customer || !$customer->getEmail()) {
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('guest', 0));
$criteria->addFilter(new EqualsFilter('email', $loginName));
$criteria->setLimit(1);
/** @var CustomerEntity $customer */
$customer = $this->customerRepository->search($criteria, $context->getContext())->first();
if ($customer) {
try {
if ($customer->getCustomFields() == null) {
return $customer;
}
if ($customer->getCustomFields() && !isset($customer->getCustomFields()[$customField]) || $customer->getCustomFields() && $customer->getCustomFields()[$customField] == $loginName) {
return $customer;
} else {
return null;
}
} catch (\Exception $exception) {
return $customer;
}
} else {
return null;
}
}
return $customer;
}
/**
* @Route("/account/recover", name="frontend.account.recover.request", methods={"POST"})
*/
public function generateAccountRecovery(Request $request, RequestDataBag $data, SalesChannelContext $context): Response
{
try {
$accountID = $data->get('email')->get("email");
$customer = $this->getCustomerByLoginNameRecovery($accountID, $context);
if ($customer) {
$data->get('email')->set("customerId", $customer->get("id"));
$data->get('email')->set("email", $customer->get("email"));
$data->get('email')->set("accountId", $accountID);
$data->get('email')
->set('storefrontUrl', $request->attributes->get(RequestTransformer::STOREFRONT_URL));
$this->sendPasswordRecoveryMailRoute->sendRecoveryMail(
$data->get('email')->toRequestDataBag(),
$context,
false
);
$email = $data->get('email')->get('email');
}
$this->addFlash('success', $this->trans('account.recoveryMailSend'));
} catch (CustomerNotFoundException $e) {
$this->addFlash('success', "");
} catch (InconsistentCriteriaIdsException $e) {
$this->addFlash('danger', $this->trans('error.message-default'));
}
return $this->redirectToRoute('frontend.account.recover.page');
}
/**
* @Route("/account/recover/password", name="frontend.account.recover.password.page", methods={"GET"})
*
* @throws CategoryNotFoundException
* @throws InconsistentCriteriaIdsException
* @throws MissingRequestParameterException
*/
public function resetPasswordForm(Request $request, SalesChannelContext $context): Response
{
$page = $this->loginPageLoader->load($request, $context);
$hash = $request->get('hash');
if (!$hash) {
$this->addFlash('danger', $this->trans('account.passwordHashNotFound'));
return $this->redirectToRoute('frontend.account.recover.request');
}
$customerHashCriteria = new Criteria();
$customerHashCriteria->addFilter(new EqualsFilter('hash', $hash));
$customerRecovery = $this->customerRecoveryRepository
->search($customerHashCriteria, $context->getContext())
->first();
if ($customerRecovery === null) {
$this->addFlash('danger', $this->trans('account.passwordHashNotFound'));
return $this->redirectToRoute('frontend.account.recover.request');
}
if (!$this->checkHash($hash, $context->getContext())) {
$this->addFlash('danger', $this->trans('account.passwordHashExpired'));
return $this->redirectToRoute('frontend.account.recover.request');
}
return $this->renderStorefront('@Storefront/storefront/page/account/profile/reset-password.html.twig', [
'page' => $page,
'hash' => $hash,
'formViolations' => $request->get('formViolations'),
]);
}
/**
* @Route("/account/recover/password", name="frontend.account.recover.password.reset", methods={"POST"})
*
* @throws InconsistentCriteriaIdsException
*/
public function resetPassword(RequestDataBag $data, SalesChannelContext $context): Response
{
$hash = $data->get('password')->get('hash');
try {
$pw = $data->get('password');
$email = "unbekannt";
$customerHashCriteria = new Criteria();
$customerHashCriteria->addFilter(new EqualsFilter('hash', $hash));
$customerHashCriteria->addAssociation('customer');
$customerRecovery = $this->customerRecoveryRepository->search($customerHashCriteria, $context->getContext())->first();
if ($customerRecovery) {
$customer = $customerRecovery->getCustomer();
if ($customer) {
$email = $customer->getEmail();
}
}
$this->resetPasswordRoute->resetPassword($pw->toRequestDataBag(), $context);
$this->addFlash('success', $this->trans('account.passwordChangeSuccess'));
$this->log->info("Passwort reset für: " . $email . " erfolgreich", false);
} catch (ConstraintViolationException $formViolations) {
$this->log->info("Passwort reset für: " . $email . " nicht erfolgreich (ConstraintViolationException)", false);
$this->addFlash('danger', $this->trans('account.passwordChangeNoSuccess'));
return $this->forwardToRoute(
'frontend.account.recover.password.page',
['hash' => $hash, 'formViolations' => $formViolations, 'passwordFormViolation' => true]
);
} catch (CustomerNotFoundByHashException $e) {
$this->log->info("Passwort reset für: " . $email . " nicht erfolgreich (CustomerNotFoundByHashException)", false);
$this->addFlash('danger', $this->trans('account.passwordChangeNoSuccess'));
return $this->forwardToRoute('frontend.account.recover.request');
} catch (CustomerRecoveryHashExpiredException $e) {
$this->log->info("Passwort reset für: " . $email . " nicht erfolgreich (CustomerRecoveryHashExpiredException)", false);
$this->addFlash('danger', $this->trans('account.passwordHashExpired'));
return $this->forwardToRoute('frontend.account.recover.request');
}
return $this->redirectToRoute('frontend.account.login.page');
}
private function checkHash(string $hash, Context $context): bool
{
$criteria = new Criteria();
$criteria->addFilter(new EqualsFilter('hash', $hash));
$recovery = $this->customerRecoveryRepository->search($criteria, $context)->first();
$validDateTime = (new \DateTime())->sub(new \DateInterval('PT2H'));
return $recovery && $validDateTime < $recovery->getCreatedAt();
}
}