Taylor tiene una capacidad de sorprendernos con nuevos features que poseen una interfaz más sencilla de lo que podrías haberte imaginado; y hoy es uno de esos días: con ayuda de Adam Wathan, Laravel estrena un nuevo componente de autorización y políticas de acceso, que te permitirá de una manera increíblemente fácil, bloquear (o permitir) el acceso a ciertas partes de tu aplicación.

Lo mejor es que puedes definirlo con closures o clases y usarlo dentro de los controladores, las plantillas de Blade o cualquier parte de tu sistema.

Setup inicial

Para comenzar a utilizar este feature, crearemos un nuevo proyecto de Laravel, por ejemplo a través de Composer, lo llamaremos «basic-blog»:

composer create-project laravel/laravel basic-blog

Luego vamos a crear un virtual host en Windows o crear un virtual host en Mac o Linux. Yo llamaré al mío «basic.blog» y por supuesto apuntará a la carpeta public/ de este nuevo proyecto.

A continuación creemos un nuevo modelo y migración para almacenar posts:

php artisan make:model Post –migration

En la migración (database/migrations/…create_posts_table.php) vamos a agregar 2 campos a la tabla posts, sólo el título y el usuario asignado:

        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');

            $table->string('title');
            $table->integer('user_id')->unsigned();
            $table->foreign('user_id')->references('id')->on('users');

            $table->timestamps();
        });

Luego en database/factories/ModelFactory.php, agreguemos un nuevo factory para el nuevo modelo Post:

$factory->define(App\Post::class, function (Faker\Generator $faker) {
    return [
        'title'   => $faker->sentence(),
        'user_id' => rand(1, 10)
    ];
});

Para nuestro demo, asumiremos que hay 10 usuarios en el sistema que tienen IDs del 1 al 10.

A continuación necesitamos agregar los seeders, para usuarios, crea un nuevo archivo en database/seeds/UserTableSeeder.php y agrega esto:

<?php
use Illuminate\Database\Seeder;
class UserTableSeeder extends Seeder
{
    public function run()
    {
        factory(App\User::class)->create([
            'name'  => 'Duilio',
            'email' => '[email protected]',
            'password' => bcrypt('admin'),
        ]);
        factory(App\User::class, 9)->create();
    }
}

Para posts, crea un archivo database/seeds/PostTableSeeder.php con lo siguiente:

<?php

use Illuminate\Database\Seeder;

class PostTableSeeder extends Seeder
{
    public function run()
    {
        factory(App\Post::class, 50)->create();
    }
}

Puedes generar los seeders con el comando make:seeder, ejemplo:

php artisan make:seeder PostTableSeeder

Por último, no olvides registrar los seeders en database/seeders/DatabaseSeeder.php en el medio de Model::unguard y Model::guard coloca esto:

        $this->call(UserTableSeeder::class);
        $this->call(PostTableSeeder::class);

 

Aprende más sobre migraciones, seeders y model factories en Laravel 5.1.

Muy bien ahora crea una nueva base de datos, por ejemplo «basic_blog», y configura tu archivo .env con los datos de conexión a la DB, en mi caso, con Homestead, es esto:

DB_HOST=localhost
DB_DATABASE=basic_blog
DB_USERNAME=homestead
DB_PASSWORD=secret

Ahora ejecuta este comando para crear todas las tablas con sus datos de prueba:

php artisan migrate --seed

Deberías recibir un mensaje de confirmación como éste:

console

Hasta acá todo fue bastante estándar, ahora veamos cómo implementar los permisos:

Creación de nuevas reglas de acceso en Laravel

Imagina que estás creando el nuevo «Medium», cada usuario tiene un post y sólo puede editar sus posts, tendrías que implementar una lógica como esta, en algún lugar de tu aplicación:

$post->user_id == $user->id

¿Pero donde? Y esa es la respuesta principal de este nuevo componente de Laravel, porque la lógica para autorizar o denegar accesos es propia de tu aplicación, ningún framework ni ningún componente puede dictarte qué condiciones deben restringir o autorizar el acceso de un usuario X a una parte Y de tu proyecto. Esto debes conversarlo con tu cliente, o analizarlo si se trata de un proyecto propio.

Entonces este nuevo componente de Laravel te da un lugar y una interfaz muy sencilla para definir, organizar y posteriormente implementar las reglas de acceso de tu proyecto.

Si recién instalaste Laravel, ahora verás un nuevo service provider, en app/providers/AuthServiceProvider.php, donde puedes implementar esta lógica:

<?php

namespace App\Providers;

use Illuminate\Contracts\Auth\Access\Gate as GateContract;
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;

class AuthServiceProvider extends ServiceProvider
{
    /**
     * Register any application authentication / authorization services.
     *
     * @param  \Illuminate\Contracts\Auth\Access\Gate  $gate
     * @return void
     */
    public function boot(GateContract $gate)
    {
        parent::registerPolicies($gate);

        //
    }
}

Dentro del método «boot» debajo de parent::registerPolicies($gate) podemos colocar lo siguiente:

        $gate->define('update-post', function ($user, $post) {
            return $user->id === $post->user_id;
        });

Esto creará una nueva regla «update-post» que podrás aplicar en cualquier parte del proyecto.

Uso de la nueva autorización en proyectos actuales

Nota: Actualizar tu proyecto es opcional, y sólo si quieres usar este nuevo feature.

Si ya estás trabajando en un proyecto de Laravel 5.1 y quieres usar este nuevo feature, es muy sencillo, sólo tienes que ejecutar:

composer update

Crea una carpeta vacía en app/ llamada Policies: app/Policies/

Luego crea el service provider app/providers/AuthServiceProvider.php, puedes tomar el código de GitHub.

A continuación regístralo en el array $providers de config/app.php.

App\Providers\AuthServiceProvider::class

y también el facade en el array de $aliases en el mismo archivo config/app.php:

‘Gate’ => Illuminate\Support\Facades\Gate::class,

Recuerda que si tu proyecto se llama «Acme» debes reemplazar el namespace del proyecto de «App» a «Acme».

Cambios al modelo de Usuario

Asegúrate de estar implementando la interface AuthorizableContract y usando el trait Authorizable del modelo de usuarios, por ejemplo:

<?php

namespace App;

use Illuminate\Auth\Authenticatable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Auth\Passwords\CanResetPassword;
use Illuminate\Foundation\Auth\Access\Authorizable;
use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
use Illuminate\Contracts\Auth\Access\Authorizable as AuthorizableContract;
use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

class User extends Model implements AuthenticatableContract,
                                    AuthorizableContract,
                                    CanResetPasswordContract
{
    use Authenticatable, Authorizable, CanResetPassword;

    //codigo del modelo de usuario...
}

Cambios al controlador base

Modifica el controlador base para agregar el nuevo trait AuthorizesRequests que aprenderemos en una próxima clase:

<?php

namespace App\Http\Controllers;

use Illuminate\Foundation\Bus\DispatchesJobs;
use Illuminate\Routing\Controller as BaseController;
use Illuminate\Foundation\Validation\ValidatesRequests;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;

abstract class Controller extends BaseController
{
    use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
}

Implementación de reglas de acceso con Laravel

Ahora dentro de app/Http/routes.php, definamos una prueba rápida, con la siguiente ruta, que hipotéticamente serviría para mostrar un formulario para editar un post, si el usuario tiene permiso, claro está:

Route::get('edit-post/{id}', function ($id) {

    // Let's just pretend we are logged in as the user with ID 1
    Auth::loginUsingId(1);

    // Now let's try to find a post
    $post = App\Post::findOrFail($id);

    // Do we have access to update it?
    if (Gate::denies('update-post', $post)) {
        abort(403);
    }

    // Then we show the form, etc. but for now just the title is fine:
    return $post->title;

});

Básicamente pretendemos que estamos conectados como el usuario 1, luego buscamos un post, y verificamos si el usuario conectado tiene permiso para editar el post, si no es así lanzamos un error 403 (no autorizado). Finalmente si la comprobación pasa mostraríamos el formulario, etc. y lo mismo para la ruta post para actualizar, validaríamos y guardaríamos el post etc.

El nuevo componente de control de acceso se puede implementar de muchas maneras

Por ejemplo puedes usar los métodos «can» y «cannot» del modelo User:

    if (Auth::user()->cannot('update-post', $post)) {
        abort(403);
    }

Incluso puedes usar este nuevo feature dentro de Blade con la directiva @can o @cannot:

@can('update-post', $post)
    <a href="/post/{{ $post->id }}/edit">Edit Post</a>
@endcan

Se lee tan fácil que no requiere mayor explicación… Pero sí fíjate que Laravel asume que quieres comprobar permisos para el usuario conectado, por lo tanto, no necesitas pasar la variable $user, haciendo así la interfaz mucho más rápida y fácil de usar.

Claro, si necesitaras comprobar los permisos de otro usuario diferente al usuario conectado, puedes hacerlo también, usando «forUser»:

if (Gate::forUser($user)->allows('update-post', $post)) {
    //
}

Conclusión

Las reglas de acceso de tu aplicación, forman parte de la lógica del proyecto y deben ser analizadas y definidas por ti mismo, sin embargo, con este componente Laravel te da un lugar muy conveniente donde definirlas y luego una manera fácil, e incluso divertida, de implementar estas reglas dentro de tu aplicación. ¿Imaginas una interfaz aún más fácil de usar?

Aún queda mucho por cubrir, ¿qué te parece si creo un par de videos y los anexo a la parte 2 del Curso introductorio de Laravel 5.1 donde ya discutimos restricción de acceso por login fallidos, recuperación de contraseña, roles de usuario y más.

Mientras tanto puedes seguir aprendiendo de este nuevo feature si lees la documentación oficial de Laravel sobre autorización, está muy completa.

Aprende más sobre cómo proteger tu aplicación en Laravel

Regístrate hoy en Styde y obtén acceso a todo nuestro contenido.

Lección anterior Cómo instalar proyectos existentes de Laravel Lección siguiente Uso de caché en Laravel 5.1