Gestión de cuentas de usuario, roles, permisos, autenticación PHP y MySQL – Parte 5

2xsoftware - Blog

Esta es la parte 5 de una serie sobre cómo crear un sistema de administración de cuentas de usuario en PHP. Puede encontrar las otras partes aquí: parte 1, parte 2, parte 3 y parte 4.

Terminamos de administrar las cuentas de usuarios administrativos en la última sección, así como los roles. En esta parte, veremos la creación de permisos y la asignación y desasignación de permisos a roles de usuario.

Asignar permisos a roles

Como dije en la primera parte de esta serie, los roles están relacionados con los permisos en una relación de muchos a muchos. Un rol puede tener muchos permisos y un permiso puede pertenecer a muchos roles.

Ya hemos creado el roles chaquete. Ahora crearemos un permisos tabla para tener permisos y una tercera tabla llamada rol_permiso para contener la información sobre la relación entre los roles y la tabla de permisos.

Cree las dos tablas para que tengan las siguientes propiedades:

permisos chaquete:

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

rol_permiso chaquete:

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`)
)

Sobre el rol_permiso mesa, la Identificación del rol hace referencia al rol identificación en la mesa de roles mientras el permiso_id hace referencia al permiso identificación columna en el permisos chaquete. Para asignar un permiso a un rol, lo hacemos simplemente insertando un registro de ese permiso_id en contra de Identificación del rol sobre el rol_permiso mesa y se establece la relación. Esto significa que si queremos anular la asignación de ese permiso de ese rol, simplemente borramos el registro de ese Identificación del rol contra eso permiso_id en este rol_permiso chaquete.

Las dos últimas líneas de la consulta SQL anterior son restricciones que aseguran que cuando se elimina un rol o permiso en particular, todas las entradas en el rol_permiso mesa que tiene ese permiso identificación o ese rol identificación también será eliminado automáticamente por la base de datos. Hacemos esto porque no queremos el rol_permiso tabla que mantiene la información de la relación sobre un rol o un permiso que ya no existe.

También puede establecer estas restricciones manualmente usando PHPMyAdmin: puede hacerlo en la interfaz simplemente seleccionando el rol_permiso tabla y yendo a Vista relacional> pestaña Estructura y simplemente completando los valores. Si aún no puede hacer esto, deje un comentario a continuación e intentaré ayudarlo.

Ahora la relación está establecida.

Creemos una página para asignar permisos a un rol. En nuestra página roleList.php, enumeramos los distintos roles con un botón de ‘permisos’ junto a cada uno. Hacer clic en este enlace nos llevará a una página llamada assignPermissions.php. Creemos ese archivo ahora dentro admin / roles carpeta.

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>

Al hacer clic en el botón «permisos» de un rol, se accede a esta página. Pero ahora mismo, si hace clic en él y llega a este AssignPermissions.php página, hay un mensaje de error que dice getAllPermissions () las funciones no están definidas. Antes de agregar este método, repasemos cómo implementamos realmente este permiso de asignación y desasignación en nuestro código PHP.

Cuando hace clic en el botón ‘permisos’ de un rol, se lo lleva a la página assignPermissions.php con el identificación de ese papel. Pero antes de mostrar la página de asignación de permisos, usamos el rol identificación para recuperar todos los permisos que ya se han asignado a ese rol de la base de datos. Y luego también sacamos todos los permisos disponibles en el permisos chaquete. Ahora tenemos dos matrices de permisos: los que se han asignado a un rol y todos los permisos disponibles en nuestra base de datos. Uno de los primeros es un subconjunto del segundo.

Asignar un permiso a un rol significa agregar ese permiso de la lista general de permisos a la matriz de permisos que pertenecen a ese rol y guardar toda esta información en el rol_permiso chaquete. Anular la asignación de un permiso de un rol significa eliminar ese permiso en particular de la lista de permisos que pertenecen a ese rol.

Después de que se haya mostrado todo, hacer clic en una casilla de verificación marcada significa anular la asignación del permiso, mientras que hacer clic en una casilla de verificación no marcada significa asignar el permiso al rol. Después de realizar todas las comprobaciones y desmarcaciones, el usuario hace clic en el botón ‘Guardar permisos’ debajo de la tabla para guardar todos los permisos que se han marcado para ese rol.

Agreguemos todas estas funciones al archivo roleLogic.php. Ellos son:

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);
  }

Poniendo los permisos a trabajar

En este punto, podemos determinar cuál es el rol de un usuario y, dado que el rol está relacionado con los permisos, también podemos conocer sus permisos.

Ahora queremos poner estos permisos en funcionamiento: es decir, para asegurarnos de que un usuario administrador tenga permitido realizar solo aquellas acciones para las que tiene los permisos. Lo lograremos utilizando funciones de middleware. Un middleware es básicamente un fragmento de código o una función que se ejecuta antes de que se realice una acción. Por lo general, esta función de middleware puede modificar el comportamiento de la acción o realizar algunas comprobaciones que podrían terminar deteniendo la acción por completo dependiendo de los resultados de la comprobación.

Por ejemplo, un usuario puede tener los permisos crear publicación, actualización-publicación, y Eliminar mensaje. Si han iniciado sesión e intentan publicar una publicación, nuestra función de middleware primero verifica si este usuario tiene la publicar-publicar permiso. Si tienen este permiso, nuestra función de middleware se volverá verdadera y se publicará la publicación. Si carecen del permiso de publicación y publicación, nuestra función de middleware los redirigirá con un mensaje que dice que no tienen permiso para publicar la publicación.

Pondremos todas nuestras funciones de middleware dentro de un solo archivo llamado middleware.php. Créelo ahora en la carpeta administración y pega este código en él:

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;
    }
  }
?>

La primera instrucción if comprueba si el usuario está conectado. Si el usuario no ha iniciado sesión, será redirigido a la página de inicio. La segunda instrucción if comprueba si el usuario está conectado y si tiene un rol (es administrador). Si se encuentra que el usuario ha iniciado sesión y tiene un rol, accederá a la página; de lo contrario, será redirigido a la página de inicio.

En cada archivo en el que desee restringir el acceso de usuarios que no son administradores, debe incluir este archivo middleware.php en ese archivo. Entonces, todos nuestros archivos de administración a los que no queremos que accedan los usuarios normales, incluiremos este archivo en ellos. Así que abre todos los archivos en las dos carpetas internas. administración carpeta a saber: usuarios, roles. En cada uno de los archivos, agregue la siguiente línea justo debajo del include para config.php.

Al igual que:

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

Y eso redirigirá a cualquier usuario no administrador que intente visitar la página.

También en este archivo middleware.php, estamos usando PHP in_array () para comprobar si el permiso que estamos probando está en la matriz de permisos de ese usuario. (Cuando un usuario administrador inicia sesión, ponemos todos sus permisos en una matriz de variables de sesión llamada $ _SESIÓN[‘userPermissions’].) Si el permiso actual está en la matriz de permisos del usuario, significa que ese usuario tiene ese permiso y, por lo tanto, la función devuelve verdadero; de lo contrario, devuelve falso.

Ahora, si desea verificar si un usuario tiene permiso, diga un publicar-publicar el permiso que tienes que hacer es llamar al método canPublishPost () como esto:

<?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 ?>

También como middleware, antes de actualizar una publicación, primero llamaremos al canUpdatePost () función de middleware. Si la función comprueba y ve que el usuario no tiene la actualización-publicación permiso, devolverá falso y luego podemos redirigirlos a la página de inicio con un mensaje que dice que no puede actualizar la publicación. Como esto:

// 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
  }
}

Lo mismo para publicar / anular la publicación de publicaciones:

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

}

Ahora nos queda la última parte de este tutorial que actualiza el perfil de usuario y también brinda a los usuarios registrados la posibilidad de eliminar sus propias cuentas.

Gracias por seguir. Si tiene algo que decir, déjelo en los comentarios.

Awa Melvine

Próxima parte: editar perfil de usuario

4.5/5 - (653 votos)

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *