code/app/Services/RolesService.php
<?php
namespace App\Services;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Gate;
use App\Exceptions\AuthException;
use App\Role;
use App\User;
class RolesService extends BaseService
{
public function store(array $request)
{
$this->ensureAuth(['gas.permissions' => 'gas']);
$role = new Role();
$this->setIfSet($role, $request, 'name');
$this->setIfSet($role, $request, 'parent_id');
$role->actions = join(',', $request['actions'] ?? []);
$role->save();
return $role;
}
public function update($id, array $request)
{
$this->ensureAuth(['gas.permissions' => 'gas']);
$role = Role::findOrFail($id);
$this->setIfSet($role, $request, 'name');
$this->setIfSet($role, $request, 'parent_id');
$role->save();
return $role;
}
public function destroy($id)
{
$this->ensureAuth(['gas.permissions' => 'gas']);
$role = Role::findOrFail($id);
$role->delete();
return $role;
}
/*
Funzione che istruisce il sistema interno di controllo autorizzazioni a
gestire i permessi personalizzati. Nella stragrande maggioranza dei casi
è sufficiente invocare questa funzione in AuthServiceProvider, ma viene
comunque messa qui affinché possa essere nuovamente invocata in casi
particolari (e.g. viene abilitata la modalità Multi-GAS, che prevede
l'esistenza di un permesso nuovo da applicare)
*/
public function registerPolicies()
{
$all_permissions = allPermissions();
foreach ($all_permissions as $rules) {
foreach (array_keys($rules) as $identifier) {
if (Gate::has($identifier)) {
continue;
}
Gate::define($identifier, function ($user, $obj = null) use ($identifier) {
foreach($user->roles as $role) {
if ($role->enabledAction($identifier)) {
if(is_null($obj) || $role->applies($obj)) {
return true;
}
}
}
return false;
});
}
}
}
public function setMasterRole($gas, $identifier, $role_id)
{
$this->ensureAuth(['gas.permissions' => 'gas']);
$conf = $gas->roles;
if ($identifier == 'friend') {
$old_friend_role = $conf['friend'];
}
else {
$old_friend_role = null;
}
$conf[$identifier] = $role_id;
$gas->setConfig('roles', $conf);
/*
Se il ruolo "amico" viene cambiato, cambio effettivamente gli utenti
coinvolti
*/
if ($old_friend_role) {
$friends = User::whereNotNull('parent_id')->get();
foreach($friends as $friend) {
$friend->removeRole($old_friend_role, $gas);
$friend->addRole($conf['friend'], $gas);
}
}
}
/*
Nota bene: le funzioni per assegnare o revocare un ruolo devono
funzionare a prescindere dal permesso gas.permissions, almeno sui ruoli
che gerarchicamente sono "inferiori" a quelli dell'utente corrente
*/
private function checkAccessToRole($role_id)
{
$user = Auth::user();
$managed_roles = $user->managed_roles->search(function($item, $key) use ($role_id) {
return $item->id == $role_id;
});
if ($managed_roles === false) {
/*
Se il ruolo desiderato è uno di quelli base "utente" e "amico"
basta il permesso di gestione degli utenti, in quanto
l'amministratore degli utenti deve poter intervenire (in
particolare sugli amici)
*/
if ((isset($user->gas->roles['friend']) && $role_id == $user->gas->roles['friend']) || (isset($user->gas->roles['user']) && $role_id == $user->gas->roles['user'])) {
$this->ensureAuth(['users.admin' => 'gas']);
}
else {
$this->ensureAuth(['gas.permissions' => 'gas']);
}
}
}
public function attachUser($user_id, $role_id, $target)
{
$this->checkAccessToRole($role_id);
$r = Role::findOrFail($role_id);
$u = User::tFind($user_id, true);
$attached = $u->addRole($r, $target);
if (is_null($target)) {
/*
Se il nuovo ruolo prevede l'associazione ad un modello di cui
esiste una sola istanza, avviene automaticamente l'assegnazione.
Questo serve in particolare ad assegnare i ruoli nel contesto di
un GAS, quando ce ne è uno solo (ovvero: la maggior parte dei
casi), ed evitare confusione da parte degli utenti
*/
foreach($r->getAllClasses() as $target_class) {
$available_targets = $target_class::tAll();
if ($available_targets->count() == 1) {
$attached->attachApplication($available_targets->get(0));
}
}
}
return [$u, $r];
}
public function detachUser($user_id, $role_id, $target)
{
$this->checkAccessToRole($role_id);
$r = Role::findOrFail($role_id);
$u = User::tFind($user_id, true);
$u->removeRole($r, $target);
return [$u, $r];
}
public function attachAction($role_id, $action)
{
$this->ensureAuth(['gas.permissions' => 'gas']);
$r = Role::findOrFail($role_id);
if ($r->enabledAction($action)) {
return;
}
$r->actions .= ',' . $action;
$r->save();
/*
Se attivo un permesso che ha un solo target (di solito: il GAS),
attacco quest'ultimo direttamente a tutti gli utenti coinvolti
*/
$class = classByRule($action);
if ($class::count() == 1) {
$only_target = $class::first();
foreach($r->users as $user) {
$urole = $user->roles()->where('roles.id', $r->id)->first();
$urole->attachApplication($only_target);
}
}
}
public function detachAction($role_id, $action)
{
$this->ensureAuth(['gas.permissions' => 'gas']);
$r = Role::findOrFail($role_id);
$actions = explode(',', $r->actions);
$new_actions = array_filter($actions, fn($a) => $a != $action);
$r->actions = join(',', $new_actions);
$r->save();
}
}