• PHP > hiérachiser les rôles des membres

      RBAC (Role Based access control). Un utilisateur est assigné à un ou plusieurs rôles avec les permissions relatives.

      Deux classes: Role.php qui gèrera les rôles adéquats, et PrivilegedUser.php that will extend your existing user class.

      Database

      La table roles stocke l’ID et le nom du rôle, la table permissions stocke l’ID et la description de la permission, la table role_perm associe les permissions avec les rôles, et la table user_role associe les rôles avec les users. Ainsi, on peut avoir un nombre infini de roles et permissions.

       

      CREATE TABLE roles (
        role_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
        role_name VARCHAR(50) NOT NULL
      );
      
      CREATE TABLE permissions (
        perm_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
        perm_desc VARCHAR(50) NOT NULL
      );
      
      CREATE TABLE role_perm (
        role_id INTEGER UNSIGNED NOT NULL,
        perm_id INTEGER UNSIGNED NOT NULL,
        FOREIGN KEY (role_id) REFERENCES roles(role_id),
        FOREIGN KEY (perm_id) REFERENCES permissions(perm_id)
      );
      
      CREATE TABLE user_role (
        user_id INTEGER UNSIGNED NOT NULL,
        role_id INTEGER UNSIGNED NOT NULL,
        FOREIGN KEY (user_id) REFERENCES users(user_id),
        FOREIGN KEY (role_id) REFERENCES roles(role_id)
      );

       

      NOTE: la table user_role fait référence à la table users qui est une table de users avec l’ID du user de forme user_id.

      classe Role

      The primary focus of the Role class is to return a role object that is populated with each roles corresponding permissions. Cela permet to easily check whether a permission is available without having to perform redundant SQL queries with every request.

       

      fichier Role.php:

      <?php
      class Role {
          protected $permissions;
      
          protected function __construct() {
              $this->permissions = array();
          }
      
          // return a role object with associated permissions
          public static function getRolePerms($role_id) {
              $role = new Role();
              $sql = "SELECT t2.perm_desc FROM role_perm as t1
                      JOIN permissions as t2 ON t1.perm_id = t2.perm_id
                      WHERE t1.role_id = :role_id";
              $sth = $GLOBALS["DB"]->prepare($sql);
              $sth->execute(array(":role_id" => $role_id));
      
              while($row = $sth->fetch(PDO::FETCH_ASSOC)) {
                  $role->permissions[$row["perm_desc"]] = true;
              }
              return $role;
          }
      
          // check if a permission is set
          public function hasPerm($permission) {
              return isset($this->permissions[$permission]);
          }
      }

       

      getRolePerms() crée un nouvel objet Role basé on a specific role ID, and then uses a JOIN clause to combine the role_perm and perm_desc tables. Pour chaque permission associated with the given role, the description is stored as the key and its value is set to true. The hasPerm() method accepts a permission description and returns the value based on the current object.

      classe Privileged User

      By creating a new class that extends your existing user class, you can reuse your existing code logic for managing users and then add some additional methods on top of those which are geared specifically towards working with privileges.

       

      Use the following code to create the file PrivilegedUser.php:

       

      <?php
      class PrivilegedUser extends User {
          private $roles;
      
          public function __construct() {
              parent::__construct();
          }
      
          // override User method
          public static function getByUsername($username) {
              $sql = "SELECT * FROM users WHERE username = :username";
              $sth = $GLOBALS["DB"]->prepare($sql);
              $sth->execute(array(":username" => $username));
              $result = $sth->fetchAll();
      
              if (!empty($result)) {
                  $privUser = new PrivilegedUser();
                  $privUser->user_id = $result[0]["user_id"];
                  $privUser->username = $username;
                  $privUser->password = $result[0]["password"];
                  $privUser->email_addr = $result[0]["email_addr"];
                  $privUser->initRoles();
                  return $privUser;
              } else {
                  return false;
              }
          }
      
          // populate roles with their associated permissions
          protected function initRoles() {
              $this->roles = array();
              $sql = "SELECT t1.role_id, t2.role_name FROM user_role as t1
                      JOIN roles as t2 ON t1.role_id = t2.role_id
                      WHERE t1.user_id = :user_id";
              $sth = $GLOBALS["DB"]->prepare($sql);
              $sth->execute(array(":user_id" => $this->user_id));
      
              while($row = $sth->fetch(PDO::FETCH_ASSOC)) {
                  $this->roles[$row["role_name"]] = Role::getRolePerms($row["role_id"]);
              }
          }
      
          // check if user has a specific privilege
          public function hasPrivilege($perm) {
              foreach ($this->roles as $role) {
                  if ($role->hasPerm($perm)) {
                      return true;
                  }
              }
              return false;
          }
      }

       

      getByUsername() renvoie un objet populated with information about a specific user. A method almost identical to this will likely already exist in your user class, but you need to override it here so that the PrivilegedUser‘s methods can be called with the appropriate object. If you try to invoke a PrivilegedUser method on a User object, you will get an error stating that the method doesn’t exist.

       

      initRoles(), uses a JOIN to combine the user_role and roles tables to collect the roles associated with the current user’s ID. Each role is then populated with its corresponding permissions with a call to the Role class method previously created, Role::getRolePerms().

      hasPrivilege(), accepts a permission description and returns true of the user has the permission or false otherwise.

       

      With the preceding two classes in place, checking if a user has a specific privilege is as simple as follows:

       

      <?php
      require_once "Role.php";
      require_once "PrivilegedUser.php";
      
      // connect to database...
      // ...
      
      session_start();
      
      if (isset($_SESSION["loggedin"])) {
          $u = PrivilegedUser::getByUsername($_SESSION["loggedin"]);
      }
      
      if ($u->hasPrivilege("thisPermission")) {
          // do something
      }

       

      Ici, le username est stocké dans la session et a new PrivilegedUser object is created for that user on which the hasPrivilege() method can be called. Suivant la BDD, your object output will look similar to the following:

       

      object(PrivilegedUser)#3 (2) {
        ["roles":"PrivilegedUser":private]=>
        array(1) {
          ["Admin"]=>
          object(Role)#5 (1) {
            ["permissions":protected]=>
            array(4) {
              ["addUser"]=>bool(true)
              ["editUser"]=>bool(true)
              ["deleteUser"]=>bool(true)
              ["editRoles"]=>bool(true)
            }
          }
        }
        ["fields":"User":private]=>
        array(4) {
          ["user_id"]=>string(1) "2"
          ["username"]=>string(7) "mpsinas"
          ["password"]=>bool(false)
          ["email_addr"]=>string(0) ""
        }
      }

      GESTION DES RÔLES

      Exemple de méthodes pour gérer les Role (nouveau, supprimer) :

       

      // insert a new role
      public static function insertRole($role_name) {
          $sql = "INSERT INTO roles (role_name) VALUES (:role_name)";
          $sth = $GLOBALS["DB"]->prepare($sql);
          return $sth->execute(array(":role_name" => $role_name));
      }
      
      // insert array of roles for specified user id
      public static function insertUserRoles($user_id, $roles) {
          $sql = "INSERT INTO user_role (user_id, role_id) VALUES (:user_id, :role_id)";
          $sth = $GLOBALS["DB"]->prepare($sql);
          $sth->bindParam(":user_id", $user_id, PDO::PARAM_STR);
          $sth->bindParam(":role_id", $role_id, PDO::PARAM_INT);
          foreach ($roles as $role_id) {
              $sth->execute();
          }
          return true;
      }
      
      // delete array of roles, and all associations
      public static function deleteRoles($roles) {
          $sql = "DELETE t1, t2, t3 FROM roles as t1
                  JOIN user_role as t2 on t1.role_id = t2.role_id
                  JOIN role_perm as t3 on t1.role_id = t3.role_id
                  WHERE t1.role_id = :role_id";
          $sth = $GLOBALS["DB"]->prepare($sql);
          $sth->bindParam(":role_id", $role_id, PDO::PARAM_INT);
          foreach ($roles as $role_id) {
              $sth->execute();
          }
          return true;
      }
      
      // delete ALL roles for specified user id
      public static function deleteUserRoles($user_id) {
          $sql = "DELETE FROM user_role WHERE user_id = :user_id";
          $sth = $GLOBALS["DB"]->prepare($sql);
          return $sth->execute(array(":user_id" => $user_id));
      }

       

      On peut faire de même avec PrivilegedUser :

       

      // check if a user has a specific role
      public function hasRole($role_name) {
          return isset($this->roles[$role_name]);
      }
      
      // insert a new role permission association
      public static function insertPerm($role_id, $perm_id) {
          $sql = "INSERT INTO role_perm (role_id, perm_id) VALUES (:role_id, :perm_id)";
          $sth = $GLOBALS["DB"]->prepare($sql);
          return $sth->execute(array(":role_id" => $role_id, ":perm_id" => $perm_id));
      }
      
      // delete ALL role permissions
      public static function deletePerms() {
          $sql = "TRUNCATE role_perm";
          $sth = $GLOBALS["DB"]->prepare($sql);
          return $sth->execute();
      }

       

      Les nouvelles permissions doivent être insérées manuellement à la BDD. Roles on the other hand can be easily created, modified or deleted via an administration interface.

       

 

Aucun commentaire

 

Laissez un commentaire