Error
Catchable Fatal Error: Argument 2 passed to Acme\StoreBundle\Security\TokenAuthenticator::__construct() must be an instance of Doctrine\Common\EventManager, instance of Doctrine\Bundle\MongoDBBundle\ManagerRegistry given, called in D:\xamppNew\htdocs\mtl_project\var\cache\dev\appDevDebugProjectContainer.php on line 6178 and defined
TokenAuthenticator.php
<?php
namespace Acme\StoreBundle\Security;
use Doctrine\Common\EventManager;
use Doctrine\Common\Persistence\ObjectManager;
use Doctrine\Common\Persistence\ObjectRepository;
use Doctrine\MongoDB\Connection;
use Doctrine\ODM\MongoDB\Mapping\ClassMetadataInfo;
use Doctrine\ODM\MongoDB\Mapping\MappingException;
use Doctrine\ODM\MongoDB\Hydrator\HydratorFactory;
use Doctrine\ODM\MongoDB\Proxy\ProxyFactory;
use Doctrine\ODM\MongoDB\Query\FilterCollection;
use Doctrine\ODM\MongoDB\Repository\RepositoryFactory;
//use Acme\StoreBundle\Security\TokenAuthenticator;
use Lexik\Bundle\JWTAuthenticationBundle\Encoder\JWTEncoderInterface;
use Lexik\Bundle\JWTAuthenticationBundle\TokenExtractor\AuthorizationHeaderTokenExtractor;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
class TokenAuthenticator extends AbstractGuardAuthenticator
{
/**
* #var JWTEncoderInterface
*/
private $jwtEncoder;
/**
* #var Doctrine\Common\Persistence\ObjectManager
*/
protected $om;
/**
* #param JWTEncoderInterface $jwtEncoder
* #param ObjectManager $om
*/
public function __construct(JWTEncoderInterface $jwtEncoder, Doctrine\Common\Persistence\ObjectManager $om)
{
$this->jwtEncoder = $jwtEncoder;
$this->om = $om;
}
/**
* #inheritdoc
*/
public function getCredentials(Request $request)
{
$extractor = new AuthorizationHeaderTokenExtractor(
'Bearer',
'Authorization'
);
$token = $extractor->extract($request);
if (!$token) {
return;
}
return $token;
}
/**
* #inheritdoc
*/
public function getUser($credentials, UserProviderInterface $userProvider)
{
$data = $this->jwtEncoder->decode($credentials);
if ($data === false) {
throw new CustomUserMessageAuthenticationException('Invalid Token');
}
$username = $data['username'];
return $this->om
->getRepository('UserBundle:User')
->findOneBy(['username' => $username]);
}
/**
* #inheritdoc
*/
public function checkCredentials($credentials, UserInterface $user)
{
return true;
}
/**
* #inheritdoc
*/
public function onAuthenticationFailure(Request $request, AuthenticationException $exception)
{
}
/**
* #inheritdoc
*/
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
}
/**
* #inheritdoc
*/
public function supportsRememberMe()
{
return false;
}
/**
* #inheritdoc
*/
public function start(Request $request, AuthenticationException $authException = null)
{
return new Response('Token is missing!', Response::HTTP_UNAUTHORIZED);
}
}
Refrence
Difference between ObjectManager and EntityManager in Symfony2?
https://github.com/doctrine/mongodb-odm/blob/785c5039d51923d22902fa1249d1e3dd64018838/lib/Doctrine/ODM/MongoDB/DocumentManager.php#L44
im new in symfonymongodb bundle
can anyone suggest how can i use object manager in contructor as symfony is throwing errors.
doctrine_mongodb is a service that returns a Doctrine\Bundle\MongoDBBundle\ManagerRegistry object. You can get ObjectManager by calling getManager from it.
<?php
namespace Acme\StoreBundle\Security;
use Doctrine\Bundle\MongoDBBundle\ManagerRegistry;
// ...
class TokenAuthenticator extends AbstractGuardAuthenticator
{
/**
* #var Doctrine\Common\Persistence\ObjectManager
*/
protected $om;
// ...
public function __construct(JWTEncoderInterface $jwtEncoder, ManagerRegistry $registry)
{
// ...
$this->om = $registry->getManager();
}
Related
I'm developing my first Symfony project and so far pretty good except for the Login since the warning "invalid credentials" started popping up everytime I try to log in and don't really know why because I'm using AbstractLoginFormAuthenticator instead of AbstractFormLoginAuthenticator (the one I see the most), which is driving me a bit crazy because there is not much info.
User entity:
<?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)
* #method string getUserIdentifier()
*/
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
/**
* #ORM\Id
* #ORM\GeneratedValue
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=180, unique=false)
*/
private $name;
/**
* #ORM\Column(type="string", length=180, unique=true)
*/
private $email;
/**
* #ORM\Column(type="json")
*/
private $roles = [];
/**
* #var string The hashed password
* #ORM\Column(type="string")
*/
private $password;
/**
* #ORM\OneToMany(targetEntity="Booking", mappedBy="user")
*/
private $userBooking;
public function getId(): ?int
{
return $this->id;
}
/**
* #return mixed
*/
public function getName()
{
return $this->name;
}
/**
* #param mixed $name
*/
public function setName($name): void
{
$this->name = $name;
}
public function getEmail(): ?string
{
return $this->email;
}
/**
* #param mixed $email
*/
public function setEmail($email): void
{
$this->email = $email;
}
/**
* A visual identifier that represents this user.
*
* #see UserInterface
*/
public function getUsername(): string
{
return (string) $this->email;
}
/**
* #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 PasswordAuthenticatedUserInterface
*/
public function getPassword(): string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
/**
* 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;
}
/**
* #see UserInterface
*/
public function eraseCredentials()
{
// If you store any temporary, sensitive data on the user, clear it here
// $this->plainPassword = null;
}
}
Security controller:
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="app_login")
*/
public function login(AuthenticationUtils $authenticationUtils): Response
{
// if ($this->getUser()) {
// return $this->redirectToRoute('target_path');
// }
// 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', ['last_username' => $lastUsername, 'error' => $error]);
}
/**
* #Route("/logout", name="app_logout")
*/
public function logout()
{
throw new \LogicException('This method can be blank - it will be intercepted by the logout key on your firewall.');
}
}
LoginFormAuthenticator:
<?php
namespace App\Security;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Http\Authenticator\AbstractLoginFormAuthenticator;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\CsrfTokenBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
use Symfony\Component\Security\Http\Authenticator\Passport\Credentials\PasswordCredentials;
use Symfony\Component\Security\Http\Authenticator\Passport\Passport;
use Symfony\Component\Security\Http\Authenticator\Passport\PassportInterface;
use Symfony\Component\Security\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractLoginFormAuthenticator
{
use TargetPathTrait;
public const LOGIN_ROUTE = 'app_login';
private UrlGeneratorInterface $urlGenerator;
public function __construct(UrlGeneratorInterface $urlGenerator)
{
$this->urlGenerator = $urlGenerator;
}
public function authenticate(Request $request): PassportInterface
{
$email = $request->request->get('email', '');
$request->getSession()->set(Security::LAST_USERNAME, $email);
return new Passport(
new UserBadge($email),
new PasswordCredentials($request->request->get('password', '')),
[
new CsrfTokenBadge('authenticate', $request->get('_csrf_token')),
]
);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, string $firewallName): ?Response
{
if ($targetPath = $this->getTargetPath($request->getSession(), $firewallName)) {
return new RedirectResponse($targetPath);
}
// For example:
//return new RedirectResponse($this->urlGenerator->generate('some_route'));
//throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
return new RedirectResponse('home');
}
protected function getLoginUrl(Request $request): string
{
return $this->urlGenerator->generate(self::LOGIN_ROUTE);
}
}
Create User:
<?php
namespace App\Controller;
use App\Entity\User;
use App\Form\UserTypeUser;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class NewUserController extends AbstractController
{
#[Route('/newuser', name: 'new_user', methods: ['GET', 'POST'])]
public function index(Request $request): Response
{
$user = new User();
$form = $this->createForm(UserTypeUser::class, $user);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$user->setRoles(['ROLE_USER']);
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
return $this->redirectToRoute('home');
}
return $this->render('new_user/index.html.twig', [
'user' => $user,
'form' => $form->createView(),
]);
}
}
Thanks a lot in advance.
In your NewUserController you need to hash the password before you persist the user.
First you need to inject the service into the controller:
private $passwordHasher;
public function __construct(UserPasswordHasherInterface $passwordHasher)
{
$this->passwordHasher = $passwordHasher;
}
Then in your action you need to add one line inside the if condition:
if ($form->isSubmitted() && $form->isValid()) {
$user->setRoles(['ROLE_USER']);
$user->setPassword($this->passwordHasher->hashPassword($user, $user->getPassword());
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
return $this->redirectToRoute('home');
}
This assumes that you already set the password in your form, but it's not properly encoded yet. You can find this in the docs here: https://symfony.com/doc/current/security.html#c-hashing-passwords
Be careful, prior to Symfony 5.3 the password hasher was called password encoder and lived inside the security component. The code is fundamentally the same, but the class looks a bit different. See: https://symfony.com/doc/5.2/security.html#c-encoding-passwords
I am trying to encode a password with the encodePassword function from Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface; inside my migration here is my code
I tried with implements UserPasswordEncoderInterface but i did not manage to use this method encodePassword
The error is
Call to a member function encodePassword() on null
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
use Ramsey\Uuid\Uuid;
use App\Entity\User;
/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version20201112164728 extends AbstractMigration
{
/**
* #var UserPasswordEncoderInterface
*/
private $encoder;
public function getPassword($password)
{
$user = new User();
return $this->encoder->encodePassword($user,$password);
}
public function getDescription(): string
{
return '';
}
public function up(Schema $schema): void
{
}
public function postUp(Schema $schema): void
{
$this->connection->insert('users', [
'id' => Uuid::uuid4(),
'email' => 'xxx#xxx.com',
'password' => $this->getPassword('foo'),
]);
}
public function down(Schema $schema): void
{
}
}
hi im trying to encode My password for My app user So i tried to encrypted in my setPassword function
unfortunately i get this error that i don't understand: Call to a member function encodePassword() on null error pic
<?php
namespace App\Entity;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use Gedmo\Mapping\Annotation as Gedmo;
use Vich\UploaderBundle\Mapping\Annotation as Vich;
use Symfony\Component\HttpFoundation\File\File;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* Admin
*#Vich\Uploadable
* #ORM\Table(name="admin")
* #ORM\Entity
*/
class Admin implements UserInterface
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* Undocumented variable
*
* #var UserPasswordEncoderInterface
*/
private $passwordEncoder ;
/**
* #see UserInterface
*/
public function getPassword(): string
{
return (string) $this->password;
}
public function setPassword(string $password): self
{
$hash= $this->passwordEncoder->encodePassword($this,$password);
$this->password=$hash;
return $this ;
}
.......
whats wrong, and how can i fix it ! thnx
but this one work
<?php
namespace App\Controller\Admin;
use App\Entity\User;
use EasyCorp\Bundle\EasyAdminBundle\Config\KeyValueStore;
use EasyCorp\Bundle\EasyAdminBundle\Context\AdminContext;
use EasyCorp\Bundle\EasyAdminBundle\Dto\EntityDto;
use EasyCorp\Bundle\EasyAdminBundle\Field\Field;
use EasyCorp\Bundle\EasyAdminBundle\Field\FormField;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
use Symfony\Component\Form\Extension\Core\Type\RepeatedType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class UserCrudController extends AbstractCrudController
{
/** #var UserPasswordEncoderInterface */
private $passwordEncoder;
public static function getEntityFqcn(): string
{
return User::class;
}
public function configureFields(string $pageName): iterable
{
return [
FormField::addPanel('Change password')->setIcon('fa fa-key'),
Field::new('plainPassword', 'New password')->onlyOnForms()
->setFormType(RepeatedType::class)
->setFormTypeOptions([
'type' => PasswordType::class,
'first_options' => ['label' => 'New password'],
'second_options' => ['label' => 'Repeat password'],
]),
];
}
public function createEditFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface
{
$formBuilder = parent::createEditFormBuilder($entityDto, $formOptions, $context);
$this->addEncodePasswordEventListener($formBuilder);
return $formBuilder;
}
public function createNewFormBuilder(EntityDto $entityDto, KeyValueStore $formOptions, AdminContext $context): FormBuilderInterface
{
$formBuilder = parent::createNewFormBuilder($entityDto, $formOptions, $context);
$this->addEncodePasswordEventListener($formBuilder);
return $formBuilder;
}
/**
* #required
*/
public function setEncoder(UserPasswordEncoderInterface $passwordEncoder): void
{
$this->passwordEncoder = $passwordEncoder;
}
protected function addEncodePasswordEventListener(FormBuilderInterface $formBuilder)
{
$formBuilder->addEventListener(FormEvents::SUBMIT, function (FormEvent $event) {
/** #var User $user */
$user = $event->getData();
if ($user->getPlainPassword()) {
$user->setPassword($this->passwordEncoder->encodePassword($user, $user->getPlainPassword()));
}
});
}
}
i tried to add an Event listener so i create this class
<?php
namespace App\Controller\Admin;
use App\Entity\Admin;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
use EasyCorp\Bundle\EasyAdminBundle\EventListener\AdminContextListener;
class AdminController extends AdminContextListener
{
/**
* #var UserPasswordEncoderInterface
*/
private $encoder ;
public function __construct(UserPasswordEncoderInterface $encoder)
{
$this->encoder=$encoder;
}
public static function getSetPasswordEvent()
{
return [
BeforeEntityPersistedEvent::class => ['setPassword'],
];
}
public function setPassword(BeforeEntityPersistedEvent $event)
{
$entity = $event->getEntityInstance();
if (!($entity instanceof Admin)) {
return;
}
$encoded = $this->encoder->encodePassword($entity, $entity->getPassword());
$entity->setPassword($encoded);
}
}
and it didn't work too
You should do that when User register, so add this before doing flush(); for new user:
$user = new UserEntity();
$user->setEmail($request->getEmail());
if ($request->getPassword())
{
$createUser->setPassword($this->encoder->encodePassword($user, $request->getPassword()));
}
$this->entityManager->persist($createUser);
$this->entityManager->flush();
$this->entityManager->clear();
Notice: $request containe payload comes form frontend {"email": "", "passwprd": ""}.
Notice: $createUser is a user object to flush.
Have followed the instructions on: https://symfony.com/doc/current/security/form_login_setup.html, and in step 3 (in the onAuthenticationSuccess method) - if I leave the Exception in, the profiler bar shows the user logged in, however if I comment in the redirect, the user is lost on the following page. Sessions are set up and working as pdo.
Anyone have any ideas?
LoginFormAuthenticator.php
<?php
namespace App\Security;
use App\Entity\User;
use Doctrine\ORM\EntityManagerInterface;
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\Exception\CustomUserMessageAuthenticationException;
use Symfony\Component\Security\Core\Exception\InvalidCsrfTokenException;
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\Http\Util\TargetPathTrait;
class LoginFormAuthenticator extends AbstractFormLoginAuthenticator
{
use TargetPathTrait;
private $entityManager;
private $urlGenerator;
private $csrfTokenManager;
public function __construct(EntityManagerInterface $entityManager, UrlGeneratorInterface $urlGenerator, CsrfTokenManagerInterface $csrfTokenManager)
{
$this->entityManager = $entityManager;
$this->urlGenerator = $urlGenerator;
$this->csrfTokenManager = $csrfTokenManager;
}
public function supports(Request $request)
{
return 'app_login' === $request->attributes->get('_route')
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'email' => $request->request->get('email'),
'password' => $request->request->get('password'),
'csrf_token' => $request->request->get('_csrf_token'),
];
$request->getSession()->set(
Security::LAST_USERNAME,
$credentials['email']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$token = new CsrfToken('authenticate', $credentials['csrf_token']);
if (!$this->csrfTokenManager->isTokenValid($token)) {
throw new InvalidCsrfTokenException();
}
$user = $this->entityManager->getRepository(User::class)->findOneBy(['email' => $credentials['email']]);
if (!$user) {
// fail authentication with a custom error
throw new CustomUserMessageAuthenticationException('Email could not be found.');
}
return $user;
}
public function checkCredentials($credentials, UserInterface $user)
{
// Check the user's password or other credentials and return true or false
// If there are no credentials to check, you can just return true
return true;
//throw new \Exception('TODO: check the credentials inside '.__FILE__);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
return new RedirectResponse($targetPath);
}
//throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
return new RedirectResponse($this->urlGenerator->generate('app_dashboard'));
}
protected function getLoginUrl()
{
return $this->urlGenerator->generate('app_login');
}
}
DashboardController.php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class DashboardController extends AbstractController
{
private $session;
function __construct(SessionInterface $session)
{
$this->session = $session;
}
/**
* #Route("/dashboard", name="app_dashboard")
*/
function dashboard()
{
return $this->render('account/dashboard.html.twig', []);
}
}
security.yaml
security:
providers:
users:
entity:
class: 'App\Entity\User'
property: 'email'
encoders:
App\Entity\User:
algorithm: 'auto'
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
guard:
authenticators:
- App\Security\LoginFormAuthenticator
logout:
path: app_logout
access_control:
- { path: ^/dashboard, roles: ROLE_USER }
User.php
<?php
namespace App\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\UserInterface;
/**
* User
*
* #ORM\Table(name="user")
* #ORM\Entity(repositoryClass="App\Repository\UserRepository")
* #UniqueEntity(fields={"email"}, message="There is already an account with this email")
*/
class User implements UserInterface, \Serializable
{
/**
* #var int
*
* #ORM\Column(name="id", type="integer", nullable=false)
* #ORM\Id
* #ORM\GeneratedValue(strategy="IDENTITY")
*/
private $id;
/**
* #var string
*
* #ORM\Column(name="email", type="string", length=2048, nullable=true)
*/
private $email;
/**
* #var string
*
* #ORM\Column(name="password", type="string", length=4096, nullable=false)
*/
private $password;
/**
* #var string
*
* #ORM\Column(name="salt", type="string", length=2048, nullable=true)
*/
private $salt;
////////
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getSalt(): ?string
{
return $this->salt;
}
public function setSalt(string $salt): self
{
$this->salt = $salt;
return $this;
}
/**
* #inheritDoc
*/
public function getUsername()
{
return $this->email;
}
/**
* #inheritDoc
*/
public function getRoles()
{
return array('ROLE_USER');
}
/**
* #inheritDoc
*/
public function eraseCredentials()
{
}
/**
* #inheritDoc
*/
public function equals(UserInterface $user)
{
return $this->id === $user->getId();
}
/**
* #see \Serializable::serialize()
*/
public function serialize()
{
return serialize(array(
$this->id,
));
}
/**
* #see \Serializable::unserialize()
*/
public function unserialize($serialized)
{
list (
$this->id,
) = unserialize($serialized);
}
public function isEqualTo(UserInterface $user)
{
if (!$user instanceof User) {
return false;
}
if ($this->password !== $user->getPassword()) {
return false;
}
if ($this->salt !== $user->getSalt()) {
return false;
}
if ($this->email !== $user->getEmail()) {
return false;
}
return true;
}
}
Edit..
When the exception is commented in, the logs say:
INFO
19:32:37
request Matched route "app_login".
Show context
INFO
19:32:38
security Guard authentication successful!
Show context
CRITICAL
19:32:38
request Uncaught PHP Exception Exception: "TODO: provide a valid redirect inside /var/www/src/Security/LoginFormAuthenticator.php" at /var/www/src/Security/LoginFormAuthenticator.php line 87
And when the redirect is there the logs say:
19:30:24
security Checking for guard authentication credentials.
Hide context
[▼
"firewall_key" => "main"
"authenticators" => 1
]
19:30:24
security Checking support on guard authenticator.
Hide context
[▼
"firewall_key" => "main"
"authenticator" => "App\Security\LoginFormAuthenticator"
]
19:30:24
security Guard authenticator does not support the request.
Hide context
[▼
"firewall_key" => "main"
"authenticator" => "App\Security\LoginFormAuthenticator"
]
..edit - and same behaviour in both http & https.
I wanted to leave a comment for you, but my reputation is less than 50 so I'll write my comment in the answer.
The support method is deprecated.
Don't use it anymore.
Use the following example:
LoginFormAuthenticator.php
public function getCredentials(Request $request)
{
$isLoginSubmit = 'user_login' === $request->attributes->get('_route') && $request->isMethod('POST');
if(!$isLoginSubmit) {
return;
}
//...other codes
}
protected function getDefaultSuccessRedirectUrl()
{
return $this->urlGenerator->generate('user_dashboard');
}
UserController.php
<?php
namespace AppBundle\Controller\User;
use AppBundle\Form\User\UserManager\UserLoginForm;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\Routing\Annotation\Route;
/**
* User controller.
*
* #Route("User")
*/
class UserController extends Controller
{
/**
* #Route("/login", name="user_login")
*/
public function loginAction()
{
$authenticationUtils = $this->get('security.authentication_utils');
// get the login error if there is one
$error = $authenticationUtils->getLastAuthenticationError();
// last username entered by the user
$lastUsername = $authenticationUtils->getLastUsername();
$form = $this->createForm(UserLoginForm::class, ["_username" => $lastUsername]);
return $this->render('User/login.html.twig', [
'form' => $form->createView(),
'error' => $error,
]);
}
/**
* #Route("/logout", name="user_logout")
*/
public function logoutAction()
{
throw new \Exception('User logout');
}
/**
* #Route("/dashboard", name="user_dashboard")
*/
public function dashboardAction()
{
return $this->render('User/dashboard.html.twig');
}
}
security.yaml
Provider and pattern is required:
firewalls:
//...other firewalls
main:
anonymous: ~
pattern: ^/user
provider: users
access_control:
- { path: ^/user/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/user, roles: ROLE_USER }
Hope to help you.
Leave a comment with any feedback.
The problem was that the user wasn't being refreshed, the Guard Authenticator was doing exactly what it should. Stripping back the User Entity to the following fixed it.
<?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(fields={"email"}, message="There is already an account with this email")
*/
class User implements UserInterface
{
/**
* #ORM\Id()
* #ORM\GeneratedValue()
* #ORM\Column(type="integer")
*/
private $id;
/**
* #ORM\Column(type="string", length=255, unique=true)
*/
private $email;
/**
* #ORM\Column(type="string", length=255)
*/
private $password;
/**
* #ORM\Column(type="string", length=255)
*/
private $name;
public function getId(): ?int
{
return $this->id;
}
public function getEmail(): ?string
{
return $this->email;
}
public function setEmail(string $email): self
{
$this->email = $email;
return $this;
}
public function getUsername()
{
return $this->email;
}
public function getPassword(): ?string
{
return $this->password;
}
public function setPassword(string $password): self
{
$this->password = $password;
return $this;
}
public function getName(): ?string
{
return $this->name;
}
public function setName(string $name): self
{
$this->name = $name;
return $this;
}
////
public function getRoles()
{
return [
'ROLE_USER'
];
}
public function getSalt()
{
}
public function eraseCredentials()
{
}
}
I'm developing Form Request for Symfony, but i have one problem.
I inject FormRequest instances into controller action using argument value resolving. But I would like to inject services to FormRequest instance without making it public in services.yaml file.
Here is my Resolver:
<?php
namespace App\Resolver;
use App\Exception\FormValidationException;
use App\Request\FormRequest;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Controller\ArgumentValueResolverInterface;
use Symfony\Component\HttpKernel\ControllerMetadata\ArgumentMetadata;
use Symfony\Component\HttpKernel\Exception\AccessDeniedHttpException;
use Symfony\Component\Serializer\Encoder\DecoderInterface;
use Symfony\Component\Serializer\SerializerInterface;
use Symfony\Component\Validator\Validator\ValidatorInterface;
class ControllerRequestResolver implements ArgumentValueResolverInterface
{
/**
* #var SerializerInterface
*/
private $serializer;
/**
* #var ValidatorInterface
*/
private $validator;
/**
* #var DecoderInterface
*/
private $decoder;
/**
* #var ContainerInterface
*/
private $container;
public function __construct(
SerializerInterface $serializer,
ValidatorInterface $validator,
DecoderInterface $decoder,
ContainerInterface $container
) {
$this->serializer = $serializer;
$this->validator = $validator;
$this->decoder = $decoder;
$this->container = $container;
}
/**
* {#inheritdoc}
* #throws FormValidationException
*/
public function resolve(Request $request, ArgumentMetadata $argument)
{
$data = $this->decoder->decode($request->getContent(), 'json');
$request->request->replace($data);
$formRequestClass = $argument->getType();
/** #var FormRequest $form */
$form = $this->container->has($formRequestClass)
? $this->container->get($formRequestClass)
: new $formRequestClass();
$form->initialize(
$request->query->all(), $request->request->all(), $request->attributes->all(),
$request->cookies->all(), $request->files->all(), $request->server->all(), $request->getContent()
);
if (!$form->authorize()) {
throw new AccessDeniedHttpException('Access denied.');
}
$violations = $this->validator->validate($data, $form->rules());
if ($violations->count() > 0) {
throw new FormValidationException($violations, 'Validation error.');
}
yield $form;
}
/**
* {#inheritdoc}
*/
public function supports(Request $request, ArgumentMetadata $argument)
{
return (new \ReflectionClass($argument->getType()))->isSubclassOf(FormRequest::class);
}
}
So to receive FormRequest with needed dependencies in $this->container->get($formRequestClass) I need to make it public.
Here is FormRequest:
<?php
namespace App\Request;
use App\Entity\User;
use App\Rule\UniqueConfig;
use App\Service\Randomizer;
use App\Validator\Constraints\UniqueEntity;
use Doctrine\ORM\QueryBuilder;
use Symfony\Component\Validator\Constraints as Assert;
class UserRequest extends FormRequest
{
/**
* #var null|Randomizer
*/
private $randomizer;
public function __construct(Randomizer $randomizer)
{
$this->randomizer = $randomizer;
}
public function authorize(): bool
{
return $this->randomizer->getNumber() > 0.5;
}
public function rules(): Assert\Collection
{
return new Assert\Collection([
'email' => [
new Assert\NotBlank(),
new Assert\Email(),
new UniqueEntity([
'config' => new UniqueConfig(User::class, 'u', 'email', function ($value, QueryBuilder $qb) {
// You can add here additional filtering
}),
]),
],
'firstName' => [
new Assert\Length(['max' => 255]),
],
'lastName' => [
new Assert\Length(['max' => 255]),
],
]);
}
}
Thanks to #Cerad for help! Here is total result:
<?php
namespace App\Locator;
use Symfony\Component\DependencyInjection\ServiceLocator;
class FormRequestServiceLocator extends ServiceLocator
{
}
I inject it (FormRequestServiceLocator) into ControllerRequestResolver
<?php
namespace App\DependencyInjection\Compiler;
use App\Locator\FormRequestServiceLocator;
use App\Request\FormRequest;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\Reference;
class FormRequestPass implements CompilerPassInterface
{
/**
* {#inheritdoc}
*/
public function process(ContainerBuilder $container)
{
if (!$container->has(FormRequest::class)) {
return;
}
$taggedServices = $container->findTaggedServiceIds('app.form_request.tag');
$serviceReferences = [];
foreach ($taggedServices as $id => $tags) {
if ($id === FormRequest::class) {
continue;
}
$serviceReferences[$id] = new Reference($id);
}
$driverLocator = $container->getDefinition(FormRequestServiceLocator::class);
$driverLocator->setArguments([$serviceReferences]);
}
}
<?php
namespace App;
// ...
class Kernel extends BaseKernel
{
// ...
protected function build(ContainerBuilder $container)
{
parent::build($container);
$container->registerForAutoconfiguration(FormRequest::class)
->addTag('app.form_request.tag');
$container->addCompilerPass(new FormRequestPass());
}
}