Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Gerenciamento de contas de usuários, funções, permissões, autenticação PHP e MySQL -- Parte 5


Esta é a parte 5 de uma série sobre como criar um sistema de gerenciamento de contas de usuário em PHP. Você pode encontrar as outras partes aqui:parte1, parte2, parte 3 e parte 4.

Terminamos de gerenciar contas de usuários administrativos na última seção, bem como funções. Nesta parte, vamos criar permissões e atribuir e cancelar a atribuição de permissões para funções de usuário.

Atribuindo permissões a funções


Como eu disse na primeira parte desta série, os papéis estão relacionados a permissões em um relacionamento Muitos-para-Muitos. Uma função pode ter muitas permissões e uma permissão pode pertencer a muitas funções.

Já criamos a tabela de funções. Agora vamos criar uma tabela de permissões para manter as permissões e uma terceira tabela chamada permission_role para armazenar as informações sobre o relacionamento entre as funções e a tabela de permissões.





Crie as duas tabelas para ter as seguintes propriedades:

tabela de permissões:
CREATE TABLE `permissions` (
 `id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
 `name` varchar(255) NOT NULL UNIQUE KEY,
 `description` text NOT NULL
)

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

Na tabela permission_role, o role_id faz referência ao código da função na tabela de papéis, enquanto o permission_id faz referência à coluna de código da permissão na tabela de permissões. Para atribuir uma permissão a um papel, fazemos isso simplesmente inserindo um registro desse permission_id no role_id na tabela permission_role e o relacionamento é estabelecido. Isso significa que, se quisermos cancelar a atribuição dessa permissão desse papel, apenas excluímos o registro desse role_id em relação a esse permission_id nesta tabela permission_role.

As duas últimas linhas da consulta SQL acima são restrições que garantem que, quando uma função ou permissão específica for excluída, todas as entradas na tabela permission_role com o ID dessa permissão ou desse ID da função também serão excluídas automaticamente pelo banco de dados. Fazemos isso porque não queremos que a tabela permission_role mantenha informações de relacionamento sobre uma função ou permissão que não existe mais.

Você também pode definir essas restrições manualmente usando PHPMyAdmin:você pode fazê-lo na interface simplesmente selecionando a tabela permission_role e indo para Visualização Relacional> guia Estrutura e apenas preenchendo os valores. Se você ainda não conseguir fazer isso, deixe um comentário abaixo e eu tentarei ajudá-lo.

Agora a relação está definida.

Vamos criar uma página para atribuir permissões a uma função. Em nossa página roleList.php, listamos as várias funções com um botão de 'permissões' ao lado de cada uma. Clicar neste link nos levará a uma página chamada assignPermissions.php. Vamos criar esse arquivo agora dentro da pasta 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>

Clicar no botão 'permissões' de uma função leva você a esta página. Mas agora se você clicar nele e chegar a esta página assignPermissions.php, há uma mensagem de erro dizendo que as funções getAllPermissions() são indefinidas. Antes de adicionarmos esse método, vamos ver como realmente implementamos essa atribuição e remoção de permissão em nosso código PHP.

Quando você clica no botão 'permissões' de uma função, você é levado para a página assignPermissions.php com o id dessa função. Mas antes de exibir a página de atribuição de permissões, usamos o ID da função para buscar todas as permissões que já foram atribuídas a essa função no banco de dados. E também trazemos todas as permissões disponíveis na tabela de permissões. Agora temos duas matrizes de permissões:aquelas que foram atribuídas a uma função e todas as permissões disponíveis em nosso banco de dados. Um primeiro é um subconjunto do último.

Atribuir uma permissão a uma função significa adicionar essa permissão da lista geral de permissões à matriz de permissão pertencente a essa função e salvar todas essas informações na tabela permission_role. Desatribuir uma permissão de uma função significa remover essa permissão específica da lista de permissões que pertencem a essa função.

Nossa lógica é percorrer um array de todas as permissões disponíveis do banco de dados, então para cada um de seus ids, determinamos se aquele id já está no array de ids para as permissões da função. se existir, significa que a função já tem essa permissão, então a exibimos ao lado de uma caixa de seleção marcada. Se não existir, exibimos ao lado de uma caixa de seleção desmarcada.

Depois que tudo for exibido, clicar em uma caixa de seleção marcada significa cancelar a atribuição da permissão enquanto clicar em uma caixa de seleção desmarcada significa atribuir a permissão à função. Depois que todas as verificações e desmarcações são feitas, o usuário clica no botão 'Salvar permissões' abaixo da tabela para salvar todas as permissões que foram marcadas para essa função.

Vamos adicionar todas essas funções ao arquivo roleLogic.php. Eles estão:

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

Colocando as permissões para funcionar


Neste ponto, podemos determinar qual é a função de um usuário e, como a função está relacionada a permissões, também podemos conhecer suas permissões.

Agora queremos colocar essas permissões em funcionamento: ou seja, garantir que um usuário administrador tenha permissão para realizar apenas as ações para as quais ele tem permissão. Vamos conseguir isso usando funções de middleware. Um middleware basicamente é um pedaço de código ou uma função que é executada antes que uma ação seja executada. Normalmente, essa função de middleware pode modificar o comportamento da ação ou realizar algumas verificações que podem acabar interrompendo a ação por completo, dependendo dos resultados da verificação.

Por exemplo, um usuário pode ter as permissões create-post, update-post e delete-post. Se eles estiverem logados e tentarem publicar uma postagem, nossa função de middleware primeiro verifica se esse usuário tem a permissão de publicação de postagem. Se eles tiverem essa permissão, nossa função de middleware retornará true e o post será publicado. Se eles não tiverem a permissão de publicação de postagem, nossa função de middleware irá redirecioná-los de volta com uma mensagem dizendo que eles não têm permissão para publicar a postagem.

Vamos colocar todas as nossas funções de middleware dentro de um único arquivo chamado middleware.php. Crie-o agora na pasta admin e cole este código nele:

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;
    }
  }
?>
"; morrer(); retorne verdadeiro; } 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; } }?>
A primeira instrução if verifica se o usuário está logado. Se o usuário não estiver logado, ele será redirecionado para a página inicial. A segunda instrução if verifica se o usuário está logado e se tem uma função (é admin). Se o usuário estiver logado e tiver uma função, ele acessará a página, caso contrário, será redirecionado para a página inicial.

Em cada arquivo onde você deseja restringir o acesso de usuários não administradores, você deve incluir este arquivo middleware.php nesse arquivo. Portanto, todos os nossos arquivos de administração nos quais não queremos que usuários normais acessem, incluiremos esse arquivo neles. Portanto, abra todos os arquivos nas duas pastas dentro da pasta admin, a saber:usuários, funções. Em cada um dos arquivos, adicione a seguinte linha logo abaixo do include para config.php.

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

E isso redirecionará qualquer usuário não administrador que tentar visitar a página.

Também neste arquivo middleware.php, estamos usando in_array() do PHP para verificar se a permissão que estamos testando está na matriz de permissões desse usuário. (Quando um usuário administrador faz login, colocamos todas as suas permissões em um array de variáveis ​​de sessão chamado $_SESSION['userPermissions'].) Se a permissão atual estiver no array de permissões do usuário, significa que esse usuário tem essa permissão e portanto, a função retorna true, caso contrário, retorna false.

Agora, se você quiser verificar se um usuário tem uma permissão, digamos que uma permissão de publicação de postagem que você precisa fazer é chamar o método canPublishPost() assim:
<?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 ?>

Também como um middleware, antes de atualizarmos uma postagem, chamaremos primeiro a função de middleware canUpdatePost(). Se a função verificar e ver que o usuário não tem permissão para atualizar o post, ele retornará false e podemos redirecioná-lo para a página inicial com uma mensagem dizendo que ele não tem permissão para atualizar o post. Assim:
// 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
  }
}

Mesma coisa para publicar/despublicar posts:
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

}

Agora ficamos com a última parte deste tutorial, que é atualizar o perfil do usuário e também dar aos usuários registrados a capacidade de excluir suas próprias contas.

Obrigado por seguir. Se você tem algo a dizer, por favor, deixe nos comentários.