Checking permissions

Tags

Checking roles

Let's write code for the user list program. Remember, the user list should only be visible to admins. Anyone else who tries to access the page should get an error.

This code will do it.

  1. <?php
  2. // List users.
  3. require_once 'library/useful-stuff.php';
  4. $errorMessage = '';
  5. // Check permission.
  6. if (! hasRole(ROLE_NAME_ADMIN)) {
  7.     header('HTTP/1.0 403 Forbidden');
  8.     exit();
  9. }
  10. $userEntities = getAllUsers();

Line 3 adds our code library, as usual. Line 6 does the check:

  • if (! hasRole(ROLE_NAME_ADMIN)) {

In the code library, we'll write a function called hasRole(). It takes one parameter: the role to check for. hasRole() returns true if the current user has that role, or false if not.

So, if the current user does not have the admin role, then:

  • header('HTTP/1.0 403 Forbidden');
  • exit();

We used header() before to redirect the browser to another page. This time, we tell the browser that it's forbidden from accessing the page. The exit() is necessary, since header() only works when it's the first thing on the page, and nothing follows it.

We also want to restrict access to the sales page, to managers. Here's code from that page.

  • // Check permission.
  • if (! hasRole(ROLE_NAME_MANAGER)) {
  •     header('HTTP/1.0 403 Forbidden');
  •     exit();
  • }

This checks a different role, manager instead of admin.

For this code to work, we need to write hasRole(). So, how is hasRole() going to know whether the current user is logged in?

Sessions

We're going to use sessions to keep track of whether people are logged in. We used sessions earlier. Let's remind ourselves.

Say there are four people using your app.

Four people using app

Each person will have a separate chunk of memory on the server, accessed with $_SESSION. We can keep any data we want there. $_SESSION for Sarah is different from $_SESSION for Linda is different from $_SESSION for Teagan is different from $_SESSION for Joe. Put data into Sarah's $_SESSION, and the others will not be affected.

Suppose Linda and Sarah are logged in, but the others are not. We can put data in their $_SESSIONs, to show:

  • They're logged in
  • Their username
  • Their roles

Four sessions

Writing hasRole()

hasRole() takes one param: a permission to check. It returns true if the current user has the role, false if not.

Here's code for it:

  1. session_start();
  2. ...
  3. const ROLE_NAME_ADMIN = 'admin';
  4. const ROLE_NAME_MANAGER = 'manager';
  5. ...
  6. /**
  7.  * Is the current user logged in?
  8.  * @return bool True if logged in, else false.
  9.  */
  10. function isLoggedIn() {
  11.     $loggedIn = false;
  12.     if (isset($_SESSION['logged_in'])) {
  13.         if ($_SESSION['logged_in']) {
  14.             $loggedIn = true;
  15.         }
  16.     }
  17.     return $loggedIn;
  18. }
  19.  
  20. /**
  21.  * Does the current user have the given role?
  22.  * @param string $roleName Role name, using consts above.
  23.  * @return bool True if has role, else false.
  24.  */
  25. function hasRole(string $roleName) {
  26.     if (!isLoggedIn()) {
  27.         return false;
  28.     }
  29.     return in_array($roleName, $_SESSION['user_roles']);
  30. }

Check out lines 3 and 4:

  • const ROLE_NAME_ADMIN = 'admin';
  • const ROLE_NAME_MANAGER = 'manager';

const defines a constant. When PHP finds ROLE_NAME_ADMIN, it substitutes the string admin. When it finds ROLE_NAME_MANAGER, it substitutes manager.

This lets us write code like:

  • if (! hasRole(ROLE_NAME_MANAGER)) {

It's easy to read, and hard to mess up. Without the constant, we might write...

  • if (! hasRole('manager')) {

... in one place, and...

  • if (! hasRole('manager ')) {

... in another. One would work, the other not, because of the extra space. That can be hard to debug.

Here's hasRole():

  1. /**
  2.  * Does the current user have the given role?
  3.  * @param string $roleName Role name, using consts above.
  4.  * @return bool True if has role, else false.
  5.  */
  6. function hasRole(string $roleName) {
  7.     if (!isLoggedIn()) {
  8.         return false;
  9.     }
  10.     return in_array($roleName, $_SESSION['user_roles']);
  11. }

First, if the current user isn't logged in, they don't have any roles, and hasRole() should return false. That's what lines 26 to 28 do.

Line 29 uses PHP's built-in in_array() function. You give it a value you want to search for, and the array you want to search. So, for this to work, we need an array in the session, with all of the user's roles in it.

Here's some examples of how in_array() works.

$needle $haystack in_array($needle, $haystack)
dog [llama, cat, dog] true
dog [dog, llama, cat] true
camel [llama, cat, dog] false
dog [dog] true

Here's what the debugger shows, on the first line of hasRole(), when checking for the admin role:

Debugger

$roleName is admin in this case. You can see that $_SESSION['user_roles'] is an array. It has one element for each role the user has. This allow a user to have multiple roles.

So, for...

  • in_array($roleName, $_SESSION['user_roles'])

... $roleName is admin, and $_SESSION['user_roles'] is ['admin']. admin is in the array, so in_array() is true. The code...

  • function hasRole(string $roleName) {
  •     if (!isLoggedIn()) {
  •         return false;
  •     }
  •     return in_array($roleName, $_SESSION['user_roles']);
  • }

... returns the value of in_array().

Ethan
Ethan

OK, I get that, but it will only work if $_SESSION['user_roles'] has the current user's roles. Is that something PHP does for us automatically?

Good question. No, it isn't automatic. The login program will look up the user's roles in a database, and put them in $_SESSION['user_roles']. We'll look at that later.

Right now, we're looking at how checks are made, so we know what the login program has to do.

isLoggedIn()

hasRole() starts by calling isLoggedIn():

  • function hasRole(string $roleName) {
  •     if (!isLoggedIn()) {
  •         return false;
  •     }
  •     return in_array($roleName, $_SESSION['user_roles']);
  • }

Let's write that.

  1. /**
  2.  * Is the current user logged in?
  3.  * @return bool True if logged in, else false.
  4.  */
  5. function isLoggedIn() {
  6.     $loggedIn = false;
  7.     if (isset($_SESSION['logged_in'])) {
  8.         if ($_SESSION['logged_in']) {
  9.             $loggedIn = true;
  10.         }
  11.     }
  12.     return $loggedIn;
  13. }

We'll make the login program set $_SESSION['logged_in'] to true if the user is logged in.

The code uses the flag pattern. Set the flag to false to start (line 11). Then, if $_SESSION['logged_in'] exists (line 12), and it's true (line 13), set the flag to true. Return the flag (line 17).

Pattern

Flag

Use a variable as a flag. Set the flag if any of a number of things happens. After, check the flag to see if any of those things happened.

What login should do

This is what we'll want the login program to do, when we write it:

  • For a valid login, put true in $_SESSION['logged_in']
  • For a valid login, put the user's roles in $_SESSION['user_roles']

Then hasRole() and isLoggedIn() will work.

Up next

Let's adjust the top menu depending on the user's roles.