1

these are my first steps with Symfony. I try to implement a simple user management with Easyadmin Bundle in Symfony 4.4. I followed the Tutorial on symfony.com and most of it is working correctly (Sign up form, backend login, backend security, backend listing of users from database).

My Problem is the creation and updating of a user in the Easyadmin backend. When I try to create a new user, I see the correct fields, I do enter some data and if I click "Save changes" it throws the following error:

An exception occurred while executing 'INSERT INTO app_users (username, email, roles, password, is_active) VALUES (?, ?, ?, ?, ?)' with params ["testname", "test@example.com", "a:1:{i:0;s:9:\"ROLE_USER\";}", null, 1]:
SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'password' cannot be null

Column 'password' cannot be null is pretty clear: I need to provide it with some encoded password string.

I think the data in the plain password field I enter is not encoded and/or not processed by the setPassword() method in my User entity.

As far as I understand some SO answers and the Symfony documentation it should work automagically!? I don't know. I tried to create an AdminController that extends EasyAdminController and hook it in somewhere in the persisting of the user entity, but I couldn't get it to work. (Something like this: https://stackoverflow.com/a/54749433)

How do I process/encode the plainpassword that it is saved to the password field in database?


User entity:

// /src/Entity/User.php
namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;

/**
 * @ORM\Table(name="app_users")
 * @ORM\Entity(repositoryClass="App\Repository\UserRepository")
 * @UniqueEntity("username")
 * @UniqueEntity("email")
 */
class User implements AdvancedUserInterface, \Serializable
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @ORM\Column(type="string", length=254, unique=true)
     * @Assert\NotBlank(groups={"edit"})
     */
    private $username;

    /**
     * @ORM\Column(type="string", length=254, unique=true)
     * @Assert\NotBlank()
     * @Assert\Email(groups={"edit"})
     */
    private $email;

    /**
     * @ORM\Column(type="array")
     */
    private $roles;

    /**
     * @Assert\Length(max=4096)
     */
    private $plainPassword;

    /**
     * @ORM\Column(type="string", length=64)
     */
    private $password;

    /**
     * @ORM\Column(name="is_active", type="boolean")
     */
    private $isActive;

    public function __construct(){
        $this->roles = array('ROLE_USER');
        $this->isActive = true;
    }

    public function getId(){
        return $this->id;
    }

    public function getUsername(){
        return $this->username;
    }

    public function setUsername($username){
        $this->username = $username;
    }

    public function getEmail(){
        return $this->email;
    }

    public function setEmail($email){
        $this->email = $email;
    }

    public function getIsActive(){
        return $this->isActive;
    }

    public function setIsActive($is_active){
        $this->isActive = $is_active;
    }

    public function getRoles(){
        return $this->roles;
    }

    public function setRoles($roles){
        $roles[] = 'ROLE_USER';
        $this->roles = $roles;
    }

    public function getPlainPassword(){
        return $this->plainPassword;
    }

    public function setPlainPassword($password){
        $this->plainPassword = $password;
    }

    public function getPassword()
    {
        return $this->password;
    }

    public function setPassword($password){
        $this->password = $password;
    }

    public function getSalt(){
        return null;
    }

    public function eraseCredentials(){}

    public function isAccountNonExpired(){
        return true;
    }

    public function isAccountNonLocked(){
        return true;
    }

    public function isCredentialsNonExpired(){
        return true;
    }

    public function isEnabled(){
        return $this->isActive;
    }

    /** @see \Serializable::serialize() */
    public function serialize(){
        return serialize(array(
            $this->id,
            $this->username,
            $this->email,
            $this->password,
            $this->isActive,
        ));
    }

    /** @see \Serializable::unserialize() */
    public function unserialize($serialized){
        list (
            $this->id,
            $this->username,
            $this->email,
            $this->password,
            $this->isActive,
        ) = unserialize($serialized, array('allowed_classes' => false));
    }
}

Security.yaml:

# /config/packages/security.yaml
security:
    encoders:
        App\Entity\User:
            algorithm: bcrypt
    providers:
        users_in_memory: { memory: null }
        our_db_provider:
            entity:
                class: App\Entity\User
    firewalls:
        dev:
            pattern: ^/(_(profiler|wdt)|css|images|js)/
            security: false
        main:
            anonymous: true
            pattern: ^/
            provider: our_db_provider
            form_login:
                login_path: login
                check_path: login
                default_target_path: account
                always_use_default_target_path: true
                csrf_token_generator: security.csrf.token_manager
            logout:
                path: /logout
                target: /login
    access_control:
        - { path: ^/admin, roles: ROLE_ADMIN }
        - { path: ^/account, roles: ROLE_USER }
        - { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/register, roles: IS_AUTHENTICATED_ANONYMOUSLY }
        - { path: ^/, roles: IS_AUTHENTICATED_ANONYMOUSLY }

Easyadmin.yaml:

# /config/packages/easy_admin.yaml
easy_admin:
    design:
        menu:
            - { entity: User, label: 'Benutzerverwaltung', icon: 'user' }
    entities:
        User:
            class: App\Entity\User
            label: 'Benutzer'
            password_encoding: { algorithm: 'bcrypt', cost: 12 }
            form:
                form_options: { validation_groups: ['Default'] }
                fields:
                    - { type: 'group', icon: 'address-card', label: 'Informationen', css_class: 'col-lg-6' }
                    - username
                    - email
                    - { type: 'group', icon: 'user-shield', label: 'Rechteverwaltung', css_class: 'col-lg-6' }
                    - { property: 'is_active', type: 'checkbox' }
                    - { property: 'roles', type: 'choice', type_options: { multiple: true, choices: { 'ROLE_USER': 'ROLE_USER', 'ROLE_ADMIN': 'ROLE_ADMIN' } } }
                    - { type: 'group', icon: 'user-lock', label: 'Passwort', css_class: 'col-lg-6' }
                    - { property: 'plainPassword', type: 'text', type_options: { required: false } }
frehder
  • 88
  • 1
  • 7

1 Answers1

0

I can now answer my own question:

Solution: I simply forgot to reference the controller in Easyadmin routes:

# config/routes/easy_admin.yaml
easy_admin_bundle:
    resource: 'App\Controller\AdminController'
    prefix: /admin
    type: annotation

and here is the complete controller for everyone with the same question:

// src/Controller/AdminController.php
namespace App\Controller;

use App\Entity\User;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use EasyCorp\Bundle\EasyAdminBundle\Controller\EasyAdminController;

class AdminController extends EasyAdminController
{
    private $passwordEncoder;

    public function __construct(UserPasswordEncoderInterface $passwordEncoder)
    {
        $this->passwordEncoder = $passwordEncoder;
    }

    private function encodeUserPlainPassword($user)
    {
        $plainPassword = $user->getPlainPassword();

        if (!empty($plainPassword)) {
            $encoded = $this->passwordEncoder->encodePassword($user, $plainPassword);
            $user->setPassword($encoded);
        }
    }

    public function persistEntity($user)
    {
        $this->encodeUserPlainPassword($user);
        parent::persistEntity($user);
    }

    public function updateEntity($user)
    {
        $this->encodeUserPlainPassword($user);
        parent::updateEntity($user);
    }
}
frehder
  • 88
  • 1
  • 7