User account management, roles, permissions, authentication PHP and MySQL - Part 3


This is part 3 of a series of tutorials on how to create a user accounts management system. You can find the other parts here: part 1, part 2.

Form Validation

At this point on the signup.php page, if you just click the signup button without filling out any of the form fields, you don't get any feedback but the form also doesn't do anything. It just stays there staring at you. It stays like that because there are errors in an $errors from our validateUser() function we defined earlier which we are not yet displaying on the form. These errors exist in key-value pairs.

For example, $errors['username'] holds the error, if any, for the username field. So we can check if the username error exists then we add the bootstrap class has-error to the div element that wraps the username input field. This makes the label text and input border red in color indicating there is an error. 

ad

We will be validating the username, email, password, password confirm fields. So open your signup.php file and replace these four fields with this code:

<div class="form-group <?php echo isset($errors['username']) ? 'has-error' : '' ?>">
  <label class="control-label">Username</label>
  <input type="text" name="username" value="<?php echo $username; ?>" class="form-control">
  <?php if (isset($errors['username'])): ?>
    <span class="help-block"><?php echo $errors['username'] ?></span>
  <?php endif; ?>
</div>
<div class="form-group <?php echo isset($errors['email']) ? 'has-error' : '' ?>">
  <label class="control-label">Email Address</label>
  <input type="email" name="email" value="<?php echo $email; ?>" class="form-control">
  <?php if (isset($errors['email'])): ?>
    <span class="help-block"><?php echo $errors['email'] ?></span>
  <?php endif; ?>
</div>
<div class="form-group <?php echo isset($errors['password']) ? 'has-error' : '' ?>">
  <label class="control-label">Password</label>
  <input type="password" name="password" class="form-control">
  <?php if (isset($errors['password'])): ?>
    <span class="help-block"><?php echo $errors['password'] ?></span>
  <?php endif; ?>
</div>
<div class="form-group <?php echo isset($errors['passwordConf']) ? 'has-error' : '' ?>">
  <label class="control-label">Password confirmation</label>
  <input type="password" name="passwordConf" class="form-control">
  <?php if (isset($errors['passwordConf'])): ?>
    <span class="help-block"><?php echo $errors['passwordConf'] ?></span>
  <?php endif; ?>
</div>

Just below each input field, we are conditionally displaying the error message for each form field.

Just in case you didn't know about the ternary operator, here is a brief explanation.

<?php echo isset($errors['username']) ? 'has-error' : '' ?>

This statement basically says that if the $errors['username'] variable is set to a value (is not empty), display has-error, otherwise display an empty string. It is basically just an if-else statement.

Now you can try this validation out by clicking on the empty form. You will see nice formatted validating messages.

vli_ad

 

 

User login

In the root folder of your application, create a file named login.php.

login.php:

<?php include('config.php'); ?>
<?php include(INCLUDE_PATH . '/logic/userSignup.php'); ?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>UserAccounts - Login</title>
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
  <!-- Custome styles -->
  <link rel="stylesheet" href="assets/css/style.css">
</head>
<body>
  <?php include(INCLUDE_PATH . "/layouts/navbar.php") ?>
  <div class="container">
    <div class="row">
      <div class="col-md-4 col-md-offset-4">
        <form class="form" action="login.php" method="post">
          <h2 class="text-center">Login</h2>
          <hr>
          <!-- display form error messages  -->
          <?php include(INCLUDE_PATH . "/layouts/messages.php") ?>
          <div class="form-group <?php echo isset($errors['username']) ? 'has-error' : '' ?>">
            <label class="control-label">Username or Email</label>
            <input type="text" name="username" id="password" value="<?php echo $username; ?>" class="form-control">
            <?php if (isset($errors['username'])): ?>
              <span class="help-block"><?php echo $errors['username'] ?></span>
            <?php endif; ?>
          </div>
          <div class="form-group <?php echo isset($errors['password']) ? 'has-error' : '' ?>">
            <label class="control-label">Password</label>
            <input type="password" name="password" id="password" class="form-control">
            <?php if (isset($errors['password'])): ?>
              <span class="help-block"><?php echo $errors['password'] ?></span>
            <?php endif; ?>
          </div>
          <div class="form-group">
            <button type="submit" name="login_btn" class="btn btn-success">Login</button>
          </div>
          <p>Don't have an account? <a href="signup.php">Sign up</a></p>
        </form>
      </div>
    </div>
  </div>
<?php include(INCLUDE_PATH . "/layouts/footer.php") ?>

Now open userSignup.php and at the end of the file, add this code to log in user:

// ...

// USER LOGIN
if (isset($_POST['login_btn'])) {
        // validate form values
        $errors = validateUser($_POST, ['login_btn']);
        $username = $_POST['username'];
        $password = $_POST['password']; // don't escape passwords.

        if (empty($errors)) {
                $sql = "SELECT * FROM users WHERE username=? OR email=? LIMIT 1";
                $user = getSingleRecord($sql, 'ss', [$username, $username]);

                if (!empty($user)) { // if user was found
                        if (password_verify($password, $user['password'])) { // if password matches
                                // log user in
                                loginById($user['id']);
                        } else { // if password does not match
                                $_SESSION['error_msg'] = "Wrong credentials";
                        }
                } else { // if no user found
                        $_SESSION['error_msg'] = "Wrong credentials";
                }
        }
}

If you click on the login button without filling it, validation messages will appear on the form just like the case of the signup page. 

Now enter the email and password for the user account we created earlier and click on login button. If the credentials were correct, you will be logged in and redirected to the homepage. A flash message will display that tells you that you are now logged in. 

But you will notice that even though the user is now logged in, the signup and login links in the navbar are still being displayed, which doesn't make sense, right? Let's replace them with the logged in username and a drop-down with a logout link on it.

vli_ad

Open the navbar.php file and replace the code that is in it with this one:

navbar.php:

<!-- the whole site is wrapped in a container div to give it some margin on the sides -->
<!-- closing container div can be found in the footer -->
<div class="container">
  <nav class="navbar navbar-default">
    <div class="container-fluid">
      <div class="navbar-header">
        <a class="navbar-brand" href="#">UserAccounts</a>
      </div>
      <!-- <ul class="nav navbar-nav">
        <li class="active"><a href="#">Home</a></li>
        <li><a href="#">Page 1</a></li>
        <li><a href="#">Page 2</a></li>
      </ul> -->
      <ul class="nav navbar-nav navbar-right">
        <?php if (isset($_SESSION['user'])): ?>
          <li class="dropdown">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
              <?php echo $_SESSION['user']['username'] ?> <span class="caret"></span></a>

              <?php if (isAdmin($_SESSION['user']['id'])): ?>
                <ul class="dropdown-menu">
                  <li><a href="<?php echo BASE_URL . 'admin/profile.php' ?>">Profile</a></li>
                  <li><a href="<?php echo BASE_URL . 'admin/dashboard.php' ?>">Dashboard</a></li>
                  <li role="separator" class="divider"></li>
                  <li><a href="<?php echo BASE_URL . 'logout.php' ?>" style="color: red;">Logout</a></li>
                </ul>
              <?php else: ?>
                <ul class="dropdown-menu">
                  <li><a href="<?php echo BASE_URL . 'logout.php' ?>" style="color: red;">Logout</a></li>
                </ul>
              <?php endif; ?>
          </li>
        <?php else: ?>
          <li><a href="<?php echo BASE_URL . 'signup.php' ?>"><span class="glyphicon glyphicon-user"></span> Sign Up</a></li>
          <li><a href="<?php echo BASE_URL . 'login.php' ?>"><span class="glyphicon glyphicon-log-in"></span> Login</a></li>
        <?php endif; ?>
      </ul>
    </div>
  </nav>

Now refresh the index.php page. If you were still logged in, you will see that the header has changed and now displays your username on the navbar. When you click on the username, a drop-down will appear with a logout link on it. If you click on it, it will say page not found because we haven't created the logout.php file yet. Let's create that file now in the root folder of our application.

logout.php

<?php
  session_start();
  session_destroy();
  unset($_SESSION['user']);
  header("location: login.php");
?>

And we are done with the normal user authentication. Now we move into the crux of the matter which is the admin section. Hope you are enjoying it. 

ad

Right now we are logging the user in through only one function, the loginById() function. In that function, if the user who is logging in is found to be an administrative user, they are redirected to the dashboard.php file. 

Admin Section

Inside admin folder, create the dashboard.php file:

dashboard.php:

<?php include('../config.php') ?>
<?php include(ROOT_PATH . '/admin/middleware.php') ?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Admin</title>
  <!-- Bootstrap CSS -->
  <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" />
  <!-- Custome styles -->
  <link rel="stylesheet" href="../static/css/style.css">
</head>
<body>
  <?php include(INCLUDE_PATH . "/layouts/admin_navbar.php") ?>

  <div class="col-md-4 col-md-offset-4">
      <h1 class="text-center">Admin</h1>
      <br />
      <ul class="list-group">
        <a href="<?php echo BASE_URL . 'admin/posts/postList.php' ?>" class="list-group-item">Manage Posts</a>
        <a href="<?php echo BASE_URL . 'admin/users/userList.php' ?>" class="list-group-item">Manage Users</a>
        <a href="<?php echo BASE_URL . 'admin/roles/roleList.php' ?>" class="list-group-item">Manage Roles</a>
      </ul>
  </div>
  <?php include(INCLUDE_PATH . "/layouts/footer.php") ?>
</body>
</html>

On your browser, visit http://localhost/user-accounts/admin/dashboard.php and you will see some warning messages. That is because we are including some two files that do not yet exist: middleware.php and admin_navbar.php.

As for middleware.php, we will work on it later. But for now just create it inside admin folder and leave it blank so that the warning message can disappear and leave us in peace.

ad

As for the admin_navbar.php, create it inside the includes/layouts folder:

admin_navbar.php:

<!-- the whole site is wrapped in a container div to give it some margin on the sides -->
<!-- closing container div can be found in the footer -->
<div class="container">
  <nav class="navbar navbar-inverse">
    <div class="container-fluid">
      <div class="navbar-header">
        <a class="navbar-brand" href="<?php echo BASE_URL . 'admin/dashboard.php' ?>">Dashboard</a>
      </div>
      <ul class="nav navbar-nav navbar-right">
        <?php if (isset($_SESSION['user'])): ?>
          <li><a href="<?php echo BASE_URL . 'index.php' ?>"><span class="glyphicon glyphicon-globe"></span></a></li>
          <li class="dropdown">
            <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
              <?php echo $_SESSION['user']['username'] . ' (' . $_SESSION['user']['role'] . ')'; ?> <span class="caret"></span></a>
            <ul class="dropdown-menu">
              <li><a href="<?php echo BASE_URL . 'admin/users/editProfile.php' ?>">Profile</a></li>
              <li><a href="<?php echo BASE_URL . 'admin/dashboard.php' ?>">Dashboard</a></li>
              <li role="separator" class="divider"></li>
              <li><a href="<?php echo BASE_URL . 'logout.php' ?>" style="color: red;">Logout</a></li>
            </ul>
          </li>
        <?php endif; ?>
      </ul>
    </div>
  </nav>
  <?php include(INCLUDE_PATH . "/layouts/messages.php") ?>

On your browser, refresh the dashboard.php page now and the warning messages are gone.

The dashboard.php is the admin area, right? It is not supposed to be accessed by ordinary users. So we need to redirect any normal user attempting to visit this page back to the homepage. Also, we are not yet creating/managing admin users and roles. All of that is coming soon.

Let's end this part here. In the next part, we proceed with managing admin user accounts and also access control.

If you are enjoying these tutorials and want more of them, please consider sharing/recommending the tutorials among your friends. That will go a really long way to support me in creating more of this.

Thanks for following and see you in the next part.

Awa Melvine

ad


Comments