Skip to content

Commit

Permalink
imp: Allow to choose locale when creating/editing a user
Browse files Browse the repository at this point in the history
  • Loading branch information
marien-probesys committed Aug 26, 2024
2 parents 187add3 + ef06261 commit 1e5fcb5
Show file tree
Hide file tree
Showing 18 changed files with 622 additions and 631 deletions.
4 changes: 3 additions & 1 deletion docs/developers/create-user.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,7 @@ public function createUser(UserCreator $userCreator)
}
```

If you already have a User entity, call `$userCreator->createUser($user)` instead.

The service automatically flushes the changes so Doctine writes the user to the database immediately.
If you want to delay the flush, you can pass `flush: false` to the method.
If you want to delay the flush, you can pass `flush: false` to the methods.
2 changes: 1 addition & 1 deletion docs/developers/import-data.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ The data is stored in a ZIP archive. It contains several files:
- email: string (unique, not empty, valid email)
- locale: string, optional (must be `en_GB`, or `fr_FR`)
- name: string, optional (not empty, max 100 chars)
- ldapIdentifier: string or null, optional
- ldapIdentifier: string, optional
- organizationId: string or null, optional (reference to an organization)
- authorizations: array of, optional:
- roleId: string (reference to a role)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<?php

// This file is part of Bileto.
// Copyright 2022-2024 Probesys
// SPDX-License-Identifier: AGPL-3.0-or-later

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Platforms\MariaDBPlatform;
use Doctrine\DBAL\Platforms\PostgreSQLPlatform;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

// phpcs:disable Generic.Files.LineLength
final class Version20240826082958SetDefaultUserLdapIdentifierToEmpty extends AbstractMigration
{
public function getDescription(): string
{
return 'Set default user.ldapIdentifier column to empty string';
}

public function up(Schema $schema): void
{

$dbPlatform = $this->connection->getDatabasePlatform();
if ($dbPlatform instanceof PostgreSQLPlatform) {
$this->addSql("UPDATE users SET ldap_identifier = '' WHERE ldap_identifier IS NULL");
$this->addSql('ALTER TABLE users ALTER ldap_identifier SET DEFAULT \'\'');
$this->addSql('ALTER TABLE users ALTER ldap_identifier SET NOT NULL');
} elseif ($dbPlatform instanceof MariaDBPlatform) {
$this->addSql('UPDATE users SET ldap_identifier = "" WHERE ldap_identifier IS NULL');
$this->addSql('ALTER TABLE users CHANGE ldap_identifier ldap_identifier VARCHAR(255) DEFAULT \'\' NOT NULL');
}
}

public function down(Schema $schema): void
{
$dbPlatform = $this->connection->getDatabasePlatform();
if ($dbPlatform instanceof PostgreSQLPlatform) {
$this->addSql('ALTER TABLE "users" ALTER ldap_identifier DROP NOT NULL');
$this->addSql('ALTER TABLE "users" ALTER ldap_identifier DROP DEFAULT');
$this->addSql("UPDATE users SET ldap_identifier = NULL WHERE ldap_identifier = ''");
} elseif ($dbPlatform instanceof MariaDBPlatform) {
$this->addSql('ALTER TABLE `users` CHANGE ldap_identifier ldap_identifier VARCHAR(255) DEFAULT NULL');
$this->addSql('UPDATE users SET ldap_identifier = NULL WHERE ldap_identifier = ""');
}
}
}
220 changes: 35 additions & 185 deletions src/Controller/UsersController.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,19 @@

namespace App\Controller;

use App\Entity\User;
use App\Repository\AuthorizationRepository;
use App\Repository\OrganizationRepository;
use App\Repository\UserRepository;
use App\Service\Sorter\AuthorizationSorter;
use App\Service\Sorter\OrganizationSorter;
use App\Service\Sorter\UserSorter;
use App\Service\UserCreator;
use App\Service\UserCreatorException;
use App\Utils\ConstraintErrorsFormatter;
use App\Utils\Time;
use Symfony\Component\DependencyInjection\Attribute\Autowire;
use App\Entity;
use App\Form;
use App\Repository;
use App\Service;
use App\Service\Sorter;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Validator\Validator\ValidatorInterface;
use Symfony\Contracts\Translation\TranslatorInterface;

class UsersController extends BaseController
{
#[Route('/users', name: 'users', methods: ['GET', 'HEAD'])]
public function index(UserRepository $userRepository, UserSorter $userSorter): Response
public function index(Repository\UserRepository $userRepository, Sorter\UserSorter $userSorter): Response
{
$this->denyAccessUnlessGranted('admin:manage:users');

Expand All @@ -41,96 +31,46 @@ public function index(UserRepository $userRepository, UserSorter $userSorter): R
}

#[Route('/users/new', name: 'new user', methods: ['GET', 'HEAD'])]
public function new(
OrganizationRepository $organizationRepository,
OrganizationSorter $organizationSorter,
): Response {
public function new(): Response
{
$this->denyAccessUnlessGranted('admin:manage:users');

$organizations = $organizationRepository->findAll();
$organizationSorter->sort($organizations);
$user = new Entity\User();
$form = $this->createNamedForm('user', Form\UserForm::class, $user);

return $this->render('users/new.html.twig', [
'organizations' => $organizations,
'email' => '',
'name' => '',
'organizationUid' => '',
'form' => $form,
]);
}

#[Route('/users/new', name: 'create user', methods: ['POST'])]
public function create(
Request $request,
OrganizationRepository $organizationRepository,
OrganizationSorter $organizationSorter,
UserCreator $userCreator,
TranslatorInterface $translator,
): Response {
public function create(Request $request, Service\UserCreator $userCreator): Response
{
$this->denyAccessUnlessGranted('admin:manage:users');

/** @var \App\Entity\User $user */
$user = $this->getUser();

/** @var string $email */
$email = $request->request->get('email', '');

/** @var string $name */
$name = $request->request->get('name', '');

/** @var string $password */
$password = $request->request->get('password', '');

/** @var string $organizationUid */
$organizationUid = $request->request->get('organization', '');

/** @var string $csrfToken */
$csrfToken = $request->request->get('_csrf_token', '');

$organizations = $organizationRepository->findAll();
$organizationSorter->sort($organizations);
$user = new Entity\User();
$form = $this->createNamedForm('user', Form\UserForm::class, $user);
$form->handleRequest($request);

if (!$this->isCsrfTokenValid('create user', $csrfToken)) {
if (!$form->isSubmitted() || !$form->isValid()) {
return $this->renderBadRequest('users/new.html.twig', [
'organizations' => $organizations,
'email' => $email,
'name' => $name,
'organizationUid' => $organizationUid,
'error' => $translator->trans('csrf.invalid', [], 'errors'),
'form' => $form,
]);
}

$organization = $organizationRepository->findOneBy(['uid' => $organizationUid]);

try {
$newUser = $userCreator->create(
email: $email,
name: $name,
password: $password,
locale: $user->getLocale(),
organization: $organization,
);
} catch (UserCreatorException $e) {
return $this->renderBadRequest('users/new.html.twig', [
'organizations' => $organizations,
'email' => $email,
'name' => $name,
'organizationUid' => $organizationUid,
'errors' => ConstraintErrorsFormatter::format($e->getErrors()),
]);
}
$user = $form->getData();
$userCreator->createUser($user);

return $this->redirectToRoute('user', [
'uid' => $newUser->getUid(),
'uid' => $user->getUid(),
]);
}

#[Route('/users/{uid:user}', name: 'user', methods: ['GET', 'HEAD'])]
public function show(
User $user,
AuthorizationRepository $authorizationRepository,
AuthorizationSorter $authorizationSorter,
#[Autowire(env: 'bool:LDAP_ENABLED')]
bool $ldapEnabled,
Entity\User $user,
Repository\AuthorizationRepository $authorizationRepository,
Sorter\AuthorizationSorter $authorizationSorter,
): Response {
$this->denyAccessUnlessGranted('admin:manage:users');

Expand All @@ -141,132 +81,42 @@ public function show(

return $this->render('users/show.html.twig', [
'user' => $user,
'ldapEnabled' => $ldapEnabled,
'managedByLdap' => $ldapEnabled && $user->getAuthType() === 'ldap',
'authorizations' => $authorizations,
]);
}

#[Route('/users/{uid:user}/edit', name: 'edit user', methods: ['GET', 'HEAD'])]
public function edit(
User $user,
OrganizationRepository $organizationRepository,
OrganizationSorter $organizationSorter,
#[Autowire(env: 'bool:LDAP_ENABLED')]
bool $ldapEnabled,
): Response {
public function edit(Entity\User $user): Response
{
$this->denyAccessUnlessGranted('admin:manage:users');

$organizations = $organizationRepository->findAll();
$organizationSorter->sort($organizations);

$userOrganization = $user->getOrganization();
$form = $this->createNamedForm('user', Form\UserForm::class, $user);

return $this->render('users/edit.html.twig', [
'user' => $user,
'organizations' => $organizations,
'email' => $user->getEmail(),
'name' => $user->getName(),
'organizationUid' => $userOrganization ? $userOrganization->getUid() : '',
'ldapIdentifier' => $user->getLdapIdentifier(),
'ldapEnabled' => $ldapEnabled,
'managedByLdap' => $ldapEnabled && $user->getAuthType() === 'ldap',
'form' => $form,
]);
}

#[Route('/users/{uid:user}/edit', name: 'update user', methods: ['POST'])]
public function update(
User $user,
Entity\User $user,
Request $request,
UserRepository $userRepository,
OrganizationRepository $organizationRepository,
OrganizationSorter $organizationSorter,
ValidatorInterface $validator,
TranslatorInterface $translator,
UserPasswordHasherInterface $passwordHasher,
#[Autowire(env: 'bool:LDAP_ENABLED')]
bool $ldapEnabled,
Repository\UserRepository $userRepository,
): Response {
$this->denyAccessUnlessGranted('admin:manage:users');

/** @var string $email */
$email = $request->request->get('email', '');

/** @var string $name */
$name = $request->request->get('name', '');

/** @var string $password */
$password = $request->request->get('password', '');

/** @var string $ldapIdentifier */
$ldapIdentifier = $request->request->get('ldapIdentifier', '');

if ($ldapIdentifier === '') {
$ldapIdentifier = null;
}

/** @var string $organizationUid */
$organizationUid = $request->request->get('organization', '');

/** @var string $csrfToken */
$csrfToken = $request->request->get('_csrf_token', '');

$organizations = $organizationRepository->findAll();
$organizationSorter->sort($organizations);

$managedByLdap = $ldapEnabled && $user->getAuthType() === 'ldap';

if ($managedByLdap) {
// If the user is managed by LDAP, these fields cannot be changed.
$email = $user->getEmail();
$name = $user->getName();
$password = '';
}

if (!$this->isCsrfTokenValid('update user', $csrfToken)) {
return $this->renderBadRequest('users/edit.html.twig', [
'user' => $user,
'organizations' => $organizations,
'email' => $email,
'name' => $name,
'organizationUid' => $organizationUid,
'ldapIdentifier' => $ldapIdentifier,
'ldapEnabled' => $ldapEnabled,
'managedByLdap' => $managedByLdap,
'error' => $translator->trans('csrf.invalid', [], 'errors'),
]);
}

$organization = $organizationRepository->findOneBy(['uid' => $organizationUid]);

$user->setEmail($email);
$user->setName($name);
$user->setOrganization($organization);

if ($ldapEnabled) {
$user->setLdapIdentifier($ldapIdentifier);
}

if ($password !== '') {
$hashedPassword = $passwordHasher->hashPassword($user, $password);
$user->setPassword($hashedPassword);
}
$form = $this->createNamedForm('user', Form\UserForm::class, $user);
$form->handleRequest($request);

$errors = $validator->validate($user);
if (count($errors) > 0) {
if (!$form->isSubmitted() || !$form->isValid()) {
return $this->renderBadRequest('users/edit.html.twig', [
'user' => $user,
'organizations' => $organizations,
'email' => $email,
'name' => $name,
'organizationUid' => $organizationUid,
'ldapIdentifier' => $ldapIdentifier,
'ldapEnabled' => $ldapEnabled,
'managedByLdap' => $managedByLdap,
'errors' => ConstraintErrorsFormatter::format($errors),
'form' => $form,
]);
}

$user = $form->getData();
$userRepository->save($user, true);

return $this->redirectToRoute('user', [
Expand Down
7 changes: 5 additions & 2 deletions src/Entity/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class User implements
#[ORM\JoinColumn(onDelete: 'SET NULL')]
private ?Organization $organization = null;

#[ORM\Column(length: 255, nullable: true)]
#[ORM\Column(length: 255, options: ['default' => ''])]
private ?string $ldapIdentifier = null;

/** @var Collection<int, Team> */
Expand All @@ -125,8 +125,11 @@ class User implements

public function __construct()
{
$this->name = '';
$this->email = '';
$this->password = '';
$this->locale = 'en_GB';
$this->ldapIdentifier = '';
$this->authorizations = new ArrayCollection();
$this->teams = new ArrayCollection();
}
Expand Down Expand Up @@ -292,7 +295,7 @@ public function setLdapIdentifier(?string $ldapIdentifier): static
*/
public function getAuthType(): string
{
if ($this->getLdapIdentifier() === null) {
if ($this->getLdapIdentifier() === '') {
return 'local';
} else {
return 'ldap';
Expand Down
Loading

0 comments on commit 1e5fcb5

Please sign in to comment.