1
- <?php declare (strict_types=1 );
1
+ <?php
2
+
3
+ declare (strict_types=1 );
2
4
3
5
namespace App \Controller \API ;
4
6
23
25
use Symfony \Component \HttpFoundation \Response ;
24
26
use Symfony \Component \HttpKernel \Attribute \MapRequestPayload ;
25
27
use Symfony \Component \HttpKernel \Exception \BadRequestHttpException ;
28
+ use Symfony \Component \HttpKernel \Exception \NotFoundHttpException ;
26
29
use Symfony \Component \Security \Http \Attribute \IsGranted ;
30
+ use Symfony \Component \Validator \Exception \ValidationFailedException ;
31
+ use Symfony \Component \Validator \Validator \ValidatorInterface ;
27
32
28
33
/**
29
34
* @extends AbstractRestController<User, User>
@@ -41,7 +46,8 @@ public function __construct(
41
46
DOMJudgeService $ dj ,
42
47
ConfigurationService $ config ,
43
48
EventLogService $ eventLogService ,
44
- protected readonly ImportExportService $ importExportService
49
+ protected readonly ImportExportService $ importExportService ,
50
+ protected readonly ValidatorInterface $ validator ,
45
51
) {
46
52
parent ::__construct ($ entityManager , $ dj , $ config , $ eventLogService );
47
53
}
@@ -86,9 +92,10 @@ public function addGroupsAction(Request $request): string
86
92
$ message = null ;
87
93
$ result = -1 ;
88
94
if ((($ tsvFile && ($ result = $ this ->importExportService ->importTsv ('groups ' , $ tsvFile , $ message ))) ||
89
- ($ jsonFile && ($ result = $ this ->importExportService ->importJson ('groups ' , $ jsonFile , $ message )))) &&
90
- $ result >= 0 ) {
91
- return "$ result new group(s) successfully added. " ;
95
+ ($ jsonFile && ($ result = $ this ->importExportService ->importJson ('groups ' , $ jsonFile , $ message )))) &&
96
+ $ result >= 0
97
+ ) {
98
+ return "$ result new group(s) successfully added. " ;
92
99
} else {
93
100
throw new BadRequestHttpException ("Error while adding groups: $ message " );
94
101
}
@@ -110,7 +117,8 @@ public function addGroupsAction(Request $request): string
110
117
property: 'json ' ,
111
118
description: 'The organizations.json files to import. ' ,
112
119
type: 'string ' ,
113
- format: 'binary ' ),
120
+ format: 'binary '
121
+ ),
114
122
]
115
123
)
116
124
)
@@ -121,7 +129,8 @@ public function addOrganizationsAction(Request $request): string
121
129
$ message = null ;
122
130
/** @var UploadedFile|null $jsonFile */
123
131
$ jsonFile = $ request ->files ->get ('json ' );
124
- if ($ jsonFile &&
132
+ if (
133
+ $ jsonFile &&
125
134
($ result = $ this ->importExportService ->importJson ('organizations ' , $ jsonFile , $ message )) &&
126
135
$ result >= 0
127
136
) {
@@ -171,8 +180,9 @@ public function addTeamsAction(Request $request): string
171
180
}
172
181
$ message = null ;
173
182
if ((($ tsvFile && ($ result = $ this ->importExportService ->importTsv ('teams ' , $ tsvFile , $ message ))) ||
174
- ($ jsonFile && ($ result = $ this ->importExportService ->importJson ('teams ' , $ jsonFile , $ message )))) &&
175
- $ result >= 0 ) {
183
+ ($ jsonFile && ($ result = $ this ->importExportService ->importJson ('teams ' , $ jsonFile , $ message )))) &&
184
+ $ result >= 0
185
+ ) {
176
186
// TODO: better return all teams here?
177
187
return "$ result new team(s) successfully added. " ;
178
188
} else {
@@ -235,8 +245,9 @@ public function addAccountsAction(Request $request): string
235
245
236
246
$ message = null ;
237
247
if ((($ tsvFile && ($ result = $ this ->importExportService ->importTsv ('accounts ' , $ tsvFile , $ message ))) ||
238
- ($ jsonFile && ($ result = $ this ->importExportService ->importJson ('accounts ' , $ jsonFile , $ message )))) &&
239
- $ result >= 0 ) {
248
+ ($ jsonFile && ($ result = $ this ->importExportService ->importJson ('accounts ' , $ jsonFile , $ message )))) &&
249
+ $ result >= 0
250
+ ) {
240
251
// TODO: better return all accounts here?
241
252
return "$ result new account(s) successfully added. " ;
242
253
} else {
@@ -259,11 +270,13 @@ public function addAccountsAction(Request $request): string
259
270
)
260
271
)]
261
272
#[OA \Parameter(ref: '#/components/parameters/idlist ' )]
262
- #[OA \Parameter(
263
- name: 'team_id ' ,
264
- description: 'Only show users for the given team ' ,
265
- in: 'query ' ,
266
- schema: new OA \Schema (type: 'string ' ))
273
+ #[
274
+ OA \Parameter(
275
+ name: 'team_id ' ,
276
+ description: 'Only show users for the given team ' ,
277
+ in: 'query ' ,
278
+ schema: new OA \Schema (type: 'string ' )
279
+ )
267
280
]
268
281
public function listAction (Request $ request ): Response
269
282
{
@@ -291,13 +304,12 @@ public function singleAction(Request $request, string $id): Response
291
304
* Add a new user.
292
305
*/
293
306
#[IsGranted('ROLE_API_WRITER ' )]
294
- #[Rest \Post]
307
+ #[Rest \Post() ]
295
308
#[OA \RequestBody(
296
309
required: true ,
297
310
content: [
298
- new OA \MediaType (
299
- mediaType: 'multipart/form-data ' ,
300
- schema: new OA \Schema (ref: new Model (type: AddUser::class))
311
+ new OA \JsonContent (
312
+ ref: new Model (type: AddUser::class)
301
313
),
302
314
]
303
315
)]
@@ -307,24 +319,23 @@ public function singleAction(Request $request, string $id): Response
307
319
content: new Model (type: User::class)
308
320
)]
309
321
public function addAction (
310
- #[MapRequestPayload(validationFailedStatusCode: Response::HTTP_BAD_REQUEST )]
322
+ #[MapRequestPayload(validationFailedStatusCode: Response::HTTP_BAD_REQUEST , validationGroups: [ ' add ' ] )]
311
323
AddUser $ addUser ,
312
324
Request $ request
313
325
): Response {
314
- return $ this ->addOrUpdateUser ($ addUser , $ request );
326
+ return $ this ->addOrUpdateUser ($ addUser , new User (), $ request );
315
327
}
316
328
317
329
/**
318
- * Update an existing User or create one with the given ID
330
+ * Update an existing User
319
331
*/
320
332
#[IsGranted('ROLE_API_WRITER ' )]
321
- #[Rest \Put ('/{id} ' )]
333
+ #[Rest \Patch ('/{id} ' )]
322
334
#[OA \RequestBody(
323
335
required: true ,
324
336
content: [
325
- new OA \MediaType (
326
- mediaType: 'multipart/form-data ' ,
327
- schema: new OA \Schema (ref: new Model (type: UpdateUser::class))
337
+ new OA \JsonContent (
338
+ ref: new Model (type: UpdateUser::class)
328
339
),
329
340
]
330
341
)]
@@ -335,58 +346,55 @@ public function addAction(
335
346
)]
336
347
public function updateAction (
337
348
#[MapRequestPayload(validationFailedStatusCode: Response::HTTP_BAD_REQUEST )]
338
- UpdateUser $ updateUser ,
349
+ UpdateUser $ mutateUser ,
350
+ string $ id ,
339
351
Request $ request
340
352
): Response {
341
- return $ this ->addOrUpdateUser ($ updateUser , $ request );
353
+ /** @var User|null $user */
354
+ $ user = $ this ->em ->getRepository (User::class)->findOneBy (['externalid ' => $ id ]);
355
+ if ($ user === null ) {
356
+ throw new NotFoundHttpException (sprintf ("User with id %s not found " , $ id ));
357
+ }
358
+ return $ this ->addOrUpdateUser ($ mutateUser , $ user , $ request );
342
359
}
343
360
344
- protected function addOrUpdateUser (AddUser $ addUser , Request $ request ): Response
361
+ protected function addOrUpdateUser (AddUser | UpdateUser $ mutateUser , User $ user , Request $ request ): Response
345
362
{
346
- if ($ addUser instanceof UpdateUser && ! $ addUser -> id ) {
347
- throw new BadRequestHttpException ( ' `id` field is required ' );
363
+ if ($ mutateUser -> username !== null ) {
364
+ $ user -> setUsername ( $ mutateUser -> username );
348
365
}
349
-
350
- if ($ this ->em ->getRepository (User::class)->findOneBy (['username ' => $ addUser ->username ])) {
351
- throw new BadRequestHttpException (sprintf ("User %s already exists " , $ addUser ->username ));
366
+ if ($ mutateUser ->name !== null ) {
367
+ $ user ->setName ($ mutateUser ->name );
352
368
}
353
-
354
- $ user = new User ();
355
- if ($ addUser instanceof UpdateUser) {
356
- $ existingUser = $ this ->em ->getRepository (User::class)->findOneBy (['externalid ' => $ addUser ->id ]);
357
- if ($ existingUser ) {
358
- $ user = $ existingUser ;
359
- }
369
+ if ($ mutateUser ->email !== null ) {
370
+ $ user ->setEmail ($ mutateUser ->email );
360
371
}
361
- $ user
362
- ->setUsername ($ addUser ->username )
363
- ->setName ($ addUser ->name )
364
- ->setEmail ($ addUser ->email )
365
- ->setIpAddress ($ addUser ->ip )
366
- ->setPlainPassword ($ addUser ->password )
367
- ->setEnabled ($ addUser ->enabled ?? true );
368
-
369
- if ($ addUser instanceof UpdateUser) {
370
- $ user ->setExternalid ($ addUser ->id );
372
+ if ($ mutateUser ->ip !== null ) {
373
+ $ user ->setIpAddress ($ mutateUser ->ip );
371
374
}
372
-
373
- if ($ addUser ->teamId ) {
375
+ if ($ mutateUser ->password !== null ) {
376
+ $ user ->setPlainPassword ($ mutateUser ->password );
377
+ }
378
+ if ($ mutateUser ->enabled !== null ) {
379
+ $ user ->setEnabled ($ mutateUser ->enabled );
380
+ }
381
+ if ($ mutateUser ->teamId ) {
374
382
/** @var Team|null $team */
375
383
$ team = $ this ->em ->createQueryBuilder ()
376
384
->from (Team::class, 't ' )
377
385
->select ('t ' )
378
386
->andWhere ('t.externalid = :team ' )
379
- ->setParameter ('team ' , $ addUser ->teamId )
387
+ ->setParameter ('team ' , $ mutateUser ->teamId )
380
388
->getQuery ()
381
389
->getOneOrNullResult ();
382
390
383
391
if ($ team === null ) {
384
- throw new BadRequestHttpException (sprintf ("Team %s not found " , $ addUser ->teamId ));
392
+ throw new BadRequestHttpException (sprintf ("Team %s not found " , $ mutateUser ->teamId ));
385
393
}
386
394
$ user ->setTeam ($ team );
387
395
}
388
396
389
- $ roles = $ addUser ->roles ;
397
+ $ roles = $ mutateUser ->roles ;
390
398
// For the file import we change a CDS user to the roles needed for ICPC CDS.
391
399
if ($ user ->getUsername () === 'cds ' ) {
392
400
$ roles = ['cds ' ];
@@ -408,18 +416,23 @@ protected function addOrUpdateUser(AddUser $addUser, Request $request): Response
408
416
$ user ->addUserRole ($ role );
409
417
}
410
418
419
+ $ validation = $ this ->validator ->validate ($ user );
420
+ if (count ($ validation ) > 0 ) {
421
+ throw new ValidationFailedException ($ user , $ validation );
422
+ }
423
+
411
424
$ this ->em ->persist ($ user );
412
425
$ this ->em ->flush ();
413
- $ this ->dj ->auditlog ('user ' , $ user ->getUserid (), 'added ' );
426
+ $ this ->dj ->auditlog ('user ' , $ user ->getUserid (), 'updated ' );
414
427
415
428
return $ this ->renderCreateData ($ request , $ user , 'user ' , $ user ->getUserid ());
416
429
}
417
430
418
431
protected function getQueryBuilder (Request $ request ): QueryBuilder
419
432
{
420
433
$ queryBuilder = $ this ->em ->createQueryBuilder ()
421
- ->from (User::class, 'u ' )
422
- ->select ('u ' );
434
+ ->from (User::class, 'u ' )
435
+ ->select ('u ' );
423
436
424
437
if ($ request ->query ->has ('team ' )) {
425
438
$ queryBuilder
0 commit comments