<?php
namespace App\Security;
use App\Entity\Institution;
use App\Entity\PdfmenuPersonalization;
use App\Entity\Profile;
use App\Entity\Setting;
use App\Entity\User;
use App\Entity\UserConnection;
use App\Service\RoutingService;
use App\Service\Tools;
use App\Service\UserService;
use App\Service\UserDefaultDataService;
use DateTime;
use Doctrine\ORM\EntityManagerInterface;
use Google\Cloud\Core\Exception\ServiceException;
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
use KnpU\OAuth2ClientBundle\Client\OAuth2ClientInterface;
use KnpU\OAuth2ClientBundle\Security\Authenticator\SocialAuthenticator;
use League\OAuth2\Client\Provider\GoogleUser;
use Stripe\Exception\ApiErrorException;
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\BadCredentialsException;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Contracts\Translation\TranslatorInterface;
class GoogleAuthenticator extends SocialAuthenticator
{
private ClientRegistry $clientRegistry;
private EntityManagerInterface $em;
private RouterInterface $router;
private TranslatorInterface $translator;
private Tools $tools;
private UserService $userService;
private UserDefaultDataService $userDefaultDataService;
private RoutingService $routingService;
private MailerInterface $mailer;
private ContainerInterface $container;
public function __construct(
ClientRegistry $clientRegistry,
EntityManagerInterface $em,
RouterInterface $router,
TranslatorInterface $translator,
Tools $tools,
UserService $userService,
UserDefaultDataService $userDefaultDataService,
RoutingService $routingService,
MailerInterface $mailer,
ContainerInterface $container
) {
$this->clientRegistry = $clientRegistry;
$this->em = $em;
$this->router = $router;
$this->translator = $translator;
$this->tools = $tools;
$this->userService = $userService;
$this->userDefaultDataService = $userDefaultDataService;
$this->routingService = $routingService;
$this->mailer = $mailer;
$this->container = $container;
}
/**
* @param Request $request
* @return bool
*/
public function supports(Request $request)
{
return 'api_security_google_check' === $request->attributes->get('_route')
&& $request->isMethod('GET');
}
/**
* @param $credentials
* @param UserProviderInterface $userProvider
* @return User
* @throws TransportExceptionInterface
*/
public function getUser($credentials, UserProviderInterface $userProvider): User
{
/** @var GoogleUser $googleUser */
$googleUser = $this->getGoogleClient()->fetchUserFromToken($credentials);
$email = $googleUser->getEmail();
/** @var User $existingUser */
$existingUser = $this->em->getRepository(User::class)->findOneBy(['googleId' => $googleUser->getId()]);
if ($existingUser) {
if (!$existingUser->isActive()) {
throw new BadCredentialsException('user_disabled');
}
if (!$existingUser->isValidated()) {
throw new BadCredentialsException('user_not_validated');
}
if ($existingUser->getProfile()->getRole() !== Profile::ROLE_CUSTOMER) {
throw new BadCredentialsException('bad_user');
}
return $existingUser;
}
/** @var User $user */
$user = $this->em->getRepository(User::class)->findOneBy(['email' => $email]);
if (!$user) {
/** @var Profile $profile */
$profile = $this->em->getRepository(Profile::class)->findOneBy(['role' => Profile::ROLE_CUSTOMER]);
/** @var Setting $setting */
$setting = $this->em->getRepository(Setting::class)->findOneBy(['name' => 'FREE_SMS_PER_MONTH']);
$user = new User();
$user->setEmail($googleUser->getEmail());
$user->setUsername($googleUser->getName());
$user->setGoogleId($googleUser->getId());
$user->setProfile($profile);
$user->setHash($this->tools->generateRandomString(20));
$user->setValidated(true);
$user->setSmsRemaining($setting->getValue());
$institution = new Institution();
$institution->setIsDefault(true);
$institution->setName('Mon restaurant');
$institution->setUser($user);
$institution->setHash($this->tools->generateRandomString(20));
$pdfmenuPersonalization = new PdfmenuPersonalization();
$institution->setPdfmenuPersonalization($pdfmenuPersonalization);
$this->userService->setDefaultDataOnRegistration($institution);
$user->addInstitution($institution);
$this->em->persist($user);
$this->em->persist($pdfmenuPersonalization);
$this->em->flush();
$this->userService->generateQRCodeInstitution($institution);
$message = (new TemplatedEmail())
->from(new Address($this->container->getParameter('MAILER_FROM'), $this->container->getParameter('MAILER_FROM_NAME')))
->to($user->getEmail())
->subject($this->translator->trans('registrationSocial.title', [], 'email'))
->htmlTemplate('front/emails/registration_social.html.twig')
->context([
'userEmail' => $user->getEmail(),
]);
$this->mailer->send($message);
} else {
$user->setGoogleId($googleUser->getId());
$user->setRegistrationValidationToken(null);
$user->setRegistrationValidationRequestAt(null);
$user->setValidated(true);
$this->em->persist($user);
$this->em->flush();
}
return $user;
}
public function getCredentials(Request $request)
{
return $this->fetchAccessToken($this->getGoogleClient());
}
/**
* @return OAuth2ClientInterface
*/
private function getGoogleClient(): OAuth2ClientInterface
{
return $this->clientRegistry->getClient('google');
}
/**
* @param Request $request
* @param AuthenticationException|null $authException
* @return string|Response
*/
public function start(Request $request, AuthenticationException $authException = null): string|Response
{
return $this->router->generate('api_security_login');
}
/**
* @param Request $request
* @param AuthenticationException $exception
* @return RedirectResponse
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception): RedirectResponse
{
return new RedirectResponse($route = $this->routingService->generate('login') . '?s=' . $exception->getMessage());
}
/**
* @param Request $request
* @param TokenInterface $token
* @param $providerKey
* @return RedirectResponse
* @throws ApiErrorException
* @throws ServiceException
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey): RedirectResponse
{
/** @var User $user */
$user = $token->getUser();
$this->userDefaultDataService->setDefaultCategories($user);
$user->setLastConnection(new DateTime());
$userConnection = new UserConnection();
$userConnection->setUser($user);
$userConnection->setConnectionAt(new DateTime());
$user->addUserConnection($userConnection);
$hasCookieCart = $this->userService->checkHaveCookieCart($request, $user);
$this->userService->checkIsValideSubscription($user);
$this->em->flush();
$response = new RedirectResponse($this->routingService->generate('login'));
if ($hasCookieCart) {
$response->headers->clearCookie('cart');
}
return $response;
}
}