When I submit my login form I get that error. I read here that this could be caused by trying to
deserialze object without loaded class for that object
however because the code is hidden away within symfony internals I can't work out why a class wouldn't be loaded in that instance. Profiler seems to say that no queries have been performed, yet if I put in the wrong credentials it says "invalid credentials" so it must be accessing the DB correctly.
User class
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity("email", message="This email is already in use.")
* #UniqueEntity("username", message="This username is already in use")
*/
class User implements UserInterface, \Serializable
{
private $roles = "ROLE_USER";
/**
* #ORM\Column(name="salt",type="string", length=255)
*/
private $salt = "saltyboye";
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(name="username",type="string", length=255, unique=true)
*/
private $username;
/**
* #ORM\Column(name = "password", type="string", length=255)
*/
private $password;
/**
* #ORM\Column(name="email", type="string", length=255, unique=true)
*/
private $email;
/**
* #ORM\Column(type="datetime")
*/
private $registeredOn;
/**
* #ORM\Column(type="integer", nullable=true)
*/
private $referrer;
/**
* #ORM\Column(type="smallint")
*/
private $entries;
/**
* #ORM\Column(type="string", length=3)
*/
private $currency;
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->registeredOn,
$this->id,
$this->email,
$this->username,
$this->password,
$this->roles,
$this->referrer,
$this->currency,
$this->entries,
$this->salt));
}
public function unserialize($serialized)
{
list (
$this->id,
$this->email,
$this->username,
$this->password,
$this->roles,
$this->referrer,
$this->currency,
$this->entries,
$this->salt) = unserialize($serialized, array('allowed_classes' => false));
}
public function eraseCredentials()
{
}
public function getRoles()
{
return array("ROLE_USER");
}
public function getSalt()
{
return $this->salt;
}
public function getId()
{
return $this->id;
}
public function getUsername(): ?string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getRegisteredOn(): ?\DateTimeInterface
{
return $this->registeredOn;
}
public function setRegisteredOn(\DateTimeInterface $registeredOn): self
{
$this->registeredOn = $registeredOn;
return $this;
}
public function getReferrer(): ?int
{
return $this->referrer;
}
public function setReferrer(?int $referrer): self
{
$this->referrer = $referrer;
return $this;
}
public function getEntries(): ?smallint
{
return $this->entries;
}
public function setEntries($entries): self
{
$this->entries = $entries;
return $this;
}
public function setCurrency($currency): self
{
$this->currency = $currency;
return $this;
}
public function getCurrency(): ?string
{
return $this->currency;
}
}
Security.yaml
security:
hide_user_not_found: false
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
encoders:
App\Entity\User: sha256
providers:
in_memory: { memory: ~ }
main_db_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
# anonymous: true
pattern: ^/ #test
anonymous: ~
form_login:
login_path: login
check_path: login
csrf_token_generator: security.csrf.token_manager
provider: main_db_provider
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/$, roles: ROLE_USER }
# - { path: ^/$, roles: ROLE_USER }
The serialize function doesnot match the unserialize function! You need to change it:
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->registeredOn,
$this->id,
$this->email,
$this->username,
$this->password,
$this->roles,
$this->referrer,
$this->currency,
$this->entries,
$this->salt));
}
public function unserialize($serialized)
{
list (
$this->registeredOn, # <--- look here it is missing
$this->id,
$this->email,
$this->username,
$this->password,
$this->roles,
$this->referrer,
$this->currency,
$this->entries,
$this->salt) = unserialize($serialized, array('allowed_classes' => false));
}
Related
I have next code:
security.yaml
security:
enable_authenticator_manager: true
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: 'auto'
providers:
app_user_provider:
entity:
class: App\Entity\User
property: login
firewalls:
login:
pattern: ~/api/user/login
stateless: true
json_login:
check_path: api_login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ~/api
stateless: true
jwt: ~
access_control:
- { path: ~/api/user, roles: PUBLIC_ACCESS }
- { path: ~/api, roles: IS_AUTHENTICATED_FULLY }
UserController.php
...
/**
* #Route("/api/user")
* #OA\Tag(name="[User] User Controller")
*/
class UserController extends AbstractController
{
...
#[Route('/login', name: 'api_login_check', methods: 'POST')]
public function getTokenUser(#[CurrentUser] ?User $user, JWTTokenManagerInterface $JWTManager): JsonResponse
{
if (null === $user) {
return $this->json([
'message' => 'missing credentials',
], Response::HTTP_UNAUTHORIZED);
}
return $this->json([
'user' => $user->getUserIdentifier(),
'token' => $JWTManager->create($user),
]);
}
}
User.php
<?php
namespace App\Entity;
use App\Repository\UserRepository;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;
#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: '`user`')]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private $id;
#[ORM\Column(type: 'string', length: 64)]
private $login;
#[ORM\Column(type: 'string', length: 255)]
private $password;
#[ORM\Column(type: 'boolean')]
private $teacher;
#[ORM\Column(type: 'string', length: 128, nullable: true)]
private $lessonStudy;
#[ORM\Column(type: 'integer', nullable: true)]
private $classNumber;
#[ORM\Column(type: 'string', length: 8, nullable: true)]
private $classChar;
#[ORM\Column(type: 'datetime')]
private $created_at;
public function __construct()
{
$this->created_at = new \DateTime();
$this->teacher = false;
}
public function getId(): ?int
{
return $this->id;
}
public function getLogin(): ?string
{
return $this->login;
}
public function setLogin(string $login): self
{
$this->login = $login;
return $this;
}
/**
* #see PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getTeacher(): ?bool
{
return $this->teacher;
}
public function setTeacher(bool $teacher): self
{
$this->teacher = $teacher;
return $this;
}
public function getLessonStudy(): ?string
{
return $this->lessonStudy;
}
public function setLessonStudy(?string $lessonStudy): self
{
$this->lessonStudy = $lessonStudy;
return $this;
}
public function getClassNumber(): ?int
{
return $this->classNumber;
}
public function setClassNumber(?int $classNumber): self
{
$this->classNumber = $classNumber;
return $this;
}
public function getClassChar(): ?string
{
return $this->classChar;
}
public function setClassChar(?string $classChar): self
{
$this->classChar = $classChar;
return $this;
}
public function getCreatedAt(): ?\DateTimeInterface
{
return $this->created_at;
}
public function setCreatedAt(\DateTimeInterface $created_at): self
{
$this->created_at = $created_at;
return $this;
}
/**
* #return array
*/
public function getRoles(): array
{
return array('ROLE_USER');
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
}
/**
* #return string
*/
public function getUserIdentifier(): string
{
return (string) $this->login;
}
/**
* Returning a salt is only needed, if you are not using a modern
* hashing algorithm (e.g. bcrypt or sodium) in your security.yaml.
*
* #see UserInterface
*/
public function getSalt(): ?string
{
return null;
}
}
When I try make request to /api/user/login -> get error from controller about bad creds.
curl --location --request POST 'http://127.0.0.1:8000/api/user/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"username": "cruiservlad",
"password": "pro100cruiser"
}'
What I did wrong?
I'm using a custom authenticator and a custom user provider in Symfony 5.0.10
Some of my users have complained that they can't login anymore : in fact in some cases, the login will be sucessful (onAuthenticationSuccess is called) but the user will still be anonymous. This causes a direct redirection to login page.
This is solved by clearing the cookies (PHPSESSID) or by using a private navigation window. I can't explain how thats comes into the login logic of an anonymous user.
If you guys can find the issue that would really help me, i've been spending a few all nighters on this and can't figure it out.
Here is my code :
security.yaml
security:
encoders:
App\Security\User:
algorithm: none
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
providers:
# used to reload user from session & other features (e.g. switch_user)
app_galette_user_provider:
id: App\Security\GaletteUserProvider
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: app_galette_user_provider
logout:
path: app_logout
guard:
authenticators:
- App\Security\AppCustomAuthenticator
# where to redirect after logout
# target: app_any_route
# activate different ways to authenticate
# https://symfony.com/doc/current/security.html#firewalls-authentication
# https://symfony.com/doc/current/security/impersonating_user.html
# switch_user: true
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: IS_AUTHENTICATED_FULLY }
Custom Authenticator (AppCustomAuthenticator.php)
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
use Symfony\Component\Security\Core\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
use Symfony\Component\Security\Guard\PasswordAuthenticatedInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class AppCustomAuthenticator extends AbstractFormLoginAuthenticator implements PasswordAuthenticatedInterface
{
private $urlGenerator;
private $csrfTokenManager;
private $passwordEncoder;
public function __construct(UrlGeneratorInterface $urlGenerator, UserPasswordEncoderInterface $passwordEncoder)
{
$this->urlGenerator = $urlGenerator;
$this->passwordEncoder = $passwordEncoder;
}
public function supports(Request $request)
{
return 'app_login' === $request->attributes->get('_route') && $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'username' => $request->request->get('email'),
'password' => $request->request->get('password'),
//'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['username']
);
return $credentials;
}
public function supportsRememberMe()
{
return false;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
// Load / create our user however you need.
// You can do this by calling the user provider, or with custom logic here.
try {
$user = $userProvider->loadUserByUsername($credentials['username']);
} catch (UsernameNotFoundException $e) {
throw new CustomUserMessageAuthenticationException("Erreur lors de la connexion : veuillez vérifier vos identifiants et l'état de votre cotisation.");
}
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Email could not be found.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
if ($credentials['password'] === $user->getPassword()) {
return true;
}
return false;
}
/**
* Used to upgrade (rehash) the user's password automatically over time.
*/
public function getPassword($credentials): ?string
{
return $credentials['password'];
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return new RedirectResponse($this->urlGenerator->generate('index'));
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate("app_login");
}
}
And my User.php entity :
<?php
namespace App\Security;
use Symfony\Component\Security\Core\User\EquatableInterface;
use Symfony\Component\Security\Core\User\UserInterface;
class User implements UserInterface, EquatableInterface
{
private $id;
private $email;
private $roles;
private $password;
private $nom;
private $prenom;
private $adresse;
private $adresse2;
private $cp;
private $ville;
private $pays;
private $tel;
private $gsm;
private $salt;
private $username;
public function isEqualTo(UserInterface $user)
{
if ($this->getUsername() !== $user->getUsername()) {
return false;
}
return true;
}
/**
* #return int
*/
public function getId(): int
{
return $this->id;
}
/**
* #param int $id
*/
public function setId($id): void
{
$this->id = $id;
}
/**
* #return string
*/
public function getNom(): string
{
return $this->nom;
}
/**
* #param string $nom
*/
public function setNom($nom): void
{
$this->nom = $nom;
}
/**
* #return string
*/
public function getPrenom(): string
{
return $this->prenom;
}
/**
* #param string $prenom
*/
public function setPrenom($prenom): void
{
$this->prenom = $prenom;
}
/**
* #return string
*/
public function getAdresse(): string
{
return $this->adresse;
}
/**
* #param string $adresse
*/
public function setAdresse($adresse): void
{
$this->adresse = $adresse;
}
/**
* #return string
*/
public function getAdresse2(): ?string
{
return $this->adresse2;
}
/**
* #param string $adresse2
*/
public function setAdresse2($adresse2): void
{
$this->adresse2 = $adresse2;
}
/**
* #return string
*/
public function getCp(): ?string
{
return $this->cp;
}
/**
* #param string $cp
*/
public function setCp($cp): void
{
$this->cp = $cp;
}
/**
* #return string
*/
public function getVille(): ?string
{
return $this->ville;
}
/**
* #param string $ville
*/
public function setVille($ville): void
{
$this->ville = $ville;
}
/**
* #return string
*/
public function getPays(): ?string
{
return $this->pays;
}
/**
* #param string $pays
*/
public function setPays($pays): void
{
$this->pays = $pays;
}
/**
* #return string
*/
public function getTel(): ?string
{
return $this->tel;
}
/**
* #param string $tel
*/
public function setTel($tel): void
{
$this->tel = $tel;
}
/**
* #return string
*/
public function getGsm(): ?string
{
return $this->gsm;
}
/**
* #param string $gsm
*/
public function setGsm($gsm): void
{
$this->gsm = $gsm;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return $this->username;
}
public function setUsername(string $username): self
{
$this->username = $username;
return $this;
}
/**
* #see UserInterface
*/
public function getRoles(): array
{
$roles = $this->roles;
// guarantee every user at least has ROLE_USER
$roles[] = 'ROLE_USER';
return array_unique($roles);
}
public function setRoles(array $roles): self
{
$this->roles = $roles;
return $this;
}
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* #see UserInterface
*/
public function getSalt()
{
return;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}
'''
-> I'm not including my custom user provider as i know it to work (it correctly returns the user)
-> My user passwords are indeed "in clear", this is a very specific scenario which poses no security threat
This may be caused by symfony session fixation protection.
It is enabled by default and should refresh session id after user authentication. More info in symfony docs
Check if the PHPSESSID cookie refreshes after EVERY request.
If it does, then your authenticator triggers this method refreshing session id on each user request.
Which leads to the following: if the user makes second request before they receive the response from the previous, their session id becomes invalid, and they become unauthenticated.
You can of course disable this protection in your security config:
security:
session_fixation_strategy: none
but better is to fix the problem and do not create a vulnerability in your system.
Following 4.1 docs, with a mash-up of the two User entities How to Load Security Users and How to Implement a Simple Registration Form, attempts to log in result in the following dev log entries:
security.INFO: User has been authenticated successfully...
security.DEBUG: Stored the security token in the session...
request.INFO: Matched route "home"...
security.DEBUG: Read existing security token from the session...
doctrine.DEBUG: SELECT t0.id AS id_1,...
security.DEBUG: Token was deauthenticated after trying to refresh it
User entity (includes this SO answer)
class User implements UserInterface, \Serializable, EquatableInterface
{
public function __construct()
{
$this->roles = array('ROLE_USER');
$this->isActive = true;
}
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(name="is_active", type="boolean")
*/
private $isActive;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Assert\NotBlank()
* #Assert\Email()
*/
private $email;
/**
* #ORM\Column(type="string", length=255, unique=true)
* #Assert\NotBlank()
*/
private $username;
/**
* #Assert\NotBlank()
* #Assert\Length(max=4096)
*/
private $plainPassword;
/**
* The below length depends on the "algorithm" you use for encoding
* the password, but this works well with bcrypt.
*
* #ORM\Column(type="string", length=64)
*/
private $password;
/**
* #ORM\Column(type="array")
*/
private $roles;
// other properties and methods
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
}
public function getUsername()
{
return $this->username;
}
public function setUsername($username)
{
$this->username = $username;
}
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 getRoles()
{
return $this->roles;
}
public function addRole($role)
{
$role = strtoupper($role);
if ($role === static::ROLE_DEFAULT) {
return $this;
}
if (!in_array($role, $this->roles, true)) {
$this->roles[] = $role;
}
return $this;
}
public function setActive($boolean)
{
$this->active = (bool) $boolean;
return $this;
}
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt,
));
}
/** #see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
// see section on salt below
// $this->salt
) = unserialize($serialized, ['allowed_classes' => false]);
}
public function isEqualTo(UserInterface $user)
{
if ($this->password !== $user->getPassword()) {
return false;
}
if ($this->username !== $user->getUsername()) {
return false;
}
return true;
}
}
security.yaml:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
db_provider:
entity:
class: App\Entity\User
property: username
firewalls:
main:
provider: db_provider
anonymous: ~
form_login:
login_path: login
check_path: login
default_target_path: home
access_control:
- { path: ^/login$, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/, roles: ROLE_USER }
Controller:
public function login(Request $request, AuthenticationUtils $authenticationUtils)
{
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('security/login.html.twig', array(
'last_username' => $lastUsername,
'error' => $error,
));
}
So after a long discussion it was determined that roles were not being set in the database. Attempts to set one in the constructor were doomed to failure since Doctrine does not use the constructor when hydrating from the database.
The proper solution is to correctly store the roles in the database. A bit of a hack is to do something like:
// User
getRoles() {
return count($roles) ? $roles : ['ROLE_USER'];
}
And if you look closely, the addRoles method in the question has an error probably because it was partially cloned from the FOSUserBundle. Always a dangerous thing to do.
I'm trying to build a login/register controller for users but i get Underfined class constant 'ROLE_DEFAULT' when I register new user.
In my UserController, I have this function to register new user:
//src/Controller/UserController
public function registerAction(Request $request, UserPasswordEncoderInterface $passwordEncoder)
{
// 1) build the form
$user = new User();
$form = $this->createForm(UserType::class, $user);
// 2) handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// 3) Encode the password (you could also do this via Doctrine listener)
$password = $passwordEncoder->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
$apiKey = $passwordEncoder->encodePassword($user, rand(1,9999));
$user->setApiKey($apiKey);
$user->setEnabled(1);
// $user->setSuperadmin(true);
$user->addRole("ROLE_DEFAULT");
// 4) save the User!
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
dump($user);
exit;
}
return $this->render(
'user/register.html.twig',
array('form' => $form->createView())
);
}
The error appear when "$user->addRole("ROLE_DEFAULT");" and this is the screenshot:
//src/Entity/User:
namespace App\Entity;
use Symfony\Component\Security\Core\User\UserInterface;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="user")
*/
class User implements UserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", unique=true)
*/
private $username;
/**
* #ORM\Column(name="lastname", type="string", nullable=true)
*/
protected $lastname;
/**
* #ORM\Column(type="string", unique=true)
*/
private $apiKey;
/**
* #ORM\Column(name="enabled", type="boolean")
*/
protected $enabled;
/**
* #ORM\Column(name="email", type="string", nullable=false)
*/
protected $email;
/**
* #ORM\Column(name="SUPERADMIN", type="boolean", nullable=true)
*/
protected $superadmin;
/**
* The salt to use for hashing
*
* #ORM\Column(name="salt", type="string", nullable=true)
*/
protected $salt;
/**
* Encrypted password. Must be persisted.
*
* #ORM\Column(name="password", type="string", nullable=false)
*/
protected $password;
/**
* Encrypted password. Must be persisted.
*
* #ORM\Column(name="plainPassword", type="string", nullable=false)
*/
protected $plainPassword;
/**
* #var \DateTime
*/
protected $lastLogin;
/**
* Random string sent to the user email address in order to verify it
*
* #var string
*/
protected $confirmationToken;
/**
* #var \DateTime
*/
protected $passwordRequestedAt;
public function __construct()
{
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
$this->enabled = false;
// $this->locked = false;
$this->roles = array();
}
public function __toString()
{
return (string) $this->getUsername();
}
public function getId()
{
return $this->id;
}
// USERNAME//
//===========================================================
public function setUsername($username)
{
$this->username = $username;
return $this;
}
public function getUsername()
{
return $this->username;
}
// EMAIL//
//===========================================================
public function getEmail()
{
return $this->email;
}
public function setEmail($email)
{
$this->email = $email;
return $this;
}
// API KEY//
//===========================================================
public function getApiKey()
{
return $this->apiKey;
}
public function setApiKey($apiKey)
{
return $this->apiKey = $apiKey;
}
// PASSWORD//
//===========================================================
/**
* Gets the encrypted password.
*
* #return string
*/
public function getPassword()
{
return $this->password;
}
public function setPassword($password)
{
$this->password = $password;
return $this;
}
/**
* Gets the encrypted password.
*
* #return string
*/
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
// SALT//
//===========================================================
public function getSalt()
{
return $this->salt;
}
public function setSalt($salt)
{
return $this->salt = $salt;
}
// LAST LOGIN//
//===========================================================
/**
* Gets the last login time.
*
* #return \DateTime
*/
public function getLastLogin()
{
return $this->lastLogin;
}
public function setLastLogin(\DateTime $time = null)
{
$this->lastLogin = $time;
return $this;
}
// CONFIRMATION TOKEN//
//===========================================================
public function getConfirmationToken()
{
return $this->confirmationToken;
}
public function setConfirmationToken($confirmationToken)
{
$this->confirmationToken = $confirmationToken;
return $this;
}
/**
* Removes sensitive data from the user.
*/
public function eraseCredentials()
{
$this->plainPassword = null;
}
// ROLES//
//===========================================================
// public function getRoles()
// {
// return array('ROLE_USER');
// }
/**
* Returns the user roles
*
* #return array The roles
*/
public function getRoles()
{
$roles = array();
$nameRole = $this->idRole->getNamerole();
$stationCreate = $this->idRole->getStationCreate();
$configurationstationEdit = $this->idRole->getConfigurationstationEdit();
$stationEditor = $this->idRole->getDatastationEdit();
$stationValidate = $this->idRole->getStationValidate();
$newdataCreate = $this->idRole->getNewdataCreate();
$superadmin = $this->superadmin;
if($stationCreate){
$roles = array_merge($roles, array('ROLE_CREATOR'));
}
if($configurationstationEdit){
$roles = array_merge($roles, array('ROLE_CONFIGURATOR'));
}
if($stationEditor){
$roles = array_merge($roles, array('ROLE_EDITOR'));
}
if($stationValidate){
$roles = array_merge($roles, array('ROLE_VALIDATOR'));
}
if($newdataCreate){
$roles = array_merge($roles, array('ROLE_MANAGE'));
}
if($superadmin){
$roles = array_merge($roles, array('ROLE_SUPER_ADMIN'));
}
//echo $nameRole." -- ".$stationCreate." -- ".$configurationstationEdit." -- ".$stationValidate." -- ".$newdataCreate;
//exit;
//dump($this->idRole);
//exit;
// we need to make sure to have at least one role
$roles[] = static::ROLE_DEFAULT;
return array_unique($roles);
}
public function setRoles(array $roles)
{
$this->roles = array();
foreach ($roles as $role) {
$this->addRole($role);
}
return $this;
}
public function removeRole($role)
{
if (false !== $key = array_search(strtoupper($role), $this->roles, true)) {
unset($this->roles[$key]);
$this->roles = array_values($this->roles);
}
return $this;
}
public function hasRole($role)
{
return in_array(strtoupper($role), $this->getRoles(), true);
}
public function addRole($role)
{
$role = strtoupper($role);
if ($role === static::ROLE_DEFAULT) {
return $this;
}
if (!in_array($role, $this->roles, true)) {
$this->roles[] = $role;
}
return $this;
}
// ENABLED//
//===========================================================
public function isEnabled()
{
return $this->enabled;
}
public function setEnabled($boolean)
{
$this->enabled = (Boolean) $boolean;
return $this;
}
// SUPERADMIN//
//===========================================================
public function isSuperAdmin()
{
return $this->hasRole(static::ROLE_SUPER_ADMIN);
}
public function setSuperAdmin($boolean)
{
if (true === $boolean) {
$this->addRole(static::ROLE_SUPER_ADMIN);
} else {
$this->removeRole(static::ROLE_SUPER_ADMIN);
}
return $this;
}
// PASSWORD REQUEST AT//
//===========================================================
/**
* Gets the timestamp that the user requested a password reset.
*
* #return null|\DateTime
*/
public function getPasswordRequestedAt()
{
return $this->passwordRequestedAt;
}
public function isPasswordRequestNonExpired($ttl)
{
return $this->getPasswordRequestedAt() instanceof \DateTime &&
$this->getPasswordRequestedAt()->getTimestamp() + $ttl > time();
}
// EXTRA FUNCTIONS//
//===========================================================
/**
* Serializes the user.
*
* The serialized data have to contain the fields used during check for
* changes and the id.
*
* #return string
*/
public function serialize()
{
return serialize(array(
$this->password,
$this->salt,
$this->username,
$this->enabled,
$this->id,
$this->email,
));
}
/**
* Unserializes the user.
*
* #param string $serialized
*/
public function unserialize($serialized)
{
$data = unserialize($serialized);
// add a few extra elements in the array to ensure that we have enough keys when unserializing
// older data which does not include all properties.
$data = array_merge($data, array_fill(0, 2, null));
list(
$this->password,
$this->salt,
$this->username,
$this->enabled,
$this->id,
$this->email,
) = $data;
}
}
And the security.yml file
//config/packages/security.yml
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
in_memory: { memory: ~ }
user:
entity:
class: App\Entity\User
# property: apiKey
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
pattern: ^/
http_basic: ~
provider: user
# activate different ways to authenticate
# http_basic: true
# https://symfony.com/doc/current/security.html#a-configuring-how-your-users-will-authenticate
# form_login: true
# https://symfony.com/doc/current/security/form_login_setup.html
# form_login:
# login_path: security_login
# check_path: security_login
# csrf_token_generator: security.csrf.token_manager
# default_target_path: userRedirectAction
# Easy way to control access for large sections of your site
# Note: Only the *first* access control that matches will be used
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/user, roles: ROLE_USER }
I want to know what I'm doing wrong or where can i find a good guide to build a login/register system in SYMFONY 4.
Thank you.
The first thing that I noticed is use of static to (presumably) get constant. If that was the intention, it is not right.
See this comment of PHP constatnt official docs to get a grasp of what could go wrong:
PHP Constant - self/static
But, regardless, what I could not see was the constant itself defined within the User class, which I saw belonged to your namespace.
So, in order to use it, please define it like:
class User
{
const ROLE_DEFAULT = "ROLE_DEFAULT";
// ....
// The rest of your code
}
And then use it like:
if ($role === self::ROLE_DEFAULT) {
return $this;
}
Though, I am not sure why would you attempt to add that role in your controller at the first place, however, I leave to you to you to decide.
Hope this helps a bit...
I'm new here and I'm from abroad, so sorry for my mistakes in English. Okey, so I have problem with login system in my first application in Symfony. Before I was writing some simple apps in Laravel. But here, I don't know what's wrong. I made new bundle and my login system stop working. I can put data in form, but when I pass it to authorization, website redirects me to login page and I'm not logged in. This is my code:
Controller:
<?php
// src/AppBundle/Controller/RegistrationController.php
namespace AppBundle\Controller;
use AppBundle\Form\UserType;
use AppBundle\Entity\User;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
class RegistrationController extends Controller
{
/**
* #Route("/register", name="user_registration")
*/
public function registerAction(Request $request)
{
// 1) build the form
$user = new User();
$form = $this->createForm(UserType::class, $user);
// 2) handle the submit (will only happen on POST)
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// 3) Encode the password (you could also do this via Doctrine listener)
$password = $this->get('security.password_encoder')
->encodePassword($user, $user->getPlainPassword());
$user->setPassword($password);
// 4) save the User!
$em = $this->getDoctrine()->getManager();
$em->persist($user);
$em->flush();
// ... do any other work - like sending them an email, etc
// maybe set a "flash" success message for the user
return $this->redirectToRoute('homepage');
}
return $this->render(
'registration/registration.html.twig',
array('form' => $form->createView())
);
}
/**
* #Route("/login", name="user_login")
*/
public function loginAction(Request $request) {
$authenticationUtils = $this->get('security.authentication_utils');
$error = $authenticationUtils->getLastAuthenticationError();
$lastUsername = $authenticationUtils->getLastUsername();
return $this->render('registration/login.html.twig', array(
'last_username' => $lastUsername,
'error' => $error,
));
}
/**
* #Route("/login_check", name="user_login_check")
*/
public function loginCheckAction() {
throw new \Exception('This should never be reached!');
}
/**
* #Route("/logout", name="user_logout")
*/
public function logoutAction()
{
throw new \Exception('This should never be reached!');
}
/**
* #Route("/login_failed", name="user_login_fail")
*/
public function loginError() {
return new Response('ERROR');
}
}
security.yml
# To get started with security, check out the documentation:
# http://symfony.com/doc/current/book/security.html
security:
encoders:
# Our user class and the algorithm we'll use to encode passwords
# http://symfony.com/doc/current/book/security.html#encoding-the-user-s-password
AppBundle\Entity\User: bcrypt
# http://symfony.com/doc/current/book/security.html#where-do-users-come-from-user-providers
providers:
our_db_provider:
entity:
class: AppBundle:User
property: username
firewalls:
default:
anonymous: ~
http_basic: ~
provider: our_db_provider
form_login:
login_path: user_login
check_path: user_login_check
failure_path: user_login_fail
logout:
path: user_logout
target: homepage
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
access_control:
- { path: ^/admin, roles: ROLE_ADMIN}
# activate different ways to authenticate
# http_basic: ~
# http://symfony.com/doc/current/book/security.html#a-configuring-how-your-users-will-authenticate
# form_login: ~
# http://symfony.com/doc/current/cookbook/security/form_login_setup.html
User Entity
<?php
namespace AppBundle\Entity;
use Symfony\Component\Validator\Constraints as Assert;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\AdvancedUserInterface;
/**
* User
*/
class User implements AdvancedUserInterface, \Serializable
{
/**
* #var int
*/
private $id;
/**
* #var string
*/
private $username;
/**
* #var string
*/
private $password;
/**
* #var string
*/
private $email;
/**
* #var bool
*/
private $isActive;
/**
* #var string
*/
private $plainPassword;
public function __construct() {
$this->setIsActive(TRUE);
}
/**
* Get id
*
* #return int
*/
public function getId()
{
return $this->id;
}
/**
* 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 isActive
*
* #param boolean $isActive
*
* #return User
*/
public function setIsActive($isActive)
{
$this->isActive = $isActive;
return $this;
}
/**
* Get isActive
*
* #return bool
*/
public function getIsActive()
{
return $this->isActive;
}
public function getRoles()
{
return array('ROLE_USER');
}
public function eraseCredentials()
{
}
public function serialize()
{
return serialize(array(
$this->id,
$this->username,
$this->password,
));
}
public function unserialize($serialized)
{
list (
$this->id,
$this->username,
$this->password,
) = unserialize($serialized);
}
public function getSalt()
{
// The bcrypt algorithm doesn't require a separate salt.
// You *may* need a real salt if you choose a different encoder.
return null;
}
public function getPlainPassword()
{
return $this->plainPassword;
}
public function setPlainPassword($password)
{
$this->plainPassword = $password;
}
public function isAccountNonExpired()
{
return true;
}
public function isAccountNonLocked()
{
return true;
}
public function isCredentialsNonExpired()
{
return true;
}
public function isEnabled()
{
return $this->isActive;
}
}
Thanks for your help.