Passed
Push — dependabot/npm_and_yarn/microm... ( e84ba6...f2f212 )
by
unknown
10:03
created

GenericAuthenticator::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 18
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
c 2
b 0
f 0
nc 1
nop 9
dl 0
loc 18
rs 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\Security\Authenticator\OAuth2;
8
9
use Chamilo\CoreBundle\Entity\AccessUrl;
10
use Chamilo\CoreBundle\Entity\AccessUrlRelUser;
11
use Chamilo\CoreBundle\Entity\User;
12
use Chamilo\CoreBundle\Repository\ExtraFieldRepository;
13
use Chamilo\CoreBundle\Repository\ExtraFieldValuesRepository;
14
use Chamilo\CoreBundle\Repository\Node\AccessUrlRepository;
15
use Chamilo\CoreBundle\Repository\Node\UserRepository;
16
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper;
17
use Chamilo\CoreBundle\ServiceHelper\AuthenticationConfigHelper;
18
use Doctrine\ORM\EntityManagerInterface;
19
use ExtraField;
20
use KnpU\OAuth2ClientBundle\Client\ClientRegistry;
21
use League\OAuth2\Client\Provider\GenericResourceOwner;
22
use League\OAuth2\Client\Token\AccessToken;
23
use League\OAuth2\Client\Tool\ArrayAccessorTrait;
24
use Symfony\Component\HttpFoundation\Request;
25
use Symfony\Component\Routing\RouterInterface;
26
use Symfony\Component\Security\Core\Exception\AuthenticationException;
27
use UnexpectedValueException;
28
29
class GenericAuthenticator extends AbstractAuthenticator
30
{
31
    use ArrayAccessorTrait;
32
33
    public const EXTRA_FIELD_OAUTH2_ID = 'oauth2_id';
34
35
    protected string $providerName = 'generic';
36
37
    public function __construct(
38
        ClientRegistry $clientRegistry,
39
        RouterInterface $router,
40
        UserRepository $userRepository,
41
        AuthenticationConfigHelper $authenticationConfigHelper,
42
        AccessUrlHelper $urlHelper,
43
        EntityManagerInterface $entityManager,
44
        protected readonly ExtraFieldRepository $extraFieldRepository,
45
        protected readonly ExtraFieldValuesRepository $extraFieldValuesRepository,
46
        protected readonly AccessUrlRepository $accessUrlRepository,
47
    ) {
48
        parent::__construct(
49
            $clientRegistry,
50
            $router,
51
            $userRepository,
52
            $authenticationConfigHelper,
53
            $urlHelper,
54
            $entityManager,
55
        );
56
    }
57
58
    public function supports(Request $request): ?bool
59
    {
60
        return 'chamilo.oauth2_generic_check' === $request->attributes->get('_route');
61
    }
62
63
    protected function userLoader(AccessToken $accessToken): User
64
    {
65
        $providerParams = $this->authenticationConfigHelper->getParams('generic');
66
67
        /** @var GenericResourceOwner $resourceOwner */
68
        $resourceOwner = $this->client->fetchUserFromToken($accessToken);
69
        $resourceOwnerData = $resourceOwner->toArray();
70
        $resourceOwnerId = $resourceOwner->getId();
71
72
        if (empty($resourceOwnerId)) {
73
            throw new UnexpectedValueException('Value for the resource owner identifier not found at the configured key');
74
        }
75
76
        $fieldType = (int) ExtraField::getExtraFieldTypeFromString('user');
77
        $extraField = $this->extraFieldRepository->findByVariable($fieldType, self::EXTRA_FIELD_OAUTH2_ID);
78
79
        $existingUserExtraFieldValue = $this->extraFieldValuesRepository->findByVariableAndValue(
80
            $extraField,
81
            $resourceOwnerId
82
        );
83
84
        if (null === $existingUserExtraFieldValue) {
85
            $username = $this->getValueByKey(
86
                $resourceOwnerData,
87
                $providerParams['resource_owner_username_field'],
88
                "oauth2user_$resourceOwnerId"
89
            );
90
91
            /** @var User $user */
92
            $user = $this->userRepository->findOneBy(['username' => $username]);
93
94
            if (!$user || 'platform' !== $user->getAuthSource()) {
0 ignored issues
show
introduced by
$user is of type Chamilo\CoreBundle\Entity\User, thus it always evaluated to true.
Loading history...
95
                if (!$providerParams['allow_create_new_users']) {
96
                    throw new AuthenticationException('This user doesn\'t have an account yet and auto-provisioning is not enabled. Please contact this portal administration team to request access.');
97
                }
98
99
                // set default values, real values are set in self::updateUserInfo method
100
                $user = (new User())
101
                    ->setFirstname('OAuth2 User default firstname')
102
                    ->setLastname('OAuth2 User default firstname')
103
                    ->setEmail('oauth2user_'.$resourceOwnerId.'@'.(gethostname() ?: 'localhost'))
104
                    ->setUsername($username)
105
                    ->setPlainPassword($username)
106
                    ->setStatus(STUDENT)
107
                    ->setCreatorId($this->userRepository->getRootUser()->getId())
108
                ;
109
            }
110
111
            $this->saveUserInfo($user, $resourceOwnerData, $providerParams);
112
113
            $this->extraFieldValuesRepository->updateItemData(
114
                $extraField,
115
                $user,
116
                $resourceOwnerId
117
            );
118
119
            $this->updateUrls($user, $resourceOwnerData, $providerParams);
120
        } else {
121
            /** @var User $user */
122
            $user = $this->userRepository->find(
123
                $existingUserExtraFieldValue->getItemId()
124
            );
125
126
            if ($providerParams['allow_update_user_info']) {
127
                $this->saveUserInfo($user, $resourceOwnerData, $providerParams);
128
129
                $this->updateUrls($user, $resourceOwnerData, $providerParams);
130
            }
131
        }
132
133
        return $user;
134
    }
135
136
    /**
137
     * Set user information from the resource owner's data or the user itself.
138
     */
139
    public function saveUserInfo(User $user, array $resourceOwnerData, array $providerParams): void
140
    {
141
        $status = $this->getUserStatus($resourceOwnerData, $user->getStatus(), $providerParams);
142
143
        $user
144
            ->setFirstname(
145
                $this->getValueByKey(
146
                    $resourceOwnerData,
147
                    $providerParams['resource_owner_firstname_field'],
148
                    $user->getFirstname()
149
                )
150
            )
151
            ->setLastname(
152
                $this->getValueByKey(
153
                    $resourceOwnerData,
154
                    $providerParams['resource_owner_lastname_field'],
155
                    $user->getLastname()
156
                )
157
            )
158
            ->setUsername(
159
                $this->getValueByKey(
160
                    $resourceOwnerData,
161
                    $providerParams['resource_owner_username_field'],
162
                    $user->getUsername()
163
                )
164
            )
165
            ->setEmail(
166
                $this->getValueByKey(
167
                    $resourceOwnerData,
168
                    $providerParams['resource_owner_email_field'],
169
                    $user->getEmail()
170
                )
171
            )
172
            ->setAuthSource('oauth2')
173
            ->setStatus($status)
174
            ->setRoleFromStatus($status)
175
        ;
176
177
        $this->userRepository->updateUser($user);
178
179
        $url = $this->urlHelper->getCurrent();
180
        $url->addUser($user);
181
182
        $this->entityManager->flush();
183
    }
184
185
    private function getUserStatus(array $resourceOwnerData, int $defaultStatus, array $providerParams): int
186
    {
187
        $status = $this->getValueByKey(
188
            $resourceOwnerData,
189
            $providerParams['resource_owner_status_field'],
190
            $defaultStatus
191
        );
192
193
        $responseStatus = [];
194
195
        if ($teacherStatus = $providerParams['resource_owner_teacher_status_field']) {
196
            $responseStatus[COURSEMANAGER] = $teacherStatus;
197
        }
198
199
        if ($sessAdminStatus = $providerParams['resource_owner_sessadmin_status_field']) {
200
            $responseStatus[SESSIONADMIN] = $sessAdminStatus;
201
        }
202
203
        if ($drhStatus = $providerParams['resource_owner_hr_status_field']) {
204
            $responseStatus[DRH] = $drhStatus;
205
        }
206
207
        if ($studentStatus = $providerParams['resource_owner_status_status_field']) {
208
            $responseStatus[STUDENT] = $studentStatus;
209
        }
210
211
        if ($anonStatus = $providerParams['resource_owner_anon_status_field']) {
212
            $responseStatus[ANONYMOUS] = $anonStatus;
213
        }
214
215
        $map = array_flip($responseStatus);
216
217
        return $map[$status] ?? $status;
218
    }
219
220
    private function updateUrls(User $user, array $resourceOwnerData, array $providerParams): void
221
    {
222
        if (!($urlsField = $providerParams['resource_owner_urls_field'])) {
223
            return;
224
        }
225
226
        $availableUrls = [];
227
228
        $urls = $this->accessUrlRepository->findAll();
229
230
        /** @var AccessUrl $existingUrl */
231
        foreach ($urls as $existingUrl) {
232
            $availableUrls[(string) $existingUrl->getId()] = $existingUrl->getId();
233
            $availableUrls[$existingUrl->getUrl()] = $existingUrl->getId();
234
        }
235
236
        $allowedUrlIds = [];
237
238
        foreach ($this->getValueByKey($resourceOwnerData, $urlsField) as $value) {
239
            if (\array_key_exists($value, $availableUrls)) {
240
                $allowedUrlIds[] = $availableUrls[$value];
241
            } else {
242
                $newValue = ('/' === $value[-1]) ? substr($value, 0, -1) : $value.'/';
243
244
                if (\array_key_exists($newValue, $availableUrls)) {
245
                    $allowedUrlIds[] = $availableUrls[$newValue];
246
                }
247
            }
248
        }
249
250
        $grantedUrlIds = [];
251
252
        foreach ($this->accessUrlRepository->findByUser($user) as $grantedUrl) {
253
            $grantedUrlIds[] = $grantedUrl->getId();
254
        }
255
256
        $urlRelUserRepo = $this->entityManager->getRepository(AccessUrlRelUser::class);
257
258
        foreach (array_diff($grantedUrlIds, $allowedUrlIds) as $extraUrlId) {
259
            $urlRelUser = $urlRelUserRepo->findOneBy(['user' => $user, 'url' => $extraUrlId]);
260
261
            if ($urlRelUser) {
262
                $this->entityManager->remove($urlRelUser);
263
            }
264
        }
265
266
        $this->entityManager->flush();
267
268
        foreach (array_diff($allowedUrlIds, $grantedUrlIds) as $missingUrlId) {
269
            /** @var AccessUrl $missingUrl */
270
            $missingUrl = $this->accessUrlRepository->find($missingUrlId);
271
            $missingUrl->addUser($user);
272
        }
273
274
        $this->entityManager->flush();
275
    }
276
}
277