PHP PDO Reset User Password

I’m working on trying to build an attendance application using PHP, PDO and MySQL. I currently have a table called users that just shows a list of users that can log into the application. The table structure is shown below.

I have an action column that shows two links: One to edit the user and one to delete the user.

image

When the edit icon is clicked, a form appears with an option to reset the password at the bottom.

However, there is no functionality to reset the user password.

Here is the edit form’s code:

<?php

include('nav/head.php');

// Define user data by User ID
if(isset($_GET['edit_id'])) {
	 $id = $_GET['edit_id'];
	 $stmt = $pdo->prepare("SELECT * FROM users WHERE id=:id");
	 $stmt->execute(array(":id" => $id));
	 $rowUser = $stmt->fetch(PDO::FETCH_ASSOC);
} else {
	 $id = null;
	 $rowUser = null;
	 
}	 

?>

<!DOCTYPE html>
<html lang="en">

<head>

  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
  <meta name="description" content="">
  <meta name="author" content="">
  
  <link rel="stylesheet" type="text/css" href="css/toggle.css">

  <title>CCRP | <?php print($rowUser['first_name'] . " " . $rowUser['last_name']); ?></title>
  
  <?php include('nav/header.php'); ?>
	
	<h1 class="h3 mb-2 text-gray-800"> Edit <?php print($rowUser['first_name'] . " " . $rowUser['last_name']); ?></h1>
	<br>
	<form action="api/users/edit.php" method="post">
			<input type="hidden" class="form-control" id="id" name="id" placeholder="" value="<?php print($rowUser['id']); ?>" maxlength="255" autocomplete="off" readonly/>	
		<div class="form-group">
			<label for="role_id">User Status</label>
			<!-- <input type="text" class="form-control" id="role_id" name="role_id" placeholder="" value="<?php print($rowUser['role_id']); ?>" maxlength="255" autocomplete="off" /> -->
			<?php
			
			if($rowUser['status'] === '1') {
				echo '<select class="form-control" id="status" name="status">
				<option selected value="1">Active</option>
				<option value="0">Inactive</option>
			</select>';
			} elseif ($rowUser['status'] === '0') {
				echo '<select class="form-control" id="status" name="status">
				<option value="1">Active</option>
				<option selected value="0">Inactive</option>
			</select>';
			}
			
			?>
		</div>	
		<div class="form-group">
			<label for="role_id">User Role</label>
			<!-- <input type="text" class="form-control" id="role_id" name="role_id" placeholder="" value="<?php print($rowUser['role_id']); ?>" maxlength="255" autocomplete="off" /> -->
			<?php
			
			if($rowUser['role_id'] === '1') {
				echo '<select class="form-control" id="role_id" name="role_id">
				<option selected value="1">Administrator</option>
				<option value="2">Operator</option>
			</select>';
			} elseif ($rowUser['role_id'] === '2') {
				echo '<select class="form-control" id="role_id" name="role_id">
				<option value="1">Administrator</option>
				<option selected value="2">Operator</option>
			</select>';
			}
			
			?>
		</div>		
		<div class="form-group">
			<label for="first_name">First Name</label>
			<input type="text" class="form-control" id="first_name" name="first_name" placeholder="" value="<?php print($rowUser['first_name']); ?>" maxlength="255" autocomplete="off" />
		</div>
		<div class="form-group">
			<label for="last_name">Last Name</label>
			<input type="text" class="form-control" id="last_name" name="last_name" placeholder="" value="<?php print($rowUser['last_name']); ?>" maxlength="14" autocomplete="off" />
		</div>
		<div class="form-group">
			<label for="email">Email</label>
			<input type="text" class="form-control" id="email" name="email" placeholder="" value="<?php print($rowUser['email']); ?>" autocomplete="off" />
		</div>
		<div class="form-group">
			<label for="username">Username</label>
			<input type="text" class="form-control" id="username" name="username" placeholder="" value="<?php print($rowUser['username']); ?>" autocomplete="off" />
		</div>
		<hr style="background-color: #a40000;">
		<div class="form-group">
			<label for="password">New Password</label>
			<input type="password" class="form-control" id="password" name="password" placeholder="" autocomplete="off" />
		</div>
		<div class="form-group">
			<label for="confirm_pwd">Confirm Password</label>
			<input type="password" class="form-control" id="confirm_pwd" name="confirm_pwd" placeholder="" autocomplete="off" />
		</div>								
			<input type="submit" name="btn_save" class="btn btn-success" value="Save">
			<input type="submit" name="btn_cancel" class="btn btn-danger" value="Cancel">							
	</form>
	&nbsp;
	&nbsp;
	
        </div>
        <!-- /.container-fluid -->

      </div>
      <!-- End of Main Content -->

	<?php include('nav/footer.php'); ?>

</html>

And here is the edit API:

<?php

include ('../dbconnect.php');

// Update
$stmt = $pdo->prepare("UPDATE users SET role_id = :role_id, first_name = :first_name, last_name = :last_name, email = :email, username = :username, status = :status WHERE id = :id");
$stmt->bindParam(':role_id', $role_id);
$stmt->bindParam(':first_name', $first_name);
$stmt->bindParam(':last_name', $last_name);
$stmt->bindParam(':email', $email);
$stmt->bindParam(':username', $username);
$stmt->bindParam(':status', $status);
$stmt->bindParam(':id', $id);

// Update User Info
if(isset($_POST['btn_save'])) {
  $role_id = $_POST["role_id"];
  $first_name = $_POST["first_name"];
  $last_name = $_POST["last_name"];
  $email = $_POST["email"];
  $username = $_POST["username"];
  $status = $_POST["status"];
  $id = $_POST["id"];  
  $stmt->execute();
  header('Location: ../../users.php');
}

// Return to Users Page
if(isset($_POST['btn_cancel'])) {
  header('Location: ../../users.php');
}

?>

My goal is to basically say if the field has text in it, then hash the password again and update the current password in the table. If the field is empty, keep the current password. What is the best way to do this?

You’ve answered yourself. Now code it. :stuck_out_tongue:

Ha! That’s funny! :smile:

Well, this is my plan:

Step 1: Make sure both fields match before updating:

if ($_POST['password'] != $_POST['confirm_pwd']) {
    die ('The two password provided do not match.');
}

Step 2: Re-Hash the Password:

$password = password_hash($_POST['password'], PASSWORD_DEFAULT);

Step 3: Bind the parameter and execute the statement:

	$stmt->bindParam(5, $password);
	$stmt->execute();

You think this would work?

EDIT

I tried changing the code around with what I posted here, and it worked! Can’t believe I figured that out and that it was that simple!

See, try first, then ask. :wink:

Though i would add a step 0 - make sure there’s SOMETHING in the password field. Otherwise you’ll end up replacing peoples passwords with a hashed empty string.

I’m not sure what to do without making the form field required?

I have this:

if (empty($_POST['password'])) {
	die;
}

But I don’t think it would work. If the field is empty, then I don’t need to update it.

Right, so turn the logic around.

Instead of “If the password is blank, do nothing”
Think of it as “If the password is not blank, do Steps 1-3.”

So would it be:

if (!empty($_POST['password'])) {
     //code here
}

or do I need to code it differently?

This is what the current API looks like:

<?php

include ('../dbconnect.php');

// Update
$stmt = $pdo->prepare("UPDATE users SET role_id = :role_id, first_name = :first_name, last_name = :last_name, email = :email, username = :username, password = :password, status = :status WHERE id = :id");
$stmt->bindParam(':role_id', $role_id);
$stmt->bindParam(':first_name', $first_name);
$stmt->bindParam(':last_name', $last_name);
$stmt->bindParam(':email', $email);
$stmt->bindParam(':username', $username);
$stmt->bindParam(':password', $password);
$stmt->bindParam(':status', $status);
$stmt->bindParam(':id', $id);



// Update User Info
if(isset($_POST['btn_save'])) {
	if ($_POST['password'] != $_POST['confirm_pwd']) {
    	die ('The two password provided do not match.');
	}
  
  $role_id = $_POST["role_id"];
  $first_name = $_POST["first_name"];
  $last_name = $_POST["last_name"];
  $email = $_POST["email"];
  $username = $_POST["username"];
  $password = password_hash($_POST['password'], PASSWORD_DEFAULT);
  $status = $_POST["status"];
  $id = $_POST["id"];  
  $stmt->execute();
  header('Location: ../../users.php');
}

// Return to Users Page
if(isset($_POST['btn_cancel'])) {
  header('Location: ../../users.php');
}

?>

It would work, but maybe take it a step further and validate the password for strength.
At the very least enforce a minimum length, which is simple enough.

What about:

if(!empty($_POST['password']) && strlen($_POST['password']) > 20 || strlen($_POST['password']) < 5)) {
	die ('Password must be between 5 and 20 characters');

I would have to disagree.

What you should do is …

  1. Trim the POST array all at once
  2. Check a whitelist of expected fields.
  3. Check for required fields and empty fields
  4. If errors (not in white list, missing required, empty), Add error to error array.
  5. Check error array. if any errors, loop over errors and display, else carry on with processing.
2 Likes

Yes, validating, now it’s been mentioned, is a different subject. Not doing the update because the input is invalid, and not doing it because the user didn’t request to update. are two different things.
I have thrown another complication in by mentioning it, but validation should be a part of any form processing.

2 Likes