I have a basic Symfony 3.3 structure (version 3.4 does not work and 4 is very different) with two entities: User , for users and Role for the roles. Several users may have a role (ManyToOne), and a role may be assigned to several users (OneToMany). Also, I have two users created in configuration. The security.yml file includes:
security:
#Como encoder usamos bcrypt para las contraseñas.
encoders:
Symfony\Component\Security\Core\User\User:
algorithm: bcrypt
cost: 10
AppBundle\Entity\User:
algorithm: bcrypt
cost: 10
#Como provider de usuarios definimos en memoria y en base de datos.
providers:
chain_provider:
chain:
providers: [in_memory, user_db]
in_memory:
memory:
users:
user:
password: $2y$10$gMoT8xcqB5OK/zBWyqeSqOrQnAqTz/8yxQrmhVhflvs7qw6r65xpC
roles: 'ROLE_USER'
admin:
password: $2y$10$pzphAl3SnvP.UvRmMxKKiuGOoMQghTw7GQ/dZ1s3Kjlm8K0JP73hW
roles: 'ROLE_ROOT_ADMIN'
user_db:
entity: {class: AppBundle\Entity\User, property: username}
The AppBundle \ Entity \ User looks like this:
<?php
namespace AppBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* User
*/
/**
* @ORM\Table(name="user")
* @ORM\Entity(repositoryClass="AppBundle\Repository\UserRepository")
*/
class User implements UserInterface
{
/**
* @var int
*/
private $id;
/**
* @var string
*/
private $firstName;
/**
* @var string
*/
private $familyName;
/**
* @var string
*/
private $username;
/**
* @var string
*/
private $password;
/**
* @var string
*/
private $email;
/**
* @var string
*/
private $language;
/**
* @var string
*/
private $image;
/**
* @var string
*/
private $active;
/**
* @var int
*/
private $role;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set firstName.
*
* @param string $firstName
*
* @return User
*/
public function setFirstName($firstName)
{
$this->firstName = $firstName;
return $this;
}
/**
* Get firstName.
*
* @return string
*/
public function getFirstName()
{
return $this->firstName;
}
/**
* Set familyName.
*
* @param string $familyName
*
* @return User
*/
public function setFamilyName($familyName)
{
$this->familyName = $familyName;
return $this;
}
/**
* Get familyName.
*
* @return string
*/
public function getFamilyName()
{
return $this->familyName;
}
/**
* Set username.
*
* @param string $username
*
* @return User
*/
public function setUsername($username)
{
$this->username = $username;
return $this;
}
/**
* Get username.
*
* @return string
*/
public function getUsername()
{
return $this->username;
}
/**
* Set password.
*
* @param string $password
*
* @return User
*/
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Get password.
*
* @return string
*/
public function getPassword()
{
return $this->password;
}
/**
* Set email.
*
* @param string $email
*
* @return User
*/
public function setEmail($email)
{
$this->email = $email;
return $this;
}
/**
* Get email.
*
* @return string
*/
public function getEmail()
{
return $this->email;
}
/**
* Set language.
*
* @param string $language
*
* @return User
*/
public function setLanguage($language)
{
$this->language = $language;
return $this;
}
/**
* Get language.
*
* @return string
*/
public function getLanguage()
{
return $this->language;
}
/**
* Set image.
*
* @param string $image
*
* @return User
*/
public function setImage($image)
{
$this->image = $image;
return $this;
}
/**
* Get image.
*
* @return string
*/
public function getImage()
{
return $this->image;
}
/**
* Set active.
*
* @param string $active
*
* @return User
*/
public function setActive($active)
{
$this->active = $active;
return $this;
}
/**
* Get active.
*
* @return string
*/
public function getActive()
{
return $this->active;
}
/**
* Set role.
*
* @param int $role
*
* @return User
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* Get role.
*
* @return int
*/
public function getRole()
{
return $this->role;
}
public function __construct($role)
{
$this->setRole($role);
}
public function getRoles()
{
// TODO: Implement getRoles() method.
}
public function getSalt()
{
// TODO: Implement getSalt() method.
}
public function eraseCredentials()
{
// TODO: Implement eraseCredentials() method.
}
}
The AppBundle / Entity / Role is like this:
<?php
namespace AppBundle\Entity;
use Doctrine\Common\Collections\ArrayCollection;
use AppBundle\Entity\User as User;
/**
* Role
*/
class Role
{
/**
* @var int
*/
private $id;
/**
* @var string
*/
private $role;
/**
* Get id.
*
* @return int
*/
public function getId()
{
return $this->id;
}
/**
* Set role.
*
* @param string $role
*
* @return Role
*/
public function setRole($role)
{
$this->role = $role;
return $this;
}
/**
* Get role.
*
* @return string
*/
public function getRole()
{
return $this->role;
}
private $users;
public function __construct()
{
$this->users = new ArrayCollection();
}
public function __toString()
{
return $this->role;
}
/**
* Add user.
*
* @param \AppBundle\Entity\User $user
*
* @return Role
*/
public function addUser(User $user)
{
$this->users[] = $user;
return $this;
}
/**
* Remove user.
*
* @param \AppBundle\Entity\User $user
*
* @return boolean TRUE if this collection contained the specified element, FALSE otherwise.
*/
public function removeUser(User $user)
{
return $this->users->removeElement($user);
}
/**
* Get users.
*
* @return \Doctrine\Common\Collections\Collection
*/
public function getUsers()
{
return $this->users;
}
}
Also, I have the relationships defined in yml. The AppBundle / Resources / config / doctrine / User.orm.yml is like this:
AppBundle\Entity\User:
type: entity
table: null
repositoryClass: AppBundle\Repository\UserRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
firstName:
type: string
length: '100'
column: first_name
familyName:
type: string
length: '100'
column: family_name
username:
type: string
length: '50'
unique: true
password:
type: string
length: '100'
email:
type: string
length: '100'
unique: true
language:
type: string
length: '2'
image:
type: string
length: '100'
active:
type: string
length: '1'
manyToOne:
role:
type: integer
targetEntity: AppBundle\Entity\Role
inversedBy: users
joinColumn:
name: role
referencedColumnName: id
lifecycleCallbacks: { }
And the AppBundle / Resources / config / doctrine / Role.orm.yml is like this:
AppBundle\Entity\Role:
type: entity
table: null
repositoryClass: AppBundle\Repository\RoleRepository
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
role:
type: string
length: '25'
unique: true
oneToMany:
users:
targetEntity: AppBundle\Entity\User
mappedBy: role
lifecycleCallbacks: { }
Access is through a page with an integrated form, like this:
{% extends 'base.html.twig' %}
{% block body %}
Página principal de la aplicación
<br><br>
{% if error %}
<div>{{ error.messageKey|trans(error.messageData, 'security') }}</div>
{% endif %}
<form action="{{ path('entry_point') }}" method="post">
<label for="username">Nombre de usuario o correo electrónico:</label>
<input type="text" id="username" name="username" value="{{ last_username }}" />
<label for="password">Contraseña:</label>
<input type="password" id="password" name="password" />
<button type="submit">login</button>
</form>
{% endblock %}
The controller (adapted from one copied from the document) is like this:
<?php
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class DefaultController extends Controller
{
public function indexAction(Request $request, AuthenticationUtils $authUtils)
{
// get the login error if there is one
$error = $authUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authUtils->getLastUsername();
if ($this->getUser() === null) {
return $this->render('AppBundle::main_app.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
]);
}
$userRol = $this->getUser()->getRoles()[0];
if ($userRol == 'ROLE_ADMIN' || $userRol == 'ROLE_SUPER_ADMIN' || $userRol == 'ROLE_ROOT_ADMIN') {
$destination = 'admin_index';
} elseif ($userRol == 'ROLE_USER') {
$destination = 'user_index';
} else {
return $this->render('AppBundle::main_app.html.twig', [
'last_username' => $lastUsername,
'error' => $error,
]);
}
return $this->redirectToRoute($destination);
}
}
When I access users who are in_memory there is no problem. He identifies me, and redirects me correctly. However, if I try to access with a user of the BD, it throws the following exception: Type error: Argument 4 passed to Symfony \ Component \ Security \ Core \ Authentication \ Token \ UsernamePasswordToken :: __ construct () must be of the type array, given null, called in C: \ xampp \ htdocs \ base_project_dark_q \ vendor \ symfony \ symfony \ src \ Symfony \ Component \ Security \ Core \ Authentication \ Provider \ UserAuthenticationProvider.php online 94
As he only mentions files that are from Symfony itself, which are not the ones I created, I do not know where to start looking. It is supposed that the user should read, read his role (for relations between entities) and redirect me.
Does anyone know what I'm doing wrong? I'm a rookie at Symfony and any help will be appreciated.