From bcaad13d6f12d039b3d3874521b46b7a12061ca3 Mon Sep 17 00:00:00 2001 From: ExostFlash <120869320+ExostFlash@users.noreply.github.com> Date: Tue, 21 Nov 2023 22:02:38 +0100 Subject: [PATCH] End update for 21/11/23 --- composer.json | 1 + composer.lock | 156 ++++++++++++++++- config/bundles.php | 1 + config/packages/security.yaml | 13 +- config/packages/sensio_framework_extra.yaml | 3 + migrations/Version20231121091411.php | 33 ---- migrations/Version20231121092109.php | 33 ---- migrations/Version20231121092649.php | 31 ---- ...21092325.php => Version20231121152707.php} | 6 +- ...21093127.php => Version20231121194307.php} | 6 +- public/css/style.css | 2 +- src/Controller/SecurityController.php | 26 +++ src/Controller/UserAdminController.php | 102 +++++++++++ src/Controller/UserController.php | 25 +-- src/Entity/User.php | 160 ++++++++++++------ src/Form/UserLoginType.php | 26 --- src/Form/UserSignupType.php | 5 +- src/Form/UserType.php | 47 +++++ src/Repository/UserRepository.php | 21 ++- symfony.lock | 21 +++ templates/home/index.html.twig | 11 +- templates/security/index.html.twig | 20 +++ templates/user/index.html.twig | 2 +- templates/user/login.html.twig | 14 +- templates/user/signup.html.twig | 2 +- templates/user_admin/_delete_form.html.twig | 4 + templates/user_admin/_form.html.twig | 4 + templates/user_admin/edit.html.twig | 15 ++ templates/user_admin/index.html.twig | 47 +++++ templates/user_admin/new.html.twig | 11 ++ templates/user_admin/show.html.twig | 50 ++++++ tests/Controller/UserControllerTest.php | 152 +++++++++++++++++ 32 files changed, 849 insertions(+), 201 deletions(-) create mode 100644 config/packages/sensio_framework_extra.yaml delete mode 100644 migrations/Version20231121091411.php delete mode 100644 migrations/Version20231121092109.php delete mode 100644 migrations/Version20231121092649.php rename migrations/{Version20231121092325.php => Version20231121152707.php} (52%) rename migrations/{Version20231121093127.php => Version20231121194307.php} (62%) create mode 100644 src/Controller/SecurityController.php create mode 100644 src/Controller/UserAdminController.php delete mode 100644 src/Form/UserLoginType.php create mode 100644 src/Form/UserType.php create mode 100644 templates/security/index.html.twig create mode 100644 templates/user_admin/_delete_form.html.twig create mode 100644 templates/user_admin/_form.html.twig create mode 100644 templates/user_admin/edit.html.twig create mode 100644 templates/user_admin/index.html.twig create mode 100644 templates/user_admin/new.html.twig create mode 100644 templates/user_admin/show.html.twig create mode 100644 tests/Controller/UserControllerTest.php diff --git a/composer.json b/composer.json index 3ea9d00..704c07f 100644 --- a/composer.json +++ b/composer.json @@ -12,6 +12,7 @@ "doctrine/orm": "^2.17", "phpdocumentor/reflection-docblock": "^5.3", "phpstan/phpdoc-parser": "^1.24", + "sensio/framework-extra-bundle": "^6.2", "symfony/asset": "6.1.*", "symfony/console": "6.1.*", "symfony/doctrine-messenger": "6.1.*", diff --git a/composer.lock b/composer.lock index 165f411..8888497 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,84 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "989072ca54ad28f1eb15250b46306f66", + "content-hash": "20a9cc49a87829044729ea14036c4de9", "packages": [ + { + "name": "doctrine/annotations", + "version": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "reference": "e157ef3f3124bbf6fe7ce0ffd109e8a8ef284e7f", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2 || ^3", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.8.0", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6", + "vimeo/psalm": "^4.10" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.1" + }, + "time": "2023-02-02T22:02:53+00:00" + }, { "name": "doctrine/cache", "version": "2.2.0", @@ -1967,6 +2043,84 @@ }, "time": "2021-07-14T16:46:02+00:00" }, + { + "name": "sensio/framework-extra-bundle", + "version": "v6.2.10", + "source": { + "type": "git", + "url": "https://github.com/sensiolabs/SensioFrameworkExtraBundle.git", + "reference": "2f886f4b31f23c76496901acaedfedb6936ba61f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sensiolabs/SensioFrameworkExtraBundle/zipball/2f886f4b31f23c76496901acaedfedb6936ba61f", + "reference": "2f886f4b31f23c76496901acaedfedb6936ba61f", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^1.0|^2.0", + "php": ">=7.2.5", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/framework-bundle": "^4.4|^5.0|^6.0", + "symfony/http-kernel": "^4.4|^5.0|^6.0" + }, + "conflict": { + "doctrine/doctrine-cache-bundle": "<1.3.1", + "doctrine/persistence": "<1.3" + }, + "require-dev": { + "doctrine/dbal": "^2.10|^3.0", + "doctrine/doctrine-bundle": "^1.11|^2.0", + "doctrine/orm": "^2.5", + "symfony/browser-kit": "^4.4|^5.0|^6.0", + "symfony/doctrine-bridge": "^4.4|^5.0|^6.0", + "symfony/dom-crawler": "^4.4|^5.0|^6.0", + "symfony/expression-language": "^4.4|^5.0|^6.0", + "symfony/finder": "^4.4|^5.0|^6.0", + "symfony/monolog-bridge": "^4.0|^5.0|^6.0", + "symfony/monolog-bundle": "^3.2", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0", + "symfony/security-bundle": "^4.4|^5.0|^6.0", + "symfony/twig-bundle": "^4.4|^5.0|^6.0", + "symfony/yaml": "^4.4|^5.0|^6.0", + "twig/twig": "^1.34|^2.4|^3.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "6.1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Sensio\\Bundle\\FrameworkExtraBundle\\": "src/" + }, + "exclude-from-classmap": [ + "/tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + } + ], + "description": "This bundle provides a way to configure your controllers with annotations", + "keywords": [ + "annotations", + "controllers" + ], + "support": { + "source": "https://github.com/sensiolabs/SensioFrameworkExtraBundle/tree/v6.2.10" + }, + "abandoned": "Symfony", + "time": "2023-02-24T14:57:12+00:00" + }, { "name": "symfony/asset", "version": "v6.1.11", diff --git a/config/bundles.php b/config/bundles.php index 3325715..668475a 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -12,4 +12,5 @@ return [ Symfony\Bundle\MonologBundle\MonologBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], Symfony\WebpackEncoreBundle\WebpackEncoreBundle::class => ['all' => true], + Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], ]; diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 0d5d0ee..7148954 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -4,14 +4,23 @@ security: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: "auto" # https://symfony.com/doc/current/security.html#loading-the-user-the-user-provider providers: - users_in_memory: { memory: null } + # used to reload user from session & other features (e.g. switch_user) + app_user_provider: + entity: + class: App\Entity\User + property: email firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true - provider: users_in_memory + provider: app_user_provider + form_login: + login_path: app_user_login + check_path: app_user_login + logout: + path: app_user_logout # activate different ways to authenticate # https://symfony.com/doc/current/security.html#the-firewall diff --git a/config/packages/sensio_framework_extra.yaml b/config/packages/sensio_framework_extra.yaml new file mode 100644 index 0000000..1821ccc --- /dev/null +++ b/config/packages/sensio_framework_extra.yaml @@ -0,0 +1,3 @@ +sensio_framework_extra: + router: + annotations: false diff --git a/migrations/Version20231121091411.php b/migrations/Version20231121091411.php deleted file mode 100644 index e52be4e..0000000 --- a/migrations/Version20231121091411.php +++ /dev/null @@ -1,33 +0,0 @@ -addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, name VARCHAR(100) NOT NULL, fullname VARCHAR(100) NOT NULL, grade VARCHAR(50) DEFAULT NULL, mail VARCHAR(255) NOT NULL, mdp VARCHAR(255) NOT NULL, address VARCHAR(255) DEFAULT NULL, id_resto INT DEFAULT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); - $this->addSql('CREATE TABLE messenger_messages (id BIGINT AUTO_INCREMENT NOT NULL, body LONGTEXT NOT NULL, headers LONGTEXT NOT NULL, queue_name VARCHAR(190) NOT NULL, created_at DATETIME NOT NULL, available_at DATETIME NOT NULL, delivered_at DATETIME DEFAULT NULL, INDEX IDX_75EA56E0FB7336F0 (queue_name), INDEX IDX_75EA56E0E3BD61CE (available_at), INDEX IDX_75EA56E016BA31DB (delivered_at), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP TABLE user'); - $this->addSql('DROP TABLE messenger_messages'); - } -} diff --git a/migrations/Version20231121092109.php b/migrations/Version20231121092109.php deleted file mode 100644 index 83fa21e..0000000 --- a/migrations/Version20231121092109.php +++ /dev/null @@ -1,33 +0,0 @@ -addSql('CREATE TABLE resto (id INT AUTO_INCREMENT NOT NULL, pay VARCHAR(255) NOT NULL, ville VARCHAR(255) NOT NULL, address VARCHAR(255) NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); - $this->addSql('ALTER TABLE user CHANGE grade grade VARCHAR(50) DEFAULT NULL'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP TABLE resto'); - $this->addSql('ALTER TABLE user CHANGE grade grade VARCHAR(50) DEFAULT \'Client\''); - } -} diff --git a/migrations/Version20231121092649.php b/migrations/Version20231121092649.php deleted file mode 100644 index c60f0c0..0000000 --- a/migrations/Version20231121092649.php +++ /dev/null @@ -1,31 +0,0 @@ -addSql('CREATE TABLE ticket (id INT AUTO_INCREMENT NOT NULL, id_resto INT NOT NULL, id_users INT NOT NULL, payement VARCHAR(50) NOT NULL, id_menu INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); - } - - public function down(Schema $schema): void - { - // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP TABLE ticket'); - } -} diff --git a/migrations/Version20231121092325.php b/migrations/Version20231121152707.php similarity index 52% rename from migrations/Version20231121092325.php rename to migrations/Version20231121152707.php index b4cd3e0..969e906 100644 --- a/migrations/Version20231121092325.php +++ b/migrations/Version20231121152707.php @@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ -final class Version20231121092325 extends AbstractMigration +final class Version20231121152707 extends AbstractMigration { public function getDescription(): string { @@ -20,12 +20,12 @@ final class Version20231121092325 extends AbstractMigration public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TABLE menu (id INT AUTO_INCREMENT NOT NULL, entre VARCHAR(255) DEFAULT NULL, plat VARCHAR(255) DEFAULT NULL, dessert VARCHAR(255) DEFAULT NULL, id_resto INT NOT NULL, id_users INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(180) NOT NULL, roles JSON NOT NULL, password VARCHAR(255) NOT NULL, name VARCHAR(200) NOT NULL, fullname VARCHAR(200) NOT NULL, address VARCHAR(255) DEFAULT NULL, id_resto INT DEFAULT NULL, UNIQUE INDEX UNIQ_8D93D649E7927C74 (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); } public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP TABLE menu'); + $this->addSql('DROP TABLE user'); } } diff --git a/migrations/Version20231121093127.php b/migrations/Version20231121194307.php similarity index 62% rename from migrations/Version20231121093127.php rename to migrations/Version20231121194307.php index 5f4592c..38e9903 100644 --- a/migrations/Version20231121093127.php +++ b/migrations/Version20231121194307.php @@ -10,7 +10,7 @@ use Doctrine\Migrations\AbstractMigration; /** * Auto-generated Migration: Please modify to your needs! */ -final class Version20231121093127 extends AbstractMigration +final class Version20231121194307 extends AbstractMigration { public function getDescription(): string { @@ -20,12 +20,12 @@ final class Version20231121093127 extends AbstractMigration public function up(Schema $schema): void { // this up() migration is auto-generated, please modify it to your needs - $this->addSql('CREATE TABLE avis (id INT AUTO_INCREMENT NOT NULL, note INT DEFAULT NULL, com VARCHAR(255) NOT NULL, id_ticket INT NOT NULL, PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB'); + $this->addSql('ALTER TABLE user ADD grade VARCHAR(50) DEFAULT NULL'); } public function down(Schema $schema): void { // this down() migration is auto-generated, please modify it to your needs - $this->addSql('DROP TABLE avis'); + $this->addSql('ALTER TABLE user DROP grade'); } } diff --git a/public/css/style.css b/public/css/style.css index 40be313..2821826 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -1,4 +1,4 @@ -body { +body .center { margin: 1em auto; max-width: 800px; width: 95%; diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php new file mode 100644 index 0000000..6a1c8ee --- /dev/null +++ b/src/Controller/SecurityController.php @@ -0,0 +1,26 @@ +render('user/login.html.twig', [ + 'controller_name' => 'SecurityController', + ]); + } + + #[Route('/user/logout', name: 'app_user_logout')] + public function logout(): Response + { + return $this->render('user/login.html.twig', [ + 'controller_name' => 'SecurityController', + ]); + } +} diff --git a/src/Controller/UserAdminController.php b/src/Controller/UserAdminController.php new file mode 100644 index 0000000..d2de6e5 --- /dev/null +++ b/src/Controller/UserAdminController.php @@ -0,0 +1,102 @@ +hasher = $hasher; + } + + #[Route('/', name: 'app_user_admin_index', methods: ['GET'])] + public function index(UserRepository $userRepository): Response + { + return $this->render('user_admin/index.html.twig', [ + 'users' => $userRepository->findAll(), + ]); + } + + #[Route('/new', name: 'app_user_admin_new', methods: ['GET', 'POST'])] + public function new(Request $request, EntityManagerInterface $entityManager): Response + { + $user = new User(); + $form = $this->createForm(UserType::class, $user); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $entityManager->persist($user); + $entityManager->flush(); + + return $this->redirectToRoute('app_user_admin_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->renderForm('user_admin/new.html.twig', [ + 'user' => $user, + 'form' => $form, + ]); + } + + #[Route('/{id}', name: 'app_user_admin_show', methods: ['GET'])] + public function show(User $user): Response + { + return $this->render('user_admin/show.html.twig', [ + 'user' => $user, + ]); + } + + #[Route('/{id}/edit', name: 'app_user_admin_edit', methods: ['GET', 'POST'])] + public function edit(Request $request, User $user, EntityManagerInterface $entityManager): Response + { + $form = $this->createForm(UserType::class, $user); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + $plainPassword = $form->get('password')->getData(); + + // Encodage sécurisé du mot de passe + $hashedPassword = $this->hasher->hashPassword( + $user, + $plainPassword + ); + + // Définition du mot de passe haché sur l'entité User + $user->setPassword($hashedPassword); + + // Enregistrement de l'utilisateur + $entityManager->persist($user); + $entityManager->flush(); + + return $this->redirectToRoute('app_user_admin_index', [], Response::HTTP_SEE_OTHER); + } + + return $this->renderForm('user_admin/edit.html.twig', [ + 'user' => $user, + 'form' => $form, + ]); + } + + #[Route('/{id}', name: 'app_user_admin_delete', methods: ['POST'])] + public function delete(Request $request, User $user, EntityManagerInterface $entityManager): Response + { + if ($this->isCsrfTokenValid('delete' . $user->getId(), $request->request->get('_token'))) { + $entityManager->remove($user); + $entityManager->flush(); + } + + return $this->redirectToRoute('app_user_admin_index', [], Response::HTTP_SEE_OTHER); + } +} diff --git a/src/Controller/UserController.php b/src/Controller/UserController.php index 377114a..c3d6862 100644 --- a/src/Controller/UserController.php +++ b/src/Controller/UserController.php @@ -10,10 +10,18 @@ use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; +use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface; use Symfony\Component\Routing\Annotation\Route; class UserController extends AbstractController { + private UserPasswordHasherInterface $hasher; + + public function __construct(UserPasswordHasherInterface $hasher) + { + $this->hasher = $hasher; + } + #[Route('/user', name: 'app_user')] public function index(): Response { @@ -31,13 +39,16 @@ class UserController extends AbstractController $form->handleRequest($request); if ($form->isSubmitted() && $form->isValid()) { // Récupération du mot de passe en clair depuis le formulaire - $plainPassword = $form->get('mdp')->getData(); + $plainPassword = $form->get('password')->getData(); // Encodage sécurisé du mot de passe - $hashedPassword = password_hash($plainPassword, PASSWORD_DEFAULT); + $hashedPassword = $this->hasher->hashPassword( + $user_entity, + $plainPassword + ); // Définition du mot de passe haché sur l'entité User - $user_entity->setMdp($hashedPassword); + $user_entity->setPassword($hashedPassword); // Enregistrement de l'utilisateur $entityManager->persist($user_entity); @@ -51,12 +62,4 @@ class UserController extends AbstractController 'form' => $form->createView(), ]); } - - #[Route('/user/login', name: 'app_user_login')] - public function login(): Response - { - return $this->render('user/login.html.twig', [ - 'controller_name' => 'UserController', - ]); - } } diff --git a/src/Entity/User.php b/src/Entity/User.php index e625497..52de2a8 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -4,41 +4,129 @@ namespace App\Entity; use App\Repository\UserRepository; use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; +use Symfony\Component\Security\Core\User\UserInterface; +use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; +use Symfony\Component\Validator\Constraints as Assert; +#[UniqueEntity('email')] #[ORM\Entity(repositoryClass: UserRepository::class)] -class User +class User implements UserInterface, PasswordAuthenticatedUserInterface { #[ORM\Id] #[ORM\GeneratedValue] #[ORM\Column] private ?int $id = null; - #[ORM\Column(length: 100)] + #[ORM\Column(length: 180, unique: true)] + private ?string $email = null; + + #[ORM\Column] + private array $roles = []; + + private ?string $plainPassword = null; + + /** + * @var string The hashed password + */ + #[ORM\Column] + private ?string $password = null; + + #[ORM\Column(length: 200)] private ?string $name = null; - #[ORM\Column(length: 100)] + #[ORM\Column(length: 200)] private ?string $fullname = null; - #[ORM\Column(length: 50, nullable: true)] - private ?string $grade = null; - - #[ORM\Column(length: 255)] - private ?string $mail = null; - - #[ORM\Column(length: 255)] - private ?string $mdp = null; - #[ORM\Column(length: 255, nullable: true)] private ?string $address = null; #[ORM\Column(nullable: true)] private ?int $id_resto = null; + #[ORM\Column(length: 50, nullable: true)] + private ?string $grade = "client"; + public function getId(): ?int { return $this->id; } + public function getEmail(): ?string + { + return $this->email; + } + + public function setEmail(string $email): static + { + $this->email = $email; + + return $this; + } + + /** + * A visual identifier that represents this user. + * + * @see UserInterface + */ + public function getUserIdentifier(): string + { + return (string) $this->email; + } + + /** + * @see UserInterface + */ + public function getRoles(): array + { + $roles = $this->roles; + + return array_unique($roles); + } + + public function setRoles(array $roles): static + { + $this->roles = $roles; + + return $this; + } + + /** + * @see PasswordAuthenticatedUserInterface + */ + public function getPassword(): string + { + return $this->password; + } + + public function setPassword(string $password): static + { + $this->password = $password; + + return $this; + } + + public function setPlainPassword(string $password): static + { + $this->plainPassword = $password; + + return $this; + } + + public function getPlainPassword(): ?string + { + return $this->plainPassword; + } + + /** + * @see UserInterface + */ + public function eraseCredentials(): void + { + // If you store any temporary, sensitive data on the user, clear it here + // $this->plainPassword = null; + } + public function getName(): ?string { return $this->name; @@ -63,42 +151,6 @@ class User return $this; } - public function getGrade(): ?string - { - return $this->grade; - } - - public function setGrade(?string $grade): static - { - $this->grade = $grade; - - return $this; - } - - public function getMail(): ?string - { - return $this->mail; - } - - public function setMail(string $mail): static - { - $this->mail = $mail; - - return $this; - } - - public function getMdp(): ?string - { - return $this->mdp; - } - - public function setMdp(string $mdp): static - { - $this->mdp = $mdp; - - return $this; - } - public function getAddress(): ?string { return $this->address; @@ -122,4 +174,16 @@ class User return $this; } + + public function getGrade(): ?string + { + return $this->grade; + } + + public function setGrade(?string $grade): static + { + $this->grade = $grade; + + return $this; + } } diff --git a/src/Form/UserLoginType.php b/src/Form/UserLoginType.php deleted file mode 100644 index ca4f40c..0000000 --- a/src/Form/UserLoginType.php +++ /dev/null @@ -1,26 +0,0 @@ -add('mail') - ->add('mdp', PasswordType::class); - } - - public function configureOptions(OptionsResolver $resolver): void - { - $resolver->setDefaults([ - 'data_class' => User::class, - ]); - } -} diff --git a/src/Form/UserSignupType.php b/src/Form/UserSignupType.php index 961dd54..3fab4fe 100644 --- a/src/Form/UserSignupType.php +++ b/src/Form/UserSignupType.php @@ -3,6 +3,7 @@ namespace App\Form; use App\Entity\User; +use PharIo\Manifest\Email; use Symfony\Component\Form\AbstractType; use Symfony\Component\Form\FormBuilderInterface; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -19,10 +20,10 @@ class UserSignupType extends AbstractType ->add('fullname', null, [ 'attr' => ['placeholder' => 'Entrez votre prénom'] ]) - ->add('mail', null, [ + ->add('email', null, [ 'attr' => ['placeholder' => 'exemple@exostflash.ovh'] ]) - ->add('mdp', PasswordType::class, [ + ->add('password', PasswordType::class, [ 'attr' => ['placeholder' => 'Entrez votre mot de passe'] ]) ->add('address', null, [ diff --git a/src/Form/UserType.php b/src/Form/UserType.php new file mode 100644 index 0000000..b20a82f --- /dev/null +++ b/src/Form/UserType.php @@ -0,0 +1,47 @@ +add('name', null, [ + 'attr' => ['placeholder' => 'Nom de famille'] + ]) + ->add('fullname', null, [ + 'attr' => ['placeholder' => 'Prénom'] + ]) + ->add('email', null, [ + 'attr' => ['placeholder' => 'exemple@exostflash.ovh'] + ]) + ->add('password', PasswordType::class, [ + 'attr' => ['placeholder' => 'Entrez votre mot de passe'] + ]) + ->add( + 'address', + null, + [ + 'attr' => ['placeholder' => '265 chemin de l\'exemple, 31840 Exemple'] + ] + ) + ->add('grade', null, [ + 'attr' => ['placeholder' => 'Client/Waiter/Chef/Root'] + ]) + ->add('id_resto'); + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => User::class, + ]); + } +} diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php index 3767526..c788f46 100644 --- a/src/Repository/UserRepository.php +++ b/src/Repository/UserRepository.php @@ -5,22 +5,41 @@ namespace App\Repository; use App\Entity\User; use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository; use Doctrine\Persistence\ManagerRegistry; +use Symfony\Component\Security\Core\Exception\UnsupportedUserException; +use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface; +use Symfony\Component\Security\Core\User\PasswordUpgraderInterface; /** * @extends ServiceEntityRepository * + * @implements PasswordUpgraderInterface + * * @method User|null find($id, $lockMode = null, $lockVersion = null) * @method User|null findOneBy(array $criteria, array $orderBy = null) * @method User[] findAll() * @method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null) */ -class UserRepository extends ServiceEntityRepository +class UserRepository extends ServiceEntityRepository implements PasswordUpgraderInterface { public function __construct(ManagerRegistry $registry) { parent::__construct($registry, User::class); } + /** + * Used to upgrade (rehash) the user's password automatically over time. + */ + public function upgradePassword(PasswordAuthenticatedUserInterface $user, string $newHashedPassword): void + { + if (!$user instanceof User) { + throw new UnsupportedUserException(sprintf('Instances of "%s" are not supported.', $user::class)); + } + + $user->setPassword($newHashedPassword); + $this->getEntityManager()->persist($user); + $this->getEntityManager()->flush(); + } + // /** // * @return User[] Returns an array of User objects // */ diff --git a/symfony.lock b/symfony.lock index 13db5f3..2806a7d 100644 --- a/symfony.lock +++ b/symfony.lock @@ -1,4 +1,13 @@ { + "doctrine/annotations": { + "version": "2.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "1.10", + "ref": "64d8583af5ea57b7afa4aba4b159907f3a148b05" + } + }, "doctrine/doctrine-bundle": { "version": "2.11", "recipe": { @@ -40,6 +49,18 @@ "tests/bootstrap.php" ] }, + "sensio/framework-extra-bundle": { + "version": "6.2", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "main", + "version": "5.2", + "ref": "fb7e19da7f013d0d422fa9bce16f5c510e27609b" + }, + "files": [ + "config/packages/sensio_framework_extra.yaml" + ] + }, "symfony/console": { "version": "6.1", "recipe": { diff --git a/templates/home/index.html.twig b/templates/home/index.html.twig index a0f497c..2a8bd69 100644 --- a/templates/home/index.html.twig +++ b/templates/home/index.html.twig @@ -4,10 +4,15 @@ {% block body %} -
-

Bienvenue à McDoPlus !

- +
+

Bienvenue chez McDoPlus !

+ +{% if not app.user %} compte utilisateur c'est ici +{% endif %} +{% if app.user and ('waiter' in app.user.roles or 'chef' in app.user.roles) %} + User roles: {{ app.user.roles|join(', ') }} +{% endif %}

test

diff --git a/templates/security/index.html.twig b/templates/security/index.html.twig new file mode 100644 index 0000000..98019cd --- /dev/null +++ b/templates/security/index.html.twig @@ -0,0 +1,20 @@ +{% extends 'base.html.twig' %} + +{% block title %}Hello SecurityController!{% endblock %} + +{% block body %} + + +
+

Hello {{ controller_name }}! ✅

+ + This friendly message is coming from: + +
+{% endblock %} diff --git a/templates/user/index.html.twig b/templates/user/index.html.twig index 281b22e..0c69f6e 100644 --- a/templates/user/index.html.twig +++ b/templates/user/index.html.twig @@ -4,7 +4,7 @@ {% block body %} -
+

Compte utilisateur !

    diff --git a/templates/user/login.html.twig b/templates/user/login.html.twig index f9fc0f9..e8e622d 100644 --- a/templates/user/login.html.twig +++ b/templates/user/login.html.twig @@ -4,8 +4,20 @@ {% block body %} -
    +

    Login !

    +
    +
    +
    + + +
    +
    + + +
    + +
    {% endblock %} diff --git a/templates/user/signup.html.twig b/templates/user/signup.html.twig index eae8bf6..ee3b750 100644 --- a/templates/user/signup.html.twig +++ b/templates/user/signup.html.twig @@ -4,7 +4,7 @@ {% block body %} -
    +

    Signup !

    diff --git a/templates/user_admin/_delete_form.html.twig b/templates/user_admin/_delete_form.html.twig new file mode 100644 index 0000000..4975b22 --- /dev/null +++ b/templates/user_admin/_delete_form.html.twig @@ -0,0 +1,4 @@ + + + + diff --git a/templates/user_admin/_form.html.twig b/templates/user_admin/_form.html.twig new file mode 100644 index 0000000..bf20b98 --- /dev/null +++ b/templates/user_admin/_form.html.twig @@ -0,0 +1,4 @@ +{{ form_start(form) }} + {{ form_widget(form) }} + +{{ form_end(form) }} diff --git a/templates/user_admin/edit.html.twig b/templates/user_admin/edit.html.twig new file mode 100644 index 0000000..d129fa7 --- /dev/null +++ b/templates/user_admin/edit.html.twig @@ -0,0 +1,15 @@ +{% extends 'base.html.twig' %} + +{% block title %}Edit User{% endblock %} + +{% block body %} + +
    +

    Edit User

    +
    + {{ include('user_admin/_form.html.twig', {'button_label': 'Update'}) }} + {{ include('user_admin/_delete_form.html.twig') }} + back to list +
    +
    +{% endblock %} diff --git a/templates/user_admin/index.html.twig b/templates/user_admin/index.html.twig new file mode 100644 index 0000000..e063844 --- /dev/null +++ b/templates/user_admin/index.html.twig @@ -0,0 +1,47 @@ +{% extends 'base.html.twig' %} + +{% block title %}User index{% endblock %} + +{% block body %} +

    User index

    + + + + + + + + + + + + + + + + + {% for user in users %} + + + + + + + + + + + + {% else %} + + + + {% endfor %} + +
    IdNameFullnameEmailPasswordAddressGradeId_restoactions
    {{ user.id }}{{ user.name }}{{ user.fullname }}{{ user.email }}{{ user.password }}{{ user.address }}{{ user.grade }}{{ user.idResto }} + show + edit +
    no records found
    + + Create new +{% endblock %} diff --git a/templates/user_admin/new.html.twig b/templates/user_admin/new.html.twig new file mode 100644 index 0000000..bc42d2f --- /dev/null +++ b/templates/user_admin/new.html.twig @@ -0,0 +1,11 @@ +{% extends 'base.html.twig' %} + +{% block title %}New User{% endblock %} + +{% block body %} +

    Create new User

    + + {{ include('user_admin/_form.html.twig') }} + + back to list +{% endblock %} diff --git a/templates/user_admin/show.html.twig b/templates/user_admin/show.html.twig new file mode 100644 index 0000000..55b94ff --- /dev/null +++ b/templates/user_admin/show.html.twig @@ -0,0 +1,50 @@ +{% extends 'base.html.twig' %} + +{% block title %}User{% endblock %} + +{% block body %} +

    User

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    Id{{ user.id }}
    Email{{ user.email }}
    Roles{{ user.roles ? user.roles|json_encode : '' }}
    Password{{ user.password }}
    Name{{ user.name }}
    Fullname{{ user.fullname }}
    Address{{ user.address }}
    Id_resto{{ user.idResto }}
    + + back to list + + edit + + {{ include('user_admin/_delete_form.html.twig') }} +{% endblock %} diff --git a/tests/Controller/UserControllerTest.php b/tests/Controller/UserControllerTest.php new file mode 100644 index 0000000..8f51c1e --- /dev/null +++ b/tests/Controller/UserControllerTest.php @@ -0,0 +1,152 @@ +client = static::createClient(); + $this->repository = static::getContainer()->get('doctrine')->getRepository(User::class); + + foreach ($this->repository->findAll() as $object) { + $this->manager->remove($object); + } + } + + public function testIndex(): void + { + $crawler = $this->client->request('GET', $this->path); + + self::assertResponseStatusCodeSame(200); + self::assertPageTitleContains('User index'); + + // Use the $crawler to perform additional assertions e.g. + // self::assertSame('Some text on the page', $crawler->filter('.p')->first()); + } + + public function testNew(): void + { + $originalNumObjectsInRepository = count($this->repository->findAll()); + + $this->markTestIncomplete(); + $this->client->request('GET', sprintf('%snew', $this->path)); + + self::assertResponseStatusCodeSame(200); + + $this->client->submitForm('Save', [ + 'user[email]' => 'Testing', + 'user[roles]' => 'Testing', + 'user[password]' => 'Testing', + 'user[name]' => 'Testing', + 'user[fullname]' => 'Testing', + 'user[address]' => 'Testing', + 'user[id_resto]' => 'Testing', + ]); + + self::assertResponseRedirects('/user/admin/'); + + self::assertSame($originalNumObjectsInRepository + 1, count($this->repository->findAll())); + } + + public function testShow(): void + { + $this->markTestIncomplete(); + $fixture = new User(); + $fixture->setEmail('My Title'); + $fixture->setRoles('My Title'); + $fixture->setPassword('My Title'); + $fixture->setName('My Title'); + $fixture->setFullname('My Title'); + $fixture->setAddress('My Title'); + $fixture->setId_resto('My Title'); + + $this->manager->persist($fixture); + $this->manager->flush(); + + $this->client->request('GET', sprintf('%s%s', $this->path, $fixture->getId())); + + self::assertResponseStatusCodeSame(200); + self::assertPageTitleContains('User'); + + // Use assertions to check that the properties are properly displayed. + } + + public function testEdit(): void + { + $this->markTestIncomplete(); + $fixture = new User(); + $fixture->setEmail('My Title'); + $fixture->setRoles('My Title'); + $fixture->setPassword('My Title'); + $fixture->setName('My Title'); + $fixture->setFullname('My Title'); + $fixture->setAddress('My Title'); + $fixture->setId_resto('My Title'); + + $this->manager->persist($fixture); + $this->manager->flush(); + + $this->client->request('GET', sprintf('%s%s/edit', $this->path, $fixture->getId())); + + $this->client->submitForm('Update', [ + 'user[email]' => 'Something New', + 'user[roles]' => 'Something New', + 'user[password]' => 'Something New', + 'user[name]' => 'Something New', + 'user[fullname]' => 'Something New', + 'user[address]' => 'Something New', + 'user[id_resto]' => 'Something New', + ]); + + self::assertResponseRedirects('/user/admin/'); + + $fixture = $this->repository->findAll(); + + self::assertSame('Something New', $fixture[0]->getEmail()); + self::assertSame('Something New', $fixture[0]->getRoles()); + self::assertSame('Something New', $fixture[0]->getPassword()); + self::assertSame('Something New', $fixture[0]->getName()); + self::assertSame('Something New', $fixture[0]->getFullname()); + self::assertSame('Something New', $fixture[0]->getAddress()); + self::assertSame('Something New', $fixture[0]->getId_resto()); + } + + public function testRemove(): void + { + $this->markTestIncomplete(); + + $originalNumObjectsInRepository = count($this->repository->findAll()); + + $fixture = new User(); + $fixture->setEmail('My Title'); + $fixture->setRoles('My Title'); + $fixture->setPassword('My Title'); + $fixture->setName('My Title'); + $fixture->setFullname('My Title'); + $fixture->setAddress('My Title'); + $fixture->setId_resto('My Title'); + + $this->manager->persist($fixture); + $this->manager->flush(); + + self::assertSame($originalNumObjectsInRepository + 1, count($this->repository->findAll())); + + $this->client->request('GET', sprintf('%s%s', $this->path, $fixture->getId())); + $this->client->submitForm('Delete'); + + self::assertSame($originalNumObjectsInRepository, count($this->repository->findAll())); + self::assertResponseRedirects('/user/admin/'); + } +}