Passed
Pull Request — master (#7302)
by Angel Fernando Quiroz
10:36
created

UserItemController::applyBulkReplace()   D

Complexity

Conditions 10
Paths 512

Size

Total Lines 41
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

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
/* For licensing terms, see /license.txt */
4
5
declare(strict_types=1);
6
7
namespace Chamilo\CoreBundle\Controller\Scim;
8
9
use Chamilo\CoreBundle\Entity\User;
10
use Chamilo\CoreBundle\Exception\ScimException;
11
use Chamilo\CoreBundle\Repository\Node\UserRepository;
12
use Chamilo\CoreBundle\Serializer\Denormalizer\Scim\UserDenormalizer;
13
use Chamilo\CoreBundle\Serializer\Normalizer\Scim\UserNormalizer;
14
use Doctrine\ORM\EntityManagerInterface;
15
use Symfony\Component\HttpFoundation\JsonResponse;
16
use Symfony\Component\HttpFoundation\Request;
17
use Symfony\Component\HttpFoundation\Response;
18
use Symfony\Component\HttpKernel\Exception\MethodNotAllowedHttpException;
19
use Symfony\Component\Routing\Attribute\Route;
20
use Symfony\Component\Serializer\SerializerInterface;
21
use UserManager;
22
23
#[Route(
24
    '/scim/v2/Users/{uuid}',
25
    name: 'scim_user',
26
    methods: ['GET', 'PUT', 'PATCH', 'DELETE']
27
)]
28
class UserItemController extends AbstractScimController
29
{
30
    public function __invoke(
31
        string $uuid,
32
        Request $request,
33
        EntityManagerInterface $entityManager,
34
        UserRepository $userRepo,
35
        SerializerInterface $serializer,
36
    ): JsonResponse {
37
        $user = $userRepo->findOneBy(['uuid' => $uuid]);
38
39
        if (!$user || $user->isSoftDeleted()) {
40
            throw $this->createNotFoundException($this->translator->trans('User not found.'));
41
        }
42
43
        return match ($request->getMethod()) {
44
            'GET' => $this->findUser(
45
                $user,
46
                $serializer,
47
            ),
48
            'PUT' => $this->replaceUser(
49
                $user,
50
                $request,
51
                $serializer,
52
                $entityManager,
53
            ),
54
            'PATCH' => $this->patchUser(
55
                $user,
56
                $request,
57
                $entityManager,
58
                $serializer,
59
            ),
60
            'DELETE' => $this->deleteUser($user),
61
            default => throw new MethodNotAllowedHttpException(['GET', 'PUT', 'PATCH', 'DELETE']),
62
        };
63
    }
64
65
    private function findUser(
66
        User $user,
67
        SerializerInterface $serializer,
68
    ): JsonResponse {
69
        $normalized = $serializer->normalize($user, UserNormalizer::FORMAT);
70
71
        return new JsonResponse(
72
            $normalized,
73
            Response::HTTP_OK,
74
            ['Content-Type' => self::SCIM_CONTENT_TYPE]
75
        );
76
    }
77
78
    private function replaceUser(
79
        User $user,
80
        Request $request,
81
        SerializerInterface $serializer,
82
        EntityManagerInterface $entityManager,
83
    ): JsonResponse {
84
        $data = $this->getAndValidateJson($request);
85
86
        $serializer->denormalize($data, User::class, UserDenormalizer::FORMAT, ['object_to_populate' => $user]);
87
88
        $entityManager->flush();
89
90
        return $this->findUser($user, $serializer);
91
    }
92
93
    private function patchUser(
94
        User $user,
95
        Request $request,
96
        EntityManagerInterface $entityManager,
97
        SerializerInterface $serializer,
98
    ): JsonResponse {
99
        $data = $this->getAndValidateJson($request);
100
101
        if (!isset($data['schemas']) || !\in_array('urn:ietf:params:scim:api:messages:2.0:PatchOp', $data['schemas'])) {
102
            throw new ScimException($this->translator->trans('Invalid schemas for PATCH operation.'));
103
        }
104
105
        if (!isset($data['Operations']) || !\is_array($data['Operations'])) {
106
            throw new ScimException($this->translator->trans('Missing required "Operations" array.'));
107
        }
108
109
        foreach ($data['Operations'] as $operation) {
110
            $op = strtolower($operation['op'] ?? '');
111
            $path = $operation['path'] ?? null;
112
            $value = $operation['value'] ?? null;
113
114
            if (!\in_array($op, ['add', 'replace', 'remove'])) {
115
                throw new ScimException(\sprintf($this->translator->trans("The operation '%s' is not supported."), $op));
116
            }
117
118
            if ($path) {
119
                if ('remove' === $op) {
120
                    $value = '';
121
                }
122
123
                $this->applyPatchWithPath($user, $path, (string) $value);
124
125
                if ('externalId' === $path) {
126
                    $this->scimHelper->saveExternalId($value, $user);
127
                }
128
            } else {
129
                if (!\is_array($value)) {
130
                    throw new ScimException($this->translator->trans('Required value for operation without path'));
131
                }
132
133
                $this->applyBulkReplace($user, $value);
134
135
                if (isset($value['externalId'])) {
136
                    $this->scimHelper->saveExternalId($value['externalId'], $user);
137
                }
138
            }
139
        }
140
141
        $entityManager->flush();
142
143
        return $this->findUser($user, $serializer);
144
    }
145
146
    private function applyPatchWithPath(User $user, string $path, string $value): void
147
    {
148
        $lowerPath = strtolower($path);
149
150
        if ('userName' === $lowerPath) {
151
            $user->setUsername($value);
152
153
            return;
154
        }
155
156
        if ('active' === $lowerPath) {
157
            $user->setActive((int) $value);
158
159
            return;
160
        }
161
162
        if ('locale' === $lowerPath) {
163
            $user->setLocale($value);
164
165
            return;
166
        }
167
168
        if ('timezone' === $lowerPath) {
169
            $user->setTimezone($value);
170
171
            return;
172
        }
173
174
        // emails[type eq "work"].value
175
        if (preg_match('/^emails\[type eq "([^"]+)"]\.value$/i', $path, $matches)) {
176
            $type = $matches[1];
177
178
            if ('work' === strtolower($type)) {
179
                $user->setEmail($value);
180
            }
181
182
            return;
183
        }
184
185
        // phoneNumbers[type eq "work"].value
186
        if (preg_match('/^phoneNumbers\[type eq "([^"]+)"]\.value$/i', $path, $matches)) {
187
            $type = $matches[1];
188
189
            if ('work' === strtolower($type)) {
190
                $user->setPhone($value);
191
            }
192
193
            return;
194
        }
195
196
        // addresses[type eq "work"].formatted
197
        if (preg_match('/^addresses\[type eq "([^"]+)"]\.formatted$/i', $path, $matches)) {
198
            $type = $matches[1];
199
200
            if ('work' === strtolower($type)) {
201
                $user->setAddress($value);
202
            }
203
204
            return;
205
        }
206
207
        if (str_starts_with($lowerPath, 'name.')) {
208
            $subPath = substr($path, 5); // exclude "name."
209
210
            switch (strtolower($subPath)) {
211
                case 'givenname':
212
                    $user->setFirstname($value);
213
214
                    break;
215
216
                case 'familyname':
217
                    $user->setLastname($value);
218
219
                    break;
220
            }
221
        }
222
    }
223
224
    private function applyBulkReplace(User $user, array $value): void
225
    {
226
        /*if (isset($value['externalId'])) {
227
            $user->setExternalId($value['externalId']);
228
        }*/
229
230
        if (isset($value['userName'])) {
231
            $user->setUsername($value['userName']);
232
        }
233
234
        // name.givenName, name.familyName
235
        if (isset($value['name.givenName'])) {
236
            $user->setFirstname($value['name.givenName']);
237
        }
238
239
        if (isset($value['name.familyName'])) {
240
            $user->setLastname($value['name.familyName']);
241
        }
242
243
        if ($email = UserDenormalizer::getPrimaryValue($value, 'emails')) {
244
            $user->setEmail($email);
245
        }
246
247
        if ($phone = UserDenormalizer::getPrimaryValue($value, 'phoneNumbers')) {
248
            $user->setPhone($phone);
249
        }
250
251
        if ($address = UserDenormalizer::getPrimaryValue($value, 'addresses', 'formatted')) {
252
            $user->setAddress($address);
253
        }
254
255
        if (isset($value['active'])) {
256
            $user->setActive((int) $value['active']);
257
        }
258
259
        if (isset($value['locale'])) {
260
            $user->setLocale($value['locale']);
261
        }
262
263
        if (isset($value['timezone'])) {
264
            $user->setTimezone($value['timezone']);
265
        }
266
    }
267
268
    private function deleteUser(User $user): JsonResponse
269
    {
270
        UserManager::delete_user($user->getId());
271
272
        return new JsonResponse(null, Response::HTTP_NO_CONTENT);
273
    }
274
}
275