/src/Sylius/Bundle/CoreBundle/OAuth/UserProvider.php

https://github.com/Sylius/Sylius · PHP · 149 lines · 97 code · 28 blank · 24 comment · 8 complexity · 7fb08a86fcb1f576cd77741e7cefa9ea MD5 · raw file

  1. <?php
  2. /*
  3. * This file is part of the Sylius package.
  4. *
  5. * (c) Paweł Jędrzejewski
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. declare(strict_types=1);
  11. namespace Sylius\Bundle\CoreBundle\OAuth;
  12. use Doctrine\Persistence\ObjectManager;
  13. use HWI\Bundle\OAuthBundle\Connect\AccountConnectorInterface;
  14. use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
  15. use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthAwareUserProviderInterface;
  16. use Sylius\Bundle\UserBundle\Provider\UsernameOrEmailProvider as BaseUserProvider;
  17. use Sylius\Component\Core\Model\CustomerInterface;
  18. use Sylius\Component\Core\Model\ShopUserInterface as SyliusUserInterface;
  19. use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
  20. use Sylius\Component\Resource\Factory\FactoryInterface;
  21. use Sylius\Component\Resource\Repository\RepositoryInterface;
  22. use Sylius\Component\User\Canonicalizer\CanonicalizerInterface;
  23. use Sylius\Component\User\Model\UserOAuthInterface;
  24. use Sylius\Component\User\Repository\UserRepositoryInterface;
  25. use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
  26. use Symfony\Component\Security\Core\User\UserInterface;
  27. use Webmozart\Assert\Assert;
  28. /**
  29. * Loading and ad-hoc creation of a user by an OAuth sign-in provider account.
  30. */
  31. class UserProvider extends BaseUserProvider implements AccountConnectorInterface, OAuthAwareUserProviderInterface
  32. {
  33. public function __construct(
  34. string $supportedUserClass,
  35. private FactoryInterface $customerFactory,
  36. private FactoryInterface $userFactory,
  37. UserRepositoryInterface $userRepository,
  38. private FactoryInterface $oauthFactory,
  39. private RepositoryInterface $oauthRepository,
  40. private ObjectManager $userManager,
  41. CanonicalizerInterface $canonicalizer,
  42. private CustomerRepositoryInterface $customerRepository
  43. ) {
  44. parent::__construct($supportedUserClass, $userRepository, $canonicalizer);
  45. }
  46. public function loadUserByOAuthUserResponse(UserResponseInterface $response): UserInterface
  47. {
  48. $oauth = $this->oauthRepository->findOneBy([
  49. 'provider' => $response->getResourceOwner()->getName(),
  50. 'identifier' => $response->getUsername(),
  51. ]);
  52. if ($oauth instanceof UserOAuthInterface) {
  53. return $oauth->getUser();
  54. }
  55. if (null !== $response->getEmail()) {
  56. $user = $this->userRepository->findOneByEmail($response->getEmail());
  57. if (null !== $user) {
  58. return $this->updateUserByOAuthUserResponse($user, $response);
  59. }
  60. return $this->createUserByOAuthUserResponse($response);
  61. }
  62. throw new UsernameNotFoundException('Email is null or not provided');
  63. }
  64. public function connect(UserInterface $user, UserResponseInterface $response): void
  65. {
  66. $this->updateUserByOAuthUserResponse($user, $response);
  67. }
  68. /**
  69. * Ad-hoc creation of user.
  70. */
  71. private function createUserByOAuthUserResponse(UserResponseInterface $response): SyliusUserInterface
  72. {
  73. /** @var SyliusUserInterface $user */
  74. $user = $this->userFactory->createNew();
  75. $canonicalEmail = $this->canonicalizer->canonicalize($response->getEmail());
  76. /** @var CustomerInterface|null $customer */
  77. $customer = $this->customerRepository->findOneBy(['emailCanonical' => $canonicalEmail]);
  78. if (null === $customer) {
  79. /** @var CustomerInterface $customer */
  80. $customer = $this->customerFactory->createNew();
  81. }
  82. $user->setCustomer($customer);
  83. // set default values taken from OAuth sign-in provider account
  84. if (null !== $email = $response->getEmail()) {
  85. $customer->setEmail($email);
  86. }
  87. if (null !== $name = $response->getFirstName()) {
  88. $customer->setFirstName($name);
  89. } elseif (null !== $realName = $response->getRealName()) {
  90. $customer->setFirstName($realName);
  91. }
  92. if (null !== $lastName = $response->getLastName()) {
  93. $customer->setLastName($lastName);
  94. }
  95. if (!$user->getUsername()) {
  96. $user->setUsername($response->getEmail() ?: $response->getNickname());
  97. }
  98. // set random password to prevent issue with not nullable field & potential security hole
  99. $user->setPlainPassword(substr(sha1($response->getAccessToken()), 0, 10));
  100. $user->setEnabled(true);
  101. return $this->updateUserByOAuthUserResponse($user, $response);
  102. }
  103. /**
  104. * Attach OAuth sign-in provider account to existing user.
  105. */
  106. private function updateUserByOAuthUserResponse(UserInterface $user, UserResponseInterface $response): SyliusUserInterface
  107. {
  108. /** @var SyliusUserInterface $user */
  109. Assert::isInstanceOf($user, SyliusUserInterface::class);
  110. /** @var UserOAuthInterface $oauth */
  111. $oauth = $this->oauthFactory->createNew();
  112. $oauth->setIdentifier($response->getUsername());
  113. $oauth->setProvider($response->getResourceOwner()->getName());
  114. $oauth->setAccessToken($response->getAccessToken());
  115. $oauth->setRefreshToken($response->getRefreshToken());
  116. $user->addOAuthAccount($oauth);
  117. $this->userManager->persist($user);
  118. $this->userManager->flush();
  119. return $user;
  120. }
  121. }