Failed Conditions
Push — issue#808 ( 868db7...3c2936 )
by Guilherme
07:23
created

FOSUBUserProvider::loadUserByOAuthUserResponse()   C

Complexity

Conditions 11
Paths 49

Size

Total Lines 87
Code Lines 52

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
cc 11
eloc 52
nc 49
nop 1
dl 0
loc 87
ccs 0
cts 67
cp 0
crap 132
rs 6.9006
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * This file is part of the login-cidadao project or it's bundles.
4
 *
5
 * (c) Guilherme Donato <guilhermednt on github>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
11
namespace LoginCidadao\CoreBundle\Security\User\Provider;
12
13
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
14
use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass;
15
use LoginCidadao\CoreBundle\Model\PersonInterface;
16
use LoginCidadao\CoreBundle\Security\Exception\DuplicateEmailException;
17
use LoginCidadao\CoreBundle\Security\User\Manager\UserManager;
18
use Ramsey\Uuid\Uuid;
19
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
20
use Symfony\Component\HttpFoundation\RedirectResponse;
21
use Symfony\Component\HttpFoundation\RequestStack;
22
use Symfony\Component\Security\Core\User\UserInterface;
23
use FOS\UserBundle\Model\UserManagerInterface;
24
use LoginCidadao\CoreBundle\Security\Exception\AlreadyLinkedAccount;
25
use Symfony\Component\HttpFoundation\Session\SessionInterface;
26
use LoginCidadao\CoreBundle\Security\Exception\MissingEmailException;
27
use FOS\UserBundle\Event\FormEvent;
28
use FOS\UserBundle\FOSUserEvents;
29
use FOS\UserBundle\Form\Factory\FactoryInterface;
30
use FOS\UserBundle\Event\FilterUserResponseEvent;
31
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
32
use LoginCidadao\ValidationBundle\Validator\Constraints\UsernameValidator;
33
use Symfony\Component\Validator\ConstraintViolationList;
34
use Symfony\Component\Validator\Validator\ValidatorInterface;
35
36
class FOSUBUserProvider extends BaseClass
37
{
38
39
    /** @var UserManagerInterface|UserManager */
40
    protected $userManager;
41
42
    /** @var SessionInterface */
43
    private $session;
44
45
    /** @var EventDispatcherInterface */
46
    private $dispatcher;
47
48
    /** @var FactoryInterface */
49
    private $formFactory;
50
51
    /** @var ValidatorInterface */
52
    private $validator;
53
54
    /** @var RequestStack */
55
    private $requestStack;
56
57
    /**
58
     * Constructor.
59
     *
60
     * @param UserManagerInterface $userManager FOSUB user provider.
61
     * @param SessionInterface $session
62
     * @param EventDispatcherInterface $dispatcher
63
     * @param FactoryInterface $formFactory
64
     * @param ValidatorInterface $validator
65
     * @param RequestStack $requestStack
66
     * @param array $properties Property mapping.
67
     * @internal param ContainerInterface $container
68
     */
69
    public function __construct(
70
        UserManagerInterface $userManager,
71
        SessionInterface $session,
72
        EventDispatcherInterface $dispatcher,
73
        FactoryInterface $formFactory,
74
        ValidatorInterface $validator,
75
        RequestStack $requestStack,
76
        array $properties
77
    ) {
78
        parent::__construct($userManager, $properties);
79
        $this->userManager = $userManager;
80
        $this->session = $session;
81
        $this->dispatcher = $dispatcher;
82
        $this->formFactory = $formFactory;
83
        $this->validator = $validator;
84
        $this->requestStack = $requestStack;
85
    }
86
87
    /**
88
     * {@inheritDoc}
89
     * @throws AlreadyLinkedAccount
90
     */
91
    public function connect(UserInterface $user, UserResponseInterface $response)
92
    {
93
        $username = $response->getUsername();
94
95
        $service = $response->getResourceOwner()->getName();
96
97
        $setter = 'set'.ucfirst($service);
98
        $setter_id = $setter.'Id';
99
        $setter_token = $setter.'AccessToken';
100
        $setter_username = $setter.'Username';
101
102
        /** @var PersonInterface $existingUser|null */
103
        $existingUser = $this->userManager->findUserBy(array("{$service}Id" => $username));
104
        if ($existingUser instanceof UserInterface && $existingUser->getId() != $user->getId()) {
0 ignored issues
show
Bug introduced by
The method getId() does not exist on Symfony\Component\Security\Core\User\UserInterface. It seems like you code against a sub-type of Symfony\Component\Security\Core\User\UserInterface such as HWI\Bundle\OAuthBundle\Tests\Fixtures\User or FOS\OAuthServerBundle\Te...\TestBundle\Entity\User or FOS\UserBundle\Model\UserInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

104
        if ($existingUser instanceof UserInterface && $existingUser->getId() != $user->/** @scrutinizer ignore-call */ getId()) {
Loading history...
105
            throw new AlreadyLinkedAccount();
106
        }
107
108
        $screenName = $response->getNickname();
109
        $user->$setter_id($username);
110
        $user->$setter_token($response->getAccessToken());
111
        $user->$setter_username($screenName);
112
113
        if ($service === 'facebook') {
114
            $this->setFacebookData($user, $response->getData());
115
        }
116
117
        $this->userManager->updateUser($user);
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     * @throws MissingEmailException
123
     * @throws DuplicateEmailException
124
     * @throws \Exception
125
     */
126
    public function loadUserByOAuthUserResponse(UserResponseInterface $response)
127
    {
128
        $userInfo = $this->getUserInfo($response);
129
        $service = $response->getResourceOwner()->getName();
130
131
        $user = $this->userManager->findUserBy(array("{$service}Id" => $userInfo['id']));
132
133
        if ($user instanceof PersonInterface) {
134
            $user = parent::loadUserByOAuthUserResponse($response);
135
136
            $serviceName = $response->getResourceOwner()->getName();
137
            $setter = 'set'.ucfirst($serviceName).'AccessToken';
138
139
            $user->$setter($response->getAccessToken());
140
141
            return $user;
142
        }
143
144
        $userInfo = $this->checkEmail($service, $userInfo);
145
146
        /** @var PersonInterface $user */
147
        $user = $this->userManager->createUser();
148
        $this->setUserInfo($user, $userInfo, $service);
149
150
        if ($userInfo['first_name']) {
151
            $user->setFirstName($userInfo['first_name']);
152
        }
153
        if ($userInfo['family_name']) {
154
            $user->setSurname($userInfo['family_name']);
155
        }
156
157
        if ($service === 'facebook') {
158
            $this->setFacebookData($user, $response->getData());
159
        }
160
161
        $username = Uuid::uuid4()->toString();
162
        if (!UsernameValidator::isUsernameValid($username)) {
163
            $username = UsernameValidator::getValidUsername();
164
        }
165
166
        $availableUsername = $this->userManager->getNextAvailableUsername(
0 ignored issues
show
Bug introduced by
The method getNextAvailableUsername() does not exist on FOS\UserBundle\Model\UserManagerInterface. It seems like you code against a sub-type of FOS\UserBundle\Model\UserManagerInterface such as LoginCidadao\CoreBundle\...ser\Manager\UserManager. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

166
        /** @scrutinizer ignore-call */ 
167
        $availableUsername = $this->userManager->getNextAvailableUsername(
Loading history...
167
            $username,
168
            10,
169
            Uuid::uuid4()->toString()
170
        );
171
172
        $user->setUsername($availableUsername);
173
        $user->setEmail($userInfo['email']);
174
        $user->setPassword('');
175
        $user->setEnabled(true);
176
        $this->userManager->updateCanonicalFields($user);
177
178
        /** @var ConstraintViolationList $errors */
179
        $errors = $this->validator->validate($user, ['LoginCidadaoProfile']);
0 ignored issues
show
Bug introduced by
array('LoginCidadaoProfile') of type array<integer,string> is incompatible with the type Symfony\Component\Valida...nt\Validator\Constraint expected by parameter $constraints of Symfony\Component\Valida...orInterface::validate(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

179
        $errors = $this->validator->validate($user, /** @scrutinizer ignore-type */ ['LoginCidadaoProfile']);
Loading history...
180
        if (count($errors) > 0) {
181
            foreach ($errors as $error) {
182
                if ($error->getPropertyPath() === 'email'
183
                    && method_exists($error, 'getConstraint')
184
                    && $error->getConstraint() instanceof UniqueEntity
185
                ) {
186
                    throw new DuplicateEmailException($service);
187
                }
188
            }
189
        }
190
191
        $form = $this->formFactory->createForm();
192
        $form->setData($user);
193
194
        $request = $this->requestStack->getCurrentRequest();
195
        $eventResponse = new RedirectResponse('/');
196
        $event = new FormEvent($form, $request);
197
        $this->dispatcher->dispatch(
198
            FOSUserEvents::REGISTRATION_SUCCESS,
199
            $event
200
        );
201
202
        $this->userManager->updateUser($user);
203
204
        $this->dispatcher->dispatch(
205
            FOSUserEvents::REGISTRATION_COMPLETED,
206
            new FilterUserResponseEvent(
207
                $user, $request,
208
                $eventResponse
209
            )
210
        );
211
212
        return $user;
213
    }
214
215
    private function getUserInfo(UserResponseInterface $response)
216
    {
217
        $fullName = explode(' ', $response->getRealName(), 2);
218
219
        $userInfo = [
220
            'id' => $response->getUsername(),
221
            'email' => $response->getEmail(),
222
            'username' => $response->getNickname(),
223
            'first_name' => $fullName[0],
224
            'family_name' => $fullName[1],
225
            'access_token' => $response->getAccessToken(),
226
        ];
227
228
        return $userInfo;
229
    }
230
231
    /**
232
     * @param PersonInterface $person
233
     * @param array $userInfo
234
     * @param string $service
235
     * @return PersonInterface
236
     */
237
    private function setUserInfo(PersonInterface $person, array $userInfo, $service)
238
    {
239
        $setter = 'set'.ucfirst($service);
240
        $setter_id = $setter.'Id';
241
        $setter_token = $setter.'AccessToken';
242
        $setter_username = $setter.'Username';
243
244
        $person->$setter_id($userInfo['id']);
245
        $person->$setter_token($userInfo['access_token']);
246
        $person->$setter_username($userInfo['username']);
247
248
        return $person;
249
    }
250
251
    /**
252
     * @param $service
253
     * @param $userInfo
254
     * @return mixed
255
     * @throws MissingEmailException
256
     */
257
    private function checkEmail($service, $userInfo)
258
    {
259
        if (!$userInfo['email'] || $this->session->has("$service.email")) {
260
            if (!$this->session->get("$service.email")) {
261
                $this->session->set("$service.userinfo", $userInfo);
262
                throw new MissingEmailException($service);
263
            }
264
            $userInfo['email'] = $this->session->get("$service.email");
265
            $this->session->remove("$service.email");
266
            $this->session->remove("$service.userinfo");
267
        }
268
269
        return $userInfo;
270
    }
271
272
    private function setFacebookData($person, $fbdata)
273
    {
274
        if (!($person instanceof PersonInterface)) {
275
            return;
276
        }
277
278
        if (isset($fbdata['id'])) {
279
            $person->setFacebookId($fbdata['id']);
280
            $person->addRole('ROLE_FACEBOOK');
281
        }
282
        if (isset($fbdata['first_name']) && is_null($person->getFirstName())) {
283
            $person->setFirstName($fbdata['first_name']);
284
        }
285
        if (isset($fbdata['last_name']) && is_null($person->getSurname())) {
286
            $person->setSurname($fbdata['last_name']);
287
        }
288
        if (isset($fbdata['email']) && is_null($person->getEmail())) {
289
            $person->setEmail($fbdata['email']);
290
        }
291
        if (isset($fbdata['birthday']) && is_null($person->getBirthdate())) {
292
            $date = \DateTime::createFromFormat('m/d/Y', $fbdata['birthday']);
293
            $person->setBirthdate($date);
294
        }
295
        if (isset($fbdata['username']) && is_null($person->getFacebookUsername())) {
296
            $person->setFacebookUsername($fbdata['username']);
297
        }
298
    }
299
}
300