Post Top Ad

Your Ad Spot

jueves, 7 de mayo de 2020

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

Esta es la parte 5 de una serie sobre cómo crear un sistema de gestión de cuentas de usuario en PHP. Puede encontrar las otras partes aquí: parte1 , parte2 ,  parte 3  y parte 4 .
Terminamos administrando cuentas de usuario administrativas en la última sección, así como roles. En esta parte, veremos cómo crear permisos y asignar y desasignar los permisos a los roles de usuario.

Asignación de 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 la tabla de roles . Ahora crearemos una tabla de permisos para mantener los permisos y una tercera tabla llamada permission_role para mantener la información sobre la relación entre las funciones y la tabla de permisos.
Cree las dos tablas para tener las siguientes propiedades:
tabla de permisos :
CREATE TABLE `permissions` (
 `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 `name` varchar(255) NOT NULL UNIQUE KEY,
 `description` text NOT NULL
)
tabla 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`)
)
En la tabla permission_role , role_id hace referencia al id. De rol  en la tabla de roles, mientras que permission_id hace referencia a la columna id. De permiso  en la tabla de permisos . Para asignar un permiso a un rol, lo hacemos simplemente insertando un registro de ese permission_id contra el role_id en la tabla permission_role y se establece la relación. Esto significa que si deseamos desasignar ese permiso de ese rol, simplemente eliminamos el registro de ese role_id contra ese permission_id en esta tabla permission_role .
Las dos últimas líneas de la consulta SQL anterior son las restricciones que aseguran que cuando se elimina un papel o un permiso especial, todas las entradas en la permission_role mesa que tienen de que el permiso ID o que el papel Identificación también se eliminarán automáticamente por la base de datos. Hacemos esto porque no queremos que la tabla permission_role mantenga información de 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 la tabla permission_role y yendo a la 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 diversos roles con un botón de 'permisos' junto a cada uno. Al hacer clic en este enlace, nos llevará a una página llamada executePermissions.php. Creemos ese archivo ahora dentro de la carpeta admin / roles .
asignarPermisos.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 en este momento, si hace clic en él y llega a esta página asignarPermisas.php , hay un mensaje de error que dice que las funciones getAllPermissions () no están definidas. Antes de agregar este método, repasemos cómo implementamos realmente esta asignación y desasignación de permisos en nuestro código PHP.
Cuando hace clic en el botón 'permisos' de un rol, se lo dirige a la página asignarPermisos.php con la identificación de ese rol. Pero antes de mostrar la página de asignación de permisos, usamos el ID de rol  para obtener todos los permisos que ya se han asignado a ese rol desde la base de datos. Y luego también sacamos todos los permisos disponibles en la tabla de permisos . Ahora tenemos dos conjuntos de permisos: los que se han asignado a un rol y todos los permisos disponibles en nuestra base de datos. Una primera es un subconjunto de la segunda.
Asignar un permiso a un rol significa agregar ese permiso de la lista general de permisos al conjunto de permisos que pertenecen a ese rol y guardar toda esta información en la tabla permission_role . 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.
Nuestra lógica es recorrer un conjunto de todos los permisos disponibles de la base de datos, luego, para cada uno de sus identificadores , determinamos si ese identificador ya está en el conjunto de identificadores para los permisos del rol. si existe, significa que el rol ya tiene ese permiso, por lo que lo mostramos junto a una casilla marcada . Si no existe, lo mostramos junto a una casilla de verificación desmarcada . 
Después de que todo se haya mostrado, hacer clic en una casilla de verificación marcada significa desasignar el permiso mientras que hacer clic en una casilla de verificación desmarcada significa asignar el permiso a la función. Después de realizar todas las comprobaciones y desmarques, el usuario hace clic en el botón 'Guardar permisos' debajo de la tabla para guardar todos los permisos que se han comprobado para ese rol.
Agreguemos todas estas funciones al archivo roleLogic.php. 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);
  }

Poner 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 que estos permisos funcionen: es decir, para garantizar que un usuario administrador solo pueda realizar aquellas acciones para las que tiene los permisos. Lo lograremos utilizando las funciones de middleware. Un middleware es básicamente una pieza de código o una función que se ejecuta antes de realizar una acción. Normalmente, 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-publicar , actualizar-publicar y eliminar-publicar . Si han iniciado sesión e intentan publicar una publicación, nuestra función de middleware primero verifica si este usuario tiene el permiso de publicación . Si tienen este permiso, nuestra función de middleware volverá verdadera y se publicará la publicación. Si carecen del permiso de publicación de publicación, nuestra función de middleware los redirigirá de nuevo 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  admin  y pegue 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 verifica si el usuario ha iniciado sesión. Si el usuario no ha iniciado sesión, será redirigido a la página de inicio. La segunda instrucción if verifica si el usuario ha iniciado sesión y si tiene un rol (es admin). Si se encuentra que el usuario ha iniciado sesión y tiene un rol, accederá a la página; de lo contrario, se lo redirigirá a la página de inicio.
En cada archivo donde desee restringir el acceso de usuarios que no sean administradores, solo debe incluir este archivo middleware.php en ese archivo. Entonces, a todos nuestros archivos de administración en los que no queremos que accedan los usuarios normales, incluiremos este archivo en ellos. Abra todos los archivos en las dos carpetas dentro de la carpeta de administración, a saber: usuarios , roles . En cada uno de los archivos, agregue la siguiente línea  justo debajo de 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 verificar 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 variable de sesión llamada $ _SESSION ['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 un permiso, digamos que un permiso de publicación-publicación que debe hacer es llamar al método canPublishPost () de esta manera:
<?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 a la función de middleware canUpdatePost () . Si la función comprueba y ve que el usuario no tiene el permiso de actualización de publicación , devolverá falso y luego podremos redirigirlo a la página de inicio con un mensaje que indique que no tiene permiso para actualizar la publicación. Me gusta 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 / publicar 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 está actualizando el perfil del usuario y también brindando a los usuarios registrados la posibilidad de eliminar sus propias cuentas.
Gracias por seguir. Si tiene algo que decir, colóquelo en los comentarios.

No hay comentarios.:

Publicar un comentario

Dejanos tu comentario para seguir mejorando!

outbrain

Páginas