Permisos de usuarios

Todo el material del fracasado proyecto aldeano :(

Permisos de usuarios

Notapor cito el 01 Nov 2007 12:29

Permisos de usuarios
Posted in Personal / Reflexiones, Software, Programación by Nicolaspar on the Diciembre 8th, 2006



Aún recuerdo mi primer sistema web, este debía cumplir un requisito indispensable en el backoffice y era que los administradores pudieran contar con diferentes permisos sobre las diferentes 5 secciones que este tenia, para así poder por ejemplo, crear un administrador Juan que le cargase noticias, y uno que fuera Pepe, que le agregue encuestas y le administre los foros.

Para ese entonces, y aún teniendo la lógica de un ex estudiante de sistemas, no se me ocurrió más que agregar por cada sección un campo en la tabla de administradores del tipo “si/no”.

Aunque la vergüenza al comentar esto es muy grande, lo compensa el hecho de que creo que hoy en día he encontrado el equilibrio exacto para esta tarea a nivel de sistemas con permisos simples de este estilo.

Primer planteo, comparación bit a bit.

Unos años después comencé a conocer más en profundidad las ventajas de ASP y PHP, y ahí vi que en ambos casos se pueden usar estas comparaciones. No voy a explicar sistemas binarios, octanos, decimales, etc, solo contarles que trabajaremos con binarios, que estos son una notación en base dos (ceros y unos, hay excelentes manuales que google les puede mostrar en la web sobre este tema) y que lo que nos interesa saber es que usaremos el operador lógico “AND” y que lo que éste hace es evaluar los bits que están activos, tanto en A como en B, y si da alguno como activo será verdadero.

Esto sería lo siguiente llevado a un ejemplo cualquiera:
01001101
10101110
00001100

Noten que solo pasan activos la 5ta y 6ta posición, otro ejemplo:
111000111
001110101
001000101

Aclaro que los activos son los 1 (por si alguien esta dormido), y que si en el número de abajo esa posición esta también activa, pues nada mas baja como 1, sino quedará como cero. Si no lo entendiste aún reléelo que es simple de comprender.

Ahora, sabiendo la siguiente conversión:
0 = 00000000
1 = 00000001
2 = 00000010
3 = 00000011
4 = 00000100
5 = 00000101
6 = 00000110
7 = 00000111
8 = 00001000
9 = 00001001

Lo que haremos es, tomaremos el número 3 en binario, y nos fijaremos que nos enciende este 3 junto con un número 5:
00000011 = 3 en decimal
00000101 = 5 en decimal
00000001 = 1 en decimal

Veremos que nos devuelve 1, que sería un resultado positivo porque nos dice que hay un bit en 3 que también esta prendido en 5. Para el sistema que propongo se usarán los bits que no coincidan con los bits prendidos en las mismas posiciones, para lo que usaremos la simple formula que será la simple n^2 que sería mas simple de entender para muchos viendo como resultado de lo anterior los siguiente valores, 1,2,4,8,16,32,64,128, etc, etc: Para que se comprenda lo que trate de decir:
00000001 = 1
00000010 = 2
00000100 = 4
00001000 = 8
00010000 = 16
00100000 = 32
01000000 = 64
10000000 = 128
100000000 = 256
1000000000 = 512

Y para que vayamos entendiendo aún mas tomaremos hasta el 16 y le asignaremos secciones inventadas para ejemplificarlo.
00000001 = 1 = Usuarios
00000010 = 2 = Administradores
00000100 = 4 = Noticias
00001000 = 8 = Encuestas
00010000 = 16 = Foros

Con este método nos aseguramos que si por ejemplo, tenemos permisos 10 (en decimal, que no sería mas que administradores + encuestas o bien 2+8) no se pisen con otra combinación. Veamos como noticias que es 4 no nos encendería jamás un bit:
00000100 = 4 de noticias
00001010 = 10 de nuestros permisos asignados
00000000 = Es todo apagado.

Teniendo la seguridad que esto es 100% efectivo podemos hacer un formulario con unos checkbox donde cada uno tendrá unos de los valores del ejemplo y al guardarlos se sumarán guardando en un solo campo un único valor. Si el administrador tiene permisos sobre todas las secciones tendrá 31, si fueran noticias, encuestas y usuarios tendrá 13, y así sucesivamente.

Se preguntarán como aplicamos esto a nuestro sistema, y la respuesta es muy simple. Por ejemplo, en la botonera o link a la sección validamos de la siguiente manera:

if ( ($permisos & 2) == 2 ) { … Administradores … }
if ( ($permisos & 4) == 4 ) { … Noticias … }

Lo que hago acá es decirle, a mis 10 de permisos (variable $permisos) le prendo los mismos bits que el valor de la sección, ejemplifiquemos que se comprende mejor:
Administradores:
00001010 (mis permisos, 10 en dec)
00000010 (administradores, 2 en dec)
00000010 (Bárbaro, tengo un bit encendido)
Veamos con Noticias:
00001010 (mis permisos, 10 en dec)
00000100 (administradores, 2 en dec)
00000000 (No se prende ninguno, no tengo acceso)

En ASP es exactamente igual:

if (adm_permiso and 2) = 2 then … Administradores … end if
if (adm_permiso and 4) = 4 then … Noticias … end if

A esto le sumaremos la seguridad de agregar en cada pagina la misma validación, para asegurarnos que no entren vía URL directa, personalmente tengo hechas funciones donde le paso que valor evaluar y accedo a sus permisos que están en una session dando un true o false como salida en la misma.

Segundo planteo, string de datos.

Un día me encontré con la falencia de que el sistema tenía secciones dinámicas que se creaban de manera excesivas, por lo que el campo “int” que solía usar me quedaba chico, y no solo eso, sino que para PHP ya dejaba de ser un int este numerote que superaba los, si!, dos billones que es el valor usual (lo que es un valor de 32 bits con signo) . La solución que reemplazará a la anterior debía ser mejor que está.
Esta solución es mucho mas simple de comprender porque no requiere conocimientos de matemáticas o conocimientos sobre sistemas de numeración y demás yerbas, si un poco mas de sql que lo anterior, pero sql básico al fin.
La solución es la siguiente: Seguimos conservando la tabla de administradores pero sin el campo de permisos. A cambio de este crearemos una tabla de permisos y una de administradores permisos. En la tabla permisos tendremos una simple Primary Key y un nombre, en la de administradores permisos tendremos una relación de muchos a muchos con la PK del permiso y el PK del administrador.
Toda lo que resta es una vez logueado crear una variable que contenga todos los permisos que este administrador tenga separados por una coma (un while sobre la tabla de relación concatenando los valores). Lo importante en este paso es que todo quede entre comas, ej: 0,1,2,10,20,12,5,0, donde los ceros son “bobos”, para que por ejemplo el 5 no quede libre. Teniendo este formato podemos hacer una función que evalue si el string “permiso” esta dentro del string que conforma los permisos del usuario. En PHP podemos usar la funcion strstr() (http://www.php.net/strstr), y haríamos algo así:

$permisoAEvaluar = 2;
if( strstr( $permisos, ‘,’ . $permisoAEvaluar . ‘,’ ) ){
true
}else{
false
}

Obviamente esto queda mucho mejor en una función o classe que cada uno sabrá adaptar según las necesidades que tenga.

Espero que estas lógicas de aplicación les sean útiles y a programar!.
Avatar de Usuario
cito
Administrador
 
Mensajes: 7833
Registrado: 06 Jul 2004 21:30
Ubicación: Aldea Bit

Re: Permisos de usuarios

Notapor FuLaNo_ el 25 Feb 2008 10:44

Che, se puede responder/opinar aca?, bueno, cualquier cosa me cagan a pedos :)

Si bien el sistema que describis es efectivo, yo utilizo un sistema "diferente".

Ante todo, tengo 3 tablas: users, modules, privileges
En usuario tengo, obviamente, los usuarios registrados, en modulos los modulos activos (noticias, foros, galeria, etc) y la tercer tabla es la que labura en serio, esta se ocupa de guardar para cada usuario los modulos que tiene permitidos.

table users
id     | username
1         tomas
2         juan

table modules
id     | module
1         news
2         photogalery
3         forums
4         privileges

table privileges
id     | id_user     | id_module
1         1                 1
2         1                 3
3         2                 2
4         2                 4


En este caso "tomas" tiene acceso a news y forums, mientras que juan tiene acceso a photogalery y privileges...

Cuando tenemos pocos usuarios y/o pocos modulos, y nuestra herramienta muy posiblemente no incorpore nuevos modulos, este sistema quizas no es el mas adecuado....
Avatar de Usuario
FuLaNo_
 
Mensajes: 105
Registrado: 10 Sep 2006 22:33

Re: Permisos de usuarios

Notapor nicolaspar el 25 Feb 2008 15:58

Supongo que se debe poder opinar:P

En si, tu planteo no esta mal, de hecho es el mismo que uso yo y llame antes como "Segundo planteo, string de datos.":

nicolaspar escribió:A cambio de este crearemos una tabla de permisos y una de administradores permisos. En la tabla permisos tendremos una simple Primary Key y un nombre, en la de administradores permisos tendremos una relación de muchos a muchos con la PK del permiso y el PK del administrador.


Igual lo manejo no directo a la db sino como string, cuando menos consulte la db mejor.
Imagen
Avatar de Usuario
nicolaspar
 
Mensajes: 3914
Registrado: 27 Jul 2004 10:09
Ubicación: Siempre detrás de lexico

Re: Permisos de usuarios

Notapor FuLaNo_ el 25 Feb 2008 16:08

Si si, vos lo utilizas los modulos en una linea, ej:

id | privilegios
1 | 10,1,3,12,5,6

Esto es bueno para aplicaciones tipo foros, por lo que decis, el tema de las consultas... lo que yo mostre lo utilizo en los paneles de control que no tienen tantos accesos como un foro o similar.. alli si es bueno tener un string ya que la consulta la hacemos una sola vez por pagina...
Avatar de Usuario
FuLaNo_
 
Mensajes: 105
Registrado: 10 Sep 2006 22:33

Re: Permisos de usuarios

Notapor nicolaspar el 26 Feb 2008 09:28

Nopo, en la db uso igual que vos:

id_administrador | id_permiso
1 | 1
1 | 2
1 | 3
1 | 4
2 | 3

Lo que si hago es, cuando logueo (y en algunos sistemas en cada load of page) genero una variable con el string ",0,1,2,3,4,0," (para el caso del admin id 1) y luego uso un strstr para evaluar si tiene o no permiso, eso dentro de una función, quedando algo como:
<?
if( tiene_permiso(1) ){
echo "si";
}else{
echo "no";
?>

Y hasta se pueden autosetear constantes con los permisos para hacer algo como:

<?
if( tiene_permiso( NOTICIAS ) ){
echo "si";
}else{
echo "no";
?>

Eso es para contenidos, para la sección al inicio pongo algo como:
<?
require_once('conn.php'); #Conexión a la db
require_once('seguridad.php'); #acá tengo las funciones de seguridad y los inicios de sessiones.
permiso_requerido( NOTICIAS ); #Esto evalúa, si no pasa redirecciona a un "no_tiene_permsisos.php".
?>

Supongo, por lo visto, que lo haces de manera similar.
Imagen
Avatar de Usuario
nicolaspar
 
Mensajes: 3914
Registrado: 27 Jul 2004 10:09
Ubicación: Siempre detrás de lexico

Re: Permisos de usuarios

Notapor FuLaNo_ el 26 Feb 2008 09:41

Si, es igual, solo que mi funcion hace la llamada a la DB... pero como dije, al estar SEGURO que nunca va a tener miles de peticiones juntas no me hago drama... es un panel de control, así tenga 100 usuarios simultaneos, esas consultas no recargaran el sistema...

Ahora en foros y eso si lo hago como decis...

Igualmente en el proximo trabajo seguramente pasaré el panel a tu sistema, mientras menos consultas, mejor... pero hare una modificiacion, en lugar de utilizar 0 y 1, utilizare los nombres de los modulos, si bien tardará mas la busqueda, luego será mas facil endenderlo -en caso de que no tenga que hacerlo yo- ej: string 'news,privileges,forums,partners';

Encuentras algun problema en esto?, es decir, alguna desventaja significativa en utilizar string en lugar de numeros?, más alla de luego tener que escribir mas :p

EDIT:

Bueno, ya cambie el sistema jejeje, soy medio compulsivo en cuanto a cambios que puedan mejorar el rendimiento..., eso si, utilice in_array()...

function privileges_modules($module) {
global $_USR;
$module = SICS($module);
if (@in_array($module,$_USR['privileges'])) { return $module; } else { return false; }
}
Avatar de Usuario
FuLaNo_
 
Mensajes: 105
Registrado: 10 Sep 2006 22:33

Re: Permisos de usuarios

Notapor nicolaspar el 26 Feb 2008 11:13

Usar in_array() es mas lento que strstr() (aún trabajando con arrays, prefiero usar un isset o empty sobre la posición a buscar), lo mismo con usar strings en vez de ints (podes declarar constantes que contengan el id), y más aún comparar en cada petición contra la db.
Ahora, como bien decís, no hace la diferencia en un backend...pero aún así todo suma para que sea más óptimo.
Imagen
Avatar de Usuario
nicolaspar
 
Mensajes: 3914
Registrado: 27 Jul 2004 10:09
Ubicación: Siempre detrás de lexico

Re: Permisos de usuarios

Notapor FuLaNo_ el 26 Feb 2008 11:20

Como usas strstr??
Avatar de Usuario
FuLaNo_
 
Mensajes: 105
Registrado: 10 Sep 2006 22:33

Re: Permisos de usuarios

Notapor nicolaspar el 26 Feb 2008 11:40

En este caso:

Toda lo que resta es una vez logueado es crear una variable que contenga todos los permisos que este administrador tenga separados por una coma (un while sobre la tabla de relación concatenando los valores). Lo importante en este paso es que todo quede entre comas, ej: 0,1,2,10,20,12,5,0, donde los ceros son “bobos”, para que por ejemplo el 5 o el 1 no queden libres. Teniendo este formato podemos hacer una función que evalue si el string “permiso” (que es el id) esta dentro del string que conforma los permisos del usuario. En PHP podemos usar la funcion strstr() (http://www.php.net/strstr), y haríamos algo así:

$permisoAEvaluar = 2;
if( strstr( $permisos, ‘,’ . $permisoAEvaluar . ‘,’ ) ){
#true
}else{
#false
}

$permisos tendrá todo el string con los id de los permsisos asignados, ej: "0,1,2,10,20,12,5,0,", y $permisoAEvaluar tendrá el id del permiso que estamos evaluando, ej: 1, que como te decía puede ser una constante llamada NOTICIAS (define("NOTICIAS",1)).
Imagen
Avatar de Usuario
nicolaspar
 
Mensajes: 3914
Registrado: 27 Jul 2004 10:09
Ubicación: Siempre detrás de lexico

Re: Permisos de usuarios

Notapor FuLaNo_ el 26 Feb 2008 12:00

aaaa, ahora si...

Igual, por ahora me quedo con variables string y no numbers :)

Lo que no entiendo es lo de ceros bobos, o sea,entiendo para que lo haces, pero no el porque, si no pones ceros deberia funcionar bien igual, mi string queda así:

$_USR['privileges'] = ',modules,news,news_categories,reports,calendar,links,links_categories,partners,';

function privileges_modules($module) {
global $_USR;
$module = SICS($module);
if (strstr($_USR['privileges'],','.$module.',')){ return $module; } else { return false; }
}
Avatar de Usuario
FuLaNo_
 
Mensajes: 105
Registrado: 10 Sep 2006 22:33


Volver a Notas del Blog

¿Quién está conectado?

Usuarios navegando por este Foro: No hay usuarios registrados visitando el Foro y 0 invitados

cron