Las políticas de acceso permiten definir qué usuarios tienen permisos para acceder a ciertos métodos de los modelos.

En mi caso, voy a crear una política para el modelo User, de tal forma que:

  • El perfil sea editable únicamente por el propio usuario dueño del perfil
  • Que los administradores del sitio puedan editar el perfil de cualquier usuario.

Crear una política en Laravel

Primero se debe crear una política:

> php artisan make:policy UserPolicy

Registrar la política

Ahora es necesario registrar la política recien creada, esto se hace en la carpeta ...\app\Providers\AuthServiceProvider.php, se busca la línea que empieza con protected $policies y se cambia de la siguiente forma:

protected $policies = [
    // 'App\Model' => 'App\Policies\ModelPolicy',
]

por el modelo al que se le va a aplicar la política y la política propia:

protected $policies = [
    'App\User' => 'App\Policies\UserPolicy',
]

Crear los métodos a los que se aplicará la política

En mi caso, quiero aplicar la política a los métodos edit y update, del modelo User, que son los que modifican el perfil del usuario. En UserPolicy se tienen que crear métodos con los nombres de los métodos que se quieren proteger que existen en la clase de origen.

Dichos métodos reciben dos parámetros, el primero, que Laravel carga automáticamente, corresponde al usuario firmado en la aplicación (\Auth::user()) y el segundo es el usuario contra el que se compara para ver si tiene privilegios.

public function edit(User $authUser, User $user)
{
    return $authUser->id == $user->id;
}

Las políticas regresan True si el usuario está autorizado, o False si no lo está. En este caso es una verificación muy sencilla que solo se usa para ver que el usuario que está firmado, sea el mismo usuario al que pertenece el perfil.

Actualizar el método protegido en el controlador

En el UserController, mi función edit(), se ve de esta forma:

public function edit($id)
{
  //
  $user = User::findOrFail($id);
  return view('users.edit', compact('user'));
}

Antes del return, hay que añadir una llamada a la función authorize() con dos parámetros, el primero, es el método sobre el que se revisa si está siendo autorizado, y el segundo, el usuario del que queremos verificar si existe la autorización:

public function edit($id)
{
  //
  $user = User::findOrFail($id);
  $this->authorize('edit', $user);
  return view('users.edit', compact('user'));
}

Nota: Es nuy importante hacer lo mismo para el método update, ya que a través de una inyección de código, podrían cambiarse los datos que envía la forma

Permitir que los amdinistradores también puedan editar perfiles

Esta funcionalidad simplemente requiere llamar a una función dentro del UserPolicy, llamada before(), la cual recibe dos parámetros, el primero que indica el usuario y el segundo el permiso o habilidad.

Esta función, como su nombre lo indica, se ejecuta antes que cualquier otra validación, y si retorna con valor True, ya no ejecuta el resto de las validaciones. En mi caso, la función quedaría escrita de esta forma:

public function before($user, $ability)
{
    return $user->role == 'admin'
}

Obviamente, en mi caso, el modelo User tiene un campo llamado role donde se guarda el rol que tiene el usuario dentro de la aplicación, si tus modelos son más complejos, deberás adecuar la función a tu necesidad.