Realizzazione piattaforma per più aziende

Salve, volevo sottoporvi questa problematica progettuale:

Ho necessità di creare un portale che avrà come utenti finali le aziende.

Ciascuna azienda avrà una password per accedere al backend dove avrà i propri servizi (contabilità, inserimento prodotti ed altro…)

Quindi il portale deve essere accessibile da più ‘identità’ aziendali

All’interno del proprio backend l(identità)'azienda può creare diversi ruoli: amministratori, utenti, magazzinieri ecc… con i rispettivi diritti di accesso e mansioni abilitate.

E’ necessario un pannello super-amministratore generale che permetta all’evenienza di impersonificare tutte le ‘identità’ aziendali, per fornire eventualmente supporto ai clienti.

In sostanza è come se oltre ai normali ruoli sia necessario creare anche un ulteriore livello (l’identità aziendale) a cui appartengono i vari utenti di quell’azienda e possono svolgere solo operazioni e vedere dati appartenenti all’azienda di cui fanno parte.

Disponendo ovviamente di Yii2 con modulo RBAC

La mia idea è di creare un unico database, nel quale ho inserito una tabella identities (con identity_id e nome_identità), e vorrei aggiungere a tutte le altre tabelle il campo identity_id per capire a quale identità i dati sono collegati.

In questo modo posso gestire gli aggiornamenti del DB e del portale in maniera centralizzata (piuttosto che avere diversi DB uguali per ogni azienda e/o diversi portali uguali)

Per la navigazione nel portale userei l’identity_id dell’utente loggato per gestire l’accesso alle varie funzioni per quello specifico utente e per filtrare i dati delle query

Secondo voi può essere una buona soluzione?

Avete suggerimenti?

Grazie!

a mio avviso prendendola da questo verso ti complichi la vita perchè dovrai ogni volta nello sviluppo gestire una strada "fuori natura".

Secondo me quelli che tu chiami “ruoli dell’azienda” che confondi con i ruoli RBAC, in realtà sono ognuno un utente, con la propria usr e passw.

L’unica cosa che devi fare è eventualmente aggiungere un campo nell’utente/profilo (come preferisci) che è id_azienda che è una Foreign Key ad una tabella in cui inserisci tutte le aziende.

Poi il resto della struttura (ad esempio contabilità) rimane come immagino con id_azienda a cui accederai ogni volta anzichè con user->id, con user->id_azienda

Ora che hai definito bene questo, vai ad utilizzare i ruoli RBAC per permettere ad esempio ad un utente (che per pura coincidenza si chiamera contabilità-azienda1) di accedere ai controller della contabilità.

A questa regola "contabilità" accederanno tutti gli utente:

contabilità-az1

contabilità-az2

contabilità-az3

contabilità-az…

ciao

Ciao federico Grazie per la risposta

Quindi se ho ben capito anche nel tuo schema il campo id_azienda va comunque inserito in tutte le tabelle (ordini fatture ecc…) per differenziare tutti i vari dati giusto?

beh si, essendo un gestionale unico multiaziendale penso che il campo id_azienda vada serva su tutte le tabelle dove occorre differenziare e soprattutto evitare x sbaglio che tu faccia vedere i dati dell’ azienda A, all’azienda B.

Mi spiego meglio, non starei troppo a scervellarmi sulla migliore architettura del database per migliorare le performance, quanto mi concentrerei sull’inserimento il più possibile del campo id_azienda (come chiave primaria) proprio perchè l’inserimento di tale dato come primo parametro di qualsiasi query debba diventare quasi un automatismo a garanzia della non promiscuità dei dati.

Ciao, ho applicato la strategia da te consigliata e tutto sembra andare bene, ora però mi si è presentata una prima problematica relativa alla sicurezza.

Per quanto riguarda le select le filtro attraverso l’id azienda ma un utente azienda può effettuare update/delete ad esempio degli articoli di un altra azienda semplicemente cambiando l’url manualmente nel browser (sostituendo l’id specifico del prodotto/fattura/categoria ecc…)

In parole semplici bisognerebbe filtrare a monte tutte le azioni di tutti i controller in base all’id passato… esiste un modo rapido? hai qualche strategia da suggerirmi?

Grazie mille!

Secondo me hai 2 scelte, entrambe valide dipende da quante volte hai la necessità.

La più semplice, che va bene se ti serve poche volte, è quella di fare un check all’interno di ogni action per vedere se l’azienda è abbinata all’utente.

La seconda che è più complessa, ma secondo me è una delle figate di usare un framework, in particolare yii2 è quella di lavorare nella parte behavior. Una cosa del tipo:




'rules' => [

            [

                'actions' => ['login'],

                'allow' => true,

            ],

            [

                'actions' => ['actionA', 'actionB'],

                'allow' => true,

                'roles' => ['@'],

                'matchCallback' => function ($rule, $action) {

                       *QUI COSTRUISCI LA TUA FUNZIONE CHE INCROCIA USER e AZIENDA

                    return *TRUE o FALSE a seconda se il match *;

                }

            ],

         ]



Questa regola basta metterla una volta all’inizio del controller e vale per tutte le action che specifichi.

Più dettagli qui: http://www.yiiframework.com/doc-2.0/guide-security-authorization.htmlhttp://www.yiiframework.com/doc-2.0/guide-security-authorization.html

Io metterei id_azienda dentro lo user identity, nel caso tu non lo abbia già fatto.

Poi metterei un controllo nel beforeSave del model per verificare se il il valore di id_azienda del record è uguale a quello dello useridentity e in caso contrario mandare un access denied e forzerei il logooff dell’utente così impara a fare impicci ;-).

Per filtrare userei sempre il valore contenuto nello user identity e non farei affidamento su un valore in url che può sempre essere cambiato facilmente e lo aggiungerei come filtro di base al model.

In una situazione simile ho preferito mettere il controllo nel model perché scritture e letture potevano avvenire da diversi controller sullo stesso model. Anche se mettere un controllo di accesso nel model non è esattamente corretto in questo modo ho centralizzato il controllo senza doverlo replicare su tutti i controller.