Changed user password programmatically by username - drupal

I'm am trying to change the password of a given user by their username in a drupal 9 module but I keep getting this error:
Error: Call to a member function setPassword() on array in _password_change()
This is the function I am using:
$userName = 'user1';
$password = 'Password1';
$nid = '1';
function _password_change($userName, $password) {
$user_storage = \Drupal::EntityTypeManager()->getStorage('user');
$user = $user_storage->loadByProperties(['name' => $userName]);
$user->setPassword($password);
$user->save();
}
If I use $user = $user_storage->load($nid); instead of $user = $user_storage->loadByProperties(['name' => $userName]); the code runs fine and the password gets applied successfully unfortunely, the given information will be a username and not the entity id.
The $userName , $password and $nid are set manually for testing proposes.
For what I can tell if I call it using the load id i get back an object but if i call it using the loadByProperties i get back and array hence it can't apply the setPassword function.
What would be a way to load the entity object by the username as an object and be able to apply the new password?

loadByProperties returns an array of entity objects.
So you want call setPassword on the first item in the array which should be your user object.
While you are there, you should also probably check that there was a user with the given username by checking the length of the array returned by loadByProperties.
function _password_change($userName, $password) {
$user_storage = \Drupal::EntityTypeManager()->getStorage('user');
$users = $user_storage->loadByProperties(['name' => $userName]);
// check we got 1 (only 1) user
if (count($users) == 1) {
//get the user from the array.
$user = reset($users);
$user->setPassword($password);
$user->save();
}
}
This code is untested, but you get the idea.

Related

I get error on Adding custom action in symfony Warning: array_merge(): Expected parameter 2 to be an array, null given

I have a dating website programmed with symfony. I want to add a function in easyadmin to be able to delete a user and her/his messages and posts. In UserCrudController I followed the instruction in symfony website and write a custom action. But I get an error
Warning: array_merge(): Expected parameter 2 to be an array, null given
public function configureActions(Actions $actions): Actions
{
// this action executes the 'renderInvoice()' method of the current CRUD controller
$delUserAction = Action::new('deleteUser', 'Delete user completely')
->linkToRoute('delete_user', function (User $user){
$id = $user->getId();
$res = $this->getDoctrine()->getRepository(User::class)->find($id);
if ($res) {
$em = $this->getDoctrine()->getManager();
$em->remove($res);
$em->flush();
}
$res = $this->getDoctrine()->getRepository(Message::class)->remove_all_message($id);
$res = $this->getDoctrine()->getRepository(Beziehungen::class)->remove_all_relations($id);
$res = $this->getDoctrine()->getRepository(Album::class)->remove_album_of_user($id);
$res = $this->getDoctrine()->getRepository(Blog::class)->remove_blog_of_user($id);
$res = $this->getDoctrine()->getRepository(Comment::class)->remove_comments_of_user($id);
$res = $this->getDoctrine()->getRepository(Subcomment::class)->remove_subcomments_of_user($id);
$res = $this->getDoctrine()->getRepository(Like::class)->remove_likes_of_user($id);
});
return $actions
// ...
->add(Crud::PAGE_INDEX, $delUserAction)
;
}
Can you please help me?
According to the tutorial, linkToRoute will connect an action to a route (as the name suggests). Similar to other route functions, it expects as first parameter the route name and as second parameter an array or a function that returns an array - the parameters to fill the placeholders of the route.
Your function doesn't return an array but instead IS the action.
So you really should put the deletion code you have and put it into an extra function like ... deleteUserAction, and then define your action as
$delUserAction = Action::new('deleteUser', 'Delete user completely')
->linkToCrudAction('deleteUserAction');

How to properply update an entity which contains an Image path?

I have a form that I use both for registration and edition of the user informations. This form contains a profile picture property on which I put #Assert\Image.
I succeed in creating a new user through my registration form but when I try to edit the user informations (with a PATCH method, just to update what need to be updated) I encounter an error with a 'File could not be found' message.
I suppose it's because the path stored in the database is a string and my #Assert\Image want an image.
I'm not sure about how I should manage this kind of update.
When I dd() the $user right after the submission, I see that the profilePicture property still contains the path saved in the database.
Here is my function regarding the form handling:
public function myProfile(Request $request)
{
$user = $this->getUser();
$form = $this->createForm(UserFormType::class, $user, ['method' => 'PATCH']);
if ($request->isMethod('PATCH')){
$form->submit($request->request->get($form->getName()), false);
if ($form->isSubmitted() && $form->isValid()) {
//...
}
}
//if no request just display the page
return $this->render('connected/myProfile.html.twig', [
'user' => $user,
'userProfileForm' => $form->createView()
]);
}
The Validator will check if your object contains a image and that seems not the case when you’re updating your object.
A workaround is to use group validation you define a specific group to the property that have the assert Image and in the method getGroupSequence you return the group if you’re in creation (id == null) or if the property is setted.

Catchable fatal error: Object of class WP_User could not be converted to string in /directory/ on line 139

I'm currently trying to get the username of whomever submitted a form, to be stored in a database along with the value they submitted.
This is line 139 from my function:
$retval = $newdb->query("SELECT * FROM $table WHERE user = '$hf_username' ");
add_action("init", "ref_access");
function ref_access() {
global $error;
if ( is_user_logged_in() ) {
global $quanid;
global $newdb;
$hf_username = wp_get_current_user();
$inputValue = isset($_POST['$quanid']);
$newdb = new wpdb( 'user', 'password', 'db', 'localhost' );
$retval = $newdb->query("SELECT * FROM $table WHERE user = '$hf_username' ");
if( mysqli_num_rows($retval) > 0)
{
$newdb->query("UPDATE table SET user = '$hf_username' "); //If user exists, update
}
else
{
global $table;
global $newdb;
$newdb->insert(
$table,
array(
'ItemID' => $quanid,
'Price' => $inputValue,
'user' => $hf_username
)
);
}
} else {
$error = "Error: You must be logged in to submit prices";
return;
}
}
wp_get_current_user returns a WP_User object, not the username as a string. The username is stored in the user_login property of the WP_User object.
$hf_user = wp_get_current_user();
$hf_username = $hf_user->user_login;
That said, there are a couple of things that seem a bit off with your code:
You seem to be mixing the WordPress database functions with the built in mysqli_ functions. $newdb->query will already return the number of selected rows, there is no need to call mysqli_num_rows and it will not work the way you expect it.
You're declaring a new instance of wpdb instead of using the existing database object via global $wpdb. This is not necessary if you're accessing the same database that WordPress is installed on.

From symfony2 form to a simple form users get incorrect password

Originally creating a normal registration form (email + password) using the symfony form builder i found no problems at all registering my users.
For some technical issues and strategic stuff im not using any more the symfony form builder and i just made a common html form. The username, salt and password gets saved in database but when i tried to login it does not work, so the password or salt are wrong, and that makes me think that maybe the salt is created using a token send as a hidden field created by the symfony form builder, am i right?
So, originally since the symfony form builder allows you to parse the data directly into an entity i did something like this:
if( 'POST' === $this->getRequest( )->getMethod() ) {
$form->bindRequest( $this->getRequest( ) );
if( $form->isValid( ) ) {
$userSignup = $form->getData( );
$user = $userSignup->getUser( );
$user->setPassword( $this->_encodePassword( $user ) );
Now, since im using a normal form:
if(isset($_GET['user_signup']['user']['username']) && $this->_validemail($_GET['user_signup']['user']['username'])) $username = $_GET['user_signup']['user']['username']; else die('BAD EMAIL');
if(isset($_GET['user_signup']['user']['password']) && strlen($_GET['user_signup']['user']['password']) >= 5 && strlen($_GET['user_signup']['user']['password']) <= 20) $password = $_GET['user_signup']['user']['password']; else die('BAD PASSWORD');
$user = new user();
$user->setUsername($username);
$user->setPassword( $this->_encodePassword( $user ) );
The encodePassword function:
protected function _encodePassword( User $user )
{
$factory = $this->get( 'security.encoder_factory' );
$encoder = $factory->getEncoder( $user );
return $encoder->encodePassword( $user->getPassword( ), $user->getSalt( ) );
}
Im re utilizing someone else code so maybe im having trouble understanding how encodePassword works.
If you want registration system in your web application install FOSUserBundle.
Then you can use the userManager for create, register and edit any user.
you should create a salt first (using some random function) and use $user->setSalt($salt) in your controller ...
... or generate the salt inside the User's __construct() method.
FOSUserBundle i.e. creates the salt in the constructor of the User object using:
public function __construct()
{
$this->salt = base_convert(sha1(uniqid(mt_rand(), true)), 16, 36);
// ...
}
reference here.
Otherwise the encoder called at $this->_encodePassword($user) won't find the salt calling the user object's getter getSalt().
tip:
In symfony2 you should never try to access GET parameters using $_GET ...
... use the Request object instead.
Please read the Symfony2 and HTTP Fundamentals chapter of the book.

Ignore Password when user updates profile with FOSUserBundle

I am using FOSUserBundle and I am trying to create a page that allows a user to update their user profile. The problem I am facing is that my form does not require that the user reenter their password if they don't want to change/update their password. So when a user submits the form with an empty password the database will be updated with an empty string, and the user will not be able to log in.
How can I get my form to ignore updating the password field if it is not set? Below is the code I am using.
$user = $this->get('security.context')->getToken()->getUser();
//user form has email and repeating password fields
$userForm = $this->createForm(new UserFormType(), $user);
if ($request->getMethod() == 'POST') {
$userForm->bindRequest($request);
if($userForm->isValid()){
//this will be be empty string in the database if the user does not enter a password
$user->setPlainPassword($userForm->getData()->getPassword());
$em->flush();
}
}
I have tried a few things such as the following, but this is still empty because the bindRequest sets the empty password to the user
if($userForm->getData()->getPassword())
$user->setPlainPassword($userForm->getData()->getPassword());
I have also tried, but this results in a similar situation and causes an unneeded query
if($userForm->getData()->getPassword())
$user->setPlainPassword($userForm->getData()->getPassword());
else
$user->setPlainPassword($user->getPlainPassword());
Are there any elegant ways to handle this use case?
The problem is that you bind a form to a User Object before controls upon password.
Let's analyze your snippet of code.
Do the following
$user = $this->get('security.context')->getToken()->getUser();
will load an existing user into a User Object. Now you "build" a form with that data and if receive a post, you'll take the posted data into the previous object
$userForm = $this->createForm(new UserFormType(), $user);
if ($request->getMethod() == 'POST') {
$userForm->bindRequest($request);
So, onto bindRequest you have alredy lost previous password into the object (obviously not into database yet) if that was leave empty. Every control from now on is useless.
A solution in that case is to manually verify value of form's field directly into $request object before binding it to the underlying object.
You can do this with this simple snippet of code
$postedValues = $request->request->get('formName');
Now you have to verify that password value is filled
if($postedValues['plainPassword']) { ... }
where plainPassword I suppose to be the name of the field we're interesting in.
If you find that this field contain a value (else branch) you haven't to do anything.
Otherwise you have to retrieve original password from User Object and set it into $request corrisponding value.
(update) Otherwise you may retrieve password from User Object but since that password is stored with an hased valued, you can't put it into the $request object because it will suffer from hashing again.
What you could do - i suppose - is an array_pop directly into $request object and put away the field that messes all the things up (plainPassword)
Now that you had done those things, you can bind posted data to underlying object.
Another solution (maybe better because you move some business logic away from controller) is to use prePersist hook, but is more conceptually advanced. If you want to explore that solution, you can read this about form events
I think you should reconsider if this is in fact a good use case. Should users be able to edit other users passwords? At our institution we do not allow even the highest level admin to perform this task.
If a user needs their password changed we let them handle that themselves. If they have forgotten their password we allow them to retrieve it via email. If they need assistance with adjusting their email we allow our admins to assist users then. But all password updating and creation is done soley by the user.
I think it is great that FOSUserBundle makes it so difficult to do otherwise but if you must DonCallisto seems to have a good solution.
<?php
class User
{
public function setPassword($password)
{
if (false == empty($password)) {
$this->password = $password;
}
}
}
This will only update the password on the user if it isn't empty.
I have found a simple hack to get rid of the "Enter a password" form error.
Manualy set a dummy plainPassword in the user entity. After form validation just reset it before you flush the entity.
<?php
public function updateAction(Request $request, $id)
{
$em = $this->getDoctrine()->getManager();
$entity = $em->getRepository('AppBundle:User')->find($id);
if (!$entity) {
throw $this->createNotFoundException('Unable to find Customer entity.');
}
$deleteForm = $this->createDeleteForm($id);
$editForm = $this->createEditForm($entity);
$postedValues = $request->request->get('appbundle_user');
/* HERE */ $entity->setPlainPassword('dummy'); // hack to avoid the "enter password" error
$editForm->handleRequest($request);
if ($editForm->isValid()) {
/* AND HERE */ $entity->setPlainPassword(''); // hack to avoid the "enter password" error
$em->flush();
return $this->redirect($this->generateUrl('customer_edit', array('id' => $id)));
}
return array(
'entity' => $entity,
'edit_form' => $editForm->createView(),
'delete_form' => $deleteForm->createView(),
);
}

Resources