diff --git a/README.md b/README.md index 0577eb4..301803d 100644 --- a/README.md +++ b/README.md @@ -2,4 +2,14 @@ A reservation manager for resources such as vehicles, video projectors, etc. -Under development, not functional yet \ No newline at end of file +Under development, not functional yet + + +## Notes à remettre au propre + +Initialiser la BDD + + php bin/console make:migration + php bin/console doctrine:migrations:migrate + + diff --git a/composer.json b/composer.json index dc29e7a..4b83726 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "symfony/mailer": "5.0.*", "symfony/monolog-bundle": "^3.1", "symfony/notifier": "5.0.*", - "symfony/orm-pack": "*", + "symfony/orm-pack": "^1.0", "symfony/process": "5.0.*", "symfony/security-bundle": "5.0.*", "symfony/serializer-pack": "*", @@ -30,8 +30,9 @@ "symfony/yaml": "5.0.*" }, "require-dev": { + "doctrine/doctrine-fixtures-bundle": "^3.3", "symfony/debug-pack": "*", - "symfony/maker-bundle": "^1.0", + "symfony/maker-bundle": "^1.14", "symfony/profiler-pack": "*", "symfony/test-pack": "*" }, diff --git a/composer.lock b/composer.lock index e93ebf1..89f0e9d 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "f0aa134c748f7bb4398f0b93f0f19bb9", + "content-hash": "b20f46a7fddb0abe3c5ff55d45b55de4", "packages": [ { "name": "doctrine/annotations", @@ -6109,6 +6109,136 @@ } ], "packages-dev": [ + { + "name": "doctrine/data-fixtures", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/data-fixtures.git", + "reference": "608a35a3b5bcc4214d116603095f8b0c51091592" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/data-fixtures/zipball/608a35a3b5bcc4214d116603095f8b0c51091592", + "reference": "608a35a3b5bcc4214d116603095f8b0c51091592", + "shasum": "" + }, + "require": { + "doctrine/common": "^2.11", + "php": "^7.2" + }, + "conflict": { + "doctrine/phpcr-odm": "<1.3.0" + }, + "require-dev": { + "alcaeus/mongo-php-adapter": "^1.1", + "doctrine/coding-standard": "^6.0", + "doctrine/dbal": "^2.5.4", + "doctrine/mongodb-odm": "^1.3.0", + "doctrine/orm": "^2.5.4", + "phpunit/phpunit": "^7.0" + }, + "suggest": { + "alcaeus/mongo-php-adapter": "For using MongoDB ODM with PHP 7", + "doctrine/mongodb-odm": "For loading MongoDB ODM fixtures", + "doctrine/orm": "For loading ORM fixtures", + "doctrine/phpcr-odm": "For loading PHPCR ODM fixtures" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.4.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Common\\DataFixtures\\": "lib/Doctrine/Common/DataFixtures" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + } + ], + "description": "Data Fixtures for all Doctrine Object Managers", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "database" + ], + "time": "2019-10-30T20:03:18+00:00" + }, + { + "name": "doctrine/doctrine-fixtures-bundle", + "version": "3.3.0", + "source": { + "type": "git", + "url": "https://github.com/doctrine/DoctrineFixturesBundle.git", + "reference": "8f07fcfdac7f3591f3c4bf13a50cbae05f65ed70" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/DoctrineFixturesBundle/zipball/8f07fcfdac7f3591f3c4bf13a50cbae05f65ed70", + "reference": "8f07fcfdac7f3591f3c4bf13a50cbae05f65ed70", + "shasum": "" + }, + "require": { + "doctrine/data-fixtures": "^1.3", + "doctrine/doctrine-bundle": "^1.11|^2.0", + "doctrine/orm": "^2.6.0", + "php": "^7.1", + "symfony/config": "^3.4|^4.3|^5.0", + "symfony/console": "^3.4|^4.3|^5.0", + "symfony/dependency-injection": "^3.4|^4.3|^5.0", + "symfony/doctrine-bridge": "^3.4|^4.1|^5.0", + "symfony/http-kernel": "^3.4|^4.3|^5.0" + }, + "require-dev": { + "doctrine/coding-standard": "^6.0", + "phpunit/phpunit": "^7.4", + "symfony/phpunit-bridge": "^4.1|^5.0" + }, + "type": "symfony-bundle", + "extra": { + "branch-alias": { + "dev-master": "3.3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Doctrine\\Bundle\\FixturesBundle\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Doctrine Project", + "homepage": "http://www.doctrine-project.org" + }, + { + "name": "Symfony Community", + "homepage": "http://symfony.com/contributors" + } + ], + "description": "Symfony DoctrineFixturesBundle", + "homepage": "http://www.doctrine-project.org", + "keywords": [ + "Fixture", + "persistence" + ], + "time": "2019-11-13T15:46:58+00:00" + }, { "name": "easycorp/easy-log-handler", "version": "v1.0.9", diff --git a/config/bundles.php b/config/bundles.php index 9e19289..36c2c89 100644 --- a/config/bundles.php +++ b/config/bundles.php @@ -12,4 +12,5 @@ return [ Sensio\Bundle\FrameworkExtraBundle\SensioFrameworkExtraBundle::class => ['all' => true], Symfony\Bundle\SecurityBundle\SecurityBundle::class => ['all' => true], Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true], + Doctrine\Bundle\FixturesBundle\DoctrineFixturesBundle::class => ['dev' => true, 'test' => true], ]; diff --git a/config/packages/security.yaml b/config/packages/security.yaml index ce69ba7..0ecece0 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -1,13 +1,28 @@ security: + encoders: + App\Entity\User: + algorithm: auto + # https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers providers: - 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: - anonymous: lazy + anonymous: ~ + guard: + authenticators: + - App\Security\LoginFormAuthentificatorAuthenticator + logout: + path: app_logout + # where to redirect after logout + # target: app_any_route # activate different ways to authenticate # https://symfony.com/doc/current/security.html#firewalls-authentication @@ -18,5 +33,6 @@ security: # Easy way to control access for large sections of your site # Note: Only the *first* access control that matches will be used access_control: + - { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY } # - { path: ^/admin, roles: ROLE_ADMIN } # - { path: ^/profile, roles: ROLE_USER } diff --git a/config/routes.yaml b/config/routes.yaml index c3283aa..3dc015f 100644 --- a/config/routes.yaml +++ b/config/routes.yaml @@ -1,3 +1,8 @@ -#index: -# path: / -# controller: App\Controller\DefaultController::index +index: + path: / + controller: App\Controller\HomeController::index + +login: + path: /login + controller: App\Controller\SecurityController::login + methods: GET|POST \ No newline at end of file diff --git a/src/Controller/HomeController.php b/src/Controller/HomeController.php new file mode 100644 index 0000000..baa4e4c --- /dev/null +++ b/src/Controller/HomeController.php @@ -0,0 +1,22 @@ +denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY'); + return new Response( + "Hey" + ); + } + +} \ No newline at end of file diff --git a/src/Controller/SecurityController.php b/src/Controller/SecurityController.php new file mode 100644 index 0000000..0357ef2 --- /dev/null +++ b/src/Controller/SecurityController.php @@ -0,0 +1,34 @@ +getLastAuthenticationError(); + // last username entered by the user + $lastUsername = $authenticationUtils->getLastUsername(); + + return $this->render('security/login.html.twig', ['last_username' => $lastUsername, 'error' => $error]); + } + + /** + * @Route("/logout", name="app_logout") + */ + public function logout() + { + throw new \Exception('This method can be blank - it will be intercepted by the logout key on your firewall'); + } +} diff --git a/src/DataFixtures/AppFixtures.php b/src/DataFixtures/AppFixtures.php new file mode 100644 index 0000000..fece475 --- /dev/null +++ b/src/DataFixtures/AppFixtures.php @@ -0,0 +1,17 @@ +persist($product); + + $manager->flush(); + } +} diff --git a/src/DataFixtures/UserFixtures.php b/src/DataFixtures/UserFixtures.php new file mode 100644 index 0000000..9a661d5 --- /dev/null +++ b/src/DataFixtures/UserFixtures.php @@ -0,0 +1,33 @@ +passwordEncoder = $passwordEncoder; + } + + + + public function load(ObjectManager $manager) + { + $user = new User(); + $user->setPassword($this->passwordEncoder->encodePassword( + $user, + 'the_new_password' + )); + + $manager->flush(); + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php new file mode 100644 index 0000000..fe4a565 --- /dev/null +++ b/src/Entity/User.php @@ -0,0 +1,113 @@ +id; + } + + public function getEmail(): ?string + { + return $this->email; + } + + public function setEmail(string $email): self + { + $this->email = $email; + + return $this; + } + + /** + * A visual identifier that represents this user. + * + * @see UserInterface + */ + public function getUsername(): string + { + return (string) $this->email; + } + + /** + * @see UserInterface + */ + public function getRoles(): array + { + $roles = $this->roles; + // guarantee every user at least has ROLE_USER + $roles[] = 'ROLE_USER'; + + return array_unique($roles); + } + + public function setRoles(array $roles): self + { + $this->roles = $roles; + + return $this; + } + + /** + * @see UserInterface + */ + public function getPassword(): string + { + return (string) $this->password; + } + + public function setPassword(string $password): self + { + $this->password = $password; + + return $this; + } + + /** + * @see UserInterface + */ + public function getSalt() + { + // not needed when using the "bcrypt" algorithm in security.yaml + } + + /** + * @see UserInterface + */ + public function eraseCredentials() + { + // If you store any temporary, sensitive data on the user, clear it here + // $this->plainPassword = null; + } +} diff --git a/src/Migrations/Version20191226133553.php b/src/Migrations/Version20191226133553.php new file mode 100644 index 0000000..eb90b9c --- /dev/null +++ b/src/Migrations/Version20191226133553.php @@ -0,0 +1,35 @@ +abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('CREATE TABLE user (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(180) NOT NULL, roles LONGTEXT NOT NULL COMMENT \'(DC2Type:json)\', password VARCHAR(255) NOT 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->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.'); + + $this->addSql('DROP TABLE user'); + } +} diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php new file mode 100644 index 0000000..01c2f3b --- /dev/null +++ b/src/Repository/UserRepository.php @@ -0,0 +1,67 @@ +setPassword($newEncodedPassword); + $this->_em->persist($user); + $this->_em->flush(); + } + + // /** + // * @return User[] Returns an array of User objects + // */ + /* + public function findByExampleField($value) + { + return $this->createQueryBuilder('u') + ->andWhere('u.exampleField = :val') + ->setParameter('val', $value) + ->orderBy('u.id', 'ASC') + ->setMaxResults(10) + ->getQuery() + ->getResult() + ; + } + */ + + /* + public function findOneBySomeField($value): ?User + { + return $this->createQueryBuilder('u') + ->andWhere('u.exampleField = :val') + ->setParameter('val', $value) + ->getQuery() + ->getOneOrNullResult() + ; + } + */ +} diff --git a/src/Security/LoginFormAuthentificatorAuthenticator.php b/src/Security/LoginFormAuthentificatorAuthenticator.php new file mode 100644 index 0000000..d6126b9 --- /dev/null +++ b/src/Security/LoginFormAuthentificatorAuthenticator.php @@ -0,0 +1,104 @@ +entityManager = $entityManager; + $this->urlGenerator = $urlGenerator; + $this->csrfTokenManager = $csrfTokenManager; + $this->passwordEncoder = $passwordEncoder; + } + + public function supports(Request $request) + { + return 'app_login' === $request->attributes->get('_route') + && $request->isMethod('POST'); + } + + public function getCredentials(Request $request) + { + $credentials = [ + 'email' => $request->request->get('email'), + 'password' => $request->request->get('password'), + 'csrf_token' => $request->request->get('_csrf_token'), + ]; + $request->getSession()->set( + Security::LAST_USERNAME, + $credentials['email'] + ); + + return $credentials; + } + + public function getUser($credentials, UserProviderInterface $userProvider) + { + $token = new CsrfToken('authenticate', $credentials['csrf_token']); + if (!$this->csrfTokenManager->isTokenValid($token)) { + throw new InvalidCsrfTokenException(); + } + + $user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]); + + if (!$user) { + // fail authentication with a custom error + throw new CustomUserMessageAuthenticationException('Email could not be found.'); + } + + return $user; + } + + public function checkCredentials($credentials, UserInterface $user) + { + return $this->passwordEncoder->isPasswordValid($user, $credentials['password']); + } + + /** + * Used to upgrade (rehash) the user's password automatically over time. + */ + public function getPassword($credentials): ?string + { + return $credentials['password']; + } + + public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) + { + if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) { + return new RedirectResponse($targetPath); + } + return new RedirectResponse($this->urlGenerator->generate('index')); + + } + + protected function getLoginUrl() + { + return $this->urlGenerator->generate('app_login'); + } +} diff --git a/symfony.lock b/symfony.lock index 260f96e..1c76e92 100644 --- a/symfony.lock +++ b/symfony.lock @@ -20,6 +20,9 @@ "doctrine/common": { "version": "v2.11.0" }, + "doctrine/data-fixtures": { + "version": "1.4.0" + }, "doctrine/dbal": { "version": "v2.10.0" }, @@ -38,6 +41,18 @@ "src/Repository/.gitignore" ] }, + "doctrine/doctrine-fixtures-bundle": { + "version": "3.0", + "recipe": { + "repo": "github.com/symfony/recipes", + "branch": "master", + "version": "3.0", + "ref": "fc52d86631a6dfd9fdf3381d0b7e3df2069e51b3" + }, + "files": [ + "src/DataFixtures/AppFixtures.php" + ] + }, "doctrine/doctrine-migrations-bundle": { "version": "1.2", "recipe": { diff --git a/templates/security/login.html.twig b/templates/security/login.html.twig new file mode 100644 index 0000000..d461ea2 --- /dev/null +++ b/templates/security/login.html.twig @@ -0,0 +1,42 @@ +{% extends 'base.html.twig' %} + +{% block title %}Log in!{% endblock %} + +{% block body %} +
+ {% if error %} +
{{ error.messageKey|trans(error.messageData, 'security') }}
+ {% endif %} + + {% if app.user %} +
+ You are logged in as {{ app.user.username }}, Logout +
+ {% endif %} + +

Please sign in

+ + + + + + + + {# + Uncomment this section and add a remember_me option below your firewall to activate remember me functionality. + See https://symfony.com/doc/current/security/remember_me.html + +
+ +
+ #} + + +
+{% endblock %}