Skip to content

Added API routes to create and delete users - #294

Open
oliverbates94 wants to merge 1 commit into
tchapi:mainfrom
oliverbates94:feature/api_user_creation_and_deletion
Open

oliverbates94 wants to merge 1 commit into
tchapi:mainfrom
oliverbates94:feature/api_user_creation_and_deletion

Conversation

@oliverbates94

Copy link
Copy Markdown

Hi! I'm not sure if this is the right approach to this, I basically duplicated the code from the UserController, but I needed these two API routes

Tell me if there's something I need to change!

@tchapi tchapi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the work. I'm overall ok for these two new APIs, but we need to make them right 🙏🏼

Apart from the in-line comments above:

  • Most lines are copy-pasted from Admin\UserController::userCreate/userDelete. Utils::createPasswordlessUserWithDefaultObjects() already exists and does 90% of the create path; I think the right move is to extend Utils (e.g. createUserWithDefaultObjects(username, displayName, email, password, isAdmin) and a deleteUserAndObjects(User) or something like that) and call it from both controllers.

  • No email validation. The admin path gets EmailType + Assert\Email on Principal::$email via the form validator but the API path never runs the validator, so any string is stored as an email (which then ends up in mailto: share hrefs).

  • I'll need some functional test added under tests/Functional + update of docs/api

Comment on lines +184 to +187
$userIsAdmin = $data['is_admin'] ?? null;
if (empty($userIsAdmin) || !in_array($userIsAdmin, [true, false, 'true', 'false'], true)) {
return $this->json(['status' => 'error', 'message' => 'Invalid User Is Admin', 'timestamp' => $this->getTimestamp()], 400);
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is broken -> "everyone becomes admin".

  • "is_admin": false → empty(false) is true → rejected with 400
  • "is_admin": "false" → passes in_array, then setIsAdmin(bool $isAdmin) receives the string 'false'. The file has no declare(strict_types=1), so PHP coerces it to true (only "" and "0" are falsy strings).

Result: there is no input that creates a non-admin user; "false" silently creates an admin. Look at the existing endpoints

$entityManager = $doctrine->getManager();
$entityManager->remove($user);

$principal = $doctrine->getRepository(Principal::class)->findOneByUri($user->getPrincipalUri());

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

findOneByUri() can return null (orphaned user, or a DB where the principal was removed), then $principal->getUri() throws an \Error, which catch (\Exception) does not catch → unhandled 500.

Comment on lines +181 to +183
if (empty($userPassword)) {
return $this->json(['status' => 'error', 'message' => 'Invalid User Password', 'timestamp' => $this->getTimestamp()], 400);
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty($userPassword) / empty($userName) rejects the literal "0"

'timestamp' => $this->getTimestamp(),
];

return $this->json($response, 200);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: 201 would be the right status for create

* @return JsonResponse A JSON response indicating the success or failure of the operation
*/
#[Route('/users/{userId}', name: 'user_delete', methods: ['DELETE'], requirements: ['userId' => '\d+'])]
public function deleteUser(Request $request, int $userId, ManagerRegistry $doctrine): JsonResponse

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Request $request is unused

return $this->json(['status' => 'error', 'message' => 'Invalid User Name', 'timestamp' => $this->getTimestamp()], 400);
}
$userDisplayName = $data['display_name'] ?? null;
if (empty($userDisplayName) || 1 !== preg_match('/^[a-zA-Z0-9 ._-]{1,64}$/', $userDisplayName)) {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't know where this display-name regex ^[a-zA-Z0-9 ._-]{1,64}$ comes from, it's nowhere else in the codebase and is stricter than the UI: "Éric Dupont" or "José" is rejected by the API but accepted by the form

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants