Creating a Permission Control System

4

I have a PHP / HTML system and want to deploy a permission system.

It would work as follows:

1) I have some pages that some features can only be seen by higher positions. Example: I have a search page, for a normal user, I want it to search only by name. Already a manager would have multiple filters available.

2) Have some page to set these permissions. Using the example search: Administrator goes on that page and determined user arrow to also be able to search by cpf.

I do not want a system ready. I want some idea where to go, what to look for, if there is any framework.

    
asked by anonymous 07.10.2014 / 13:52

4 answers

9

Creating a permission system

First we need the user table:

usuarios
  id - INT AUTO_INCREMENT PRIMARY KEY
  nome - VARCHAR
  email - VARCHAR
  senha - VARCHAR
  ativo - INT (0 - Inativo / 1 - Ativo)
  nivel - (0 - usuário comum / 1 - Administrador / 2 - Desenvolvedor / 3 - Administrador e Desenvolvedor)

If the user is an administrator or developer, ignore the access validations, in the way they think best.

Now we have to map the system pages

paginas
  id - PIRMARY KEY -- Código único que identifique a página (pode até ser o nome do arquivo ou um INT AUTO_INCREMENT)
  arquivo - VARCHAR -- Nome do arquivo
  nome - VARCHAR
  sistema - INT -- (0 - Não / 1 - Sim) é uma página do sistema como por exemplo uma página Erro 404 / Acesso Restrito / Etc...

Save all system pages in the database, and put a variable with the ID of each

Now the access table:

acesso_paginas
  usuario_id
  pagina_id
  consultar (0 - Não / 1 - Sim)
  incluir (0 - Não / 1 - Sim)
  editar (0 - Não / 1 - Sim)
  excluir (0 - Não / 1 - Sim)

Use the consultar field to see if the user has access to this page, and the other fields you can put on the include / exclude buttons and the fields in case the user can not edit.

If you want each field to have a different access, for each user, you will have to map all the fields of your system, something that gives a lot of work, but could do so:

fields
  id - INT PRIMARY KEY
  nome - VARCHAR
  pagina_id

For each field of each page, save a record in the database that identifies that field.

And access:

acesso_fields
  field_id
  pagina_id
  usuario_id
  visivel (0 - Não / 1 - Sim)
  editavel (0 - Não / 1 - Sim)

Use the visivel field to display the field in the system and the editable field to enable or disable.

Rule

Whenever a user accesses a page, you search the page in the acesso_paginas table and check that it has access to that page. If the user has access, you check what type of access, and apply the appropriate rules according to the type of access. Example, if it can only query the page just display the necessary with all fields disabled.

If you are going to validate the fields, look in the acesso_fields table for the ID of the page and search all the fields of the page in a single SELECT , and make a function to search the data returned the field and the permissions .

Example

Page access:

$pageid = 36;
$sql = "SELECT * FROM acesso_paginas WHERE usuario_id = {$_SESSION['user']['id']} AND pagina_id = {$pageid};"
// Receba o resultado e verifique o tipo de acesso.

if ($res['consultar'] == '0') {
   header("HTTP/1.1 401 Unauthorized");
   exit;
}

.
.
.

if ($res['excluir'] == '1') echo '<button type="button" id="excluir" class="button">Excluir</button>';

Field access:

$pageid = 36;
$sql = "SELECT * FROM acesso_fields WHERE usuario_id = {$_SESSION['user']['id']} AND pagina_id = {$pageid};"
// Receba os dados

function FieldAccess($dados){
   // Faça uma função quer percorra o array recebido do baco e retorne os devidos acessos ao campo
   // Pode usar códigos do tipo

   // 0 - Sem acesso total (não exibe campo)
   if ($dados[$x]['visivel'] == '0') return 0;  

   // 1 - Acesso visual (apenas exibe o campo, mas não permite editar o valor)
   if ($dados[$x]['visivel'] == '1' && $dados[$x]['editavel'] == '0' ) return 1; 

   // 2 - Acesso total
   // if ($dados[$x]['visivel'] == '1' && $dados[$x]['editavel'] == '1' ) return 2; 
   // Sempre retorne 2 para exibir normalmente os campos que não estiverem 
   // cadastrados no acesso do usuário
   return 2;
}

.
.
.

<?php
  $acesso = FieldAccess(348);
  if ($acesso > 0){
?>
<input type="text" name="cpf" id="cpf" placeholder="Informe o CPF"<?=($acesso == '1' ? ' readonly' : '')?>>
<?php
  }
?>



Note: Be very careful about excessive field validation, as it may end up hampering system performance. Use only when necessary.

    
07.10.2014 / 16:01
4

The question is wide as there are many ways to implement a permissions control system. Even so, I will try to show one of the possible paths, in general lines.

Permission control takes into account who accesses (users and / or groups), what is accessed (pages, page parts, form fields etc) and often how this can be accessed (for example, in the case of fields, if access is read only).

Taking this into account, the first step is to model your database to support these entities. At a minimum, a table of users, an "accessible" table (the objects with controlled permission), and a table of permissions relating the two, logging who has access to what.

Modeling the base, I would create an access control abstraction layer, in your case PHP, so that it is simple for the developer to "ask" the system if a given user has access to a particular object. Considering your example, when mounting the search view HTML, you could do something like this:

if(acesso->permitido($id_usuario, 'busca_cpf')) {
    // Desenha o campo de busca por CPF
}

Of course access control should not be done only in the view. In the code that actually performs the search, it is recommended to do the same verification when receiving a CPF for search, before assembling the query that brings the records by CPF. In this way, if any user without access tries to cheat the system by passing a CPF, the search would not be done.

    
07.10.2014 / 16:51
3

I'm working on a PHP site that has more or less 6500 access. The principle is a "variation" of an Assembly Language concept and we call it XGen.

Each page of the site has a String "Xgen" composed of values of type: Example:

pagina A -> 01000
pagina B -> 00100
pagine C -> 00001

Each user has a "string" of the same type. Example:

Marcello -> 01100
José -> 00001

When Marcello arrives on page A, the systems look at the 2 values:

Da pagina -> 01000
Do usario -> 01100

result: Marcello has direct access on this page

But Marcello, in this example, does not have access on page C

You can "ameliorate" the beginning using different values. Example:

001000 significa simplismente accesso
002000 significa accesar + editar
etc...

On my system, each Xgen has 100 values: for each page and for each Firefighter (and a quartet admin system). I can determine access with degree, level of training, age etc.

    
07.10.2014 / 16:57
-2

The system will consist of a simple login, validated by user and password (encrypted) against a table in the database and storing the data in the session. There will be two levels of access for our users: normal (1) and administrator (2).


Creating the MySQL Table

You can execute this MySQL code to create our table of users that has 7 fields: id, name, user, password, levels, active and register:

CREATE TABLE IF NOT EXISTS 'usuarios' (
    'id' INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
    'nome' VARCHAR( 50 ) NOT NULL ,
    'usuario' VARCHAR( 25 ) NOT NULL ,
    'senha' VARCHAR( 40 ) NOT NULL ,
    'email' VARCHAR( 100 ) NOT NULL ,
    'nivel' INT(1) UNSIGNED NOT NULL DEFAULT '1',
    'ativo' BOOL NOT NULL DEFAULT '1',
    'cadastro' DATETIME NOT NULL ,
    PRIMARY KEY ('id'),
    UNIQUE KEY 'usuario' ('usuario'),
    KEY 'nivel' ('nivel')
) ENGINE=MyISAM ;

With this you already have a table ready for our tutorial ... Run this script if you want to feed the table with some test users:

INSERT INTO 'usuarios' VALUES (NULL, 'Usuário Teste', 'demo', SHA1( 'demo' ), '[email protected]', 1, 1, NOW( ));

INSERT INTO 'usuarios' VALUES (NULL, 'Administrador Teste', 'admin', SHA1( 'admin' ), '[email protected]', 2, 1, NOW( ));
As you can see, our password field has 40 characters and when we register users we use SHA1(‘{senha}’) which means that we will use an encrypted password ... If you want to know more about sha1 see this article: Cryptography in PHP using md5 , sha1, and base64.


The XHTML Login Form

We will now create our form that will be where the visitor will enter the data and will be sent to the page validacao.php where the data will be validated (ohh).

<!-- Formulário de Login -->
<form action="validacao.php" method="post">
<fieldset>
<legend>Dados de Login</legend>
    <label for="txUsuario">Usuário</label>
    <input type="text" name="usuario" id="txUsuario" maxlength="25" />
    <label for="txSenha">Senha</label>
    <input type="password" name="senha" id="txSenha" />
    <input type="submit" value="Entrar" />
</fieldset>
</form>

As this article is not a class on forms and POST method I will skip the part that talks about the names of these inputs and their relationship with PHP itself.


Validation of data

We already have the database and login form ... Now let's start validating. The following codes should be placed within the validation.php that will handle the data received form:

First of all we need to verify that the user actually filled in something on the form, otherwise we would send him back to index.php :

<?php
// Verifica se houve POST e se o usuário ou a senha é(são) vazio(s)

if (!empty($_POST) AND (empty($_POST['usuario']) OR empty($_POST['senha']))) {
    header("Location: index.php"); exit;
}


?>

With this, any code that comes after that if will be sure that the data has been filled in the form.

Now we will open a connection to MySQL but this connection can be made in a different way, even before if you prefer ... After opening the connection we will transmit the two values entered by the visitor (user and password) to new variables and will use mysql_real_escape_string() to avoid errors in MySQL.

<?php

// Verifica se houve POST e se o usuário ou a senha é(são) vazio(s)
if (!empty($_POST) AND (empty($_POST['usuario']) OR empty($_POST['senha']))) {
    header("Location: index.php"); exit;
}

// Tenta se conectar ao servidor MySQL
mysql_connect('localhost', 'root', '') or trigger_error(mysql_error());
// Tenta se conectar a um banco de dados MySQL
mysql_select_db('usuarios') or trigger_error(mysql_error());

$usuario = mysql_real_escape_string($_POST['usuario']);
$senha = mysql_real_escape_string($_POST['senha']);

?>


Now it's time to validate the data against the user table:

<?php

// Verifica se houve POST e se o usuário ou a senha é(são) vazio(s)
if (!empty($_POST) AND (empty($_POST['usuario']) OR empty($_POST['senha']))) {
    header("Location: index.php"); exit;
}
// Tenta se conectar ao servidor MySQL
mysql_connect('localhost', 'root', '') or trigger_error(mysql_error());
// Tenta se conectar a um banco de dados MySQL
mysql_select_db('usuarios') or trigger_error(mysql_error());
$usuario = mysql_real_escape_string($_POST['usuario']);
$senha = mysql_real_escape_string($_POST['senha']);

// Validação do usuário/senha digitados
$sql = "SELECT 'id', 'nome', 'nivel' FROM 'usuarios' WHERE ('usuario' = '". $usuario ."') AND ('senha' = '". sha1($senha) ."') AND ('ativo' = 1) LIMIT 1";
$query = mysql_query($sql);
if (mysql_num_rows($query) != 1) {
    // Mensagem de erro quando os dados são inválidos e/ou o usuário não foi encontrado
    echo "Login inválido!"; exit;
} else {
    // Salva os dados encontados na variável $resultado
    $resultado = mysql_fetch_assoc($query);
}

?>

Please note that we are looking for records that have the same user ID as the visitor entered and have a password equal to the SHA1 version of the password entered by the visitor. We also only look for user records that are active, so when you need to remove a user of the system, but can not simply delete the record, just change the value of the active column to zero. ;)

The generated query looks something like this:

SELECT 'id', 'nome', 'nivel' FROM 'usuarios' WHERE ('usuario' = 'a') AND ('senha' = 'e9d71f5ee7c92d6dc9e92ffdad17b8bd49418f98') AND ('ativo' = 1) LIMIT 1

After running query ( query ) we check whether the number of results found (or not) is different from one, if an error message is displayed, accompanied by exit ends the script ... If it finds only one result we have our user and have already pulled your ID, name and access level of the database.


Saving the data in the session

Now we need to save the data found in the session because they will be used later in other pages and they need to "persist" by then ... After saving the data in the session we will redirect the visitor to a restricted page: p>

if (mysql_num_rows($query) != 1) {
    // Mensagem de erro quando os dados são inválidos e/ou o usuário não foi encontrado
    echo "Login inválido!"; exit;
} else {
    // Salva os dados encontados na variável $resultado
    $resultado = mysql_fetch_assoc($query);
    // Se a sessão não existir, inicia uma
    if (!isset($_SESSION)) session_start();
    // Salva os dados encontrados na sessão
    $_SESSION['UsuarioID'] = $resultado['id'];
    $_SESSION['UsuarioNome'] = $resultado['nome'];
    $_SESSION['UsuarioNivel'] = $resultado['nivel'];
    // Redireciona o visitante
    header("Location: restrito.php"); exit;
}


Checking if user is logged in

Our login system is almost complete! Now we just need to check if the user is logged into the system and if your access level matches that of the page ... Let's now write a small block of PHP at the beginning of the restrict.php file (which only must be accessed by logged in users):

<?php

// A sessão precisa ser iniciada em cada página diferente
if (!isset($_SESSION)) session_start();

// Verifica se não há a variável da sessão que identifica o usuário
if (!isset($_SESSION['UsuarioID'])) {
    // Destrói a sessão por segurança
    session_destroy();
    // Redireciona o visitante de volta pro login
    header("Location: index.php"); exit;
}

?>

<h1>Página restrita</h1>

<p>Olá, <?php echo $_SESSION['UsuarioNome']; ?>!</p>

Ready, my friend! Your login system is ready to go ... We're only going to make a few incremental steps to make it more "usable" ... Now you will see how to do the user verification logged in and access level, for example to a page where only administrators can have access:

<?php

// A sessão precisa ser iniciada em cada página diferente
if (!isset($_SESSION)) session_start();

$nivel_necessario = 2;

// Verifica se não há a variável da sessão que identifica o usuário
if (!isset($_SESSION['UsuarioID']) OR ($_SESSION['UsuarioNivel'] < $nivel_necessario)) {
    // Destrói a sessão por segurança
    session_destroy();
    // Redireciona o visitante de volta pro login
    header("Location: index.php"); exit;
}

?>


Logout Code

The file logout.php is so simple that it can have one line only:

<?php session_start(); session_destroy(); header("Location: index.php"); exit; ?>

Or if you prefer, a longer version:

<?php
    session_start(); // Inicia a sessão
    session_destroy(); // Destrói a sessão limpando todos os valores salvos
    header("Location: index.php"); exit; // Redireciona o visitante
?>


Source: Thiago Belem

    
07.10.2014 / 14:03