For a new project, I use Symfony 4.3 and FosUserBundle 2.1. I installed with success this bundle but when I try to use it, I've got somes issues.
When I try to access on login page (http://127.0.0.1:8000/login), it works well. But, on register page I've got some errors:
Property "superAdmin" does not exist in class "App\Entity\User"
I try to add the properties superAdmin and group in my User entity (I don't know if it's a good idea). The page is displayed correctly. Now, when I try to submit register form, 4 errors appear (all fields are filled):
This value should not be null. => "data.password"
This value should not be null. => "data.superAdmin"
This value should not be null. => "data.groups"
This value should not be null. => "data.group"
Is FosUserBundle compatible with Symfony 4.3? How to solve these problems? Are there alternatives?
here is the solution :
==> you must add $group and $superAdmin in your entity User, so you can just set $group to nullable=true, and $superAdmin is boolean you can set it to true or false. NB: you must rename the column group because this group is reserved word in mysql , I set these in constructor so you can set these in custom form to like :
<?php
// src/AppBundle/Entity/User.php
namespace App\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/**
* #ORM\Column(type="string", nullable=true)
*/
protected $prenom;
/**
* #ORM\Column(type="boolean")
*/
protected $superAdmin;
/**
* #ORM\Column(name="giroupy", type="string", nullable=true)
*/
protected $group;
public function __construct()
{
parent::__construct();
$this->superAdmin = false;
$this->groups = new ArrayCollection();
// your own logic
if (empty($this->registerDate)) {
$this->registerDate = new \DateTime();
}
}
public function getId(): ?int
{
return $this->id;
}
public function getPrenom(): ?string
{
return $this->prenom;
}
public function setPrenom(string $prenom): self
{
$this->prenom = $prenom;
return $this;
}
public function getGroup(): ?string
{
return $this->group;
}
public function setGroup(?string $group): self
{
$this->group = $group;
return $this;
}
public function getSuperAdmin(): ?bool
{
return $this->superAdmin;
}
public function setSuperAdmin($superAdmin): self
{
$this->superAdmin = $superAdmin;
return $this;
}
}
so after to set password you must hook formEvent for event SUBMIT to get the entity User like :
<?php
// src/AppBundle/Form/RegistrationType.php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
class RegistrationType extends AbstractType
{
private $userPassWordInterface;
public function __construct(
UserPasswordEncoderInterface $userPassWordInterface
)
{
$this->userPassWordInterface = $userPassWordInterface;
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->add('prenom')
->addEventListener(
FormEvents::SUBMIT,
[$this, 'onSubmit']
)
;
}
public function onSubmit(FormEvent $event)
{
$user = $event->getData();
$form = $event->getForm();
if (!$user) {
return;
}
$passWord = $this->userPassWordInterface->encodePassword($user, $user->getPlainPassword());
$user->setPassWord($passWord);
// checks whether the user has chosen to display their email or not.
// If the data was submitted previously, the additional value that
// is included in the request variables needs to be removed.
}
public function getParent()
{
return 'FOS\UserBundle\Form\Type\RegistrationFormType';
}
public function getBlockPrefix()
{
return 'app_user_registration';
}
// For Symfony 2.x
public function getName()
{
return $this->getBlockPrefix();
}
}
and in security like :
security:
# https://symfony.com/doc/current/security.html#where-do-users-come-from-user-providers
encoders:
App\Entity\User: bcrypt
role_hierarchy:
ROLE_SUPER_ADMIN: ROLE_ADMIN
ROLE_ADMIN: ROLE_USER
providers:
fos_userbundle:
id: fos_user.user_provider.username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
logout: true
anonymous: 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$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
so after set the form login in your package : config/packages/fos_user.yaml
fos_user:
db_driver: orm # other valid values are 'mongodb' and 'couchdb'
firewall_name: main
user_class: App\Entity\User
registration:
form:
type: App\Form\RegistrationType
from_email:
address: "%mailer_user%"
sender_name: "%mailer_user%"
and don't forget to load routes in : config/routes/fos_user.yaml
fos_user:
resource: "#FOSUserBundle/Resources/config/routing/all.xml"
The problem is much simpler. FOSUserBundle is still compatible with Symfony 4.3, it's just doesn't work with 4.3 validator component automatic validation settings, which is turned on by default:
framework:
validation:
email_validation_mode: html5
# Enables validator auto-mapping support.
# For instance, basic validation constraints will be inferred from Doctrine's metadata.
auto_mapping: # <----- REMOVE THIS THIS
App\Entity\: [] # <-------- AND THIS
It solved the problem in my case, but it also means that you need to set validation rules manually.
Related
In the basic login form I'm trying to check if the user from the database I inserted which I entered in the login form but the page is redirecting to the same page because there is a error in my getUser or checkCredentials from LoginAuthenticator. I'm confused what's wrong in my code.
LoginAuthenticator
<?php
namespace App\Security;
use App\Repository\UsersRepository;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\RouterInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\Security;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\Authenticator\AbstractFormLoginAuthenticator;
class LogAuthenticator extends AbstractFormLoginAuthenticator
{
private $usersRepository;
private $router;
public function __construct(UsersRepository $usersRepository, RouterInterface $router){
$this->usersRepository = $usersRepository;
$this->router = $router;
}
public function supports(Request $request)
{
return $request->attributes->get('_route') === 'app_login'
&& $request->isMethod('POST');
}
public function getCredentials(Request $request)
{
$credentials = [
'email' => $request->request->get('user_email'),
'password' => $request->request->get('password')
];
$request ->getSession()->set(
Security::LAST_USERNAME,
$credentials['email']
);
return $credentials;
}
public function getUser($credentials, UserProviderInterface $userProvider)
{
$this->usersRepository->findOneBy(['user_email' =>$credentials['email']]);
}
public function checkCredentials($credentials, UserInterface $user)
{
return true;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
return $this->router->generate('app_homepage');
}
protected function getLoginUrl()
{
return $this->router->generate('app_login');
}
}
LoginController
<?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 LoginController 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.');
}
}
userRepository
<?php
namespace App\Repository;
use App\Entity\User;
use Doctrine\Bundle\DoctrineBundle\Repository\ServiceEntityRepository;
use Doctrine\Persistence\ManagerRegistry;
/**
* #method User|null find($id, $lockMode = null, $lockVersion = null)
* #method User|null findOneBy(array $criteria, array $orderBy = null)
* #method User[] findAll()
* #method User[] findBy(array $criteria, array $orderBy = null, $limit = null, $offset = null)
*/
class UserRepository extends ServiceEntityRepository
{
public function __construct(ManagerRegistry $registry)
{
parent::__construct($registry, User::class);
}
// /**
// * #return User[] Returns an array of User objects
// */
/*
public function findByExampleField($value)
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->orderBy('u.id', 'ASC')
->setMaxResults(10)
->getQuery()
->getResult()
;
}
*/
/*
public function findOneBySomeField($value): ?User
{
return $this->createQueryBuilder('u')
->andWhere('u.exampleField = :val')
->setParameter('val', $value)
->getQuery()
->getOneOrNullResult()
;
}
*/
}
Security.yaml
security:
encoders:
App\Entity\Users:
algorithm: auto
# 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_user_provider:
entity:
class: App\Entity\Users
property: email
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: lazy
provider: app_user_provider
logout:
path: app_logout
guard:
authenticators:
- App\Security\LoginAuthenticator
# 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: ^/admin, roles: ROLE_ADMIN }
# - { path: ^/profile, roles: ROLE_USER }
enter image description here
You are using the wrong user provider, right now your firewall uses the memory provider with a null value:
security:
...
providers:
users_in_memory: { memory: null }
...
When you want to retrieve users from the database you should use entity provider:
# config/packages/security.yaml
security:
# ...
providers:
users:
entity:
# the class of the entity that represents users
class: 'App\Entity\User'
# the property to query by - e.g. username, email, etc
property: 'username'
# optional: if you're using multiple Doctrine entity
# managers, this option defines which one to use
# manager_name: 'customer'
# ...
https://symfony.com/doc/4.4/security/user_provider.html#security-entity-user-provider
Full documentation can be found here: https://symfony.com/doc/4.4/security.html
Edit: I just noticed you also set the enable_authenticator_manager in your security.yaml. If your project runs Symfony 4.x this will not work because it was introduced in Symfony 5.1.
I have two tables 'User' and 'Address', there is OneToOne relation between them as User table has a column 'address_id' which stores 'id' of Address table.
Here is the annotation in User Entity:
/**
* #ORM\OneToOne(targetEntity="App\Entity\address", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $address;
Security.yaml:
security:
encoders:
App\Entity\User:
algorithm: bcrypt
providers:
my_provider:
entity:
class: App\Entity\User
property: username
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: true
form_login:
login_path: login
check_path: login
provider: my_provider
default_target_path: dashboard
logout:
path: /logout
target: /
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/dashboard, roles: ROLE_USER }
Controller:
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Routing\Annotation\Route;
use Symfony\Component\Security\Http\Authentication\AuthenticationUtils;
class SecurityController extends AbstractController
{
/**
* #Route("/login", name="login")
*/
public function login(Request $request,AuthenticationUtils $utils)
{
// get the login error if there is one
$error = $utils->getLastAuthenticationError();
// dump($error);die;
// last username entered by the user
$lastUsername = $utils->getLastUsername();
return $this->render('security/login.html.twig', [
'last_username' => $lastUsername,
'error' => $error
]);
}
public function logout()
{
# code...
}
}
I get this error when I insert my username and password:
Authentication request could not be processed due to a system problem.
This is what I get when I dump() the error in controller.
SecurityController.php on line 20:
AuthenticationServiceException {#189 ▼
-token: UsernamePasswordToken {#236 ▶}
#message: "The target-entity App\Entity\address cannot be found in 'App\Entity\User#address'."
#code: 0
#file: "/home/users/kumar.saurabh/www/html/symfony/test-project/vendor/symfony/security-core/Authentication/Provider/DaoAuthenticationProvider.php"
#line: 85
trace: {▶}
}
When I remove $address variables and its getter and setter function then the login is working fine, but when I am using this it gives me above mentioned error.
How can I can achieve this by using Foreign key? I am new to symfony, and I am learning on version 4.2.
Beware the case, replace App\Entity\address by App\Entity\Address, update your schema and it should be fine
/**
* #ORM\OneToOne(targetEntity="App\Entity\address", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $address;
should be
/**
* #ORM\OneToOne(targetEntity="App\Entity\Address", cascade={"persist", "remove"})
* #ORM\JoinColumn(nullable=false)
*/
private $address;
I was trying to add authenticator, which should be responsible only for access to path /Abc, and by default other security areas secured by default authenticator.
As long as I add pattern line pattern: ^/Abc into my security.yml everything works fine. I can login and logout. The user is authenticated with
Token class PostAuthenticationGuardToken and Firewall abc.
When I add line pattern: ^/Abc I can not login anymore (into /Abc area). After I submit my login form application reload into path login_abc
security.yml
security:
firewalls:
# disables authentication for assets and the profiler, adapt it according to your needs
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
abc:
anonymous: ~
pattern: ^/Abc
guard:
authenticators:
- abc_authenticator
form_login:
login_path: login_abc
check_path: login_abc
remember_me: false
logout:
path: logout_abc
target: main_index
default:
anonymous: ~
pattern: ^/(?!Abc)
form_login:
always_use_default_target_path: false
login_path: login
check_path: login
remember_me: false
logout:
path: logout
target: main_index
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login_abc, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/Abc, roles: [ROLE_ABC] }
- { path: ^/Work, roles: [ROLE_WORK, ROLE_WORK2] }
- { path: ^/Home, roles: [ROLE_HOME] }
abcAuthenticator.php
namespace AppBundle\Security;
use AppBundle\Entity\User;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\RouterInterface;
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\Exception\UsernameNotFoundException;
use Symfony\Component\Security\Core\User\UserInterface;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Symfony\Component\Security\Guard\AbstractGuardAuthenticator;
use Doctrine\Bundle\DoctrineBundle\Registry as Doctrine;
class abcAuthenticator extends AbstractGuardAuthenticator {
const ACCESS_DENNIED = "Access dennied";
/**
* #var \Symfony\Component\Routing\RouterInterface
*/
private $router;
/**
* #var Doctrine
*/
private $doctrine;
/**
* abcAuthenticator constructor.
*/
public function __construct(RouterInterface $router, Doctrine $doctrine) {
$this->router = $router;
$this->doctrine = $doctrine;
}
public function start(Request $request, AuthenticationException $authException = null) {
$url = $this->router->generate('login_abc');
return new RedirectResponse($url);
}
public function getCredentials(Request $request) {
if ($request->getPathInfo() != '/login_abc' || !$request->isMethod('POST')) {
return;
}
return array(
'cardToken' => $request->request->get('_cardToken'),
);
}
public function getUser($credentials, UserProviderInterface $userProvider) {
try {
try {
$user = $this->doctrine->getRepository(User::class)->findOneByCardToken($credentials['cardToken']);
if (is_null($user)) {
throw new UsernameNotFoundException();
}
return $userProvider->loadUserByUsername($user->getUsername());
} catch (UsernameNotFoundException $e) {
throw new CustomUserMessageAuthenticationException(self::ACCESS_DENNIED);
}
}
catch (UsernameNotFoundException $e) {
throw new CustomUserMessageAuthenticationException(self::ACCESS_DENNIED);
}
}
public function checkCredentials($credentials, UserInterface $user) {
return true;
}
public function onAuthenticationFailure(Request $request, AuthenticationException $exception) {
$url = $this->router->generate('login_abc');
return new RedirectResponse($url);
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey) {
$url = $this->router->generate('abc_panel');
return new RedirectResponse($url);
}
public function supportsRememberMe() {
return false;
}
}
Authenticator is of course registered as a service.
I have no idea what is wrong. Pls help.
In your getCredentials() method, you appear to only allow login if you go to the URL /login_abc (URL, not a route name) - but only allowing the guard to be run on the URL /Abc.
Using multiple firewalls is complex - you may find things easier to have a single firewall and then allow multiple authentications to run, until you have a success (based on the URL, and then any username, password/tokens etc), and then the rest would be skipped.
How do I store record every time a user logged in in Symfony2?
I created UserLog entity where I want to record id of record, user's id and date of login.
I am using FOS user bundle for user management. I saw question of recording only last login for every user Symfony2 Login and Security , but couldn't figure out how to record every login date
Here is the solution for Symfony4.2
As per Symfony documentation one has to listen to security.interactive_login event and create LoginListener.
Code 1
app.login_listener:
class: App\EventListener\LoginListener
tags:
- { name: 'kernel.event_listener', event: 'security.interactive_login' }
Code 2
// src/EventListener/LoginListener.php
namespace App\EventListener;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Security\Http\Event\InteractiveLoginEvent;
class LoginListener
{
private $em;
public function __construct(EntityManagerInterface $em)
{
$this->em = $em;
}
public function onSecurityInteractiveLogin(InteractiveLoginEvent $event)
{
// Get the User entity.
$user = $event->getAuthenticationToken()->getUser();
// Update your field here.
$user->setLastLogin(new \DateTime());
// Persist the data to database.
$this->em->persist($user);
$this->em->flush();
}
}
One has to adjust this example to meet custom needs.
Credit for example LoginListener goes to Rihards Steinbergs
You have to implement an AuthenticationHandler which listen on the onAuthenticationSuccess event.
First, create the lastLogin field in your User entity (datetime, nullable), with getters and setters.
Then, create the like follows :
<?php
namespace Acme\TestBundle\Handler;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\DependencyInjection\ContainerAware;
class AuthenticationHandler extends ContainerAware implements AuthenticationSuccessHandlerInterface
{
function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$lastLogin = new \DateTime();
$user->setLastLogin($lastLogin);
$this->container->get('doctrine')->getEntityManager()->flush();
// redirect the user for example
return new RedirectResponse($this->container->get('router')->generate('login_success'));
}
}
Register it as a service :
// app/config/services.yml
services:
authentication_handler:
class: Acme\TestBundle\Handler\AuthenticationHandler
calls:
- [ setContainer, [ #service_container ] ]
And configure it as authentication success_handler :
// app/config/security.yml
# ...
form_login:
# ...
success_handler: authentication_handler
Hopes this help you.
EDIT
Mistake from me, thanks #JasonRoman.
Instead of a lastLogin property, create an entity such as LoginRecord which contains a date property.
/**
* LoginRecord.
*
* #ORM\Entity
* #ORM\Table(name="user_logins")
*/
class LoginRecord
{
// Identifier
/** #ORM\Column(name="date", type="date") */
protected $date;
/**
* #ORM\ManyToOne(targetEntity="\Acme\TestBundle\Entity\User", inversedBy="loginRecords")
* #ORM\JoinColumn(name="user_id", referencedColumnName="id", nullable=false)
*/
protected $user;
public function setDate($date)
{
$this->date = $date;
return $date;
}
public function getDate()
{
return $this->date;
}
public function setUser(User $user)
{
$this->user = $user;
return $this;
}
public function getUser()
{
return $this->user;
}
}
Then, add a property called $loginRecords in your User entity, representing a one-to-many association with the LoginRecord as targetClass.
// User entity
class User
{
// ... Your other properties
/** #ORM\OneToMany(targetEntity="\Acme\TestBundle\Entity\LoginRecord", mappedBy="user", cascade={"persist", "remove"}) */
protected $loginRecords;
public function __construct()
{
// ...
$this->loginRecords = new \Doctrine\Common\Collections\ArrayCollection();
}
public function addLoginRecord(LoginRecord $loginRecord)
{
$this->loginRecords[] = $loginRecord;
$loginRecord->setUser($this);
return $this;
}
public function removeLoginRecord(LoginRecord $loginRecord)
{
$this->loginRecords->removeElement($loginRecord);
}
public function getLoginRecords()
{
return $this->loginRecords;
}
}
And, instead of use setLastLogin , use addLoginRecord($date) to record the user logins in your AuthenticationHandler :
function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
$user = $token->getUser();
$lastLogin = new \DateTime();
$record = new LoginRecord();
$record->setDate($lastLogin);
$user->addLoginRecord($record);
$this->container->get('doctrine')->getEntityManager()->flush();
// redirect the user for example
return new RedirectResponse($this->container->get('router')->generate('login_success'));
}
You will need to override the default authentication handler and do the login there.
You will need to create a service of your new authentication handler:
services.yml:
parameters:
fos_user_security.component.authentication.handler.login_success_handler.class: Path\To\New\Handler
services:
fos_user_security.component.authentication.handler.login_success_handler:
class: %fos_user_security.component.authentication.handler.login_success_handler.class%
arguments: [#router, #security.context]
tags:
- { name: 'monolog.logger', channel: 'security' }
Create the Handler and do the logic:
namespace Application\Sonata\UserBundle\Services;
use Symfony\Component\Security\Http\Authentication\AuthenticationSuccessHandlerInterface;
use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Router;
class LoginSuccessHandler implements AuthenticationSuccessHandlerInterface
{
protected $router;
protected $security;
public function __construct(Router $router, SecurityContext $security)
{
$this->router = $router;
$this->security = $security;
}
public function onAuthenticationSuccess(Request $request, TokenInterface $token)
{
if ($this->security->isGranted('ROLE_USER'))
// create your new entity and add the data you need
}
return $response;
}
In security.yml you will need to define the new success_handler, something like this:
main:
pattern: ^/
context: user
form_login:
provider: fos_userbundle
csrf_provider: form.csrf_provider
login_path: /login
#use_forward: false
check_path: fos_user_security_check
success_handler: fos_user_security.component.authentication.handler.login_success_handler // the handler
#failure_path: null
always_use_default_target_path: false
default_target_path: profile
i'm using FosUserBundle in my Symfony2 project, i have the login working, i want to now further customize login to include checking for id along with username, email and password. i have a User entity in the Entity folder, i have a UserRepository in the Repository folder
How do i customize Fos UserBundle login to include id in its query
What are the list of possible different ways i could do this
any other suggestion to improve code are welcomed
-thankyou
The User Entity:
<?php
namespace Example\Bundle\ExampleBundle\Entity;
use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;
/**
* User
*
* #ORM\Table(name="Example_user")
* #ORM\Entity(repositoryClass="Example\Bundle\ExampleBundle\Repository\UserRepository")
*/
class User extends BaseUser {
/**
* #var integer
*
* #ORM\Column(name="id", type="integer")
* #ORM\Id
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
/*
* some Class properties here
*
*/
/**
* Get id
*
* #return integer
*/
public function getId() {
return $this->id;
}
/*
* some setters and getters here
*/
}
The User Repository
<?php
namespace Example\Bundle\ExampleBundle\Repository;
use Doctrine\ORM\EntityRepository;
use Symfony\Component\Security\Core\User\UserProviderInterface;
use Example\Bundle\ExampleBundle\siteConfig;
class UserRepository extends EntityRepository implements UserProviderInterface{
public $university = siteConfig::university_id;
/**
*
* #param type $username
*/
public function FindUsernameOrEmailInUniversity($username, $universityId) {
return $this->createQueryBuilder('user')
->where('user.university_id = :universityId')
->andWhere('user.username = :username OR user.email = :email')
->setParameter('universityId', $universityId)
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
}
/**
*
* #param type $username
*/
public function loadUserByUsername($username) {
$user = $this->FindUsernameOrEmailInUniversity($username, $this->university); //check order of parameters use type hinting
if(!$user){
throw new \Symfony\Component\Security\Core\Exception\UsernameNotFoundException('User Name '.$username.' Not Found');
}
return $user;
}
/**
*
* #param \Symfony\Component\Security\Core\User\UserInterface $user
*/
public function refreshUser(\Symfony\Component\Security\Core\User\UserInterface $user) {
return $user;
}
/**
*
* #param type $class
*/
public function supportsClass($class) {
return ;
}
}
The Security.yml
# app/config/security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: bcrypt
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
entity: { class: Example\Bundle\ExampleBundle\Repository\UserRepository }
firewalls:
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_provider: security.csrf.token_manager # Use form.csrf_provider instead for Symfony <2.4
logout: true
anonymous: true
access_control:
- { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin/, role: ROLE_ADMIN }
Here is the error i'm getting
The class 'Example\Bundle\ExampleBundle\Repository\UserRepository' was not found in the chain configured namespaces Example\Bundle\ExampleBundle\Entity, FOS\UserBundle\Entity
Create a class inside entity folder that extends FOS\UserBundle\Doctrine\UserManager (i'm assuming there is a class called siteConfig.php with a static University_id field updated by a db query)
<?php
namespace Example\Bundle\ExampleBundle\Entity
use FOS\UserBundle\Doctrine\UserManager as BaseUserManager;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface;
use FOS\UserBundle\Util\CanonicalizerInterface;
use FOS\UserBundle\Model\UserInterface;
use Example\Bundle\ExampleBundle\siteConfig;
class UserManager extends BaseUserManager {
public function __construct(EncoderFactoryInterface $encoderFactory, CanonicalizerInterface $usernameCanonicalizer,
CanonicalizerInterface $emailCanonicalizer, EntityManager $em, $class) {
parent::__construct($encoderFactory, $usernameCanonicalizer, $emailCanonicalizer, $em, $class);
}
/**
* this overides the findUserByUsernameOrEmail in FOS\UserBundle\Doctrine\UserManager
**/
public function findUserByUsernameOrEmail($usernameOrEmail) {
if (filter_var($usernameOrEmail, FILTER_VALIDATE_EMAIL)) {
return $this->findUserBy(array('emailCanonical' => $this->canonicalizeEmail($usernameOrEmail), 'university' => siteConfig::$university_id));
}
return $this->findUserBy(array('usernameCanonical' => $this->canonicalizeUsername($usernameOrEmail), 'university' => siteConfig::$university_id));
}
}
Dependency injection for User manager, inside services.yml
ExampleUserManager:
class: namespace Example\Bundle\ExampleBundle\Entity\UserManager
arguments: [#security.encoder_factory, #fos_user.util.username_canonicalizer, #fos_user.util.email_canonicalizer, #fos_user.entity_manager, namespace Example\Bundle\ExampleBundle\Entity\User]
Inside config.yml add the following configuration under fos_user
service:
user_manager: ExampleUserManager
Inside security.yml under provider add the following
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
First of all there is mistype - an extra paranthese in defining repository class for entity line:
/**
* User
*
* #ORM\Table(name="Example_user")
* #ORM\Entity(repositoryClass="Example\Bundle\ExampleBundle\Repository\UserRepository")
*/
class User extends BaseUser {
Try this, I made some corrections to your method:
/**
* #param type $username
*/
public function FindUsernameOrEmailInUniversity($username, University $university) {
return $this->createQueryBuilder('user') //It should be alias for your entity name, and it could be anything you write, like 'user' or just 'u'
->where('user.university = :university')
->andWhere('user.username = :username OR user.email = :email')
->setParameter('university', $university)
->setParameter('username', $username)
->setParameter('email', $username)
->getQuery()
->getOneOrNullResult();
}
Also do you have getters, setters and properties for university, email and username? I assume that you want to make university as relation to User, so you would want to setup properly doctrine relation.