Uno de los principales problemas al crear una aplicación ya sea de escritorio o web es la definición de los permisos, donde muchas veces terminamos con incontables «if» anidados que no facilitan el mantenimiento de nuestra aplicación y no estoy contanto el hecho de que tarde o temprano nos pidan añadir más permisos o que nuestra aplicación así lo requiera.

También podríamos optar por crear grupos de usuarios en el servidor, y darles acceso o no a ciertas partes de nuestro sistema, pero de nuevo, esto cambia para cada servidor y hace que una mudanza o pequeño cambio conlleve mucho trabajo.

Una solución que he usado en mis últimos proyectos es el uso de los operadores bitwise, muy fáciles y útiles de manejar si sabes binario y como funcionan y si no sabes quizá te cueste un poco entenderle pero no es nada de otro mundo.

Introducción – ¿Recordando un poco de binario?

No quiero alargar mucho el post explicándolo pero creo que es un poco necesario para quienes no tengan ni idea, muchos o la mayoría de quienes programamos sabemos como representar un número base 10 en base 2 ( sistema decimal a sistema binario ), nuestro sistema es un sistema base 10 y podemos escribir y entender números de acuerdo a la siguiente tabla:

10^4 = 10000 10^3 = 1000 10^2 = 100 10^1 = 10 10^0 = 1
15 0 0 0 1 5

Donde en cada casilla podemos colocar números de acuerdo a la base (en este caso 10 números, del 0 al 9 ) y los números en la casilla 10^0 valdrán unidades

10^0 = 1 * 5 = 5

Y la segunda casilla 10^1 serán las decenas:

10^1 = 10 * 1 = 10

Y sumando los resultados tenemos que 10 + 5 = 15

Hagamos exactamente lo mismo pero ahora en base 2 ( binario )

 

2^4 = 16 2^3 = 8 2^2 = 4 2^1 = 2 2^0 = 1
15 0 1 1 1 1

Así como en base 10 podemos usar 10 números para cada casilla ( 0 – 9 ) en base 2 únicamente podemos usar 2 ( 0 – 1 ) por lo que tenemos

2^0 = 1 * 1 = 1
2^1 = 2 * 1 = 2
2^2 = 4 * 1 = 4
2^3 = 8 * 1 = 8

Sumando los resultados tenemos que

1 + 2 + 4 + 8 = 15, entonces sabiendo esto podemos decir que ( 1111 ) es la representación binaria de 15

Ahora, ¿Porqué es importante aclararlo? pues porque nuestras pcs entienden todo en binario precisamente, y cada «casilla» ( por así llamarles ) representa 1 bit, habiendo entendido esto pasemos a lo siguiente.

¿Como funciona bitwise?

Bitwise son solo operadores binarios ( si, así como en el sistema decimal podemos sumar, restar y multiplicar, los bits también tienen sus operadores ) podemos mencionar:

AND ( & ) / OR ( | ) / NOT ( ~ ) ( igual que en electrónica donde de hecho, se usan muchísimo ).

¿Cómo funciona cada operador?, comparemos la siguiente tabla usando los números 6 y 13 binarios

 

AND ( & ) 2^4 = 16 2^3 = 8 2^2 = 4 2^1 = 2 2^0 = 1
6 0 0 1 1 0
13 0 1 1 0 1
6 & 13 0 0 1 0 0

Se puede ver claramente como funciona AND donde para que el bit quede encendido forzosamente tiene que estar encendido ( ser 1 ) en ambos números, en este caso solo se encuentran 2 iguales en 2^2 en ambos números, por lo que la respuesta de 6 & 13 = 100b2 o 4b10  (b2 = base 2, b10 = base 10).

Vamos a ver ahora como funciona el operador OR con la misma tabla pero aplicándolo en vez de AND.

 

OR ( | ) 2^4 = 16 2^3 = 8 2^2 = 4 2^1 = 2 2^0 = 1
6 0 0 1 1 0
13 0 1 1 0 1
6 | 13 0 1 1 1 1

A diferencia del operador AND el OR no importa que no se encuentra encendido el bit en ambos números, con que esté en 1 basta para tomarlo como válido en el resultado final, entonces vemos que 6 | 13 = 1111b2 o 15b10.

¿Fácil no?, no explicaré el NOT ya que para nuestro fin no es del todo necesario, al menos en esta explicación de permisos sencillos solo puedo decirles en teoría que, lo que hace es invertir los bits ejemplo teniendo 13b10 = 1101b2  el hacer ~1101b2 = 0010b2.

Aplicando permisos a nuestra aplicación

Estoy seguro que con lo anterior ya dicho, muchos ya se están imaginando las formas de utilizarlo para nuestro fin, pues bien, vamos allá.

Podemos iniciar creando una clase PHP con nuestros permisos definidos:

<?php
class Permisos {

    const LEER = 1;
    const ESCRIBIR = 2;
    const EDITAR = 4;
    const ELIMINAR = 8;

}
?>

Bueno quiero aclarar algo en corto, crear una clase no es necesario, bien pueden definir los permisos en cada archivo PHP que hagan ( aunque siendo honesto creo que eso es lo que se quiere evitar ) o manejando un archivo principal de configuración propio y guardándolos como variables globales ( con los problemas que conlleva el que para evitar problemas en cada archivo php la llamen usando «global» ) así que crear una clase para mi es la mejor opción, así solo se preocupan del include.

Lo primero que van a notar es que cada permiso tiene un valor de las exponenciales de 2 ( 1, 2, 4, 8, 16, 32 ) faltando números como el 3,5,7… La razón de esto es que, queremos que a cada permiso lo represente un bit diferente, así podemos volver a nuestra tabla y decir:

 

2^4 = 16 2^3 = 8 2^2 = 4 2^1 = 2 2^0 = 1
LEER = 1 0 0 0 0 1
Escribir = 2 0 0 0 1 0
Editar = 4 0 0 1 0 0
Borrar = 8 0 1 0 0 0

Y así sucesivamente seguimos con las definiciones de los tipos de usuarios y asignando sus permisos:

$invitado = Permisos::LEER;
$escritor = $invitado | Permisos::ESCRIBIR;
$editor = $escritor | Permisos::EDITAR;
$administrador = $editor | Permisos::ELIMINAR;

Bien, puede que algunos tengan dudas sobre como estamos haciendo esto, pero, si regresamos a nuestras tablas anteriores, van a ver como $invitado, únicamente puede leer, mientras que $escritor al tener el OR $invitado, hace que tenga todos los privilegios de invitado + Permisos::ESCRIBIR ( recuerden como se manejan los bits en OR ).

Si en algún momento quisiéramos quitarle 1 permiso en específico a algún usuario que ya se le había agregado antes, usamos el NOT ( no lo hemos visto en este post, pero si es necesario después podemos ampliarlo.

¿Cómo saber si un usuario posee un permiso específico?

Solo tenemos que aplicarle un AND

// Este if devuelve TRUE
if ( $invitado & Permisos::LEER ) {
    echo "PUEDE LEER";
}
else {
    echo "NO PUEDE LEER";
}
// SALIDA: PUEDE LEER

//Este if devuelve FALSE
if ( $invitado & Permisos::ESCRIBIR) {
    echo "PUEDE ESCRIBIR";
}
else {
    echo "NO PUEDE ESCRIBIR";
}
// SALIDA: NO PUEDE ESCRIBIR

Si hacemos una función rápida:

TIENE_PERMISO( $el_permiso, $permisos_actuales ) {
    if ($el_permiso & $permisos_actuales) {
        return true;
    }
    else {
        return false;
    }
}

así de sencillo.

¿Cuántos permisos podemos crear?

Depende, se pueden crear tantos permisos como bits puedas usar, y esto depende de nuestro Sistema Operativo, de nuestro servidor ( en el caso de ser web ), del lenguaje de programación y en este caso de la configuración de PHP, pero en general tenemos 32 bits.

[alert type=»info»]Aquí puedes ver una forma de implementar este sistema dando permisos individuales para cada usuario[/alert]