Passed
Push — issue#767 ( 39899e...c3d4bb )
by Guilherme
10:59
created

FOSUBUserProvider::loadUserByOAuthUserResponse()   C

Complexity

Conditions 11
Paths 49

Size

Total Lines 88
Code Lines 53

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 132

Importance

Changes 0
Metric Value
cc 11
eloc 53
nc 49
nop 1
dl 0
loc 88
ccs 0
cts 54
cp 0
crap 132
rs 5.2653
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
namespace LoginCidadao\CoreBundle\Security\User\Provider;
4
5
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
6
use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass;
7
use LoginCidadao\CoreBundle\Model\PersonInterface;
8
use LoginCidadao\CoreBundle\Security\Exception\DuplicateEmailException;
9
use Ramsey\Uuid\Uuid;
10
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
11
use Symfony\Component\HttpFoundation\RedirectResponse;
12
use Symfony\Component\Security\Core\User\UserInterface;
13
use FOS\UserBundle\Model\UserManagerInterface;
14
use LoginCidadao\CoreBundle\Security\Exception\AlreadyLinkedAccount;
15
use Symfony\Component\HttpFoundation\Session\SessionInterface;
16
use LoginCidadao\CoreBundle\Security\Exception\MissingEmailException;
17
use FOS\UserBundle\Event\FormEvent;
18
use FOS\UserBundle\FOSUserEvents;
19
use FOS\UserBundle\Form\Factory\FactoryInterface;
20
use FOS\UserBundle\Event\FilterUserResponseEvent;
21
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
22
use Symfony\Component\DependencyInjection\ContainerInterface;
23
use LoginCidadao\ValidationBundle\Validator\Constraints\UsernameValidator;
24
use Symfony\Component\Validator\ConstraintViolationList;
25
use Symfony\Component\Validator\Validator\ValidatorInterface;
26
27
class FOSUBUserProvider extends BaseClass
28
{
29
30
    /** @var UserManagerInterface */
31
    protected $userManager;
32
33
    /** @var SessionInterface */
34
    protected $session;
35
36
    /** @var EventDispatcherInterface */
37
    protected $dispatcher;
38
39
    /** @var ContainerInterface */
40
    protected $container;
41
42
    /** @var FactoryInterface */
43
    protected $formFactory;
44
45
    /**
46
     * Constructor.
47
     *
48
     * @param UserManagerInterface $userManager FOSUB user provider.
49
     * @param SessionInterface $session
50
     * @param EventDispatcherInterface $dispatcher
51
     * @param ContainerInterface $container
52
     * @param FactoryInterface $formFactory
53
     * @param array $properties Property mapping.
54
     */
55 14
    public function __construct(
56
        UserManagerInterface $userManager,
57
        SessionInterface $session,
58
        EventDispatcherInterface $dispatcher,
59
        ContainerInterface $container,
60
        FactoryInterface $formFactory,
61
        array $properties
62
    ) {
63 14
        $this->userManager = $userManager;
64 14
        $this->session = $session;
65 14
        $this->dispatcher = $dispatcher;
66 14
        $this->container = $container;
67 14
        $this->formFactory = $formFactory;
68 14
        $this->properties = $properties;
69 14
    }
70
71
    /**
72
     * {@inheritDoc}
73
     */
74
    public function connect(UserInterface $user, UserResponseInterface $response)
75
    {
76
        $username = $response->getUsername();
77
78
        $service = $response->getResourceOwner()->getName();
79
80
        $setter = 'set'.ucfirst($service);
81
        $setter_id = $setter.'Id';
82
        $setter_token = $setter.'AccessToken';
83
        $setter_username = $setter.'Username';
84
85
        $existingUser = $this->userManager->findUserBy(array("{$service}Id" => $username));
86
        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

86
        if ($existingUser instanceof UserInterface && $existingUser->getId() != $user->/** @scrutinizer ignore-call */ getId()) {
Loading history...
87
            throw new AlreadyLinkedAccount();
88
            $previousUser->$setter_id(null);
0 ignored issues
show
Unused Code introduced by
$previousUser->$setter_id(null) is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
89
            $previousUser->$setter_token(null);
90
            $this->userManager->updateUser($previousUser);
91
        }
92
93
        $screenName = $response->getNickname();
94
        $user->$setter_id($username);
95
        $user->$setter_token($response->getAccessToken());
96
        $user->$setter_username($screenName);
97
98
        if ($service === 'facebook') {
99
            $this->setFacebookData($user, $response->getResponse());
100
        }
101
102
        $this->userManager->updateUser($user);
103
    }
104
105
    /**
106
     * {@inheritDoc}
107
     */
108
    public function loadUserByOAuthUserResponse(UserResponseInterface $response)
109
    {
110
        $userInfo = $this->getUserInfo($response);
111
        $service = $response->getResourceOwner()->getName();
112
113
        $user = $this->userManager->findUserBy(array("{$service}Id" => $userInfo['id']));
114
115
        if ($user instanceof PersonInterface) {
116
            $user = parent::loadUserByOAuthUserResponse($response);
117
118
            $serviceName = $response->getResourceOwner()->getName();
119
            $setter = 'set'.ucfirst($serviceName).'AccessToken';
120
121
            $user->$setter($response->getAccessToken());
122
123
            return $user;
124
        }
125
126
        $userInfo = $this->checkEmail($service, $userInfo);
127
128
        $user = $this->userManager->createUser();
129
        $this->setUserInfo($user, $userInfo, $service);
130
131
        if ($userInfo['first_name']) {
132
            $user->setFirstName($userInfo['first_name']);
0 ignored issues
show
Bug introduced by
The method setFirstName() does not exist on FOS\UserBundle\Model\UserInterface. It seems like you code against a sub-type of FOS\UserBundle\Model\UserInterface such as LoginCidadao\CoreBundle\Model\PersonInterface or LoginCidadao\CoreBundle\Entity\Person. ( Ignorable by Annotation )

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

132
            $user->/** @scrutinizer ignore-call */ 
133
                   setFirstName($userInfo['first_name']);
Loading history...
133
        }
134
        if ($userInfo['family_name']) {
135
            $user->setSurname($userInfo['family_name']);
0 ignored issues
show
Bug introduced by
The method setSurname() does not exist on FOS\UserBundle\Model\UserInterface. It seems like you code against a sub-type of FOS\UserBundle\Model\UserInterface such as LoginCidadao\CoreBundle\Model\PersonInterface or LoginCidadao\CoreBundle\Entity\Person. ( Ignorable by Annotation )

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

135
            $user->/** @scrutinizer ignore-call */ 
136
                   setSurname($userInfo['family_name']);
Loading history...
136
        }
137
138
        if ($service === 'facebook') {
139
            $this->setFacebookData($user, $response->getResponse());
140
        }
141
142
        $username = Uuid::uuid4()->toString();
143
        if (!UsernameValidator::isUsernameValid($username)) {
144
            $username = UsernameValidator::getValidUsername();
145
        }
146
147
        $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

147
        /** @scrutinizer ignore-call */ 
148
        $availableUsername = $this->userManager->getNextAvailableUsername(
Loading history...
148
            $username,
149
            10,
150
            Uuid::uuid4()->toString()
151
        );
152
153
        $user->setUsername($availableUsername);
154
        $user->setEmail($userInfo['email']);
155
        $user->setPassword('');
156
        $user->setEnabled(true);
157
        $this->userManager->updateCanonicalFields($user);
158
159
        /** @var ValidatorInterface $validator */
160
        $validator = $this->container->get('validator');
161
        /** @var ConstraintViolationList $errors */
162
        $errors = $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

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