Completed
Push — pull-request/8600 ( 367a7c )
by Kamil
56:28 queued 34:06
created

UserProvider::createUserByOAuthUserResponse()   C

Complexity

Conditions 8
Paths 48

Size

Total Lines 42
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 42
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 20
nc 48
nop 1
1
<?php
2
3
/*
4
 * This file is part of the Sylius package.
5
 *
6
 * (c) Paweł Jędrzejewski
7
 *
8
 * For the full copyright and license information, please view the LICENSE
9
 * file that was distributed with this source code.
10
 */
11
12
declare(strict_types=1);
13
14
namespace Sylius\Bundle\CoreBundle\OAuth;
15
16
use Doctrine\Common\Persistence\ObjectManager;
17
use HWI\Bundle\OAuthBundle\Connect\AccountConnectorInterface;
18
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
19
use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthAwareUserProviderInterface;
20
use Sylius\Bundle\UserBundle\Provider\UsernameOrEmailProvider as BaseUserProvider;
21
use Sylius\Component\Core\Model\CustomerInterface;
22
use Sylius\Component\Core\Model\ShopUserInterface as SyliusUserInterface;
23
use Sylius\Component\Core\Repository\CustomerRepositoryInterface;
24
use Sylius\Component\Resource\Factory\FactoryInterface;
25
use Sylius\Component\Resource\Repository\RepositoryInterface;
26
use Sylius\Component\User\Canonicalizer\CanonicalizerInterface;
27
use Sylius\Component\User\Model\UserOAuthInterface;
28
use Sylius\Component\User\Repository\UserRepositoryInterface;
29
use Symfony\Component\Security\Core\User\UserInterface;
30
31
/**
32
 * Loading and ad-hoc creation of a user by an OAuth sign-in provider account.
33
 *
34
 * @author Fabian Kiss <[email protected]>
35
 * @author Joseph Bielawski <[email protected]>
36
 * @author Łukasz Chruściel <[email protected]>
37
 */
38
class UserProvider extends BaseUserProvider implements AccountConnectorInterface, OAuthAwareUserProviderInterface
39
{
40
    /**
41
     * @var FactoryInterface
42
     */
43
    private $oauthFactory;
44
45
    /**
46
     * @var RepositoryInterface
47
     */
48
    private $oauthRepository;
49
50
    /**
51
     * @var FactoryInterface
52
     */
53
    private $customerFactory;
54
55
    /**
56
     * @var FactoryInterface
57
     */
58
    private $userFactory;
59
60
    /**
61
     * @var ObjectManager
62
     */
63
    private $userManager;
64
65
    /**
66
     * @var CustomerRepositoryInterface
67
     */
68
    private $customerRepository;
69
70
    /**
71
     * @param string $supportedUserClass
72
     * @param FactoryInterface $customerFactory
73
     * @param FactoryInterface $userFactory
74
     * @param UserRepositoryInterface $userRepository
75
     * @param FactoryInterface $oauthFactory
76
     * @param RepositoryInterface $oauthRepository
77
     * @param ObjectManager $userManager
78
     * @param CanonicalizerInterface $canonicalizer
79
     * @param CustomerRepositoryInterface $customerRepository
80
     */
81
    public function __construct(
82
        string $supportedUserClass,
83
        FactoryInterface $customerFactory,
84
        FactoryInterface $userFactory,
85
        UserRepositoryInterface $userRepository,
86
        FactoryInterface $oauthFactory,
87
        RepositoryInterface $oauthRepository,
88
        ObjectManager $userManager,
89
        CanonicalizerInterface $canonicalizer,
90
        CustomerRepositoryInterface $customerRepository
91
    ) {
92
        parent::__construct($supportedUserClass, $userRepository, $canonicalizer);
93
94
        $this->customerFactory = $customerFactory;
95
        $this->oauthFactory = $oauthFactory;
96
        $this->oauthRepository = $oauthRepository;
97
        $this->userFactory = $userFactory;
98
        $this->userManager = $userManager;
99
        $this->customerRepository = $customerRepository;
100
    }
101
102
    /**
103
     * {@inheritdoc}
104
     */
105
    public function loadUserByOAuthUserResponse(UserResponseInterface $response): UserInterface
106
    {
107
        $oauth = $this->oauthRepository->findOneBy([
108
            'provider' => $response->getResourceOwner()->getName(),
109
            'identifier' => $response->getUsername(),
110
        ]);
111
112
        if ($oauth instanceof UserOAuthInterface) {
113
            return $oauth->getUser();
114
        }
115
116
        if (null !== $response->getEmail()) {
117
            $user = $this->userRepository->findOneByEmail($response->getEmail());
118
            if (null !== $user) {
119
                return $this->updateUserByOAuthUserResponse($user, $response);
120
            }
121
        }
122
123
        return $this->createUserByOAuthUserResponse($response);
124
    }
125
126
    /**
127
     * {@inheritdoc}
128
     */
129
    public function connect(UserInterface $user, UserResponseInterface $response): void
130
    {
131
        $this->updateUserByOAuthUserResponse($user, $response);
132
    }
133
134
    /**
135
     * Ad-hoc creation of user.
136
     *
137
     * @param UserResponseInterface $response
138
     *
139
     * @return SyliusUserInterface
140
     */
141
    private function createUserByOAuthUserResponse(UserResponseInterface $response): SyliusUserInterface
142
    {
143
        /** @var SyliusUserInterface $user */
144
        $user = $this->userFactory->createNew();
145
146
        $canonicalEmail = $this->canonicalizer->canonicalize($response->getEmail());
147
148
        /** @var CustomerInterface $customer */
149
        $customer = $this->customerRepository->findOneBy(['emailCanonical' => $canonicalEmail]);
150
151
        if (null === $customer) {
152
            $customer = $this->customerFactory->createNew();
153
        }
154
155
        $user->setCustomer($customer);
156
157
        // set default values taken from OAuth sign-in provider account
158
        if (null !== $email = $response->getEmail()) {
159
            $customer->setEmail($email);
160
        }
161
162
        if (null !== $name = $response->getFirstName()) {
163
            $customer->setFirstName($name);
164
        } elseif (null !== $realName = $response->getRealName()) {
165
            $customer->setFirstName($realName);
166
        }
167
168
        if (null !== $lastName = $response->getLastName()) {
169
            $customer->setLastName($lastName);
170
        }
171
172
        if (!$user->getUsername()) {
173
            $user->setUsername($response->getEmail() ?: $response->getNickname());
174
        }
175
176
        // set random password to prevent issue with not nullable field & potential security hole
177
        $user->setPlainPassword(substr(sha1($response->getAccessToken()), 0, 10));
178
179
        $user->setEnabled(true);
180
181
        return $this->updateUserByOAuthUserResponse($user, $response);
182
    }
183
184
    /**
185
     * Attach OAuth sign-in provider account to existing user.
186
     *
187
     * @param UserInterface $user
188
     * @param UserResponseInterface $response
189
     *
190
     * @return UserInterface
191
     */
192
    private function updateUserByOAuthUserResponse(UserInterface $user, UserResponseInterface $response): UserInterface
193
    {
194
        $oauth = $this->oauthFactory->createNew();
195
        $oauth->setIdentifier($response->getUsername());
196
        $oauth->setProvider($response->getResourceOwner()->getName());
197
        $oauth->setAccessToken($response->getAccessToken());
198
        $oauth->setRefreshToken($response->getRefreshToken());
199
200
        /* @var $user SyliusUserInterface */
201
        $user->addOAuthAccount($oauth);
202
203
        $this->userManager->persist($user);
204
        $this->userManager->flush();
205
206
        return $user;
207
    }
208
}
209