GUÍA COMPLETA DE LARAVEL GATE: AUTORIZACIÓN AVANZADA

Gate es el sistema de autorización basado en closures, funciones, que Laravel ofrece para determinar si un usuario tiene permiso para realizar una acción específica.

Mientras que las Policies están orientadas a un modelo Eloquent, los Gates son más flexibles y se usan para permisos que no están directamente relacionados con un modelo o que son más complejos: acciones globales, reportes, etc..

Diferencia entre Gates y Policies

Característica Gates Policies
Enfoque Basado en closures Basado en clases por modelo
Uso recomendado Permisos globales o complejos Operaciones CRUD por modelo
Reutilización Media Alta
Facilidad de prueba Muy alta Alta

Cómo definir Gates

Los Gates se definen normalmente en el fichero "AuthServiceProvider"

// app/Providers/AuthServiceProvider.php

public function boot(): void
{
    Gate::define('editar-post', function (User $user, Post $post) {
        return $user->id === $post->user_id;
    });

    Gate::define('publicar-post', function (User $user) {
        return $user->hasRole('editor') || $user->hasRole('admin');
    });

    Gate::define('ver-reporte-financiero', function (User $user) {
        return $user->canAccessFinancialReports();
    });
}

Gates con parámetros adicionales

Gate::define('actualizar-imagen', function (User $user, Post $post, string $tipo) {
    if ($tipo === 'portada' && !$user->esPremium()) {
        return false;
    }
    return $user->id === $post->user_id || $user->esAdmin();
});

Métodos importantes de Gate

// Verificar permiso
if (Gate::allows('editar-post', $post)) {
    // Puede editar
}

if (Gate::denies('publicar-post')) {
    abort(403);
}

// Con usuario específico
if (Gate::forUser($otroUsuario)->allows('editar-post', $post)) { ... }

// Comprobar varios al mismo tiempo
Gate::any(['editar-post', 'publicar-post'], $post);
Gate::none(['eliminar-post', 'ocultar-post'], $post);

before() y after() – Hooks globales

Son muy útiles para lógica centralizada (por ejemplo, super admins):

Gate::before(function (User $user, string $ability) {
    if ($user->esSuperAdmin()) {
        return true; // Puede hacer TODO
    }
    // null = continuar con la validación normal
});

Gate::after(function (User $user, string $ability, bool $result, $arguments) {
    // Registrar intentos de acceso, auditoría, etc.
    Log::info("Usuario {$user->id} intentó {$ability}: " . ($result ? 'OK' : 'DENEGADO'));
});

Uso en Controladores

public function update(Request $request, Post $post)
{
    $this->authorize('editar-post', $post);  // Lanza 403 automáticamente
    
    // O manualmente
    Gate::authorize('editar-post', $post);

    // ...
}

En Blade (Vistas)


@can('editar-post', $post)
    Editar
@endcan

@cannot('publicar-post')
    

No tienes permiso para publicar.

@endcannot @canany(['editar-post', 'eliminar-post'], $post) @endcanany

Gates en rutas (Middleware)

Route::middleware('can:ver-reporte-financiero')->group(function () {
    Route::get('/reportes/financieros', [ReporteController::class, 'financieros']);
});

Ejemplo Real: Sistema de Contenidos

Gate::define('gestionar-contenido', function (User $user) {
    return $user->hasAnyRole(['admin', 'editor', 'moderador']);
});

Gate::define('aprobar-comentario', function (User $user, Comment $comment) {
    return $user->hasRole('moderador') || 
           ($user->hasRole('editor') && $comment->post->author_id === $user->id);
});