I am using Sonata admin bundle for my application all works well,In my application i have users and admin,admin can add/edit/delete the users when i am trying to update a user there is a problem the password data is overrided from user table. i have overrided the preUpdate method of admin controller ,I got $object which has an instance of user entity manager so if user leaves to update password and saves data the password is lost.
public function preUpdate($object)
{
$Password = $object->getUserPassword();
if (!empty($Password)) { /* i check here if user has enter password then update it goes well*/
$salt = md5(time());
$encoderservice = $this->getConfigurationPool()->getContainer()->get('security.encoder_factory');
$User = new User();
$encoder = $encoderservice->getEncoder($User);
$encoded_pass = $encoder->encodePassword($Password, $salt);
$object->setUserSalt($salt)->setUserPassword($encoded_pass);
} else { /* here i try to set the old password if user not enters the new password but fails */
$object->setUserPassword($object->getUserPassword());
}
}
When i try to set $object->setUserPassword($object->getUserPassword()); it gets null and updates the password as null its not getting the edit data i have tried to get the repository (below) again to get the password but no luck its getting the same
$DM = $this->getConfigurationPool()->getContainer()->get('Doctrine')->getManager()->getRepository("...")->find(id here);
Is there a way i can access the original data of current entity in entity manager
You can access the original data by getting doctrine's Unit of Work.As from docs
You can get direct access to the Unit of Work by calling
EntityManager#getUnitOfWork(). This will return the UnitOfWork
instance the EntityManager is currently using.An array containing the
original data of entity
Grab the password from Unit of Work and use in your setter method
public function preUpdate($object)
{
$DM = $this->getConfigurationPool()->getContainer()->get('Doctrine')->getManager();
$uow = $DM->getUnitOfWork();
$OriginalEntityData = $uow->getOriginalEntityData( $object );
$Password = $object->getUserPassword();
if (!empty($Password)) { /* i check here if user has enter password then update it goes well*/
$salt = md5(time());
$encoderservice = $this->getConfigurationPool()->getContainer()->get('security.encoder_factory');
$User = new User();
$encoder = $encoderservice->getEncoder($User);
$encoded_pass = $encoder->encodePassword($Password, $salt);
$object->setUserSalt($salt)->setUserPassword($encoded_pass);
} else { /* here i try to set the old password if user not enters the new password but fails */
$object->setUserPassword($OriginalEntityData['Password']);/* your property name for password field */
}
}
Hope it works fine
Direct access to a Unit of Work
Reset entity in entity manage, example for onFlush event
/**
* #param OnFlushEventArgs $args
*
* #throws \Doctrine\ORM\ORMException
* #throws \Doctrine\ORM\OptimisticLockException
*/
public function onFlush(OnFlushEventArgs $args)
{
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
foreach ($uow->getScheduledEntityUpdates() as $keyEntity => $entity) {
if ($entity instanceof Bill) {
$em->refresh($entity);
$this->createPdfs($entity);
}
}
}
$this->getConfigurationPool()
->getContainer()
->get('Doctrine')
->getRepository("...")
->find(id here);
So leave out the getManager() part;
Related
The built-in controller for resetting the password Auth \ Reset Password Controller has the reset function
public function reset(Request $request)
{
$request->validate($this->rules(), $this->validationErrorMessages());
// Here we will attempt to reset the user's password. If it is successful we
// will update the password on an actual user model and persist it to the
// database. Otherwise we will parse the error and return the response.
$response = $this->broker()->reset(
$this->credentials($request), function ($user, $password) {
$this->resetPassword($user, $password);
}
);
// If the password was successfully reset, we will redirect the user back to
// the application's home authenticated view. If there is an error we can
// redirect them back to where they came from with their error message.
return $response == Password::PASSWORD_RESET
? $this->sendResetResponse($request, $response)
: $this->sendResetFailedResponse($request, $response);
}
Well, here we are working with the user and their incoming data. However, I can't understand where the work with the password_resets table (built-in) is going? After all, after password recovery, entries are added/deleted there. I think (maybe incorrectly) that this is implemented in the broker () method, but I can't find it in the hierarchy of traits, interfaces, and other classes.
Checkout /vendor/laravel/framework/src/Illuminate/Auth/Passwords/DatabaseTokenRepository.php
This is the default class that implements the TokenRepositoryInterface and is used by the /vendor/laravel/framework/src/Illuminate/Auth/Passwords/PasswordBroker.php.
Inside this class you can find all the actual functionality that handles the password resets including the table operations you mention. For example, one method you'll find is:
/**
* Create a new token record.
*
* #param \Illuminate\Contracts\Auth\CanResetPassword $user
* #return string
*/
public function create(CanResetPasswordContract $user)
{
$email = $user->getEmailForPasswordReset();
$this->deleteExisting($user);
// We will create a new, random token for the user so that we can e-mail them
// a safe link to the password reset form. Then we will insert a record in
// the database so that we can verify the token within the actual reset.
$token = $this->createNewToken();
$this->getTable()->insert($this->getPayload($email, $token));
return $token;
}
I need to login a user manually by using the username and password and
returning json response without using the symfony form.
This is the controller where the login happens I get the username and password as requests
use Gabriel\LiveLoginBundle\Entity\LiveUser;
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
class AuthController extends Controller
{
/**
* #Route("/livelogin",name="liveloginroute")
* #Template()
*/
public function loginAction(Request $request)
{
$username = $request->request->get('usrn');
$password = $request->request->get('pwd');
$roles = array('ROLE_USER');
$providerkey = 'livemain'; //firewall name
$em = $this->getDoctrine()->getManager();
$user = $em->getRepository('GabrielLiveLoginBundle:LiveUser')->findBy(array('username'=>$username));
if(!$user)
{
$response = array('usernmae'=>'not_found');
new Response(json_encode($response));
}
$token = new UsernamePasswordToken(
$user, $password,$providerkey,$roles);
$this->get( 'security.context' )->setToken( $token );
$this->get( 'event_dispatcher' )->dispatch(
AuthenticationEvents::AUTHENTICATION_SUCCESS,
new AuthenticationEvent( $token ) );
$this->container->get('security.context')->setToken($token);
//success
When I try to login this way symfony throws an exception
$user must be an instanceof UserInterface, an object implementing a
__toString method, or a primitive string.
Although my liveUser class is indeed implementing the UserInterface
/**
* LiveUser
*
* #ORM\Table()
* #ORM\Entity(repositoryClass="Gabriel\LiveLoginBundle\Entity\LiveUserRepository")
*/
class LiveUser implements UserInterface
{
I also added the necessary methods that UserInterface requires.
Maybe there's something wrong with my firewall setup?
security:
encoders:
Gabriel\LiveLoginBundle\Entity\LiveUser: sha512
role_hierarchy:
ROLE_USER: ROLE_USER
ROLE_ADMIN: [ROLE_ADMIN,ROLE_USER]
providers:
livemain:
entity: { class: GabrielLiveLoginBundle:LiveUser, property: username }
firewalls:
livemain:
pattern: ^/
anonymous: true
Answer
This is the code on the controller that works
$username = $request->request->get('usrn');
$password = $request->request->get('pwd');
$roles = array('ROLE_USER');
$providerkey = 'livemain'; //firewall name
$em = $this->get('doctrine')->getManager();
$query = $em->createQuery("SELECT u FROM GabrielLiveLoginBundle:LiveUser u WHERE u.username = :username");
$query->setParameter('username', $username);
$user = $query->getOneOrNullResult();
if(!$user)
{
$response = array('usernmae'=>'not_found');
new Response(json_encode($response));
}
$token = new UsernamePasswordToken(
$user, $password,$providerkey,$roles);
$this->get( 'security.context' )->setToken( $token );
$this->get( 'event_dispatcher' )->dispatch(
AuthenticationEvents::AUTHENTICATION_SUCCESS,
new AuthenticationEvent( $token ) );
// Fire the login event
// Logging the user in above the way we do it doesn't do this automatically
$event = new InteractiveLoginEvent($request, $token);
$this->get("event_dispatcher")->dispatch("security.interactive_login", $event);
//success
$response = array('i'=>0,'password'=>$password, 'username'=>$username);
return new Response(json_encode($response));
When you use $repository->findBy() an array of objects is returned. In your case you want to find only one (or no) user. In that case you should use:
$em = $this->get('doctrine')->getManager();
$query = $em->createQuery("SELECT u FROM GabrielLiveLoginBundle:LiveUser u WHERE u.username = :username");
$query->setParameter('username', $username);
$user = $query->getOneOrNullResult();
Using the FOSUserbundle, I want to achieve the following:
1) User submits a POST with "username" and "password" parameters. Password is in plaintext.
2) In a controller, I parse the parameters as usual:
/**
* #Route("/api/token",name="api_token")
*/
public function tokenAction(Request $request) {
$username = $request->get('username');
$password = $request->get('password');
// ...
}
3) Finally, if the credentials match, I return something.
Note that I want to do this WITHOUT modifying the session, i.e. without actually authenticating the user and setting a token. I only want to check if the credentials match.
UPDATE
The accepted answer works, but only under the assumption that you have an active session. If you want to solve the case where you simply expose a REST layer or the like (which was my usecase), you can do the following, assuming your usernames are unique:
/**
* #Route("/api/token",name="api_token", options={"expose"=true})
*/
public function getTokenAction(Request $request) {
$username = $request->get('username');
$password = $request->get('password');
$user = $this->getDoctrine()
->getRepository('YourUserClass')
->findOneBy(["username" => $username]);
$encoder = $this->get('security.encoder_factory')->getEncoder($user);
$isValidPassword = $encoder->isPasswordValid($user->getPassword(),
$password,
$user->getSalt());
if ($isValidPassword) {
// Handle success
} else {
// Handle error
}
}
You should use an authentication service to do this, writing all code in controller is not a best practice. Anyway, to your answer, you can use this:
/**
* #Route("/api/token",name="api_token")
*/
public function tokenAction(Request $request) {
$username = $request->get('username');
$password = $request->get('password');
// fetch your user using user name
$user = ...
//If your controller is extended from Symfony\Bundle\FrameworkBundle\Controller\Controller
$encoder = $this->get('security.encoder_factory')->getEncoder($user);
//If you are extending from other ContainerAware Controller you may have to do
//$this->container->get('security.encoder_factory')->getEncoder($user)
//Check your user
$isValidPassword = $encoder->isPasswordValid(
$user->getPassword(),
$password,
$user->getSalt()
);
if ($isValidPassword) {
//.... do your valid stuff
}else{
//... Do your in valid stuff
}
}
I'm trying to create a user with the Zizaco/confide library, the user data comes from Facebook and not from a form.
I try this :
$user_profile = Facebook::api('/me','GET');
$new_user = new User;
$new_user->username = $user_profile['name'];
$new_user->password = Hash::make('secret');
$new_user->email = $user_profile['email'];
$new_user->confirmation_code = '456456';
$new_user->confirmed = true;
$new_user->save();
but it doesn't save the user. Any help ?
I found the problem, the confide library sets some default rules to create the user, you need to pass this rules to save a user:
public static $rules = array(
'username' => 'required|alpha_dash|unique:users',
'email' => 'required|email|unique:users',
'password' => 'required|between:4,11|confirmed',
'password_confirmation' => 'between:4,11',
);
with this example it works:
$user = new User();
$user->username = 'asdasd';
$user->email = 'angorusadf#gmail.com';
$user->password = '12312312';
$user->password_confirmation = '12312312';
$user->save();
Mabe the method should give you some information when you don't pass the rules.
Maybe it's a pretty late answer, but I'm posting this for future reference because I was myself looking for an answer to the same question, How to create a user programatically in zizaco/confide? or more generally, how to bypass zizaco/confide's requirement for setting a password when saving a user for the first time?
well, the answer is pretty simple, thanks to the new architecture which Zizaco implemented in the 4.* branch, it's now possible to register a new user validator class see more at the package's readme in short all you need in this very case though, is just to extend the current User validator and override validatePassword() to make it accept empty passwords for new users.
Below is an example implementation:
In routes.php
// we need to register our validator so that it gets used instead of the default one
// Register custom Validator for ConfideUsers
App::bind('confide.user_validator', 'MyUserValidator');
In app/models/MyUserValidator.php (that's basically a copy of the function in the class, simply just added a check whether this is a old user or not (if the user has an ID then this is an update operation) if this is a new user, the method always returns true!
/**
* Class MyUserValidator
* Custom Validation for Confide User
*/
class MyUserValidator extends \Zizaco\Confide\UserValidator //implements \Zizaco\Confide\UserValidatorInterface
{
/**
* Validates the password and password_confirmation of the given
* user
* #param ConfideUserInterface $user
* #return boolean True if password is valid
*/
public function validatePassword(\Zizaco\Confide\ConfideUserInterface $user)
{
$hash = App::make('hash');
if($user->getOriginal('password') != $user->password && $user->getOriginal('id')) {
if ($user->password == $user->password_confirmation) {
// Hashes password and unset password_confirmation field
$user->password = $hash->make($user->password);
unset($user->password_confirmation);
return true;
} else {
$this->attachErrorMsg(
$user,
'validation.confirmed::confide.alerts.wrong_confirmation',
'password_confirmation'
);
return false;
}
}
unset($user->password_confirmation);
return true;
}
}
Say for example I grant a new role to the currently authenticated user in a controller, like so:
$em = $this->getDoctrine()->getManager();
$loggedInUser = $this->get('security.context')->getToken()->getUser();
$loggedInUser->addRole('ROLE_XYZ');
$em->persist($loggedInUser);
$em->flush();
On the next page load, when I grab the authenticated user again:
$loggedInUser = $this->get('security.context')->getToken()->getUser();
They are not granted the role. I am guessing this is because the user is stored in the session and needs to be refreshed.
How do I do this?
I am using FOSUserBundle if that makes a difference.
EDIT: This question was originally asked in the context of Symfony version 2.3 but there are answers for more recent versions below as well.
Try this:
$em = $this->getDoctrine()->getManager();
$loggedInUser = $this->get('security.context')->getToken()->getUser();
$loggedInUser->addRole('ROLE_XYZ');
$em->persist($loggedInUser);
$em->flush();
$token = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken(
$loggedInUser,
null,
'main',
$loggedInUser->getRoles()
);
$this->container->get('security.context')->setToken($token);
There's no need for the token reset in the previous answer. Just, in your security config file (security.yml, etc...), add this:
security:
always_authenticate_before_granting: true
While an answer is accepted, Symfony actually has a native way to refresh the User object. Credit goes out to Joeri Timmermans for this article.
Steps for refreshing the User object:
Make your User entity implement the interface
Symfony\Component\Security\Core\User\EquatableInterface
Implement the abstract function isEqualTo:
public function isEqualTo(UserInterface $user)
{
if ($user instanceof User) {
// Check that the roles are the same, in any order
$isEqual = count($this->getRoles()) == count($user->getRoles());
if ($isEqual) {
foreach($this->getRoles() as $role) {
$isEqual = $isEqual && in_array($role, $user->getRoles());
}
}
return $isEqual;
}
return false;
}
The code above refreshes the User object if any new roles are added. The same principle also holds true for other fields you compare.
$user = $this->getUser();
$userManager = $this->get('fos_user.user_manager');
$user->addRole('ROLE_TEACHER');
$userManager->updateUser($user);
$newtoken = new \Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken($user,null,'main', $user->getRoles());
$token = $this->get('security.token_storage')->setToken($newtoken);
In Symfony 4
public function somename(ObjectManager $om, TokenStorageInterface $ts)
{
$user = $this->getUser();
if ($user) {
$user->setRoles(['ROLE_VIP']); //change/update role
// persist if need
$om->flush();
$ts->setToken(
new PostAuthenticationGuardToken($user, 'main', $user->getRoles())
);
//...
} else {
//...
}
}