Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
November 11, 2022 01:23 pm GMT

Symfony 6 and EasyAdmin 4: Hashing password

* The cover image is originally by geralt and edited with great appreciation.

Summary

With EasyAdmin bundle, you can create admin panel easily:

Well, as to User entity, given it has password field, you must want to hash it before it stored for security.

This post shows how to implement it with EasyAdmin.

Environment

How to hash password

The source code

Here is an example.
Modify src/Controller/Admin/UserCrudController.php like this:

<?phpnamespace App\Controller\Admin;use App\Entity\User;use EasyCorp\Bundle\EasyAdminBundle\Config\{Action, Actions, Crud, KeyValueStore};use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;use EasyCorp\Bundle\EasyAdminBundle\Controller\AbstractCrudController;use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;use EasyCorp\Bundle\EasyAdminBundle\Field\{IdField, EmailField, TextField};use Symfony\Component\Form\Extension\Core\Type\{PasswordType, RepeatedType};use Symfony\Component\Form\{FormBuilderInterface, FormEvent, FormEvents};use Symfony\Component\PasswordHasher\Hasher\UserPasswordHasherInterface;class UserCrudController extends AbstractCrudController{    public function __construct(        public UserPasswordHasherInterface $userPasswordHasher    ) {}    public static function getEntityFqcn(): string    {        return User::class;    }    public function configureActions(Actions $actions): Actions    {        return $actions            ->add(Crud::PAGE_EDIT, Action::INDEX)            ->add(Crud::PAGE_INDEX, Action::DETAIL)            ->add(Crud::PAGE_EDIT, Action::DETAIL)            ;    }    public function configureFields(string $pageName): iterable    {        $fields = [            IdField::new('id')->hideOnForm(),            EmailField::new('email'),        ];        $password = TextField::new('password')            ->setFormType(RepeatedType::class)            ->setFormTypeOptions([                'type' => PasswordType::class,                'first_options' => ['label' => 'Password'],                'second_options' => ['label' => '(Repeat)'],                'mapped' => false,            ])            ->setRequired($pageName === Crud::PAGE_NEW)            ->onlyOnForms()            ;        $fields[] = $password;        return $fields;    }    public function createNewFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface    {        $formBuilder = parent::createNewFormBuilder($entityDto, $formOptions, $context);        return $this->addPasswordEventListener($formBuilder);    }    public function createEditFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface    {        $formBuilder = parent::createEditFormBuilder($entityDto, $formOptions, $context);        return $this->addPasswordEventListener($formBuilder);    }    private function addPasswordEventListener(FormBuilderInterface $formBuilder): FormBuilderInterface    {        return $formBuilder->addEventListener(FormEvents::POST_SUBMIT, $this->hashPassword());    }    private function hashPassword() {        return function($event) {            $form = $event->getForm();            if (!$form->isValid()) {                return;            }            $password = $form->get('password')->getData();            if ($password === null) {                return;            }            $hash = $this->userPasswordHasher->hashPassword($this->getUser(), $password);            $form->getData()->setPassword($hash);        };    }}

Description

I will break it down into several parts.

Constructor Property Promotion

This style is valid since PHP 8.0.

    public function __construct(        public UserPasswordHasherInterface $userPasswordHasher    ) {}

When your PHP version is prior to them, write like below instead:

    private $userPasswordHasher;    public function __construct(        UserPasswordHasherInterface $userPasswordHasher    ) {        $this->userPasswordHasher = $userPasswordHasher;    }

Add menus

This is optional. Menus are added to index page and edit.

    public function configureActions(Actions $actions): Actions    {        return $actions            ->add(Crud::PAGE_EDIT, Action::INDEX)            ->add(Crud::PAGE_INDEX, Action::DETAIL)            ->add(Crud::PAGE_EDIT, Action::DETAIL)            ;    }

Generate password field

configureFields is one of EasyAdmin's functions to configure the fields to display.
Here, password field is defined as PasswordType and RepeatedType. Also, it is as an unmapped field to prevent validation exception in the case when null is set in order not to change password.

    public function configureFields(string $pageName): iterable    {        $fields = [            IdField::new('id')->hideOnForm(),            EmailField::new('email'),        ];        $password = TextField::new('password')            ->setFormType(RepeatedType::class)            ->setFormTypeOptions([                'type' => PasswordType::class,                'first_options' => ['label' => 'Password'],                'second_options' => ['label' => '(Repeat)'],                'mapped' => false,            ])            ->setRequired($pageName === Crud::PAGE_NEW)            ->onlyOnForms()            ;        $fields[] = $password;        return $fields;    }

Handle events

Here, Symfony form events are used with EasyAdmin event handlers. It's because as of now EasyAdmin's events don't support handling form validation.

    public function createNewFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface    {        $formBuilder = parent::createNewFormBuilder($entityDto, $formOptions, $context);        return $this->addPasswordEventListener($formBuilder);    }    public function createEditFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface    {        $formBuilder = parent::createEditFormBuilder($entityDto, $formOptions, $context);        return $this->addPasswordEventListener($formBuilder);    }    private function addPasswordEventListener(FormBuilderInterface $formBuilder): FormBuilderInterface    {        return $formBuilder->addEventListener(FormEvents::POST_SUBMIT, $this->hashPassword());    }

Hash password

This is the key part on hashing password with Symfony's PasswordHasher.
When password is not entered, skip the field.
When it is, hash it and add the field to the entity data

    private function hashPassword() {        return function($event) {            $form = $event->getForm();            if (!$form->isValid()) {                return;            }            $password = $form->get('password')->getData();            if ($password === null) {                return;            }            $hash = $this->userPasswordHasher->hashPassword($this->getUser(), $password);            $form->getData()->setPassword($hash);        };    }

That's it.
I'm happy if the code and description in this post would help you :)


Original Link: https://dev.to/nabbisen/symfony-6-and-easyadmin-4-hashing-password-3eec

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To