Security & Privacy
Sicurezza delle App: Configurazione Sicura con Parse e Back4App
22 min
come creare un'app sicura utilizzando parse introduzione ahoy comunità di back4app! questo è un tutorial ospite di joren winge su startup soul http //startup soul com/ aiutiamo le startup a costruire e lanciare i loro prodotti rapidamente i nostri amici di back4app ci hanno chiesto di mostrarvi come costruire un'app sicura su back4app in questo post, ti guideremo attraverso i passaggi per creare un'app to do sicura su back4app la sicurezza è importante se la tua app decolla, dovrai assicurarti che i dati della tua app siano sicuri e che il tuo sistema non possa essere hackerato funzionalità di sicurezza su parse parliamo prima del primo livello di sicurezza, acl (lista di controllo degli accessi) le acl sono fondamentalmente solo regole che imposti quando crei un oggetto diciamo che crei un elemento da fare, al momento della creazione puoi dire chi può leggerlo e chi può scriverlo puoi assegnare a determinati utenti la possibilità di leggere quell'elemento o scrivere su quell'elemento oppure puoi impostare uno dei due come pubblico, il che consente l'accesso a chiunque ma le acl non funzionano sempre ci sono casi in cui potresti aver bisogno di avere una logica più sofisticata invece di una semplice acl a volte puoi anche trovarti in una situazione in cui potresti dover dare a qualcuno accesso a un oggetto su base condizionale invece di una base assoluta come con le acl quindi saltiamo l'uso delle acl sono troppo rigide e allo stesso tempo consentono troppo accesso ai dati quindi, sto per darti il segreto per costruire un'app sicura su back4app e parse server sei pronto? permessi a livello di classe! sì, imposteremo permessi a livello di classe per ogni tabella nel nostro database e il livello di permesso che imposteremo è nessun permesso bloccheremo ogni singola tabella in modo che non sia consentito alcun accesso in lettura o scrittura a nessuno! sembra estremo, lo so, ma questo è il primo passo per creare un'app sicura l'unico permesso che consentiremo è sulla tabella utente, che sarà per la creazione di nuovi oggetti utente e per l'utente per visualizzare i propri dati, il che è necessario per aggiornare l'utente corrente proteggeremo l'utente dall'essere in grado di visualizzare i dati di altri utenti utilizzando le acl questa è l'unica volta in cui utilizzeremo le acl, quindi suppongo che non siano totalmente inutili è utile averle, ma non fare affidamento su di esse per fare tutto ma come accederemo ai dati, chiedi? bella domanda, sono contento che tu ci stia pensando! bene, il segreto per consentire ai clienti di accedere ai dati in modo controllato è fare in modo che ogni singola interazione tra il client e il database sia filtrata attraverso una funzione di codice cloud sì, ogni volta che fai qualcosa con la tua app, ora sarà attraverso una funzione di codice cloud personalizzata niente più pfqueries basate sul client praticamente salti l'uso dell'intero sdk parse basato sul client tranne che per le funzioni di registrazione, le funzioni di accesso, le funzioni di recupero password e le funzioni di disconnessione per queste, utilizzeremo ancora gli sdk client nativi è solo più facile hai mai scritto codice cloud prima? no, dici? bene, è piuttosto facile è solo javascript e utilizza l'sdk javascript di parse, ma internamente sul server della tua app infatti, poiché parse server è basato su node js, è piuttosto simile a scrivere rotte con express, ma ancora più facile poiché il tuo linguaggio di query è già installato e le funzioni di codice cloud sono molto più facili da scrivere rispetto a un'intera app node js express quindi ecco cosa faremo utilizzeremo un'app todo per ios che ho già creato non ci preoccuperemo di mostrarti come l'ho creata invece, ci concentreremo sulla scrittura del codice cloud e sulla protezione del database l'app todo sarà un'app sicura in cui puoi accedere solo ai tuoi todo e puoi scrivere solo i tuoi todo i dati saranno sicuri sul server, al riparo da client malintenzionati ti mostrerò anche come scrivere un lavoro di background sicuro di parse fondamentalmente, la stessa cosa di un cron job in modo che tu possa avere servizi automatizzati che manipolano i tuoi dati secondo un programma sembra complicato, ma non lo è immagina piccoli robot server che fanno quello che vuoi secondo un programma automatizzato sembra interessante, vero? ok, quindi ci siamo!!!!!! impostiamo l'app todo sicura di back4app 1\) crea un'app su back4app crea una nuova app su back4app chiama l'app ‘secure todo app’ nota segui il tutorial nuova app parse per imparare come creare un'app su back4app vai nella pagina delle impostazioni core dell'app e poi clicca su modifica dettagli app disabilita la casella chiamata ‘consenti creazione classe client’ per disabilitare la creazione di classi client e premi salva vogliamo limitare ciò che il client può fare come regola 2\) imposta i permessi di sicurezza a livello di classe per la classe utente successivamente imposteremo i permessi per la classe utente vai nel dashboard del database back4app e clicca sulla classe utente poi clicca sulla scheda sicurezza, quindi clicca sull'icona dell'ingranaggio in alto a destra dovresti vedere un menu che dice semplice/avanzato sposta il cursore su avanzato dovresti quindi vedere i permessi a livello di classe completi per questa classe disabilita la casella di controllo trova disabilita la casella di controllo aggiorna e elimina infine, disabilita la casella di controllo aggiungi campo poi premi salva le tue impostazioni di sicurezza dovrebbero apparire così 3\) crea la classe todo clicca su crea una classe e chiamala todo imposta il tipo di classe come personalizzato 4\) imposta i permessi di sicurezza a livello di classe per la classe todo successivamente imposteremo i permessi per la classe todo vai nel dashboard del database back4app e clicca sulla classe todo poi clicca sulla scheda sicurezza, quindi clicca sull'icona dell'ingranaggio in alto a destra dovresti vedere un menu che dice semplice/avanzato sposta il cursore su avanzato dovresti quindi vedere i permessi a livello di classe completi per questa classe disabilita tutto e poi premi salva le tue impostazioni di sicurezza dovrebbero apparire così 5\) aggiungiamo alcune colonne alla classe todo prima uniamo la classe todo alla classe user lo faremo aggiungendo 2 colonne la prima colonna si chiamerà ‘user’ e sarà un puntatore alla classe user successivamente creiamo una colonna per l'id dell'oggetto dell'utente che l'ha creato sarà di tipo stringa e si chiamerà ‘userobjectid’ successivamente creiamo una colonna per contenere le informazioni effettive del todo sarà anche di tipo stringa e si chiamerà ‘tododescription’ creiamo un boolean per tenere traccia dello stato del todo chiamiamolo ‘finished’ infine, aggiungiamo un'altra colonna per tenere la data in cui hai completato il tuo todo chiamiamola ‘finisheddate’ e impostiamola su un tipo data la tua classe todo dovrebbe apparire così 6\) rivediamo il client il client è un'app to do piuttosto semplice utilizza le funzioni parse integrate per accedere, creare un nuovo utente e reimpostare la password a parte questo, tutto è basato su codice cloud e sicuro gli acl dell'utente vengono impostati non appena accedono o si registrano, solo per essere sicuri al 100% che il sistema sia sicuro iniziamo scrivendo la funzione di codice cloud per impostare gli acl dell'utente al momento dell'accesso o della registrazione in qualsiasi momento, puoi accedere al progetto ios completo costruito con questo tutorial a questo repository github puoi anche accedere al file di codice cloud main js costruito per questo tutorial a questo repository github 1 nel client vai a todocontroller swift e cerca la funzione setusersaclsnow questa funzione viene chiamata quando accedi o visualizzi loggedinviewcontroller swift la funzione controlla se sei connesso e, se lo sei, chiama la funzione cloud per impostare le acl utente personali todocontroller swift 1 func setusersaclsnow(){ 2 if pfuser current() != nil{ 3 let cloudparams \[anyhashable\ string] = \["test" "test"] 4 pfcloud callfunction(inbackground setusersacls, withparameters cloudparams, block { 5 (result any?, error error?) > void in 6 if error != nil { 7 //print(error debugdescription) 8 if let descrip = error? localizeddescription{ 9 print(descrip) 10 } 11 }else{ 12 print(result as! string) 13 } 14 }) 15 } 16 } 2 ora scriviamo la funzione di codice cloud parse server 3 x 1 parse cloud define('setusersacls', async(request) => { 2 let currentuser = request user; 3 currentuser setacl(new parse acl(currentuser)); 4 return await currentuser save(null, { usemasterkey true }); 5 }); parse server 2 x 1 parse cloud define('setusersacls', function (request, response) { 2 var currentuser = request user; 3 currentuser setacl(new parse acl(currentuser)); 4 currentuser save(null, { 5 usemasterkey true, 6 success function (object) { 7 response success("acls updated"); 8 }, 9 error function (object, error) { 10 response error("got an error " + error code + " " + error description); 11 } 12 }); 13 }); 3 questo codice cloud utilizza due caratteristiche chiave per rendere la tua app sicura, request user e masterkey request user ti consente di accedere all'utente che sta effettuando la chiamata al codice cloud e ti permette di limitare l'accesso per quell'utente in questo caso, lo stiamo usando per impostare gli acl dell'utente per limitare l'accesso in lettura solo all'utente attuale in questo modo solo l'utente può leggere le proprie informazioni le autorizzazioni a livello di classe impediscono l'accesso in scrittura anche per l'utente attuale in questo modo gli utenti non possono modificare le proprie informazioni possono solo cambiare le cose riguardanti il proprio utente tramite codice cloud è possibile importare informazioni false quando l'utente si registra per la prima volta, ma ti consiglio di scrivere una funzione di codice cloud per controllare le informazioni dell'utente dopo che un nuovo utente è stato creato la funzione integrata di parse per creare un nuovo utente è davvero utile, quindi penso che sia un buon compromesso, ma puoi sempre impostare i valori predefiniti per l'utente tramite codice cloud subito dopo che si è registrato ci sono molte misure di sicurezza che puoi anche scrivere nel codice cloud e farle eseguire automaticamente e continuamente utilizzando lavori in background per rilevare eventuali informazioni utente malevole che sono state importate quando l'utente è stato creato per la prima volta se vuoi essere davvero sicuro, puoi memorizzare qualsiasi informazione sensibile come stato di appartenenza o informazioni di pagamento in una tabella separata dalla tabella degli utenti in questo modo l'utente non può falsificare alcuna informazione sensibile durante la creazione dell'utente 4 ora vediamo come creare un todo nel client vai su todocontroller swift e cerca la funzione savetodo questa funzione viene chiamata quando crei un nuovo todo la funzione prende una stringa che descrive il todo e lo salva nel database todocontroller swift 1 func savetodo(todostring\ string, completion @escaping ( result bool, message\ string, todoarray \[todo]) >()){ 2 var resulttodoarray \[todo] = \[] 3 let cloudparams \[anyhashable\ any] = \["todostring"\ todostring] 4 pfcloud callfunction(inbackground createtodosforuser, withparameters cloudparams, block { 5 (result any?, error error?) > void in 6 if error != nil { 7 if let descrip = error? localizeddescription{ 8 completion(false, descrip, resulttodoarray) 9 } 10 }else{ 11 resulttodoarray = result as! \[todo] 12 completion(true, "success", resulttodoarray) 13 } 14 }) 15 } 5 ora scriviamo la funzione di codice cloud per salvare il todo nel database parse server 3 x 1 parse cloud define("createtodosforuser", async(request) => { 2 let currentuser = request user; 3 let todostring = request params todostring; 4 let todo = parse object extend("todo"); 5 let todo = new todo(); 6 todo set("user", currentuser); 7 todo set("userobjectid", currentuser id); 8 todo set("tododescription", todostring); 9 todo set("finished", false); 10 return await todo save(null, { usemasterkey true }); 11 }); parse server 2 x 1 parse cloud define("createtodosforuser", function(request, response) { 2 var currentuser = request user; 3 var todostring = request params todostring; 4 var todo = parse object extend("todo"); 5 var todo = new todo(); 6 todo set("user", currentuser); 7 todo set("userobjectid", currentuser id); 8 todo set("tododescription", todostring); 9 todo set("finished", false); 10 todo save(null, { 11 usemasterkey true, 12 success function (object) { 13 response success(\[todo]); 14 }, 15 error function (object, error) { 16 response error("got an error " + error code + " " + error description); 17 } 18 }); 19 }); 6 questa funzione di codice cloud crea un oggetto todo e imposta l'utente corrente come proprietario dell'oggetto questo è importante affinché solo l'utente che lo ha creato possa trovarlo o modificarlo non consentendo la creazione di todo nel client, costringiamo l'oggetto todo a conformarsi ai nostri standard e assicuriamo che i todo siano di proprietà dell'utente che li ha creati 7 ora vediamo come recuperare i todo che hai creato dal server nel client vai su todocontroller swift e cerca la funzione gettodosfordate questa funzione viene chiamata quando recuperi i tuoi todo la funzione prende una data come parametro e la utilizza per recuperare un elenco di todo che sono stati creati da te prima di quella data in ordine decrescente utilizzare una data è un ottimo modo per scrivere una query di caricamento pigro che non utilizza salti il salto può a volte fallire su un grande set di dati todocontroller swift 1 func savetodo(todostring\ string, completion @escaping ( result bool, message\ string, todoarray \[todo]) >()){ 2 var resulttodoarray \[todo] = \[] 3 let cloudparams \[anyhashable\ any] = \["date"\ date] 4 pfcloud callfunction(inbackground gettodosforuser, withparameters cloudparams, block { 5 (result any?, error error?) > void in 6 if error != nil { 7 if let descrip = error? localizeddescription{ 8 completion(false, descrip, resulttodoarray) 9 } 10 }else{ 11 resulttodoarray = result as! \[todo] 12 completion(true, "success", resulttodoarray) 13 } 14 }) 15 } 8 ora scriviamo la funzione di codice cloud per recuperare i todo dal database in base a una data di inizio interroghiamo i todo che sono stati creati prima della data parametro, quindi utilizziamo ‘query lessthan’ perché le date sono fondamentalmente numeri che diventano più grandi man mano che ci si allontana nel futuro ho anche incluso un codice complicato qui supponiamo di includere l'oggetto utente che ha creato il todo, ma non vogliamo condividere informazioni sensibili su quell'utente con altri utenti, dobbiamo rimuoverlo dalla risposta json quindi abbiamo un ciclo for in cui prendiamo l'oggetto utente dal todo, rimuoviamo l'email e il nome utente dal json e poi lo rimettiamo nel todo questo è utile per rimuovere dati sensibili da una chiamata api in situazioni in cui non puoi controllare quali campi restituisci come un oggetto utente incluso in questo caso non ne abbiamo davvero bisogno perché questa funzione restituirà solo i todo che hai creato tu stesso lo facciamo utilizzando di nuovo currentuser per interrogare solo i todo creati dal currentuser che era allegato alla richiesta i risultati vengono restituiti in ordine decrescente in modo che i todo più recenti appaiano per primi quando hai bisogno di caricare pigro un altro lotto di todo, prendi la data createdat dall'ultimo todo e usala come parametro di data per la prossima richiesta parse server 3 x 1 parse cloud define("gettodosforuser", async(request) => { 2 let currentuser = request user; 3 let date = request params date; 4 let query = new parse query("todo"); 5 query equalto("user", currentuser); 6 query lessthan("createdat", date); 7 query descending("createdat"); 8 query limit(100); 9 query include("user"); 10 let results = await query find({ usemasterkey true }); 11 if(results length === 0) throw new error('no results found!'); 12 13 let resultsarray = \[]; 14 for (let i = 0; i < results length; i++) { 15 let todo = results\[i]; 16 let tempuser = todo get("user"); 17 let jsonuser = tempuser tojson(); 18 delete jsonuser email; 19 delete jsonuser username; 20 21 jsonuser type = "object"; 22 jsonuser classname = " user"; 23 24 let cleanedtodo = todo tojson(); 25 cleanedtodo user = jsonuser; 26 cleanedtodo type = "object"; 27 cleanedtodo classname = "todo"; 28 resultsarray push(cleanedtodo); 29 } 30 return resultsarray; 31 }); parse server 2 x 1 parse cloud define("gettodosforuser", function(request, response) { 2 var currentuser = request user; 3 var date = request params date; 4 var query = new parse query("todo"); 5 query equalto("user", currentuser); 6 query lessthan("createdat", date); 7 query descending("createdat"); 8 query limit(100); 9 query include("user"); 10 query find({ 11 usemasterkey true, 12 success function (results) { 13 var resultsarray = \[]; 14 for (var i = 0; i < results length; i++) { 15 var todo = results\[i]; 16 var tempuser = todo get("user"); 17 var jsonuser = tempuser tojson(); 18 delete jsonuser email; 19 delete jsonuser username; 20 21 jsonuser type = "object"; 22 jsonuser classname = " user"; 23 24 var cleanedtodo = todo tojson(); 25 cleanedtodo user = jsonuser; 26 cleanedtodo type = "object"; 27 cleanedtodo classname = "todo"; 28 resultsarray push(cleanedtodo); 29 } 30 response success(resultsarray); 31 }, 32 error function (error) { 33 response error(" error " + error code + " " + error message); 34 } 35 }); 36 }); 9 ora che abbiamo i todo, possiamo vederli nell'app e segnarli come completati se vogliamo copriamo questo prossimo 10 per contrassegnare un todo come completato, basta premere il pulsante ‘contrassegna come completato’ su uno qualsiasi dei todo che hai creato questo attiverà un metodo in todocontroller swift chiamato ‘marktodosascompletedfor’ che prende il todo selezionato come parametro invia l'objectid del todo al server come parametro e poi restituisce il todo aggiornato come risultato todocontroller swift 1 func marktodosascompletedfor(todo\ todo, completion @escaping ( result bool, message\ string, todoarray \[todo]) >()){ 2 var resulttodoarray \[todo] = \[] 3 let cloudparams \[anyhashable\ any] = \["todoid"\ todo objectid ?? ""] 4 pfcloud callfunction(inbackground marktodoascompletedforuser, withparameters cloudparams, block { 5 (result any?, error error?) > void in 6 if error != nil { 7 if let descrip = error? localizeddescription{ 8 completion(false, descrip, resulttodoarray) 9 } 10 }else{ 11 resulttodoarray = result as! \[todo] 12 completion(true, "success", resulttodoarray) 13 } 14 }) 15 } 11\ ora scriveremo il codice cloud per aggiornare questo todo cerca il todo da aggiornare in base all'objectid, ma utilizza anche l'currentuser per assicurarsi che il todo associato all'objectid sia stato creato dall'utente che effettua la query questo garantisce che puoi visualizzare solo i todo che hai creato e quindi è sicuro includiamo un limite di 1 risultato per assicurarci che il server non continui a cercare dopo aver trovato il todo c'è un altro metodo per trovare un oggetto in base a un objectid, ma non mi piace usarlo poiché può restituire risultati strani se non trova l'oggetto associato all'objectid stiamo anche impostando la 'finisheddate' con la data attuale quando l'oggetto è stato aggiornato avendo impostato la finisheddate solo tramite questa funzione, ci siamo assicurati che la finisheddate sia sicura e non possa essere falsificata o modificata abbiamo anche usato 'query equalto("finished", false)' per assicurarci che solo un todo non finito possa essere contrassegnato come finito e avere la finisheddate impostata ciò significa che una volta che un todo è stato contrassegnato come finito, non può mai essere contrassegnato come finito di nuovo in una data successiva parse server 3 x 1 parse cloud define("marktodoascompletedforuser", async(request) => { 2 let currentuser = request user; 3 let todoid = request params todoid; 4 let query = new parse query("todo"); 5 query equalto("user", currentuser); 6 query equalto("objectid", todoid); 7 query equalto("finished", false); 8 let todo = await query first({ usemasterkey true }); 9 if(object keys(todo) length === 0) throw new error('no results found!'); 10 todo set("finished", true); 11 let date = new date(); 12 todo set("finisheddate", date); 13 try { 14 await todo save(null, { usemasterkey true}); 15 return todo; 16 } catch (error){ 17 return("getnewstore error " + error code + " " + error message); 18 } 19 }); parse server 2 x 1 parse cloud define("marktodoascompletedforuser", function(request, response) { 2 var currentuser = request user; 3 var todoid = request params todoid; 4 var query = new parse query("todo"); 5 query equalto("user", currentuser); 6 query equalto("objectid", todoid); 7 query equalto("finished", false); 8 query limit(1); 9 query find({ 10 usemasterkey true, 11 success function (results) { 12 if (results length > 0) { 13 var todo = results\[0]; 14 todo set("finished", true); 15 var date = new date(); 16 todo set("finisheddate", date); 17 todo save(null, { 18 usemasterkey true, 19 success function (object) { 20 response success(\[todo]); 21 }, 22 error function (object, error) { 23 response error("got an error " + error code + " " + error description); 24 } 25 }); 26 } else { 27 response error("todo not found to update"); 28 } 29 30 }, 31 error function (error) { 32 response error(" error " + error code + " " + error message); 33 } 34 }); 35 }); 7\) conclusione! e questo è tutto hai costruito un'app todo sicura ancora una volta, la chiave per creare un'app sicura su parse server è disabilitare tutte le autorizzazioni a livello di classe per tutte le classi tranne che per la classe utente nella classe utente, disabiliti tutte le autorizzazioni tranne create e get assicurati anche di impostare tutte le acl degli utenti in modo che l'utente possa solo get i propri dati quindi tutte le tue interazioni passano attraverso il codice cloud e sono filtrate utilizzando request user, alias l'utentecorrente quindi ecco, ora puoi costruire sistemi sicuri su parse server e back4app ma aspetta, dici? e i lavori in background e le query live? beh, hai un buon punto, quindi lo tratterò in due sezioni bonus successivamente 8\) sezioni bonus 1\ lavori in background a volte è necessario creare un lavoro in background da eseguire ogni ora, o ogni giorno o ogni settimana se stai eseguendo con tutte le autorizzazioni a livello di classe disattivate, il tuo lavoro in background non sarà in grado di interrogare il database a meno che non sia configurato correttamente questo è un po' complicato da fare, quindi voglio includere un esempio qui in questo caso creeremo un lavoro in background che controlla il database per todo non completati che hanno più di 1 anno e poi li segna automaticamente come completati il trucco qui è usare 'usemasterkey' correttamente deve essere aggiunto alla query prima della promessa then segui semplicemente questo modello e dovresti essere in grado di scrivere facilmente lavori in background sicuri inizi sempre scrivendo una query che desideri iterare su tutto il database e poi assicurati di includere status error se c'è un errore e terminarlo con status success per assicurarti che venga completato puoi controllare i log su back4app per vedere il lavoro in background funzionare mentre lo esegui parse server 3 x 1 parse cloud job("markunfinishedtodosolderthan1yearasfinished", async(request) => { 2 let date = new date(); 3 let intyear = date getfullyear() 1; 4 let query = new parse query("todo"); 5 query equalto("finished", intyear); 6 query lessthan("createdat", date); 7 8 let todo = await query find({ usemasterkey true }); 9 for (let i = 0; i < results length; i++) { 10 let todo = results\[i]; 11 todo set("finished", true); 12 todo set("finisheddate", date); 13 try { 14 await todo save(null, { usemasterkey true}); 15 } catch (error){ 16 console log("getnewstore error " + error code + " " + error message); 17 } 18 } 19 return "migration completed successfully "; 20 }); parse server 2 x 1 parse cloud define("marktodoascompletedforuser", function(request, response) { 2 var currentuser = request user; 3 var todoid = request params todoid; 4 var query = new parse query("todo"); 5 query equalto("user", currentuser); 6 query equalto("objectid", todoid); 7 query equalto("finished", false); 8 query limit(1); 9 query find({ 10 usemasterkey true, 11 success function (results) { 12 if (results length > 0) { 13 var todo = results\[0]; 14 todo set("finished", true); 15 var date = new date(); 16 todo set("finisheddate", date); 17 todo save(null, { 18 usemasterkey true, 19 success function (object) { 20 response success(\[todo]); 21 }, 22 error function (object, error) { 23 response error("got an error " + error code + " " + error description); 24 } 25 }); 26 } else { 27 response error("todo not found to update"); 28 } 29 30 }, 31 error function (error) { 32 response error(" error " + error code + " " + error message); 33 } 34 }); 35 }); 2 query dal vivo a volte è necessario utilizzare la funzione query dal vivo di parse per qualcosa come un'app di chat dal vivo vuoi utilizzare la query dal vivo per vedere quando vengono creati nuovi messaggi per il tuo utente la query dal vivo è fondamentalmente il modo di parse di utilizzare i socket per ottenere aggiornamenti in tempo reale è piuttosto utile, ma non funzionerà con una classe i cui permessi find sono stati disattivati quindi, in questo caso, riattiveremo i permessi find per la classe messaggio e invece assegneremo direttamente gli acl per quel messaggio l'acl dovrebbe essere impostato in modo che solo il destinatario possa utilizzare un find per ottenere il messaggio dal server poi esegui la tua pf live query nel tuo client cercando messaggi per il tuo utente e funzionerà perfettamente se stai trattando messaggi di gruppo, è un po' diverso puoi assegnare più persone all'acl, ma in realtà non scala invece, c'è un modo migliore imposti l'acl in base a un ruolo parse role e poi qualsiasi utente a cui vuoi dare accesso a quel messaggio lo assegni semplicemente a quel parse role se vuoi impedire loro di leggere i messaggi per quel gruppo, li rimuovi da quel ruolo questo è molto più facile che rimuoverli dall'acl di ogni singolo messaggio e scala per gruppi molto grandi questo è il modo corretto per farlo non lascerò un campione di codice per questo poiché è troppo complesso per questo tutorial, ma forse spiegherò come farlo nel mio prossimo grazie per aver letto questo tutorial sulla sicurezza con parse e back4app se hai domande, sentiti libero di contattarmi e sarò felice di rispondere grazie! joren