Mysql
 sql >> Base de données >  >> RDS >> Mysql

Gestion des comptes utilisateurs, rôles, autorisations, authentification PHP et MySQL -- Partie 5

Il s'agit de la partie 5 d'une série sur la création d'un système de gestion des comptes d'utilisateurs en PHP. Vous pouvez trouver les autres parties ici :partie 1, partie 2, partie 3 et partie 4.

Nous avons fini de gérer les comptes d'utilisateurs administratifs dans la dernière section ainsi que les rôles. Dans cette partie, nous allons passer par la création d'autorisations et l'attribution et la désattribution des autorisations aux rôles d'utilisateur.

Attribuer des autorisations aux rôles

Comme je l'ai dit dans la première partie de cette série, les rôles sont liés aux autorisations dans une relation plusieurs-à-plusieurs. Un rôle peut avoir plusieurs autorisations et une autorisation peut appartenir à plusieurs rôles.

Nous avons déjà créé la table des rôles. Nous allons maintenant créer une table des autorisations pour contenir les autorisations et une troisième table appelée permission_role pour contenir les informations sur la relation entre les rôles et la table des autorisations.

Créez les deux tables pour qu'elles aient les propriétés suivantes :

tableau des autorisations :

CREATE TABLE `permissions` (
 `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 `name` varchar(255) NOT NULL UNIQUE KEY,
 `description` text NOT NULL
)

table permission_role :

CREATE TABLE `permission_role` (
 `id` int(11) NOT NULL PRIMARY KEY AUTO_INCREMENT,
 `role_id` int(11) NOT NULL,
 `permission_id` int(11) NOT NULL,
 KEY `role_id` (`role_id`),
 KEY `permission_id` (`permission_id`),
 CONSTRAINT `permission_role_ibfk_1` FOREIGN KEY (`role_id`) REFERENCES `roles` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
 CONSTRAINT `permission_role_ibfk_2` FOREIGN KEY (`permission_id`) REFERENCES `permissions` (`id`)
)

Dans la table permission_role, role_id fait référence à l'ID de rôle dans la table des rôles tandis que permission_id fait référence à la colonne permission id dans la table des autorisations. Pour attribuer une autorisation à un rôle, nous le faisons en insérant simplement un enregistrement de ce permission_id contre le role_id sur la table permission_role et la relation est établie. Cela signifie que si nous voulons annuler l'attribution de cette autorisation à ce rôle, nous supprimons simplement l'enregistrement de ce role_id par rapport à ce permission_id sur cette table permission_role.

Les deux dernières lignes de la requête SQL ci-dessus sont des contraintes qui garantissent que lorsqu'un rôle ou une autorisation particulière est supprimée, toutes les entrées de la table permission_role ayant l'identifiant de cette autorisation ou cet identifiant de rôle seront également automatiquement supprimées par la base de données. Nous faisons cela parce que nous ne voulons pas que la table permission_role conserve des informations de relation sur un rôle ou une autorisation qui n'existe plus.

Vous pouvez également définir ces contraintes manuellement à l'aide de PHPMyAdmin :vous pouvez le faire sur l'interface simplement en sélectionnant la table permission_role et en allant dans l'onglet Relational view> Structure et en remplissant simplement les valeurs. Si vous ne pouvez toujours pas le faire, laissez un commentaire ci-dessous et je vais essayer de vous aider.

Maintenant, la relation est établie.

Créons une page pour attribuer des autorisations à un rôle. Sur notre page roleList.php, nous listons les différents rôles avec un bouton "permissions" à côté de chacun. Cliquer sur ce lien nous amènera à une page appelée assignPermissions.php. Créons maintenant ce fichier dans le dossier admin/roles.

assignPermissions.php :

<?php include('../../config.php') ?>
<?php include(ROOT_PATH . '/admin/roles/roleLogic.php') ?>
<?php
  $permissions = getAllPermissions();
  if (isset($_GET['assign_permissions'])) {
    $role_id = $_GET['assign_permissions']; // The ID of the role whose permissions we are changing
    $role_permissions = getRoleAllPermissions($role_id); // Getting all permissions belonging to role

    // array of permissions id belonging to the role
    $r_permissions_id = array_column($role_permissions, "id");
  }
?>
<!DOCTYPE html>
<html>
<head>
  <meta charset="utf-8">
  <title>Admin Area - Assign permissions </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">
    <a href="roleList.php" class="btn btn-success">
      <span class="glyphicon glyphicon-chevron-left"></span>
      Roles
    </a>
    <hr>
    <h1 class="text-center">Assign permissions</h1>
    <br />
    <?php if (count($permissions) > 0): ?>
    <form action="assignPermissions.php" method="post">
      <table class="table table-bordered">
        <thead>
          <tr>
            <th>N</th>
            <th>Role name</th>
            <th class="text-center">Status</th>
          </tr>
        </thead>
        <tbody>
          <?php foreach ($permissions as $key => $value): ?>
            <tr class="text-center">
              <td><?php echo $key + 1; ?></td>
              <td><?php echo $value['name']; ?></td>
              <td>
                  <input type="hidden" name="role_id" value="<?php echo $role_id; ?>">
                  <!-- if current permission id is inside role's ids, then check it as already belonging to role -->
                  <?php if (in_array($value['id'], $r_permissions_id)): ?>
                    <input type="checkbox" name="permission[]" value="<?php echo $value['id'] ?>" checked>
                  <?php else: ?>
                    <input type="checkbox" name="permission[]" value="<?php echo $value['id'] ?>" >
                  <?php endif; ?>
              </td>
            </tr>
          <?php endforeach; ?>
          <tr>
            <td colspan="3">
              <button type="submit" name="save_permissions" class="btn btn-block btn-success">Save permissions</button>
            </td>
          </tr>
        </tbody>
      </table>
    </form>
    <?php else: ?>
      <h2 class="text-center">No permissions in database</h2>
    <?php endif; ?>
  </div>
  <?php include(INCLUDE_PATH . "/layouts/footer.php") ?>
</body>
</html>

Cliquer sur le bouton 'permissions' d'un rôle vous amène à cette page. Mais en ce moment, si vous cliquez dessus et que vous venez sur cette page assignPermissions.php, il y a un message d'erreur indiquant que les fonctions getAllPermissions() sont indéfinies. Avant d'ajouter cette méthode, voyons comment nous implémentons réellement cette attribution et désattribution d'autorisation dans notre code PHP.

Lorsque vous cliquez sur le bouton 'permissions' d'un rôle, vous êtes redirigé vers la page assignPermissions.php avec l'identifiant de ce rôle. Mais avant d'afficher la page d'attribution des autorisations, nous utilisons l'identifiant du rôle pour récupérer toutes les autorisations qui ont déjà été attribuées à ce rôle dans la base de données. Et puis nous faisons également ressortir toutes les autorisations disponibles dans le tableau des autorisations. Nous avons maintenant deux tableaux d'autorisations :celles qui ont été attribuées à un rôle et toutes les autorisations disponibles dans notre base de données. Le premier est un sous-ensemble du second.

Attribuer une autorisation à un rôle signifie ajouter cette autorisation de la liste globale des autorisations au tableau des autorisations appartenant à ce rôle et enregistrer l'ensemble de ces informations dans la table permission_role. Annuler l'attribution d'une autorisation à un rôle signifie supprimer cette autorisation particulière de la liste des autorisations appartenant à ce rôle.

Notre logique est de parcourir un tableau de toutes les autorisations disponibles à partir de la base de données, puis pour chacun de leurs identifiants, nous déterminons si cet identifiant est déjà dans le tableau des identifiants pour les autorisations du rôle. s'il existe, cela signifie que le rôle a déjà cette autorisation, nous l'affichons donc à côté d'une case cochée. S'il n'existe pas, nous l'affichons à côté d'une case à cocher non cochée.

Une fois que tout a été affiché, cliquer sur une case cochée signifie désattribuer l'autorisation tandis que cliquer sur une case décochée signifie attribuer l'autorisation au rôle. Une fois toutes les vérifications et désactivations effectuées, l'utilisateur clique ensuite sur le bouton "Enregistrer les autorisations" sous le tableau pour enregistrer toutes les autorisations qui ont été cochées pour ce rôle.

Ajoutons toutes ces fonctions au fichier roleLogic.php. Ce sont :

roleLogic.php :

// ... other functions up here ...
  function getAllPermissions(){
    global $conn;
    $sql = "SELECT * FROM permissions";
    $permissions = getMultipleRecords($sql);
    return $permissions;
  }

  function getRoleAllPermissions($role_id){
    global $conn;
    $sql = "SELECT permissions.* FROM permissions
            JOIN permission_role
              ON permissions.id = permission_role.permission_id
            WHERE permission_role.role_id=?";
    $permissions = getMultipleRecords($sql, 'i', [$role_id]);
    return $permissions;
  }

  function saveRolePermissions($permission_ids, $role_id) {
    global $conn;
    $sql = "DELETE FROM permission_role WHERE role_id=?";
    $result = modifyRecord($sql, 'i', [$role_id]);

    if ($result) {
      foreach ($permission_ids as $id) {
        $sql_2 = "INSERT INTO permission_role SET role_id=?, permission_id=?";
        modifyRecord($sql_2, 'ii', [$role_id, $id]);
      }
    }

    $_SESSION['success_msg'] = "Permissions saved";
    header("location: roleList.php");
    exit(0);
  }

Faire fonctionner les autorisations

À ce stade, nous pouvons déterminer quel est le rôle d'un utilisateur et puisque le rôle est lié aux autorisations, nous pouvons donc également connaître ses autorisations.

Nous voulons maintenant faire fonctionner ces autorisations :c'est-à-dire garantir qu'un utilisateur administrateur n'est autorisé à effectuer que les actions pour lesquelles il dispose des autorisations. Nous y parviendrons en utilisant des fonctions middleware. Un middleware est essentiellement un morceau de code ou une fonction qui est exécutée avant qu'une action ne soit effectuée. En règle générale, cette fonction middleware peut modifier le comportement de l'action ou effectuer certaines vérifications qui peuvent finir par arrêter complètement l'action en fonction des résultats de la vérification.

Par exemple, un utilisateur peut disposer des autorisations create-post, update-post et delete-post. S'il est connecté et qu'il essaie de publier une publication, notre fonction middleware vérifie d'abord si cet utilisateur dispose de l'autorisation de publication. S'ils ont cette autorisation, notre fonction middleware renverra true et le message sera publié. S'ils n'ont pas l'autorisation de publication, notre fonction middleware les redirigera avec un message indiquant qu'ils n'ont pas l'autorisation de publier la publication.

Nous allons mettre toutes nos fonctions middleware dans un seul fichier appelé middleware.php. Créez-le maintenant dans le dossier admin et collez-y ce code :

middleware.php :

<?php

  // if user is NOT logged in, redirect them to login page
  if (!isset($_SESSION['user'])) {
    header("location: " . BASE_URL . "login.php");
  }
  // if user is logged in and this user is NOT an admin user, redirect them to landing page
  if (isset($_SESSION['user']) && is_null($_SESSION['user']['role'])) {
    header("location: " . BASE_URL);
  }
  // checks if logged in admin user can update post
  function canUpdatePost($post_id = null){
    global $conn;

    if(in_array('update-post', $_SESSION['userPermissions'])){
      if ($_SESSION['user']['role'] === "Author") { // author can update only posts that they themselves created
          $sql = "SELECT user_id FROM posts WHERE id=?";
          $post_result = getSingleRecord($sql, 'i', [$post_id]);
          $post_user_id = $post_result['user_id'];

          // if current user is the author of the post, then they can update the post
          if ($post_user_id === $user_id) {
            return true;
          } else { // if post is not created by this author
            return false;
          }
      } else { // if user is not author
        return true;
      }
    } else {
      return false;
    }
  }

  // accepts user id and post id and checks if user can publis/unpublish a post
  function canPublishPost() {
    if(in_array(['permission_name' => 'publish-post'], $_SESSION['userPermissions'])){
      // echo "<pre>"; print_r($_SESSION['userPermissions']); echo "</pre>"; die();
      return true;
    } else {
      return false;
    }
  }

  function canDeletePost() {
    if(in_array('delete-post', $_SESSION['userPermissions'])){
      return true;
    } else {
      return false;
    }
  }
  function canCreateUser() {
    if(in_array('create-user', $_SESSION['userPermissions'])){
      return true;
    } else {
      return false;
    }
  }
  function canUpdateUser() {
    if(in_array('update-user', $_SESSION['userPermissions'])){
      return true;
    } else {
      return false;
    }
  }
  function canDeleteUser() {
    if(in_array('delete-user', $_SESSION['userPermissions'])){
      return true;
    } else {
      return false;
    }
  }
  function canCreateRole($role_id) {
    if(in_array('create-role', $_SESSION['userPermissions'])){
      return true;
    } else {
      return false;
    }
  }
  function canUpdateRole($role_id) {
    if(in_array('update-role', $_SESSION['userPermissions'])){
      return true;
    } else {
      return false;
    }
  }
  function canDeleteRole($user_id, $post_id) {
    if(in_array('delete-role', $_SESSION['userPermissions'])){
      return true;
    } else {
      return false;
    }
  }
?>
" ; mourir(); retourner vrai ; } sinon { renvoie faux ; } } function canDeletePost() { if(in_array('delete-post', $_SESSION['userPermissions'])){ return true ; } sinon { renvoie faux ; } } function canCreateUser() { if(in_array('create-user', $_SESSION['userPermissions'])){ return true ; } sinon { renvoie faux ; } } function canUpdateUser() { if(in_array('update-user', $_SESSION['userPermissions'])){ return true ; } sinon { renvoie faux ; } } function canDeleteUser() { if(in_array('delete-user', $_SESSION['userPermissions'])){ return true ; } sinon { renvoie faux ; } } function canCreateRole($role_id) { if(in_array('create-role', $_SESSION['userPermissions'])){ return true ; } sinon { renvoie faux ; } } function canUpdateRole($role_id) { if(in_array('update-role', $_SESSION['userPermissions'])){ return true ; } sinon { renvoie faux ; } } function canDeleteRole($user_id, $post_id) { if(in_array('delete-role', $_SESSION['userPermissions'])){ return true ; } sinon { renvoie faux ; } }?>

La première instruction if vérifie si l'utilisateur est connecté. Si l'utilisateur n'est pas connecté, il sera redirigé vers la page d'accueil. La seconde instruction if vérifie si l'utilisateur est connecté et s'il a un rôle (est administrateur). Si l'utilisateur est connecté et a un rôle, il accèdera à la page, sinon il sera redirigé vers la page d'accueil.

Dans chaque fichier auquel vous souhaitez restreindre l'accès des utilisateurs non administrateurs, vous devez simplement inclure ce fichier middleware.php dans ce fichier. Ainsi, tous nos fichiers d'administration auxquels nous ne voulons pas que les utilisateurs normaux accèdent, nous y inclurons ce fichier. Ouvrez donc tous les fichiers dans les deux dossiers du dossier admin, à savoir :utilisateurs, rôles. Dans chacun des fichiers, ajoutez la ligne suivante juste en dessous de l'inclusion pour config.php.

Comme ceci :

<?php include('../../config.php'); ?>
<?php require_once '../middleware.php'; ?>

Et cela redirigera tout utilisateur non administrateur tentant de visiter la page.

Toujours dans ce fichier middleware.php, nous utilisons le in_array() de PHP pour vérifier si l'autorisation que nous testons se trouve dans le tableau des autorisations de cet utilisateur. (Lorsqu'un utilisateur administrateur se connecte, nous plaçons toutes ses autorisations dans un tableau de variables de session appelé $_SESSION['userPermissions'].) Si l'autorisation actuelle se trouve dans le tableau des autorisations de l'utilisateur, cela signifie que cet utilisateur a cette autorisation et donc la fonction retourne true, sinon elle retourne false.

Maintenant, si vous voulez vérifier si un utilisateur a une autorisation, disons qu'une autorisation de publication que vous devez faire est d'appeler la méthode canPublishPost() comme ceci :

<?php if (canPublishPost()): ?>
	<!-- User can publish post. Display publish post button -->
<?php else: ?>
	<!-- User cannot publish post. Do not display publish post button -->
<?php endif ?>

Toujours en tant que middleware, avant de mettre à jour un article, nous appellerons d'abord la fonction middleware canUpdatePost(). Si la fonction vérifie et voit que l'utilisateur n'a pas l'autorisation de mise à jour de la publication, elle renverra false et nous pourrons alors le rediriger vers la page d'accueil avec un message indiquant qu'il n'est pas autorisé à mettre à jour la publication. Comme ceci :

// checks if logged in admin user can update post
function updatePost($post_values){
  global $conn;

  if(canUpdatePost($post_values['id']){
     // proceed to update post
  
  } else {
    // redirect back to homepage with message that says user is not permitted to update post
  }
}

Même chose pour publier/dépublier des articles :

function togglePublishPost($post_id)
{
	if (!canPublishPost($_SESSION['user']['id'])) {
		// redirect them back to dashboard with the message that they don't have the permission to publish post
	} 
    
    // proceed to publish post

}

Il nous reste maintenant la dernière partie de ce didacticiel qui met à jour le profil de l'utilisateur et donne également aux utilisateurs enregistrés la possibilité de supprimer leurs propres comptes.

Merci de votre soutien. Si vous avez quelque chose à dire, n'hésitez pas à le laisser dans les commentaires.