40 KiB
Executable File
40 KiB
Executable File
🔐 PERMISSIONS - Documentazione Completa NetGescon
Sistema Permessi dell'interfaccia universale
📅 Creato: 21/07/2025
🔄 Ultimo aggiornamento: 21/07/2025
🎯 Priorità: ⚡ ALTO
📋 OVERVIEW PERMISSIONS SYSTEM
Il Sistema Permessi NetGescon gestisce ACCESSO GRANULARE per:
- 👤 User Roles (Super Admin, Admin, Manager, User, Guest)
- 🔑 Permissions (Create, Read, Update, Delete, Execute)
- 🏢 Resource Access (Stabili, Condomini, Unità, Reports)
- 📊 Data Visibility (Own, Team, Company, All)
- 🎛️ UI Components (Navigation, Actions, Features)
🎯 Security Philosophy
- Least Privilege - Accesso minimo necessario per default
- Role-Based Access - Permessi basati su ruoli definiti
- Resource-Level Security - Controllo granulare per ogni risorsa
- UI-Driven Permissions - Interfaccia che si adatta ai permessi
👥 ROLE SYSTEM
🔑 Role Definitions
// app/Models/User.php
class User extends Authenticatable implements HasMedia
{
// Role constants
const ROLE_SUPER_ADMIN = 'super_admin';
const ROLE_ADMIN = 'admin';
const ROLE_MANAGER = 'manager';
const ROLE_USER = 'user';
const ROLE_GUEST = 'guest';
// Permission levels
const PERMISSION_ALL = 'all';
const PERMISSION_COMPANY = 'company';
const PERMISSION_TEAM = 'team';
const PERMISSION_OWN = 'own';
const PERMISSION_NONE = 'none';
/**
* Get user role
*/
public function getRole()
{
return $this->role ?? self::ROLE_USER;
}
/**
* Check if user has role
*/
public function hasRole($role)
{
return $this->getRole() === $role;
}
/**
* Check if user has any of the roles
*/
public function hasAnyRole(array $roles)
{
return in_array($this->getRole(), $roles);
}
/**
* Check if user is admin or above
*/
public function isAdmin()
{
return $this->hasAnyRole([
self::ROLE_SUPER_ADMIN,
self::ROLE_ADMIN
]);
}
/**
* Check if user is manager or above
*/
public function isManager()
{
return $this->hasAnyRole([
self::ROLE_SUPER_ADMIN,
self::ROLE_ADMIN,
self::ROLE_MANAGER
]);
}
/**
* Get permission level for resource
*/
public function getPermissionLevel($resource)
{
$permissions = $this->permissions ?? [];
return $permissions[$resource] ?? $this->getDefaultPermissionLevel();
}
/**
* Get default permission level based on role
*/
public function getDefaultPermissionLevel()
{
return match($this->getRole()) {
self::ROLE_SUPER_ADMIN => self::PERMISSION_ALL,
self::ROLE_ADMIN => self::PERMISSION_COMPANY,
self::ROLE_MANAGER => self::PERMISSION_TEAM,
self::ROLE_USER => self::PERMISSION_OWN,
self::ROLE_GUEST => self::PERMISSION_NONE,
default => self::PERMISSION_NONE
};
}
}
🏢 Role Matrix
// config/roles.php
return [
'roles' => [
'super_admin' => [
'name' => 'Super Amministratore',
'description' => 'Accesso completo al sistema',
'color' => 'danger',
'icon' => 'fas fa-crown',
'permissions' => [
'users' => 'all',
'companies' => 'all',
'stabili' => 'all',
'condomini' => 'all',
'unita' => 'all',
'reports' => 'all',
'settings' => 'all',
'logs' => 'all'
]
],
'admin' => [
'name' => 'Amministratore',
'description' => 'Gestione completa della company',
'color' => 'warning',
'icon' => 'fas fa-user-shield',
'permissions' => [
'users' => 'company',
'stabili' => 'company',
'condomini' => 'company',
'unita' => 'company',
'reports' => 'company',
'settings' => 'company'
]
],
'manager' => [
'name' => 'Manager',
'description' => 'Gestione team e risorse assegnate',
'color' => 'info',
'icon' => 'fas fa-user-tie',
'permissions' => [
'users' => 'team',
'stabili' => 'team',
'condomini' => 'team',
'unita' => 'team',
'reports' => 'team'
]
],
'user' => [
'name' => 'Utente',
'description' => 'Accesso alle proprie risorse',
'color' => 'success',
'icon' => 'fas fa-user',
'permissions' => [
'stabili' => 'own',
'condomini' => 'own',
'unita' => 'own',
'reports' => 'own'
]
],
'guest' => [
'name' => 'Ospite',
'description' => 'Accesso solo lettura limitato',
'color' => 'secondary',
'icon' => 'fas fa-eye',
'permissions' => [
'stabili' => 'none',
'condomini' => 'none',
'unita' => 'none',
'reports' => 'none'
]
]
],
'resources' => [
'users' => 'Gestione Utenti',
'companies' => 'Gestione Aziende',
'stabili' => 'Gestione Stabili',
'condomini' => 'Gestione Condomini',
'unita' => 'Unità Immobiliari',
'reports' => 'Reports e Analytics',
'settings' => 'Impostazioni Sistema',
'logs' => 'Logs di Sistema'
],
'actions' => [
'create' => 'Creare',
'read' => 'Visualizzare',
'update' => 'Modificare',
'delete' => 'Eliminare',
'execute' => 'Eseguire'
]
];
🛡️ PERMISSION MIDDLEWARE
🔐 Role Middleware
// app/Http/Middleware/CheckRole.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class CheckRole
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next, ...$roles)
{
if (!Auth::check()) {
return redirect('login');
}
$user = Auth::user();
if (!$user->hasAnyRole($roles)) {
abort(403, 'Non hai i permessi per accedere a questa risorsa.');
}
return $next($request);
}
}
🔑 Permission Middleware
// app/Http/Middleware/CheckPermission.php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
class CheckPermission
{
/**
* Handle an incoming request.
*/
public function handle(Request $request, Closure $next, string $resource, string $action = 'read')
{
if (!Auth::check()) {
return redirect('login');
}
$user = Auth::user();
if (!$this->hasPermission($user, $resource, $action)) {
if ($request->expectsJson()) {
return response()->json([
'message' => 'Non hai i permessi per eseguire questa azione.'
], 403);
}
abort(403, 'Non hai i permessi per eseguire questa azione.');
}
return $next($request);
}
/**
* Check if user has permission for resource action
*/
private function hasPermission($user, $resource, $action)
{
$permissionLevel = $user->getPermissionLevel($resource);
// Super admin has all permissions
if ($user->hasRole(User::ROLE_SUPER_ADMIN)) {
return true;
}
// No permission
if ($permissionLevel === User::PERMISSION_NONE) {
return false;
}
// All permission
if ($permissionLevel === User::PERMISSION_ALL) {
return true;
}
// Check specific action permissions
return $this->checkActionPermission($user, $resource, $action, $permissionLevel);
}
/**
* Check action-specific permissions
*/
private function checkActionPermission($user, $resource, $action, $permissionLevel)
{
// Define action requirements
$actionPermissions = [
'create' => ['company', 'team', 'own'],
'read' => ['company', 'team', 'own'],
'update' => ['company', 'team', 'own'],
'delete' => ['company', 'team'],
'execute' => ['company', 'team']
];
$requiredLevels = $actionPermissions[$action] ?? ['company'];
return in_array($permissionLevel, $requiredLevels);
}
}
🎛️ UI PERMISSION GATES
🎨 Blade Directives
// app/Providers/AppServiceProvider.php
use Illuminate\Support\Facades\Blade;
use Illuminate\Support\Facades\Auth;
public function boot()
{
// Role-based directives
Blade::if('role', function ($role) {
return Auth::check() && Auth::user()->hasRole($role);
});
Blade::if('anyrole', function (...$roles) {
return Auth::check() && Auth::user()->hasAnyRole($roles);
});
// Permission-based directives
Blade::if('can', function ($resource, $action = 'read') {
if (!Auth::check()) return false;
$user = Auth::user();
$permissionLevel = $user->getPermissionLevel($resource);
if ($user->hasRole(User::ROLE_SUPER_ADMIN)) return true;
if ($permissionLevel === User::PERMISSION_NONE) return false;
if ($permissionLevel === User::PERMISSION_ALL) return true;
// Check action permissions
$actionPermissions = [
'create' => ['company', 'team', 'own'],
'read' => ['company', 'team', 'own'],
'update' => ['company', 'team', 'own'],
'delete' => ['company', 'team'],
'execute' => ['company', 'team']
];
$requiredLevels = $actionPermissions[$action] ?? ['company'];
return in_array($permissionLevel, $requiredLevels);
});
// Admin check
Blade::if('admin', function () {
return Auth::check() && Auth::user()->isAdmin();
});
// Manager check
Blade::if('manager', function () {
return Auth::check() && Auth::user()->isManager();
});
}
🧩 Permission Components
<!-- resources/views/components/permission-gate.blade.php -->
@props([
'resource' => '',
'action' => 'read',
'role' => null,
'fallback' => null
])
@php
$hasPermission = false;
if ($role) {
$hasPermission = Auth::check() && Auth::user()->hasAnyRole(is_array($role) ? $role : [$role]);
} elseif ($resource) {
$hasPermission = Auth::check() && Auth::user()->hasPermission($resource, $action);
}
@endphp
@if($hasPermission)
{{ $slot }}
@elseif($fallback)
{!! $fallback !!}
@endif
<!-- resources/views/components/role-badge.blade.php -->
@props(['user' => null])
@php
$user = $user ?? Auth::user();
$roleConfig = config('roles.roles')[$user->getRole()] ?? null;
@endphp
@if($roleConfig)
<span class="badge bg-{{ $roleConfig['color'] }} badge-role"
data-bs-toggle="tooltip"
title="{{ $roleConfig['description'] }}">
<i class="{{ $roleConfig['icon'] }} me-1"></i>
{{ $roleConfig['name'] }}
</span>
@endif
🔧 JAVASCRIPT PERMISSIONS
⚡ Permission Manager
// /public/js/permissions/permission-manager.js
class PermissionManager {
constructor() {
this.permissions = {};
this.user = null;
this.init();
}
async init() {
// Load user permissions from API or meta tags
await this.loadPermissions();
// Apply UI permissions
this.applyUIPermissions();
// Setup dynamic permission checks
this.setupDynamicChecks();
}
async loadPermissions() {
try {
// Try to load from meta tag first
const permissionsMeta = document.querySelector('meta[name="user-permissions"]');
if (permissionsMeta) {
this.permissions = JSON.parse(permissionsMeta.content);
return;
}
// Fallback to API
const response = await fetch('/api/user/permissions');
if (response.ok) {
const data = await response.json();
this.permissions = data.permissions;
this.user = data.user;
}
} catch (error) {
console.warn('Failed to load permissions:', error);
this.permissions = {};
}
}
applyUIPermissions() {
// Hide/show elements based on permissions
document.querySelectorAll('[data-permission]').forEach(element => {
const permission = element.dataset.permission;
const action = element.dataset.action || 'read';
if (!this.can(permission, action)) {
element.style.display = 'none';
element.setAttribute('aria-hidden', 'true');
}
});
// Hide/show based on roles
document.querySelectorAll('[data-role]').forEach(element => {
const roles = element.dataset.role.split(',');
if (!this.hasAnyRole(roles)) {
element.style.display = 'none';
element.setAttribute('aria-hidden', 'true');
}
});
// Disable buttons/links
document.querySelectorAll('[data-permission-action]').forEach(element => {
const permission = element.dataset.permissionAction;
const action = element.dataset.action || 'execute';
if (!this.can(permission, action)) {
element.disabled = true;
element.classList.add('disabled');
element.setAttribute('aria-disabled', 'true');
// Remove click handlers
element.onclick = (e) => {
e.preventDefault();
this.showPermissionDenied();
return false;
};
}
});
}
setupDynamicChecks() {
// Intercept form submissions
document.addEventListener('submit', (e) => {
const form = e.target;
const permission = form.dataset.permission;
const action = form.dataset.action || 'create';
if (permission && !this.can(permission, action)) {
e.preventDefault();
this.showPermissionDenied();
return false;
}
});
// Intercept navigation
document.addEventListener('click', (e) => {
const link = e.target.closest('a[data-permission]');
if (link) {
const permission = link.dataset.permission;
const action = link.dataset.action || 'read';
if (!this.can(permission, action)) {
e.preventDefault();
this.showPermissionDenied();
return false;
}
}
});
// Intercept AJAX requests
const originalFetch = window.fetch;
window.fetch = (...args) => {
const [url, options = {}] = args;
// Check if request has permission requirements
if (options.permission) {
const action = options.action || 'read';
if (!this.can(options.permission, action)) {
return Promise.reject(new Error('Permission denied'));
}
}
return originalFetch.apply(this, args);
};
}
can(resource, action = 'read') {
// Super admin can do everything
if (this.hasRole('super_admin')) {
return true;
}
const permissionLevel = this.permissions[resource];
if (!permissionLevel || permissionLevel === 'none') {
return false;
}
if (permissionLevel === 'all') {
return true;
}
// Check action-specific permissions
const actionPermissions = {
'create': ['company', 'team', 'own'],
'read': ['company', 'team', 'own'],
'update': ['company', 'team', 'own'],
'delete': ['company', 'team'],
'execute': ['company', 'team']
};
const requiredLevels = actionPermissions[action] || ['company'];
return requiredLevels.includes(permissionLevel);
}
hasRole(role) {
return this.user && this.user.role === role;
}
hasAnyRole(roles) {
return this.user && roles.includes(this.user.role);
}
isAdmin() {
return this.hasAnyRole(['super_admin', 'admin']);
}
isManager() {
return this.hasAnyRole(['super_admin', 'admin', 'manager']);
}
showPermissionDenied() {
if (window.NetGesconNotifications) {
NetGesconNotifications.error('Non hai i permessi per eseguire questa azione.');
} else {
alert('Non hai i permessi per eseguire questa azione.');
}
}
// Permission-based navigation
navigate(url, permission = null, action = 'read') {
if (permission && !this.can(permission, action)) {
this.showPermissionDenied();
return false;
}
window.location.href = url;
return true;
}
// Dynamic permission checking for AJAX
async checkPermissionAPI(resource, action = 'read') {
try {
const response = await fetch('/api/check-permission', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ resource, action })
});
return response.ok;
} catch (error) {
console.error('Permission check failed:', error);
return false;
}
}
// Refresh permissions
async refreshPermissions() {
await this.loadPermissions();
this.applyUIPermissions();
}
}
// Initialize permission manager
document.addEventListener('DOMContentLoaded', () => {
window.PermissionManager = new PermissionManager();
});
📱 RESPONSIVE PERMISSIONS
🎛️ Permission-Aware Navigation
<!-- resources/views/components/navigation/sidebar.blade.php -->
<nav class="netgescon-sidebar" id="sidebar">
<div class="sidebar-content">
<!-- Dashboard - Available to all authenticated users -->
<div class="nav-section">
<a href="{{ route('admin.dashboard') }}"
class="nav-link {{ request()->routeIs('admin.dashboard') ? 'active' : '' }}">
<i class="fas fa-tachometer-alt nav-icon"></i>
<span class="nav-text">Dashboard</span>
</a>
</div>
<!-- Gestione Stabili -->
<x-permission-gate resource="stabili" action="read">
<div class="nav-section">
<div class="nav-section-title">Gestione Immobiliare</div>
<!-- Stabili -->
<a href="{{ route('admin.stabili.index') }}"
class="nav-link {{ request()->routeIs('admin.stabili.*') ? 'active' : '' }}">
<i class="fas fa-building nav-icon"></i>
<span class="nav-text">Stabili</span>
<x-permission-gate resource="stabili" action="create">
<span class="nav-badge bg-primary">+</span>
</x-permission-gate>
</a>
<!-- Condomini -->
<a href="{{ route('admin.condomini.index') }}"
class="nav-link {{ request()->routeIs('admin.condomini.*') ? 'active' : '' }}"
data-permission="condomini"
data-action="read">
<i class="fas fa-home nav-icon"></i>
<span class="nav-text">Condomini</span>
</a>
<!-- Unità Immobiliari -->
<a href="{{ route('admin.unita.index') }}"
class="nav-link {{ request()->routeIs('admin.unita.*') ? 'active' : '' }}"
data-permission="unita"
data-action="read">
<i class="fas fa-door-open nav-icon"></i>
<span class="nav-text">Unità</span>
</a>
</div>
</x-permission-gate>
<!-- Reports e Analytics -->
<x-permission-gate resource="reports" action="read">
<div class="nav-section">
<div class="nav-section-title">Reports</div>
<a href="{{ route('admin.reports.index') }}"
class="nav-link {{ request()->routeIs('admin.reports.*') ? 'active' : '' }}">
<i class="fas fa-chart-bar nav-icon"></i>
<span class="nav-text">Analytics</span>
</a>
<x-permission-gate resource="reports" action="execute">
<a href="{{ route('admin.reports.financial') }}"
class="nav-link">
<i class="fas fa-euro-sign nav-icon"></i>
<span class="nav-text">Report Finanziari</span>
</a>
</x-permission-gate>
</div>
</x-permission-gate>
<!-- Gestione Utenti - Solo per Admin -->
<x-permission-gate role="admin">
<div class="nav-section">
<div class="nav-section-title">Amministrazione</div>
<a href="{{ route('admin.users.index') }}"
class="nav-link {{ request()->routeIs('admin.users.*') ? 'active' : '' }}">
<i class="fas fa-users nav-icon"></i>
<span class="nav-text">Utenti</span>
</a>
<a href="{{ route('admin.settings.index') }}"
class="nav-link {{ request()->routeIs('admin.settings.*') ? 'active' : '' }}"
data-permission="settings"
data-action="read">
<i class="fas fa-cog nav-icon"></i>
<span class="nav-text">Impostazioni</span>
</a>
</div>
</x-permission-gate>
<!-- System Logs - Solo per Super Admin -->
<x-permission-gate role="super_admin">
<div class="nav-section">
<div class="nav-section-title">Sistema</div>
<a href="{{ route('admin.logs.index') }}"
class="nav-link {{ request()->routeIs('admin.logs.*') ? 'active' : '' }}">
<i class="fas fa-file-alt nav-icon"></i>
<span class="nav-text">Logs</span>
</a>
</div>
</x-permission-gate>
</div>
</nav>
🎯 Permission-Based Actions
<!-- resources/views/admin/stabili/index.blade.php -->
<div class="page-header">
<div class="page-title">
<h1>Gestione Stabili</h1>
<p class="text-muted">Gestisci tutti gli stabili del tuo portafoglio</p>
</div>
<div class="page-actions">
<!-- Create Action - Only if user can create -->
<x-permission-gate resource="stabili" action="create">
<a href="{{ route('admin.stabili.create') }}"
class="btn btn-primary">
<i class="fas fa-plus mr-2"></i>
Nuovo Stabile
</a>
</x-permission-gate>
<!-- Export Action - Only if user can execute -->
<x-permission-gate resource="stabili" action="execute">
<button class="btn btn-outline-secondary"
onclick="exportStabili()"
data-permission-action="stabili"
data-action="execute">
<i class="fas fa-download mr-2"></i>
Esporta
</button>
</x-permission-gate>
</div>
</div>
<!-- Data Table with Permission-Based Actions -->
<div class="netgescon-card">
<div class="table-responsive">
<table class="table netgescon-table" id="stabili-table">
<thead>
<tr>
<th>Nome</th>
<th>Indirizzo</th>
<th>Unità</th>
<th>Valore</th>
<th class="text-center">Azioni</th>
</tr>
</thead>
<tbody>
@foreach($stabili as $stabile)
<tr>
<td>
<strong>{{ $stabile->nome }}</strong>
<x-role-badge :user="$stabile->responsible" />
</td>
<td>{{ $stabile->indirizzo_completo }}</td>
<td>{{ $stabile->unita_count }}</td>
<td>{{ number_format($stabile->valore, 0, ',', '.') }} €</td>
<td class="text-center">
<div class="btn-group">
<!-- View Action - Always available if user can read -->
<a href="{{ route('admin.stabili.show', $stabile) }}"
class="btn btn-sm btn-outline-primary"
data-bs-toggle="tooltip"
title="Visualizza">
<i class="fas fa-eye"></i>
</a>
<!-- Edit Action - Only if user can update -->
<x-permission-gate resource="stabili" action="update">
<a href="{{ route('admin.stabili.edit', $stabile) }}"
class="btn btn-sm btn-outline-secondary"
data-bs-toggle="tooltip"
title="Modifica">
<i class="fas fa-edit"></i>
</a>
</x-permission-gate>
<!-- Delete Action - Only if user can delete -->
<x-permission-gate resource="stabili" action="delete">
<button class="btn btn-sm btn-outline-danger"
onclick="deleteStabile({{ $stabile->id }})"
data-permission-action="stabili"
data-action="delete"
data-bs-toggle="tooltip"
title="Elimina">
<i class="fas fa-trash"></i>
</button>
</x-permission-gate>
<!-- Reports Action - Only if user can execute reports -->
<x-permission-gate resource="reports" action="execute">
<button class="btn btn-sm btn-outline-info"
onclick="generateReport({{ $stabile->id }})"
data-permission-action="reports"
data-action="execute"
data-bs-toggle="tooltip"
title="Report">
<i class="fas fa-chart-bar"></i>
</button>
</x-permission-gate>
</div>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
</div>
🚀 DYNAMIC PERMISSIONS
📡 Real-time Permission Updates
// /public/js/permissions/dynamic-permissions.js
class DynamicPermissions {
constructor() {
this.websocket = null;
this.reconnectAttempts = 0;
this.maxReconnectAttempts = 5;
this.init();
}
init() {
// Connect to WebSocket for real-time updates
this.connectWebSocket();
// Setup periodic permission refresh
this.setupPeriodicRefresh();
// Listen for permission change events
this.setupEventListeners();
}
connectWebSocket() {
if (!window.WebSocket) return;
try {
const wsUrl = `wss://${window.location.host}/ws/permissions`;
this.websocket = new WebSocket(wsUrl);
this.websocket.onopen = () => {
console.log('Permission WebSocket connected');
this.reconnectAttempts = 0;
};
this.websocket.onmessage = (event) => {
const data = JSON.parse(event.data);
this.handlePermissionUpdate(data);
};
this.websocket.onclose = () => {
console.log('Permission WebSocket disconnected');
this.attemptReconnect();
};
this.websocket.onerror = (error) => {
console.error('Permission WebSocket error:', error);
};
} catch (error) {
console.error('Failed to connect to permission WebSocket:', error);
}
}
attemptReconnect() {
if (this.reconnectAttempts < this.maxReconnectAttempts) {
this.reconnectAttempts++;
const delay = Math.pow(2, this.reconnectAttempts) * 1000;
setTimeout(() => {
console.log(`Attempting to reconnect permission WebSocket (${this.reconnectAttempts}/${this.maxReconnectAttempts})`);
this.connectWebSocket();
}, delay);
}
}
setupPeriodicRefresh() {
// Refresh permissions every 5 minutes
setInterval(() => {
this.refreshPermissions();
}, 5 * 60 * 1000);
}
setupEventListeners() {
// Listen for custom permission events
document.addEventListener('netgescon:permission:changed', (e) => {
this.handlePermissionUpdate(e.detail);
});
// Listen for role changes
document.addEventListener('netgescon:role:changed', (e) => {
this.handleRoleChange(e.detail);
});
// Listen for user context changes
document.addEventListener('netgescon:user:context:changed', (e) => {
this.refreshPermissions();
});
}
handlePermissionUpdate(data) {
const { userId, permissions, changes } = data;
// Check if update is for current user
const currentUserId = window.NetGesconApp?.user?.id;
if (userId && userId !== currentUserId) {
return;
}
// Update local permissions
if (permissions) {
window.PermissionManager.permissions = permissions;
window.PermissionManager.applyUIPermissions();
}
// Handle specific changes
if (changes) {
changes.forEach(change => {
this.handleSpecificChange(change);
});
}
// Show notification if permissions were revoked
const revokedPermissions = changes?.filter(c => c.type === 'revoked') || [];
if (revokedPermissions.length > 0) {
this.showPermissionRevokedNotification(revokedPermissions);
}
// Dispatch event
document.dispatchEvent(new CustomEvent('netgescon:permissions:updated', {
detail: { permissions, changes }
}));
}
handleSpecificChange(change) {
const { resource, action, type, oldLevel, newLevel } = change;
switch (type) {
case 'granted':
this.enablePermissionElements(resource, action);
break;
case 'revoked':
this.disablePermissionElements(resource, action);
break;
case 'modified':
this.updatePermissionLevel(resource, oldLevel, newLevel);
break;
}
}
enablePermissionElements(resource, action) {
const selector = `[data-permission="${resource}"][data-action="${action}"], [data-permission-action="${resource}"][data-action="${action}"]`;
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
element.style.display = '';
element.disabled = false;
element.classList.remove('disabled');
element.removeAttribute('aria-hidden');
element.removeAttribute('aria-disabled');
});
}
disablePermissionElements(resource, action) {
const selector = `[data-permission="${resource}"][data-action="${action}"], [data-permission-action="${resource}"][data-action="${action}"]`;
const elements = document.querySelectorAll(selector);
elements.forEach(element => {
element.style.display = 'none';
element.disabled = true;
element.classList.add('disabled');
element.setAttribute('aria-hidden', 'true');
element.setAttribute('aria-disabled', 'true');
});
}
updatePermissionLevel(resource, oldLevel, newLevel) {
// Update all elements related to this resource
const elements = document.querySelectorAll(`[data-permission="${resource}"]`);
elements.forEach(element => {
const action = element.dataset.action || 'read';
const hasPermission = window.PermissionManager.can(resource, action);
if (hasPermission) {
this.enablePermissionElements(resource, action);
} else {
this.disablePermissionElements(resource, action);
}
});
}
handleRoleChange(data) {
const { userId, oldRole, newRole } = data;
// Check if change is for current user
const currentUserId = window.NetGesconApp?.user?.id;
if (userId && userId !== currentUserId) {
return;
}
// Update user role
if (window.PermissionManager.user) {
window.PermissionManager.user.role = newRole;
}
// Refresh all permissions
this.refreshPermissions();
// Show role change notification
this.showRoleChangeNotification(oldRole, newRole);
// Potentially redirect if role was downgraded
if (this.isRoleDowngrade(oldRole, newRole)) {
this.handleRoleDowngrade(newRole);
}
}
async refreshPermissions() {
try {
await window.PermissionManager.refreshPermissions();
console.log('Permissions refreshed successfully');
} catch (error) {
console.error('Failed to refresh permissions:', error);
}
}
showPermissionRevokedNotification(revokedPermissions) {
const resources = revokedPermissions.map(p => p.resource).join(', ');
if (window.NetGesconNotifications) {
NetGesconNotifications.warning(
`I tuoi permessi per ${resources} sono stati modificati. Alcune funzionalità potrebbero non essere più disponibili.`,
{ duration: 10000 }
);
}
}
showRoleChangeNotification(oldRole, newRole) {
const roleNames = {
'super_admin': 'Super Amministratore',
'admin': 'Amministratore',
'manager': 'Manager',
'user': 'Utente',
'guest': 'Ospite'
};
const message = `Il tuo ruolo è stato cambiato da ${roleNames[oldRole]} a ${roleNames[newRole]}.`;
if (window.NetGesconNotifications) {
NetGesconNotifications.info(message, { duration: 8000 });
}
}
isRoleDowngrade(oldRole, newRole) {
const roleHierarchy = {
'super_admin': 5,
'admin': 4,
'manager': 3,
'user': 2,
'guest': 1
};
return (roleHierarchy[newRole] || 0) < (roleHierarchy[oldRole] || 0);
}
handleRoleDowngrade(newRole) {
// If user was downgraded to guest, redirect to limited dashboard
if (newRole === 'guest') {
setTimeout(() => {
window.location.href = '/dashboard/limited';
}, 3000);
}
// If user lost admin privileges while on admin page, redirect
if (!['super_admin', 'admin'].includes(newRole) && window.location.pathname.includes('/admin')) {
setTimeout(() => {
window.location.href = '/dashboard';
}, 3000);
}
}
// Manual permission check for critical actions
async verifyPermission(resource, action) {
try {
const response = await fetch('/api/verify-permission', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({ resource, action })
});
return response.ok;
} catch (error) {
console.error('Permission verification failed:', error);
return false;
}
}
}
// Initialize dynamic permissions
document.addEventListener('DOMContentLoaded', () => {
window.DynamicPermissions = new DynamicPermissions();
});
📚 FILES CORRELATI
🔐 Permission System Files
/app/Http/Middleware/CheckRole.php- Role middleware/app/Http/Middleware/CheckPermission.php- Permission middleware/app/Models/User.php- User model with permissions/config/roles.php- Role and permission configuration
🧩 Blade Components
/resources/views/components/permission-gate.blade.php/resources/views/components/role-badge.blade.php/resources/views/components/navigation/sidebar.blade.php
⚡ JavaScript Files
/public/js/permissions/permission-manager.js/public/js/permissions/dynamic-permissions.js
🎨 CSS Files
/public/css/components/permissions.css/public/css/components/role-badges.css
🧪 TESTING CHECKLIST
✅ Role-Based Testing
- Super Admin - Accesso completo
- Admin - Gestione company
- Manager - Gestione team
- User - Solo risorse proprie
- Guest - Solo lettura limitata
✅ Permission Testing
- Create permissions funzionali
- Read permissions funzionali
- Update permissions funzionali
- Delete permissions funzionali
- Execute permissions funzionali
✅ UI Testing
- Navigation basata su permessi
- Buttons e actions nascosti/disabilitati
- Forms permission-aware
- Real-time permission updates
🎯 NEXT STEPS
🔮 Permission System Evolution
- Fine-Grained Permissions - Permessi a livello di campo
- Temporary Permissions - Permessi con scadenza
- Permission Inheritance - Ereditarietà basata su gerarchia
- Audit Trail - Log completo delle modifiche permessi
- Permission Templates - Template predefiniti per ruoli
📝 Documento mantenuto da: GitHub Copilot AI Assistant
🔗 Componente: Permission System NetGescon Interface v2.1.0