Search by

salvatorecervone / filterbymodel

SalvatoreCervone

Package Laravel per la gestione dei filtri di sicurezza perimetrale basati sui modelli Eloquent e sulle competenze utente.

Package info

github.com/SalvatoreCervone/filterbymodel

Language:Blade

pkg:composer/salvatorecervone/filterbymodel

Statistics

Installs: 60

Dependents: 0

Suggesters: 0

Stars: 1

Open Issues: 0

1.3.5 2026-10-02 10:19 UTC

README

Package Laravel per la sicurezza perimetrale e il filtraggio dati a livello di riga (row-level security). Permette di definire regole di visibilità basate sui modelli Eloquent e sulle competenze assegnate agli utenti.

Funzionalità

  • Protezione Automatica Zero-Code: Protegge automaticamente tutti i modelli con regole definite senza richiedere di modificare il codice sorgente o inserire Trait.
  • Bypass Globale & Architettura Fail-Closed: Gestione granulare dell'accesso totale per singoli operatori (es. SuperAdmin, Auditor) tramite flag/tabella polimorfica filter_user_bypasses o ID statici, con default fail-closed (WHERE 1 = 0) per utenti senza permessi configurati.
  • Modifica Rapida Parametri di Competenza: Possibilità di modificare al volo i parametri di una competenza già assegnata (criterio, ambito, valore ID, gruppo, gerarchia) ricaricandoli nel pannello di configurazione con controllo anti-duplicati intelligente.
  • Supporto Multi-Modello Utente & Display Composto: Configurazione flessibile di modelli utente multipli per la ricerca e l'assegnazione, con concatenazione personalizzabile dei campi di visualizzazione (es. ['cognome', 'nome']).
  • Scoping Granulare per Modello Target: Possibilità di applicare una competenza utente a livello globale (su tutti i modelli che usano quel criterio) oppure circoscriverla selettivamente solo a specifici modelli target (es. solo per Anagrafica o Contratto).
  • Visual Rule Builder & Introspezione Colonne: Interfaccia a righe intuitiva con autocompletamento in tempo reale delle colonne dello Schema Database (/api/model-columns).
  • Suggerimento Valori (DISTINCT) & Segnaposto Dinamici: Campionamento protetto dei valori reali a database (/api/column-values) con supporto per segnaposto dinamici (@auth_id, @current_year, @today, @null).
  • Assegnazione Competenze con Nomi Descrittivi: Autocomplete e dropdown con risoluzione automatica delle etichette dei criteri (/api/criteria-items, es. ID: 5 — Ufficio Risorse Umane) invece di dover ricordare a memoria gli ID numerici.
  • Filtraggio automatico in lettura: Global Scope Eloquent che limita i risultati delle query in base ai permessi dell'utente autenticato.
  • Validazione in scrittura/cancellazione: Intercettazione automatica degli eventi saving e deleting per verificare il perimetro di sicurezza.
  • Relazioni Dirette (1:N) e Pivot (N:M): Supporto completo per entrambi i tipi di collegamento tra modelli, chiavi esterne personalizzate (target_foreign_key) e tabelle ponte.
  • Gerarchia ad albero: Opzione include_children per includere automaticamente i discendenti nella catena gerarchica (padre_id, parent_id, ecc.).
  • Gruppi logici AND/OR: I filtri nello stesso gruppo operano in AND, gruppi diversi in OR.
  • Condizioni aggiuntive avanzate: Supporto per operatori estesi (=, !=, >, <, >=, <=, LIKE, NOT LIKE, IN, NOT IN, IS NULL, IS NOT NULL, BETWEEN) e retrocompatibilità 100% con oggetti JSON legacy.
  • Zero Dipendenze Esterne (GDPR Ready / Offline-First): Include bundle CSS locale e runtime Vue 3 isolato senza ricorrere a CDN esterne.
  • Maschere Vue 3 con Live SQL Preview e Resoconto Globale: Componenti frontend moderni per la gestione admin e utente con simulazione live della query SQL, autocomplete DB e clonazione rapida dei permessi.

Installazione

1. Aggiungi il package via Composer

composer require salvatorecervone/filterbymodel

Il ServiceProvider SalvatoreCervone\FilterByModel\FilterByModelServiceProvider viene registrato automaticamente tramite il Package Discovery di Laravel.

Comandi di Pubblicazione Risorse

Puoi pubblicare tutti gli asset del package con un unico comando, oppure pubblicare selettivamente solo ciò di cui hai bisogno:

Pubblicazione Completa (Raccomandata)

Pubblica contemporaneamente la configurazione, le migrazioni, le viste Blade del pannello, i componenti Vue 3 e i servizi JavaScript:

php artisan vendor:publish --tag=filterbymodel

Pubblicazione Selettiva

Comando Tag Destinazione Descrizione
php artisan vendor:publish --tag=filterbymodel-assets filterbymodel-assets public/vendor/filterbymodel/ Asset compilati autonomi (CSS minificato, Vue 3 runtime)
php artisan vendor:publish --tag=filterbymodel-views filterbymodel-views resources/views/vendor/filterbymodel/ Vista Blade della Dashboard Amministrativa
php artisan vendor:publish --tag=filterbymodel-vue filterbymodel-vue resources/js/Components/FilterByModel/ Componenti Vue 3 per la UI (Inertia/Vite)
php artisan vendor:publish --tag=filterbymodel-services filterbymodel-services resources/js/services/ Client API JS (filterService.js)
php artisan vendor:publish --tag=filterbymodel-config filterbymodel-config config/filterbymodel.php File di configurazione
php artisan vendor:publish --tag=filterbymodel-migrations filterbymodel-migrations database/migrations/ Migrazioni per le tabelle del package
php artisan vendor:publish --tag=filterbymodel-routes filterbymodel-routes routes/filterbymodel-*.php Rotte API e Web personalizzabili

Aggiornamento e Sovrascrittura (--force)

Se aggiorni il package e desideri sovrascrivere i file precedentemente pubblicati:

# Sovrascrive viste, componenti Vue e servizi JS
php artisan vendor:publish --tag=filterbymodel-views --force
php artisan vendor:publish --tag=filterbymodel-vue --force
php artisan vendor:publish --tag=filterbymodel-services --force

# Oppure sovrascrive tutto
php artisan vendor:publish --tag=filterbymodel --force

3. Esegui le migrazioni

php artisan migrate

Configurazione

Il package funziona out-of-the-box con zero configurazione: di default rileva e scansiona automaticamente tutti i modelli Eloquent presenti nella cartella standard di Laravel app/Models (e app/).

Se desideri personalizzare le impostazioni, modifica il file config/filterbymodel.php pubblicato:

return [
    /*
    |--------------------------------------------------------------------------
    | Scoperta dei Modelli Eloquent
    |--------------------------------------------------------------------------
    | Di default (auto_discover = true), il package scansiona automaticamente
    | tutti i modelli Eloquent in app/Models.
    | Puoi aggiungere ulteriori percorsi custom o modelli espliciti se necessario.
    */
    'models' => [
        'auto_discover' => true,

        // Percorsi/cartelle da scansionare per i modelli Eloquent (default: app/Models)
        'paths' => [
            app_path('Models'),
            // app_path('Domain/Accounting/Models'), // Esempio percorso personalizzato
        ],

        // Modelli da ignorare
        'ignore' => [
            \SalvatoreCervone\FilterByModel\Models\FilterDefinition::class,
            \SalvatoreCervone\FilterByModel\Models\UserFilter::class,
        ],

        // Modelli manuali/espliciti aggiuntivi (opzionale)
        'explicit' => [
            // ['class' => 'App\Models\Anagrafica', 'name' => 'Anagrafica'],
        ],
    ],

    /*
    |--------------------------------------------------------------------------
    | Modelli Utente e Ricerca (Multi-Modello)
    |--------------------------------------------------------------------------
    | Modelli interrogabili dalla Dashboard per assegnare filtri o bypass globale.
    | Puoi definire più modelli (es. User, Admin) con concatenazione personalizzata
    | dei campi per l'etichetta (es. ['cognome', 'nome'] o ['matricola', 'name']).
    */
    'users' => [
        'models' => [
            'App\Models\User' => [
                'label'       => 'Utenti',
                'table'       => 'users',
                'foreign_key' => 'user_id',
                'primary_key' => 'id',
                'display'     => ['cognome', 'nome'], // Array di campi concatenati
                'separator'   => ' ',
                'subtext'     => ['email'],
                'searchable'  => ['name', 'cognome', 'nome', 'email', 'matricola'],
            ],
        ],

        // ID di super-utenti con bypass permanente hardcodato (bootstrap / CLI)
        'super_user_ids' => [],
    ],

    // Risoluzione gerarchica ad albero
    'hierarchy' => [
        'parent_column'     => 'padre_id',
        'key_column'        => 'id',
        'model_columns'     => [],
        'model_key_columns' => [],
    ],

    // Sicurezza e Architettura Fail-Closed
    'security' => [
        'auto_apply_to_all_models' => env('FILTERBYMODEL_AUTO_APPLY', true),
        // 'deny' = Fail-Closed (WHERE 1 = 0) per utenti non configurati (CONSIGLIATO)
        // 'allow' = Fail-Open (accesso completo)
        'unassigned_behavior' => env('FILTERBYMODEL_UNASSIGNED_BEHAVIOR', 'deny'),
    ],

    // Introspezione e Suggerimento Valori (Visual Rule Builder)
    'introspection' => [
        'distinct_values_limit' => env('FILTERBYMODEL_DISTINCT_LIMIT', 50),
        'search_limit'          => 15,
        'cache_ttl_seconds'     => 300,
    ],

    // Configurazione rotte del package (API REST e Dashboard Web)
    'routes' => [
        'api' => [
            'enabled'    => true,
            'prefix'     => 'api',
            'middleware' => ['api'],
        ],
        'web' => [
            'enabled'    => true,
            'prefix'     => 'filterbymodel', // es. http://tuo-dominio.test/filterbymodel
            'middleware' => ['web'],         // In prod: ['web', 'auth']
        ],
    ],
];

Dashboard Web Amministrativa (Plug & Play)

Il package include un'interfaccia web completa e reattiva pronta all'uso. Senza configurare pagine o componenti, ti basta accedere dal browser all'URL:

http://tuo-dominio.test/filterbymodel

La dashboard include 3 sezioni integrate:

  1. Regole Modelli: Configurazione visuale delle relazioni, Visual Rule Builder per condizioni addizionali e simulazione live delle query SQL generate.
  2. Competenze Utenti: Autocomplete per selezionare gli operatori, combobox per assegnare criteri per nome/ID e gestione gerarchie ad albero.
  3. Resoconto Globale: Statistiche aggregate, panoramica dello stato di tutti gli utenti e clonazione massiva rapida dei permessi.

Tip

Puoi proteggere la dashboard impostando il middleware nel file config/filterbymodel.php (es. ['web', 'auth'] o un middleware personalizzato per soli amministratori).

Utilizzo Backend

1. Protezione Automatica Zero-Code (Predefinita ⭐)

Di default, il package ha attiva la modalità Auto-Apply ('auto_apply_to_all_models' => true).

Questo significa che non devi modificare i tuoi modelli Eloquent:

  • Non serve aggiungere alcun Trait.
  • Non appena configuri una regola nella Dashboard per un modello (es. App\Models\Anagraficaassenza), Laravel applicherà automaticamente il Global Scope di lettura e i controlli in scrittura (saving) e cancellazione (deleting).
  • Funziona anche su modelli provenienti da package esterni o directory vendor su cui non puoi modificare il codice sorgente.

2. Protezione Manuale tramite Trait (Opzionale)

Se preferisci specificare esplicitamente quali modelli proteggere a livello di codice, puoi disabilitare l'auto-applicazione in .env:

FILTERBYMODEL_AUTO_APPLY=false

E aggiungere manualmente il Trait HasModelFilters (o l'equivalente italiano IntercettaFiltriSistemi) nei soli modelli desiderati:

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use SalvatoreCervone\FilterByModel\Traits\HasModelFilters;

class Anagrafica extends Model
{
    use HasModelFilters;
}

3. Usare il Servizio Direttamente nel Codice

use SalvatoreCervone\FilterByModel\Services\ModelFilterService;

$service = app(ModelFilterService::class);

// Ottieni i filtri risolti per un modello specifico (utente autenticato)
$filters = $service->ottieniFiltriRisolti(Anagrafica::class);

// Per un utente specifico (es. processi in background o simulazioni)
$filters = $service->ottieniFiltriRisolti(Anagrafica::class, $userId);

API Endpoints

Le seguenti rotte REST sono esposte automaticamente se routes.api.enabled è true:

Metodo Endpoint Descrizione
GET /api/filter-definitions Lista di tutte le regole di visibilità
POST /api/filter-definitions Crea o aggiorna una regola
DELETE /api/filter-definitions/{id} Elimina una regola
GET /api/available-models Modelli configurati disponibili
GET /api/model-columns Introspezione colonne schema DB per un modello o pivot
GET /api/column-values Valori univoci campionati (DISTINCT) o ricerca live
GET /api/search-users Ricerca operatori/utenti per autocomplete
GET /api/user-models Lista dei modelli utente configurati per la dashboard
GET /api/criteria-items Elementi e descrizioni reali per modelli di competenza
GET /api/user-filters-summary Resoconto globale di tutti gli utenti e permessi bindati
GET /api/user-filters?user_id={id} Filtri attivi per un determinato utente
POST /api/user-filters Assegna un filtro a un utente
PUT /api/user-filters/{id} Aggiorna un filtro utente esistente
DELETE /api/user-filters/{id} Rimuove un filtro utente
POST /api/user-filters/copy Clona i filtri da un utente sorgente a 1 o più destinatari
GET /api/bypass/status Verifica lo stato di bypass globale per un operatore
POST /api/bypass/toggle Attiva o disattiva il flag di bypass globale per un utente

Integrazione Frontend & Componenti Vue 3

Dopo aver eseguito php artisan vendor:publish --tag=filterbymodel-vue, i componenti Vue 3 saranno pronti all'uso nella directory resources/js/Components/FilterByModel/:

  • FilterByModelDashboard.vue — Dashboard Completa Unificata: include navigazione a schede tra Regole Modelli e Competenze Operatori, ideale per un'integrazione a singola riga.
  • FilterDefinitionManager.vue — Pannello di amministrazione per configurare le regole di visibilità con simulazione query SQL live in tempo reale (SELECT, UPDATE, DELETE).
  • User/FilterManager.vue — Maschera principale con switch a schede (Configura Operatore e Resoconto Globale).
  • User/UserSummaryTable.vue — Datatable di Resoconto Globale: panoramica di tutti gli utenti, filtri per stato (con permessi / senza vincoli), badge di conteggio regole e azioni rapide di configurazione e clonazione.
  • User/UserAutocomplete.vue — Autocomplete di ricerca operatore collegato al DB con debounce, navigazione da tastiera e supporto tabelle/campi custom.
  • User/CopyFiltersModal.vue — Modale per clonare e duplicare i permessi su 1 o più operatori contemporaneamente (con modalità Replace o Merge).
  • User/FilterForm.vue — Modulo di assegnazione filtro con supporto inclusioni alberi gerarchici (include_children) e scoping granulare per modello target (permette di selezionare "Tutti i modelli" o scegliere specifici modelli a cui applicare la competenza).
  • User/FilterList.vue — Tabella riassuntiva dei filtri attivi dell'utente con visualizzazione del target di applicazione (badge con modello specifico o ambito globale) e opzione di revoca.

Resoconto Globale e Panoramica Utenti

Il componente UserSummaryTable.vue (o la scheda Resoconto Globale in FilterManager.vue) offre agli amministratori:

  1. Indicatori Statistici (Stat Cards): totale operatori, quanti hanno filtri di sicurezza attivi e quanti sono senza vincoli.
  2. Filtri di Stato Rapidi: pulsanti per filtrare all'istante Tutti, Con Permessi o Senza Vincoli.
  3. Ricerca Testuale: filtro immediato per nome, cognome, email o ID.
  4. Azioni Rapide Dirette: pulsante "Configura" per passare all'editor del singolo operatore e pulsante "Clona" per duplicarne le competenze.

Configurazione di Default e Personalizzazione Ricerca Utente

Il componente FilterManager.vue include già di default l'autocomplete integrato collegato alla tabella users e alla colonna id. Restituisce obbligatoriamente il solo ID numerico/stringa:

<!-- Utilizzo standard (collegato di default alla tabella 'users' e campo 'id') -->
<FilterManager />

<!-- Personalizzazione tabella, campo ID, campo etichetta e placeholder -->
<FilterManager
  user-table="operatori"
  user-id-field="id_operatore"
  user-label-field="cognome_nome"
  placeholder="Cerca per matricola o cognome..."
  @user-selected="(id) => console.log('ID Operatore selezionato:', id)"
  @filters-cloned="(result) => console.log('Clonati:', result)"
/>

Sovrascrittura con Slot Personalizzato (Opzionale)

Se preferisci usare un tuo componente di ricerca custom invece dell'autocomplete integrato, puoi usare lo slot #user-search:

<FilterManager>
  <!-- Il tuo componente deve restituire l'ID chiamando la funzione onUserSelected(id) -->
  <template #user-search="{ onUserSelected }">
    <MyCustomUserSearch @selected="(user) => onUserSelected(user.id)" />
  </template>
</FilterManager>

Esempio d'uso con Inertia.js

Puoi integrare direttamente l'intera dashboard con una sola riga:

<script setup>
import FilterByModelDashboard from "@/Components/FilterByModel/FilterByModelDashboard.vue";
</script>

<template>
  <FilterByModelDashboard />
</template>

Oppure comporre manualmente i singoli componenti con i tuoi tab personalizzati:

<script setup>
import FilterDefinitionManager from "@/Components/FilterByModel/FilterDefinitionManager.vue";
import FilterManager from "@/Components/FilterByModel/User/FilterManager.vue";
import { ref } from "vue";

const activeTab = ref("definitions");
</script>

<template>
  <div class="p-6 max-w-7xl mx-auto">
    <!-- Tab di navigazione -->
    <div class="flex gap-4 mb-6">
      <button
        @click="activeTab = 'definitions'"
        :class="
          activeTab === 'definitions'
            ? 'bg-indigo-600 text-white'
            : 'bg-slate-200 text-slate-700'
        "
        class="px-4 py-2 rounded-xl font-semibold text-sm transition-all shadow-xs"
      >
        Regole di Visibilità (Admin)
      </button>
      <button
        @click="activeTab = 'users'"
        :class="
          activeTab === 'users'
            ? 'bg-indigo-600 text-white'
            : 'bg-slate-200 text-slate-700'
        "
        class="px-4 py-2 rounded-xl font-semibold text-sm transition-all shadow-xs"
      >
        Competenze Utenti
      </button>
    </div>

    <!-- Gestione Regole -->
    <FilterDefinitionManager v-if="activeTab === 'definitions'" />

    <!-- Gestione Filtri Utente con Autocomplete integrato -->
    <FilterManager v-else-if="activeTab === 'users'" />
  </div>
</template>

Esempio d'uso con Blade + Vue 3 / Vite

Se utilizzi Blade tradizionale con Vue montato su un elemento:

Nel tuo file resources/js/app.js:

import { createApp } from "vue";
import FilterDefinitionManager from "./Components/FilterByModel/FilterDefinitionManager.vue";
import FilterManager from "./Components/FilterByModel/User/FilterManager.vue";

const app = createApp({});
app.component("filter-definition-manager", FilterDefinitionManager);
app.component("filter-manager", FilterManager);
app.mount("#app");

Nella tua vista Blade (es. resources/views/admin/filters.blade.php):

@extends('layouts.app') @section('content')
<div id="app" class="container mx-auto py-8">
  <filter-definition-manager></filter-definition-manager>
</div>
@endsection

Strutture ad Albero e Colonne Gerarchiche (include_children)

Quando si abilitano i nodi figli (include_children), il package calcola ricorsivamente tutti i discendenti dell'albero:

  • Colonna Padre (parent_column): puoi indicare esplicitamente il campo gerarchico genitore (es. parent_id, id_padre, padre_id). Se lasciato vuoto, viene dedotto automaticamente tramite metodi del model (getParentColumnName()), proprietà ($parentColumn), mappatura o auto-detection su DB Schema (default: padre_id).
  • Colonna Chiave / Identificativo del Nodo (key_column): non sempre la chiave identificativa del criterio è il campo id! In molti schemi legacy o reali la chiave può chiamarsi codice, id_ufficio, uuid, matricola, ecc. Puoi personalizzare esplicitamente la colonna chiave sia nelle definizioni di filtro sia nelle competenze utente. Se lasciato vuoto, il package la deduce automaticamente dalla Primary Key del modello Eloquent ($model->getKeyName()), da metodi dedicati (getTreeKeyName()), o da schema (default: id).
  • Supporto a Chiavi Alfanumeriche / Stringhe: la risoluzione dei figli supporta in modo trasparente sia codici numerici che stringhe (es. UFF_CENTRO, UUID, codici alfanumerici), senza forzare cast a intero e con protezione anti-loop su relazioni circolari.

Scoping Granulare per Modello Target

Di default, quando assegni a un utente una competenza su un'entità (es. Azienda: ID 5), tale filtro viene applicato su tutti i modelli Eloquent protetti che fanno riferimento a quella regola.

Se invece desideri che un operatore veda una determinata sede o filiale solo su un modello specifico (ad esempio solo sulle Fatture ma non sulle Presenze), puoi selezionare i Modelli Target:

  • Tutti i modelli (default, target_model = null): la competenza si applica a tutte le entità collegate a quel criterio.
  • Modelli specifici (target_model = App\Models\Fattura): la competenza si applicherà unicamente a quel modello, lasciando inalterati gli altri.

Visual Rule Builder & Condizioni Aggiuntive (additional_where)

Nelle regole di visibilità puoi definire vincoli extra a livello di colonna con operatori logici avanzati e segnaposto dinamici:

1. Formato Strutturato (Visual Rule Builder)

[
  { "column": "stato", "operator": "=", "value": "attivo" },
  { "column": "anno", "operator": ">=", "value": "@current_year" },
  { "column": "owner_id", "operator": "=", "value": "@auth_id" },
  { "column": "deleted_at", "operator": "IS NULL", "value": "" },
  { "column": "categoria", "operator": "IN", "value": "VIP, GOLD, SILVER" }
]

2. Operatori Supportati

  • Confronto: =, !=, >, >=, <, <=
  • Testo: LIKE, NOT LIKE (supporta wildcard % o fa matching parziale)
  • Liste: IN, NOT IN (valori separati da virgola o array)
  • Nullabilità: IS NULL, IS NOT NULL
  • Intervalli: BETWEEN (es. 100, 500)

3. Segnaposto Dinamici (Risolti a Runtime)

  • @auth_id oppure @user.id: ID dell'utente attualmente autenticato.
  • @current_year: Anno solare corrente (es. 2026).
  • @today: Data odierna in formato YYYY-MM-DD.
  • @null: Valore NULL esplicito.

4. Retrocompatibilità con JSON Legacy

Il package supporta al 100% anche il formato mappa chiave-valore legacy:

{
  "deleted_at": null,
  "is_active": true,
  "tipo_record": "UFFICIALE"
}

Licenza

Questo package è software open source rilasciato sotto i termini della licenza MIT.