ACL - Rôles et droits sur le tool 👻
Contexte
Comme vous le savez, jusqu'aujourd'hui le tool avait 0 ou très peu de gestion rôle et droit. On limité uniquement les freelances, mais pas les users internes à NSN qui avaient accès à toutes les briques de fonctionnalité du tool.
Pour des raisons de sécurité, on a décidé de mettre en place un système de rôle et droit afin de cloisonner au maximum les différentes parties du tool.
Pour réaliser cette mission, on a utilisé le package https://spatie.be/docs/laravel-permission/v4/introduction qui permet de pousser la chose très loin.
En gros, ou peut imaginer un système de rôle avec différentes permissions etc etc.
On a décidé uniquement d'utiliser la partie "rôle" du package, mais plus tard (quand on sera 15K employé que l'on aura racheté Google) rien ne nous empêchera d'utiliser la partie "permissions".
Mais avant, refacto des routes
On a profité de cette évolution pour faire un tri sur les routes du tool. Avant, on avait tout dans un seul et même fichier, el famoso routes/web.php
Aujourd'hui, toutes les routes sont séparées dans plusieurs fichiers, plus ou moins par thème. web.php est encore utilisé pour des routes "générales" ou pour des routes sans réel thème.
Mais par exemple, tout ce qui représente la partie "website" se trouve dans routes/website.php
Toute la partie biz, dans routes/monetiz.php etc etc...
D'ailleurs, voici comment faire pour créer un nouveau fichier de routes, c'est kdo:
Rendez vous dans: app/Providers/RouteServiceProvider.php
Ensuite, dans la function map(), vous allez retrouver masse appel à différentes functions. Prenons par exemple la function mapCopylaunchRoutes:
protected function mapCopylaunchRoutes(): void
{
Route::middleware('web')
->prefix('copylaunch')
->namespace($this->namespace)
->group(base_path('routes/copylaunch.php'));
}
On peut ici, déclarer notre fichier de route et par la même occasion, assigner un middleware, un prefix etc etc...
Ensemble des rôles du tool
Cette liste est non exhaustive, donc si on rajoute des rôles, les rajouter ici:
Pour les personnes internes à NSN :
- super_admin: big boss du game, il peut faire ce qu'il veut
- interne: pour les personnes faisant partie de NSN
- dev: rôle complémentaire à interne, à accès aux outils de développement
- seo: rôle complémentaire à interne, accès aux outils SEO du tool
- paris: rôle complémentaire à interne, rôle qui rassemble à la fois les markets et copylaunch
Petite précision sur le rôle interne, il doit être assigner à une personne faisant partie de NSN. Exemple, si on prend un SEO par exemple, il doit impérativement avoir le rôle de interne et de seo (on peut assigner plusieurs rôles).
Pour les externes:
- freelance_seo: accès très limité au tool aux outils seo
- freelance_cl: accès limité à copylaunch
Pour les internes, on ne leur donne pas le rôle d'interne, c'est obvious mais précisons le 😄
Assigner une route à un ou des rôles
Tout ce gère via middleware, donc rendez vous dans un des nombreux fichier de routes:
routes/website.php
Route::group(['middleware' => ['auth', 'role:super_admin|interne']], function (): void {
// toutes mes routes
});
Ici, l'ensemble du groupe sera donc vérifié par le middleware 'role' et il suffit de lui passer en paramètre le ou les rôles autoriser à accéder à cette route.
Donc, toutes les routes de ce groupe seront accessibles par les rôles super_admin & interne. Les autres se prendront une jolie 403.
Vérifier le rôle d'un utilisateur via la relation roles()
Verrouiller l'ensemble des routes, c'est le top, mais il arrive parfois que l'on souhaite faire ça directement dans notre controller ou autre afin d'être plus précis.
Prenons la function isSeoUser du model User.
app/User.php ligne 197
public function isSeoUser()
{
return $this->hasRole('freelance_seo') || $this->isAdmin();
}
On peut donc vérifier avec hasRole('notre_role') le rôle d'un utilisateur.
On peut également la function hasAnyRole pour vérifier plusieurs rôles, plus d'info ici: https://spatie.be/docs/laravel-permission/v4/basic-usage/role-permissions#checking-roles
A noter que ça fonctionne grâce au trait HasRoles rajouté dans le model User
use HasRoles;
Récupérer les utilisateurs selon leurs rôles etc...
Forcément, il est important de pouvoir interroger les bdd pour récupérer les users avec tel ou tel rôles.
Prenons comme exemple la query utiliser ici app/DataTables/FreelanceDataTable.php
public function query(User $model): \Illuminate\Database\Eloquent\Builder
{
return $model->newQuery()
->role('freelance_cl')
->orDoesntHave('roles')
->with('markets');
}
Ici, on fetch tous les uers qui ont le rôle freelance_cl mais également ceux sans rôles avec la method orDoesntHave('roles').
C'est un exemple, mais y a encore plus d'exemples et d'infos ici:
https://spatie.be/docs/laravel-permission/v4/basic-usage/basic-usage#eloquent
Ajout du HomeController
Désormais, la route '/' tape dans le app/Http/Controllers/HomeController.php
Route::get('/', 'HomeController@index')
->middleware(['auth', 'role:super_admin|interne|seo|dev|paris|freelance_cl|freelance_seo']);
Ici, c'est route est accessible par tous (faudra juste retirer le middleware role n'est donc plus utilise).
Si on regarde de plus prêt la function index du controller:
app/Http/Controllers/HomeController.php:15
public function index(Request $request, WebsiteDataTable $websiteDataTable)
{
$user = $request->user();
if ($user->hasRole('freelance_cl')) {
return redirect('/copylaunch');
}
if ($user->hasRole('freelance_seo')) {
return redirect('/seotool/seo_task');
}
return $websiteDataTable->render('websites.index');
}
Son rôle est simple, rediriger l'utilisateur sur la bonne route en fonction de ses rôles.
Cela permet de gérer à un seul endroit la home des différents types d'user.
Assigner un ou plusieurs rôles à un user
On peut le faire depuis le tool, forcément, mais j'ai fais un doc séparée pour ça:
Côté code, c'est vraiment simple.
Rendez vous ligne 120 de app/Http/Controllers/UserController.php
private function saveUser(User $user, $form) : void
{
$user->enable = $form->getRequest()->enable ?? 0;
$user->save();
$user->markets()->sync($form->getRequest()->get('markets'));
if ($form->getRequest()->roles) {
$roles = Role::query()->whereIn('id', $form->getRequest()->roles)->get()->pluck('name')->toArray();
} else {
$roles = [];
}
$user->syncRoles($roles);
}
Ici, il me suffit d'utiliser la function syncRoles, en passant un tableau avec le nom des rôles, pour assigner des rôles à un utilisateur.
Plus d'infos ici: https://spatie.be/docs/laravel-permission/v4/basic-usage/role-permissions#assigning-roles