Hola. Tengo una estructura básica de Symfony con dos entidades: User, para los usuarios y Role para los roles. Varios usuarios podrán tener un role (ManyToOne), y un role podrá estar asignado a varios usuarios (OneToMany). Además, tengo dos usuarios creados en configuración. El archivo security.yml incluye:
```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}```
El AppBundle\Entity\User es así:
```<?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. } }```
El AppBundle/Entity/Role es así: ```<?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; } }```
Además, tengo las relaciones definidas en yml. El AppBundle/Resources/config/doctrine/User.orm.yml es así:
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: { }
Y el AppBundle/Resources/config/doctrine/Role.orm.yml es así:
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: { }
El acceso se hace a través de una página con un formulario integrado, así:
```{% extends 'base.html.twig' %}
{% block body %}
Página principal de la aplicación
{% 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 %}```
El controlador (adaptado a partir de uno copiado de la docu) es así: ```<?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); }
}```
Cuando accedo con los usuarios que están in_memory no hay problema. Me lo identifica, y me redirecciona correctamente. Sin embargo, si intento acceder con un usuario de la BD, me lanza la siguiente excepción: Type error: Argument 4 passed to Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken::__construct() must be of the type array, null given, called in C:\xampp\htdocs\base_project_dark_q\vendor\symfony\symfony\src\Symfony\Component\Security\Core\Authentication\Provider\UserAuthenticationProvider.php on line 94
Como sólo menciona archivos que son del propio Symfony, que no son los que yo he creado, no sé por donde empezar a mirar. Se supone que debería leer el usuario, leer su rol (por las relaciones entre entidades) y redireccionarme.
¿Alguien sabe lo que estoy haciendo mal? Soy novato en Symfony y cualquier ayuda será agradecida.