Desenvolupament d'Aplicacions

Gestió de rols a Laravel

La creació de rols a un projecte web amb Laravel serà essencial per gestionar els permisos i quines accions pot realitzar dins de la plataforma.

Gestió de rols a Laravel
Sergio Bonito
Sergio Bonito

December 05, 2019

Per què és important crear rols a un projecte web?

Tan important com distingir entre usuaris autenticats de visitants és distingir entre els usuaris autenticats quin rol o permisos tenen.

Suposem una pàgina d'empresa en la qual tenim un usuari que pot afegir/esborrar/modificar membres de l'equip i després tenim un altre usuari que pot escriure articles per al blog.

La forma més fàcil si només tenim aquests dos tipus i un d'ells té un rol i els altres tenen un altre és afegir a la configuració un llistat d'emails que validen a l'usuari amb un permís determinat. Després a la classe User podem fer una funció que faci un in_array($this->email, config('administradors', [])). Així sabrem que d'aquest tipus d'usuaris es faran un o dos i no es crearan mai més.

El que passa amb aquest sistema és que té unes febleses com, per exemple, que aquests "administradors" són els que posem en codi directament i per canviar-los cal modificar un fitxer, no podem crear més des de l'aplicació de cap manera.

Una altra opció molt "millor" (la solució anterior en molts casos és suficient) seria tenir una taula de rols o permisos i relacionar-los amb els usuaris. Imaginem els diferents nivells de permís:

  • Crear un post
  • Editar un post
  • Publicar un post
  • Eliminar un post
  • Crear un membre de l'equip
  • Editar un membre de l'equip
  • Eliminar un membre de l'equip
  • Llistar els registres del formulari de contacte

Aquests serien els permisos, després podem relacionar-los amb rols, per exemple el bloc dels posts (excepte "publicar") l'hi podem assignar al rol "Escriptor", els de membres de l'equip els poden modificar els membres de "Secretaria" i després per abreujar posarem que hi ha un admin que pot fer tot.

Així doncs tenim Usuaris que tenen Rols, cadascun amb els seus Permisos:

class User
{
    public function rol()
    {
        return $this->belongsTo(Rol::class);
    }
}

class Rol
{
    public function users()
    {
        return $this->hasMany(User::class);
    }

    public function permisos()
    {
        return $this->belongsToMany(Permiso::class);
    }
}

class Permis
{
    public function roles()
    {
        return $this->belongsToMany(Rol::class);
    }
}

Amb això ja sabem quins permisos (a través dels rols) té cada usuari. Després al auth service provider definim aquestes habilitats:

class AuthServiceProvider
{
    public funciton boot(GateContract $gate)
    {
        $permisos = Permis::all();

        foreach($permisos as $permis){

            $gate->define($permis->name, function($user) use ($permis){

                return $user->whereHas('rol', function($query) use ($permis){

                    return $query->whereHas('permisos', function($query) use ($permis){

                        return $query->whereName($permis->name);
                    });
                });
            });
        }
    }
}

Amb això estem definint per a cada permís que els usuaris que tinguin un rol al que li assignem aquest permís passaran aquesta validació. La query que es realitza hauria d'extreure's a mètodes que la simplifiquin. Aquest $gate->define($clau, $check) ens permet després utilitzar el middleware "can:$clau" i així podrem assignar-li-ho a rutes, per exemple:

Route::post('posts', 'PostController@store')->middleware('can:crear-posts');

Així només usuaris amb rol Escriptor i Admin podran accedir, ja que és a aquests rols als quals els hem assignat el permís de 'crear-posts'. Si al middleware li demanem una $clau que no hàgim definit, l'aplicació suposarà que l'usuari no té aquest permís. Com els permisos poden arribar a ser molts (centenars) i "Admin" ha de poder fer-ho tot a l'Authserviceprovider@boot podem afegir el següent:

$gate->before(function($user, $ability){

    if($user->hasRol('admin')){ //aquesta funció s'hauria de definir

        return true;
    }
});

D'aquesta manera, podem no afegir-li al rol "admin" cap permís i els seguirà tenint tots (si els té tots menys un o dos, la funció del before inclou el paràmetre $ability així que podrem filtrar-ho en aquest moment). Una altra cosa a tenir en compte és que el return true ho fem dins d'un if en comptes de fer return $user->hasRol('admin') perquè si aquesta funció té un return false es quedarà amb aquest resultat en comptes de continuar mirant els $gate->define().

Articles Relacionats