Tutorial para validar el rol de un usuario al momento de hacer una consulta
Muchas veces, manejamos diferentes roles para los usuarios, de esta manera, por ejemplo, en la app de recetas, tenemos a un Administrador del Restaurante, quien debe ser el único que cree nuevas recetas, un auditor de calidad, quien es el único que puede ver una receta en particular, y tenemos módulos que son para todo el mundo.
Si bien no tengo la misma experiencia que tiene el profe Italo con Laravel, y es probable que mi solución no sea la más linda u optimizada, se las quiero compartir de nivel de tutorial. Los invito a que la usen y la mejoren.
Antes de empezar, les dejo dos prompts importantes por hacerle a Chat Gpt, para que entiendan el trasfonod de las herramientas que les voy a enseñar:
Paso 1. Instalar laravel permission
Laravler permission es la biblioteca standard para manejar roles y permisos en Laravel, hoy solamente vamos a usar la parte de roles, pero quizás alguien se anime a hacer la parte de permisos. Para instalarla se usa el comando:
composer require spatie/laravel-permission
Posteriormente, activa las migraciones y configuraciones de la librería, mediante los comandosphp artisan vendor:publish --provider="Spatie\Permission\PermissionServiceProvider" php artisan migrate
Lo cual traerá los archivos necesarios y creara las tablas necesarias, recuerda desactivar las configuraciones de phpunit para que se creen de verdad
Paso 2: Crea los roles y asigna los roles a los usuarios
Se debió a ver creado un Seeder que se llama RoleSeeder, si no se creó, crealo.
Dentro del método run, crea los roles de la siguiente manera$role1 = Role::create(['name' => 'Administrator']); $role2 = Role::create(['name' => 'Auditor']); $role3 = Role::create(['name' => 'User']);
Recuerda que un usuario puede tener varios roles, pero para este caso, haremos que solo tenga 1
Dentro del mismo run, le asignamos al usuario 1 el rol de Administrador, al usuario 2 el rol de auditor, y al resto de los usuarios, el rol de Usuario.
`User::find(1)->assignRole($role1);
User::find(2)->assignRole($role2);
$rest_of_users = User::where(‘id’,‘not in’, ‘(1,2)’)->get();
foreach ($rest_of_users as $key => $each_user) {
$each_user->assignRole($role3);
}`
En el archivo DatabaseSeeder.php, debemos indicarle que ejecute RoleSeeder, colocando al final del método run el siguiente código.$this->call(RoleSeeder::class);
Hacemos una migración de la base de datos limpia, es decir, le decimos al sistema que borre toda la data y la vuelva a crear con los nuevos cambios; para eso, en consola escribimos:php artisan migrate:fresh –seed
Si todo sale bien, tendremos en la base de datos la siguiente tabla: model_has_roles
Para entender como funciona la tabla, debes entender sobre relaciones polimórficas, he ahí la razón de la consulta del principio del tutorial. Sin embargo, como no vas a trabajar sobre la tabla directamente, no es obligatorio que entiendas como funciona.
**Paso 3: Asigna el middleware necesario **
De nada sirve asignar los roles, si no nos ponemos juiciosos para que solos los usuarios correctos puedan acceder a los módulos correctos.
Debido a que esta es una característica nueva, probablemente la versión 1 no este preparada para soportarla, por tanto, la vamos a implementar en la versión 2
Lo primero que se debe hacer es decirle a la aplicación que va a utilizar el middleware de role, para eso, se edita el archivo app/Http/Kernel.php y se agrega en el arreglo de $middlewareAliases la siguiente línea:
‘role’ => \Spatie\Permission\Middlewares\RoleMiddleware::class
Finalmente, en la ruta de api_v2, se agrega el middleware de la siguiente forma
Route::get(‘recipes’, [RecipeController::class, ‘index’])->middleware([‘role:Administrator’]);
Es suuuper importante que el rol tenga el mismo nombre que el que definiste en el paso 2, ten en cuenta mayúsculas y trata de no usar tildes ni caracteres especiales
Paso 5: Realiza las pruebas end to end
Ingresamos como el usuario 1, que tiene rol de Administrator
Consultamos las recetas autenticados como le usuario 1
Nos autenticamos como el usuario 2
Intentamos realizar la consulta
Nos dice que no tenemos el rol adecuado para hacer la búsqueda
Paso 6: Realiza las pruebas unitarias
Dentro del directorio tests\Feature\Http\Controllers\Api\V2\RecipeTest.php que ya existe, vamos a crear comentar el test llamado test_index_v2, y vamos a crear dos nuevos test: test_get_recipies_by_administrator() y test_get_recipies_by_auditor() La estructura de los tests será la siguiente:
Este es mi código, pero, ya tu sabeh, el tuyo no tiene que ser exactamente igual al mio, lo que importa es que tengas claro la lógica
Finalmente, corre el test
Arreglemos entonces el test que habíamos comentado
`class RecipeTest extends TestCase
{
use RefreshDatabase;
public function test_index_v2(): void
{
// Crear un usuario con el rol de administrador
$adminRole = Role::create(['name' => 'Administrator']);
$user = User::factory()->create();
$user->assignRole($adminRole);
// Autenticar al usuario
$this->actingAs($user);
Category::factory()->create();
$recipes = Recipe::factory(5)->create();
$response = $this->getJson('/api/v2/recipes');
$response->assertStatus(Response::HTTP_OK)
->assertJsonCount(5, 'data')
->assertJsonStructure([
'data' => [],
'links' => [],
'meta' => [],
]);
}
public function test_get_recipies_by_administrator(): void
{
// Crear un usuario con el rol de administrador
$adminRole = Role::create(['name' => 'Administrator']);
$user = User::factory()->create();
$user->assignRole($adminRole);
// Autenticar al usuario
$this->actingAs($user);
// Acceder a una ruta protegida por el middleware 'role'
$response = $this->get('/api/v2/recipes');
// Verificar que la respuesta sea exitosa (código de estado 200)
$response->assertStatus(200);
}
public function test_get_recipies_by_auditor(): void
{
// Crear un usuario con el rol de administrador
$adminRole = Role::create(['name' => 'Auditor']);
$user = User::factory()->create();
$user->assignRole($adminRole);
// Autenticar al usuario
$this->actingAs($user);
// Acceder a una ruta protegida por el middleware 'role'
$response = $this->get('/api/v2/recipes');
// Verificar que la respuesta sea no autorizada (código de estado 403)
$response->assertStatus(403);
}
}
`
Te dejo la tarea de implementar la lógica para que únicamente el Auditor pueda ver una única receta. Espero te haya gustado este tutorial.
Lo revisareee, muchas gracias por tu aporte…