FlightPHP/Permisos

Se trata de un módulo de permisos que se puede utilizar en tus proyectos si tienes múltiples roles en tu aplicación y cada rol tiene una funcionalidad un poco diferente. Este módulo te permite definir permisos para cada rol y luego verificar si el usuario actual tiene el permiso para acceder a una página específica o realizar una acción determinada.

Haz clic aquí para acceder al repositorio en GitHub.

Instalación

¡Ejecuta composer require flightphp/permissions y estás listo/a!

Uso

Primero necesitas configurar tus permisos, luego le indicas a tu aplicación lo que significan los permisos. Finalmente verificarás tus permisos con $Permissions->has(), ->can(), o is(). has() y can() tienen la misma funcionalidad, pero están nombrados de manera diferente para que tu código sea más legible.

Ejemplo Básico

Imaginemos que tienes una función en tu aplicación que verifica si un usuario ha iniciado sesión. Puedes crear un objeto de permisos de esta manera:

// index.php
require 'vendor/autoload.php';

// algún código

// luego probablemente tengas algo que te diga cuál es el rol actual de la persona
// probablemente tengas algo donde obtienes el rol actual
// de una variable de sesión que lo define
// después de que alguien inicie sesión, de lo contrario tendrán un rol de 'invitado' o 'público'.
$current_role = 'admin';

// configurar permisos
$permiso = new \flight\Permiso($current_role);
$permiso->defineRegla('iniciadoSesion', function($current_role) {
    return $current_role !== 'invitado';
});

// Probablemente querrás persistir este objeto en Flight en algún lugar
Flight::set('permiso', $permiso);

Luego en un controlador en algún lugar, podrías tener algo como esto.

<?php

// algún controlador
class AlgunControlador {
    public function algunaAccion() {
        $permiso = Flight::get('permiso');
        if ($permiso->has('iniciadoSesion')) {
            // hacer algo
        } else {
            // hacer algo más
        }
    }
}

También puedes usar esto para rastrear si tienen permiso para hacer algo en tu aplicación. Por ejemplo, si tienes una forma en la que los usuarios pueden interactuar con la publicación en tu software, puedes verificar si tienen permiso para realizar ciertas acciones.

$current_role = 'admin';

// configurar permisos
$permiso = new \flight\Permiso($current_role);
$permiso->defineRegla('publicación', function($current_role) {
    if($current_role === 'admin') {
        $permisos = ['crear', 'leer', 'actualizar', 'eliminar'];
    } else if($current_role === 'editor') {
        $permisos = ['crear', 'leer', 'actualizar'];
    } else if($current_role === 'autor') {
        $permisos = ['crear', 'leer'];
    } else if($current_role === 'colaborador') {
        $permisos = ['crear'];
    } else {
        $permisos = [];
    }
    return $permisos;
});
Flight::set('permiso', $permiso);

Luego en un controlador en algún lugar...

class ControladorPublicación {
    public function crear() {
        $permiso = Flight::get('permiso');
        if ($permiso->can('publicación.crear')) {
            // hacer algo
        } else {
            // hacer algo más
        }
    }
}

Inyectar dependencias

Puedes inyectar dependencias en el cierre que define los permisos. Esto es útil si tienes algún tipo de interruptor, identificación, u otro punto de datos que deseas verificar. Lo mismo funciona para llamadas de tipo Clase->Método, excepto que defines los argumentos en el método.

Cierres

$Permiso->defineRegla('orden', function(string $current_role, MiDependencia $MiDependencia = null) {
    // ... código
});

// en tu archivo de controlador
public function crearOrden() {
    $MiDependencia = Flight::miDependencia();
    $permiso = Flight::get('permiso');
    if ($permiso->can('orden.crear', $MiDependencia)) {
        // hacer algo
    } else {
        // hacer algo más
    }
}

Clases

namespace MiApp;

class Permisos {

    public function orden(string $current_role, MiDependencia $MiDependencia = null) {
        // ... código
    }
}

Atajo para establecer permisos con clases

También puedes usar clases para definir tus permisos. Esto es útil si tienes muchos permisos y deseas mantener tu código limpio. Puedes hacer algo como esto:

<?php

// código de inicio
$Permisos = new \flight\Permiso($current_role);
$Permisos->defineRegla('orden', 'MiApp\Permisos->orden');

// myapp/Permisos.php
namespace MiApp;

class Permisos {

    public function orden(string $current_role, int $user_id) {
        // Suponiendo que configuraste esto de antemano
        /** @var \flight\database\PdoWrapper $db */
        $db = Flight::db();
        $permisos_permitidos = [ 'leer' ]; // todos pueden ver una orden
        if($current_role === 'gerente') {
            $permisos_permitidos[] = 'crear'; // los gerentes pueden crear órdenes
        }
        $algún_interruptor_especial_de_db = $db->fetchField('SELECT algún_interruptor_especial FROM ajustes WHERE id = ?', [ $user_id ]);
        if($algún_interruptor_especial_de_db) {
            $permisos_permitidos[] = 'actualizar'; // si el usuario tiene un interruptor especial, pueden actualizar órdenes
        }
        if($current_role === 'admin') {
            $permisos_permitidos[] = 'eliminar'; // los administradores pueden eliminar órdenes
        }
        return $permisos_permitidos;
    }
}

La parte genial es que también hay un atajo que puedes usar (¡también puede ser almacenado en caché!) donde simplemente indicas a la clase de permisos que mapee todos los métodos de una clase en permisos. Por lo tanto, si tienes un método llamado orden() y un método llamado empresa(), se mapearán automáticamente para que puedas simplemente ejecutar $Permisos->has('orden.leer') o $Permisos->has('empresa.leer') y funcionará. Definir esto es muy complejo, así que quédate conmigo aquí. Solo necesitas hacer esto:

Crea la clase de permisos que deseas agrupar.

class MisPermisos {
    public function orden(string $current_role, int $orden_id = 0): array {
        // código para determinar permisos
        return $arreglo_permisos;
    }

    public function empresa(string $current_role, int $empresa_id): array {
        // código para determinar permisos
        return $arreglo_permisos;
    }
}

Luego haz que los permisos sean descubribles usando esta biblioteca.

$Permisos = new \flight\Permiso($current_role);
$Permisos->defineReglasDesdeMétodosDeClase(MiApp\Permisos::class);
Flight::set('permisos', $Permisos);

Finalmente, llama al permiso en tu base de código para verificar si el usuario tiene permiso para realizar un permiso dado.

class AlgunControlador {
    public function crearOrden() {
        if(Flight::get('permisos')->can('orden.crear') === false) {
            die('¡No puedes crear una orden. ¡Lo siento!');
        }
    }
}

Caché

Para habilitar la caché, consulta la sencilla biblioteca wruczak/phpfilecache. Un ejemplo de cómo habilitar esto se muestra a continuación.


// esta variable $app puede ser parte de tu código, o
// simplemente puedes pasar null y se
// obtendrá de Flight::app() en el constructor
$app = Flight::app();

// Por ahora solo acepta esto como una caché de archivos. Otros pueden agregarse fácilmente en el futuro.
$Caché = new Wruczek\PhpFileCaché\PhpFileCaché;

$Permisos = new \flight\Permiso($current_role, $app, $Caché);
$Permisos->defineReglasDesdeMétodosDeClase(MiApp\Permisos::class, 3600); // 3600 indica cuántos segundos almacenar en caché. Omite esto para no usar la caché

¡Y listo!