Symfony 4.2: How to get Encoder Instance outside of Controller - symfony

I need to check my password in Login form, so
I want to get the Encoder in class Authenticator (made by bin/console make:auth) in function checkCredentials($credentials, UserInterface $user).
The $credentials['password'] is plain text, and the $user->getPassword() is encoded password.
My security.yaml is:
encoders:
App\Entity\User:
algorithm: bcrypt
cost: 12
How to get instance of the Encoder?

You can get the encoder from the encoder factory which has a security.encoder_factory service ID (or Symfony\Component\Security\Core\Encoder\EncoderFactoryInterface interface if using autowiring).
After injecting the factory, you can get the encoder for a specific user entity with:
use App\Entity\User;
...
$encoder = $encoderFactory->getEncoder(User:class);

Related

Why the passwords don't match in symfony after migration?

Currently we are migrating our website to another server, but we are still using the same database.
So we have 2 exact websites connected to 1 database.
On the first one we can log in with the defaul credentials, and on the other the passwords do not match.
This is how we check if the password matches:
use Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface;
public function checkCredentials($password, UserInterface $user ){
return $this->passwordEncoder->isPasswordValid($user, $password );
}
This is my security config:
security:
encoders:
App\Entity\User:
algorithm: auto
# ...
is there some hash that I have to use in order to obtain the same password?

User password not encrypted when using FOSUserBundle + EasyAdminBundle with Symfony 3.4

I use Symfony 3.4 with FOSUserBundle and EasyAdminBundle.
I've been stuck for a while on the following problem: when I create a new user via EasyAdmin, the password entered is not hashed, it remains clear in the database and in the edit form of the created user (in EasyAdmin), while there is no problem when I create a user via the form generated by FOSUserBundle (register).
My User entity :
<?php
// src/Repas/UserBundle/Entity/User.php
namespace Repas\UserBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
}
My AdminController.php file :
<?php
namespace Repas\MenusBundle\Controller;
use EasyCorp\Bundle\EasyAdminBundle\Controller\AdminController as BaseAdminController;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\Form\Extension\Core\Type\PasswordType;
class AdminController extends BaseAdminController
{
public function createNewUserEntity()
{
return $this->get('fos_user.user_manager')->createUser();
}
public function persistUserEntity($user)
{
$this->get('fos_user.user_manager')->updateUser($user, false);
parent::persistEntity($user);
}
public function updateUserEntity($user)
{
$this->get('fos_user.user_manager')->updateUser($user, false);
parent::updateEntity($user);
}
}
In my config.yml file :
easy_admin:
entities:
User:
class: Repas\UserBundle\Entity\User
export_path: '%kernel.root_dir/../var/export/user'
password_encoding: { algorithm: 'bcrypt', cost: 12 }
In my security.yml file :
encoders:
Repas\UserBundle\Entity\User: bcrypt
In my routing.yml file :
fos_user:
resource: "#FOSUserBundle/Resources/config/routing/all.xml"
easy_admin_bundle:
resource: "#RepasMenusBundle/Controller/AdminController.php"
type: annotation
prefix: /admin
I've been through many forums, as well as the official docs, I think I followed everything properly but I certainly missed something.
Thank you for your help.
The EasyAdminBundle doesn't define an option to encrypt the password, it only provides options to save the entities (a crud) which you can extend by defining custom actions based on routes or actions inside an overrided AdminController in order to integrate with FOSUserBundle.
Example
easy_admin:
entities:
User:
list:
actions:
- { name: 'create_user', type: 'route' } //or nothing on type to use the action option
At this point you already have either a defined controller accessible by route or an overriden controller which handles the specified User actions. You only need to use the FOSUser methods to encrypt the password properly, as read in this doc.
Hope it helps!
Ok, I guess my mistake is that in the form generated by Easyadmin to create a new user, Easyadmin generates a field named "password" instead of "plainPassword" which is the one FOSUser uses to encrypt the entered password. So I think I just have to create a new "plainPassword" field in my "Easyadmin new user" form and enter the user password in that field to encrypt it. Then the encrypted password will be stored in "password" field of the database.
I will tell you if that is the solution.

Get a UserPasswordEncoder Instance in Symfony 4

I did a user security system without FOSUserBundle and now I'm trying to place the oldUser entity in the newUser entity that implement a user interface.
For do that i have to encode the password like in the documentation inside the entity
But I can't get an instance of UserPasswordEncoder
When I try to get it from the autowiring symfony4 tell me
Controller "App\Controller\SecurityController::login()" requires that you provide a value for the "$passwordEncoder" argument. Either the argument is nullable and no null value has been provided, no default value has been provided or because there is a non optional argument after this one.
So I tried to do a new UserPasswordEncoder() (just for try)
Type error: Too few arguments to function Symfony\Component\Security\Core\Encoder\UserPasswordEncoder::__construct(), 0 passed in /home/connexio/dev/project/src/Controller/SecurityController.php on line 87 and exactly 1 expected
I also tried bin/console debug:container --show-private | grep -i Password
Symfony\Component\Security\Core\Encoder\UserPasswordEncoderInterface alias for "security.user_password_encoder.generic"
security.authentication.listener.form
Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener
security.authentication.listener.form.main Symfony\Component\Security\Http\Firewall\UsernamePasswordFormAuthenticationListener
security.authentication.listener.json Symfony\Component\Security\Http\Firewall\UsernamePasswordJsonAuthenticationListener
security.command.user_password_encoder Symfony\Bundle\SecurityBundle\Command\UserPasswordEncoderCommand
security.password_encoder alias for "security.user_password_encoder.generic"
security.user_password_encoder.generic Symfony\Component\Security\Core\Encoder\UserPasswordEncoder
security.validator.user_password Symfony\Component\Security\Core\Validator\Constraints\UserPasswordValidator
And this is where I want to invoke the instance
/**
* #Route("/login", name="security_login")
*/
public function login(AuthenticationUtils $helper, UserPasswordEncoder $passwordEncoder): Response
{
$this->addUsersToUser($passwordEncoder);
return $this->render('login.html.twig', [....
Someone know how to get an instance of UserPasswordEncoder ?
Thank's !
Use UserPasswordEncoderInterface instead of UserPasswordEncoder worked and encode as well....

Symfony: How to use configuration parameters in a service class?

I want to use the parameters in parameters.yml in my service class mailer
but I got this error while instantiate the mailer class:
$mailer = new Mailer();
knowing that the parameters are defined in parameters.yml:
Warning: Missing argument 1 for AppBundle\Service\Mailer::__construct(), called in
namespace AppBundle\Service
class Mailer
{
private $mailer_user;
private $mailer_password;
private $mailer_name;
private $mailer_host;
public function __construct($mailer_user, $mailer_password ,$mailer_name ,$mailer_host)
{
$this->mailer_name = $mailer_user;
$this->mailer_password = $mailer_password;
$this->mailer_user = $mailer_name;
$this->mailer_host = $mailer_host;
}
//.....
}
services.yml:
mailer:
class: 'AppBundle\Service\Mailer'
arguments: [%mailer_user%, %mailer_password% ,%mailer_name% ,%mailer_host%]
To learn "how can I use the parameters in parameters.yml in my service class" (and to see how a service can be used in a Symfony app) just read the Symfony docs paying attention to the Symfony version of the documentation you are reading:
Introduction to Parameters
How to Set external Parameters in the Service Container
Service Container
BTW you should never instanciate a service class directly like you did:
$mailer = new Mailer();
but retrieve the service instance from the Service Container (Symfony will take care to automatically inject all the configured dependancies) like we usually do in a Controller (the example below access the Service using the shortcode provided extending the base Controller provided by the FrameworkBundle included in the Symfony standard version):
$mailer = $this->container->get('mailer');

symfony2 login by username or email

I'm using the standard authentication mechanism of Symfony2 and I want to let the user use either his username or email to login, but I can't find out why it's not working. I've tested the repository class and it works as expected. I've followed this how-to.
Here's my user provider class:
<?php
namespace My\UserBundle\Entity;
use Doctrine\ORM\EntityRepository ,
Symfony\Component\Security\Core\User\UserProviderInterface ,
Symfony\Component\Security\Core\User\UserInterface;
/**
* UserRepository
*
* This class was generated by the Doctrine ORM. Add your own custom
* repository methods below.
*/
class UserRepository extends EntityRepository implements UserProviderInterface
{
function loadUserByUsername($username)
{
$qb = $this->createQueryBuilder('u') ;
return
$qb->select('u')
->where(
$qb->expr()->orx(
$qb->expr()->like('u.username' ,':username') ,
$qb->expr()->like('u.email' ,':username')
)
)
//->andWhere($qb->expr()->eq('u.enabled' ,'true') )
->setParameters(array('username' =>$username ) )
->getQuery()
->getResult() ;
}
function refreshUser(UserInterface $user)
{
return $this->loadUserByUsername($user->getUsername() );
}
function supportsClass($class)
{
return $class === 'My\UserBundle\Entity\User';
}
}
I propose a more simple approach that only requires to edit security.yml file.
You must to create two diferent security providers but both using the same User class. The first has username as property and the second has email as property.
Then you create a chain_provider that includes the two providers and use it in your firewall section
security:
providers:
chain_provider:
chain:
providers: [db_username, db_email]
db_username:
entity:
class: MyUserBundle:User
property: username
db_email:
entity:
class: MyUserBundle:User
property: email
firewalls:
default:
anonymous: ~
provider: chain_provider
form_login:
login_path: /login
check_path: /login
I don't know if this approach is a clean practice, but is fast, simple and it works ok.
well guys the thing is in my security.yml i had this
providers:
main:
entity: { class: My\UserBundle\Entity\User ,property : username}
so i had to take off that parameter property :username
Taking off the property: username lets the UserProviderInterface load the user as expected when it logs in, but does not call the refreshUser() method as expected. I put in checks to see if it gets called but it doesn't.
The class that reloads the user on each access is ContextListener::refreshUser(TokenInterface $token) method. In this the interface iterates through the UserProviders and calls the refreshUser that first returns a non-null user.
I could make sure of this because, in the original load, I combine all different entities to make one SQL call instead of 7. And when the user reloads, it calls 7 times.
Also the method EntityUserProvider::refreshUser() doesn't call the repository's method and instead reloads from the database directly.
Your provider class is correct, and you are correct that the problem is in security.yml, however, your solution is incorrect.
According to the documentation, your security.yml file should look like this:
security:
# ...
providers:
administrators:
entity: { class: MyUserBundle:User }
Notice that the class is defined as the Bundle, not the direct class.
The way you have your code right now, symfony is completly ignoring your repository class until you define your security.yml correctly. And as #Anand pointed out, just removing the property does not invoke refreshUser. However, it looks like if you are using your own Repository, you do not need to define the property (since it's being defined in your query).

Resources