In my Symfony 4/5 projects, to enable some data in my tables for raw development and for the testing, I use Hautelook/Alice Bundle.

Attention, the article below is valid only for Symfony up to 5.4 as some elements are deprecated in Symfony 6.0 (the UserPasswordEncodeInterface is deprecated as the Hashing functionalities have been extracted into a new Component in Symfony 5.3 : https://symfony.com/blog/new-in-symfony-5-3-passwordhasher-component).
To find the details for the Symfony 5.3 and upwards, please look here : https://yalit.be/blog/2021/08/17/symfony-hautelook-alice-hash-passwords-at-fixtures-load-5-3-and-upwards/

One of the elements that I want to be able to fake are users. Unfortunately, I didn’t find any direct way using the bundle pre-existing features to load hashed passwords. Meaning, there is no faker function to hash any string based on the defined Symfony User Password hashing (cfr. Symfony Security Definition)

  1. I could indeed use plain passwords. I’m only in dev and test, but the issue is that during the testing, I’ll try to connect and I’ll fall against the Security wall where Symfony will try to hash my input password against the stored password and so will fail
  2. I could define it by hand in the fixtures.yaml file of Hautelook/Alice. It could work regarding the above issue, but I would have to look at it by hand (there is console command to do that…) and I do want to find a structural way to do it
  3. I could use the Custom Faker Providers functionality of Alice bundle. That’s what I’m describing below.

It’s quite easy actually, you only need to follow the following Custom Faker Provider page.

So, let’s create a HashPasswordProvider

<?php

namespace App\DataFixtures\Providers;

use App\Entity\User;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;

class HashPasswordProvider
{
    /**
     * @var UserPasswordEncoderInterface
     */
    private $encoder;
    public function __construct(UserPasswordEncoderInterface $encoder)
    {
        $this->encoder = $encoder;
    }

    public function hashPassword(string $plainPassword): string
    {
        return $this->encoder->encodePassword(new User(), $plainPassword);
    }

}

Let’s break down the different elements:

  1. to be able to hash a password, you need to know which hashing algorithm is defined for the application. Fortunately, the following interface : UserPasswordEncoderInterface will provide you the access to that knowledge.
    So, let’s inject it into our Class to benefit from it
    /**
     * @var UserPasswordEncoderInterface
     */
    private $encoder;
    public function __construct(UserPasswordEncoderInterface $encoder)
    {
        $this->encoder = $encoder;
    }

2. then, simply define a public function that will take the necessary input (here a plain password) and returns the hashed password using the UserPasswordEncoder injected

public function hashPassword(string $plainPassword): string
    {
        return $this->encoder->encodePassword(new User(), $plainPassword);
    }

3. after that, you need to define your new Custom Provider as a service tagging him for the Hautelook/Alice bundle to recognize it as a Custom data Provider

# config/services.yaml

services:
    App\DataFixtures\Providers\HashPasswordProvider:
        tags: [ { name: nelmio_alice.faker.provider } ]

4. finally, you are able to use that function into your fixtures file.

//fixtures\User.yaml
App\Entity\User:
    user_{1..10}:
        email: <email()>
        password: <hashPassword('plainPassword')>

It’s as simple as that.

Categories:

4 Responses

  1. Hi, thanks for the tutorial,
    I implement your code as it is, and I got the following error:

    In SimpleObjectGenerator.php line 111:
    An error occurred while generating the fixture “admin1” (App\Entity\Admin): Call to undefined method Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher::encodePassword()

    In HashPasswordProvider.php line 20:
    Call to undefined method Symfony\Component\PasswordHasher\Hasher\UserPasswordHasher::en
    codePassword()

    I’m using Symfony 5.3.3 with PHP 7.3.28, I solved it: this is the new code:

    You can notice that “encodePassword” does not exist, it is “hashPassword”.
    “`
    encoder = $encoder;
    }

    public function hashPassword(string $plainPassword): string
    {
    return $this->encoder->hashPassword(new Admin(), $plainPassword);
    }

    }
    “`
    Thanks!!!

  2. Hey, I’m on symfony 5, and followed your instructions , but I get errors :
    I suppose its because of this line:

    App\DataFixtures\Provider\HashPasswordProvider:
    tags: [ { name: nelmio_alice.faker.provider } ]

    error :
    =====

    Attempted to load class “HashPasswordProvider” from namespace “App\DataFixtures\Provider”.
    Did you forget a “use” statement for another namespace?

    my service.yaml
    ==========================

    # This file is the entry point to configure your own services.
    # Files in the packages/ subdirectory configure your dependencies.

    # Put parameters here that don’t need to change on each machine where the app is deployed
    # https://symfony.com/doc/current/best_practices.html#use-parameters-for-application-configuration
    parameters:

    services:
    # default configuration for services in *this* file
    _defaults:
    autowire: true # Automatically injects dependencies in your services.
    autoconfigure: true # Automatically registers your services as commands, event subscribers, etc.

    # makes classes in src/ available to be used as services
    # this creates a service per class whose id is the fully-qualified class name

    App\:
    resource: ‘../src/’
    exclude:
    – ‘../src/DependencyInjection/’
    – ‘../src/Entity/’
    – ‘../src/Kernel.php’
    – ‘../src/Tests/’

    App\DataFixtures\Provider\HashPasswordProvider:
    tags: [ { name: nelmio_alice.faker.provider } ]

    # add more service definitions when explicit configuration is needed
    # please note that last definitions always *replace* previous ones

    ==================

    thanks for help

    • Thanks for the comment.

      I have forgotten a ‘s’ at the end of \Provider in the service definition. If you look at the namespace in the HashPasswordProvider, it’s “`namespace App\DataFixtures\Providers“`

      I’ve updated the post.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.