Failed Conditions
Push — issue#702 ( 91bd46...0b5bf0 )
by Guilherme
19:37 queued 12:15
created

FOSUBUserProvider::setFacebookData()   C

Complexity

Conditions 13
Paths 65

Size

Total Lines 25
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 182

Importance

Changes 0
Metric Value
cc 13
eloc 16
nc 65
nop 2
dl 0
loc 25
ccs 0
cts 24
cp 0
crap 182
rs 5.1234
c 0
b 0
f 0

How to fix   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->/** @scrutinizer ignore-call */ getId() != $user->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->getResponse());
115
        }
116
117
        $this->userManager->updateUser($user);
118
    }
119
120
    /**
121
     * {@inheritDoc}
122
     * @throws MissingEmailException
123
     * @throws DuplicateEmailException
124
     */
125
    public function loadUserByOAuthUserResponse(UserResponseInterface $response)
126
    {
127
        $userInfo = $this->getUserInfo($response);
128
        $service = $response->getResourceOwner()->getName();
129
130
        $user = $this->userManager->findUserBy(array("{$service}Id" => $userInfo['id']));
131
132
        if ($user instanceof PersonInterface) {
133
            $user = parent::loadUserByOAuthUserResponse($response);
134
135
            $serviceName = $response->getResourceOwner()->getName();
136
            $setter = 'set'.ucfirst($serviceName).'AccessToken';
137
138
            $user->$setter($response->getAccessToken());
139
140
            return $user;
141
        }
142
143
        $userInfo = $this->checkEmail($service, $userInfo);
144
145
        /** @var PersonInterface $user */
146
        $user = $this->userManager->createUser();
147
        $this->setUserInfo($user, $userInfo, $service);
148
149
        if ($userInfo['first_name']) {
150
            $user->setFirstName($userInfo['first_name']);
151
        }
152
        if ($userInfo['family_name']) {
153
            $user->setSurname($userInfo['family_name']);
154
        }
155
156
        if ($service === 'facebook') {
157
            $this->setFacebookData($user, $response->getResponse());
158
        }
159
160
        $username = Uuid::uuid4()->toString();
161
        if (!UsernameValidator::isUsernameValid($username)) {
162
            $username = UsernameValidator::getValidUsername();
163
        }
164
165
        $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

165
        /** @scrutinizer ignore-call */ 
166
        $availableUsername = $this->userManager->getNextAvailableUsername(
Loading history...
166
            $username,
167
            10,
168
            Uuid::uuid4()->toString()
169
        );
170
171
        $user->setUsername($availableUsername);
172
        $user->setEmail($userInfo['email']);
173
        $user->setPassword('');
174
        $user->setEnabled(true);
175
        $this->userManager->updateCanonicalFields($user);
176
177
        /** @var ConstraintViolationList $errors */
178
        $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

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