diff --git a/composer.json b/composer.json index 9d16962..49a210d 100644 --- a/composer.json +++ b/composer.json @@ -30,6 +30,7 @@ "symfony/proxy-manager-bridge": "6.0.*", "symfony/runtime": "6.0.*", "symfony/security-bundle": "6.0.*", + "symfony/security-csrf": "6.0.*", "symfony/serializer": "6.0.*", "symfony/string": "6.0.*", "symfony/translation": "6.0.*", diff --git a/composer.lock b/composer.lock index 3935f05..a9d6300 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": "dabb783b3f2096007e6fbd0ea80f7505", + "content-hash": "d4f8936e80f8eda092bcbd5412193f15", "packages": [ { "name": "composer/package-versions-deprecated", diff --git a/config/packages/framework.yaml b/config/packages/framework.yaml index 7853e9e..5264885 100644 --- a/config/packages/framework.yaml +++ b/config/packages/framework.yaml @@ -1,7 +1,7 @@ # see https://symfony.com/doc/current/reference/configuration/framework.html framework: secret: '%env(APP_SECRET)%' - #csrf_protection: true + csrf_protection: ~ http_method_override: false # Enables session support. Note that the session will ONLY be started if you read or write from it. diff --git a/config/packages/security.yaml b/config/packages/security.yaml index 789a9ac..7aa253b 100644 --- a/config/packages/security.yaml +++ b/config/packages/security.yaml @@ -3,16 +3,23 @@ security: # https://symfony.com/doc/current/security.html#registering-the-user-hashing-passwords password_hashers: Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto' + App\Entity\User: + algorithm: 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: username firewalls: dev: pattern: ^/(_(profiler|wdt)|css|images|js)/ security: false main: lazy: true - provider: users_in_memory + provider: app_user_provider # activate different ways to authenticate # https://symfony.com/doc/current/security.html#the-firewall @@ -23,7 +30,7 @@ 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: ^/admin, roles: ROLE_ADMIN } +# - { path: ^/admin, roles: ROLE_ADMIN } # - { path: ^/profile, roles: ROLE_USER } when@test: diff --git a/config/packages/twig.yaml b/config/packages/twig.yaml index f9f4cc5..7b8dabc 100644 --- a/config/packages/twig.yaml +++ b/config/packages/twig.yaml @@ -1,5 +1,6 @@ twig: default_path: '%kernel.project_dir%/templates' + form_themes: ['bootstrap_5_horizontal_layout.html.twig'] when@test: twig: diff --git a/src/Controller/Admin/DashboardController.php b/src/Controller/Admin/DashboardController.php index b2e9007..dae473b 100644 --- a/src/Controller/Admin/DashboardController.php +++ b/src/Controller/Admin/DashboardController.php @@ -3,6 +3,7 @@ namespace App\Controller\Admin; use App\Entity\Product; +use App\Entity\User; use EasyCorp\Bundle\EasyAdminBundle\Config\Dashboard; use EasyCorp\Bundle\EasyAdminBundle\Config\MenuItem; use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractDashboardController; @@ -35,5 +36,8 @@ class DashboardController extends AbstractDashboardController yield MenuItem::section('Products', 'fas fa-folder-open'); yield MenuItem::linkToDashboard('Dashboard', 'fa fa-home'); yield MenuItem::linkToCrud('Products', 'fas fa-list', Product::class); + + yield MenuItem::section('Administration', 'fas fa-folder-open'); + yield MenuItem::linkToCrud('User', 'fas fa-list', User::class); } } diff --git a/src/Controller/Admin/UserCrudController.php b/src/Controller/Admin/UserCrudController.php new file mode 100644 index 0000000..1575931 --- /dev/null +++ b/src/Controller/Admin/UserCrudController.php @@ -0,0 +1,25 @@ +createForm(RegistrationFormType::class, $user); + $form->handleRequest($request); + + if ($form->isSubmitted() && $form->isValid()) { + // encode the plain password + $user->setPassword( + $userPasswordHasher->hashPassword( + $user, + $form->get('plainPassword')->getData() + ) + ); + + $entityManager->persist($user); + $entityManager->flush(); + // do anything else you need here, like send an email + + return $this->redirectToRoute('app_test_test1'); + } + + return $this->render('registration/register.html.twig', [ + 'registrationForm' => $form->createView(), + ]); + } +} diff --git a/src/Entity/User.php b/src/Entity/User.php new file mode 100644 index 0000000..3faa618 --- /dev/null +++ b/src/Entity/User.php @@ -0,0 +1,100 @@ +id; + } + + public function getUsername(): ?string + { + return $this->username; + } + + public function setUsername(string $username): self + { + $this->username = $username; + + return $this; + } + + /** + * A visual identifier that represents this user. + * + * @see UserInterface + */ + public function getUserIdentifier(): string + { + return (string) $this->username; + } + + /** + * @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 PasswordAuthenticatedUserInterface + */ + public function getPassword(): string + { + return $this->password; + } + + public function setPassword(string $password): self + { + $this->password = $password; + + return $this; + } + + /** + * @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/Form/RegistrationFormType.php b/src/Form/RegistrationFormType.php new file mode 100644 index 0000000..e9651c8 --- /dev/null +++ b/src/Form/RegistrationFormType.php @@ -0,0 +1,55 @@ +add('username') + ->add('agreeTerms', CheckboxType::class, [ + 'mapped' => false, + 'constraints' => [ + new IsTrue([ + 'message' => 'You should agree to our terms.', + ]), + ], + ]) + ->add('plainPassword', PasswordType::class, [ + // instead of being set onto the object directly, + // this is read and encoded in the controller + 'mapped' => false, + 'attr' => ['autocomplete' => 'new-password'], + 'constraints' => [ + new NotBlank([ + 'message' => 'Please enter a password', + ]), + new Length([ + 'min' => 3, + 'minMessage' => 'Your password should be at least {{ limit }} characters', + // max length allowed by Symfony for security reasons + 'max' => 4096, + ]), + ], + ]) + ; + } + + public function configureOptions(OptionsResolver $resolver): void + { + $resolver->setDefaults([ + 'data_class' => User::class, + ]); + } +} diff --git a/src/Repository/UserRepository.php b/src/Repository/UserRepository.php new file mode 100644 index 0000000..5eaa8c5 --- /dev/null +++ b/src/Repository/UserRepository.php @@ -0,0 +1,67 @@ +setPassword($newHashedPassword); + $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/templates/base/base.html.twig b/templates/base/base.html.twig index 900466b..180f8d4 100644 --- a/templates/base/base.html.twig +++ b/templates/base/base.html.twig @@ -3,7 +3,7 @@ - {% block title %}Welcome!{% endblock %} + {% block title %}IceCold!{% endblock %} {# Run `composer require symfony/webpack-encore-bundle` to start using Symfony UX #} @@ -17,7 +17,7 @@ {% include 'base/navbar.html.twig' %} -
+
{% block body %} {% endblock %}
diff --git a/templates/registration/register.html.twig b/templates/registration/register.html.twig new file mode 100644 index 0000000..667f5ac --- /dev/null +++ b/templates/registration/register.html.twig @@ -0,0 +1,17 @@ +{% extends 'base/base.html.twig' %} + +{% block body %} +
+

Register

+ + {{ form_start(registrationForm) }} + {{ form_row(registrationForm.username) }} + {{ form_row(registrationForm.plainPassword, { + label: 'Password' + }) }} + {{ form_row(registrationForm.agreeTerms) }} + + + {{ form_end(registrationForm) }} +
+{% endblock %}