Imporre nomi utente univoci con simplelogin Firebase

Di recente ho seguito un tutorial su Thinkster per la creazione di un’app Web utilizzando Angular e Firebase.

Il tutorial utilizza il metodo SimpleLogin di Firebase che consente di creare un “profilo” che include un nome utente.

Fabbrica:

app.factory('Auth', function($firebaseSimpleLogin, $firebase, FIREBASE_URL, $rootScope) { var ref = new Firebase(FIREBASE_URL); var auth = $firebaseSimpleLogin(ref); var Auth = { register: function(user) { return auth.$createUser(user.email, user.password); }, createProfile: function(user) { var profile = { username: user.username, md5_hash: user.md5_hash }; var profileRef = $firebase(ref.child('profile')); return profileRef.$set(user.uid, profile); }, login: function(user) { return auth.$login('password', user); }, logout: function() { auth.$logout(); }, resolveUser: function() { return auth.$getCurrentUser(); }, signedIn: function() { return !!Auth.user.provider; }, user: {} }; $rootScope.$on('$firebaseSimpleLogin:login', function(e, user) { angular.copy(user, Auth.user); Auth.user.profile = $firebase(ref.child('profile').child(Auth.user.uid)).$asObject(); console.log(Auth.user); }); $rootScope.$on('$firebaseSimpleLogin:logout', function() { console.log('logged out'); if (Auth.user && Auth.user.profile) { Auth.user.profile.$destroy(); } angular.copy({}, Auth.user); }); return Auth; }); 

controller:

 $scope.register = function() { Auth.register($scope.user).then(function(user) { return Auth.login($scope.user).then(function() { user.username = $scope.user.username; return Auth.createProfile(user); }).then(function() { $location.path('/'); }); }, function(error) { $scope.error = error.toString(); }); }; 

Alla fine del tutorial c’è una sezione “passaggi successivi” che include:

Imporre l’univocità del nome utente – questo è complicato, controlla le priorità di Firebase e vedi se puoi usarli per interrogare i profili utente per nome utente

Ho cercato e cercato ma non riesco a trovare una spiegazione chiara su come farlo, in particolare in termini di funzione setPriority () di Firebase

Sono piuttosto il principiante di Firebase quindi qualsiasi aiuto qui sarebbe stato grato.

Ci sono alcune domande simili , ma non riesco a capire come risolvere questo problema.

Enorme grazie in anticipo.


MODIFICARE

Dalla risposta di Marein ho aggiornato la funzione di registro nel mio controller per:

 $scope.register = function() { var ref = new Firebase(FIREBASE_URL); var q = ref.child('profile').orderByChild('username').equalTo($scope.user.username); q.once('value', function(snapshot) { if (snapshot.val() === null) { Auth.register($scope.user).then(function(user) { return Auth.login($scope.user).then(function() { user.username = $scope.user.username; return Auth.createProfile(user); }).then(function() { $location.path('/'); }); }, function(error) { $scope.error = error.toString(); }); } else { // username already exists, ask user for a different name } }); }; 

Ma lancia un errore ‘non definito non è una funzione’ nella riga var q = ref.child('profile').orderByChild('username').equalTo($scope.user.username); . Ho commentato il codice dopo e ho provato solo console.log (q), ma ancora nessuna gioia.

MODIFICA 2

Il problema di cui sopra è che il tutorial di Thinkster utilizza Firebase 0.8 e orderByChild è disponibile solo nelle versioni successive. Aggiornato e la risposta di Marein è perfetta.

Ci sono due cose da fare qui, un controllo sul lato client e una regola sul lato server.

Sul lato client, si desidera verificare se il nome utente esiste già, in modo da poter dire all’utente che il loro input non è valido, prima di inviarlo al server. Dove lo implementi esattamente, ma il codice sarebbe simile a questo:

 var ref = new Firebase('https://YourFirebase.firebaseio.com'); var q = ref.child('profiles').orderByChild('username').equalTo(newUsername); q.once('value', function(snapshot) { if (snapshot.val() === null) { // username does not yet exist, go ahead and add new user } else { // username already exists, ask user for a different name } }); 

È ansible utilizzarlo per controllare prima di scrivere sul server. Tuttavia, cosa succede se un utente è maligno e decide di utilizzare la console JS per scrivere comunque sul server? Per evitare ciò è necessaria la sicurezza lato server.

Ho provato a trovare una soluzione di esempio, ma mi sono imbattuto in un problema. Spero che qualcuno più esperto verrà. Il mio problema è il seguente. Supponiamo che la struttura del tuo database assomigli a questo:

 { "profiles" : { "profile1" : { "username" : "Nick", "md5_hash" : "..." }, "profile2" : { "username" : "Marein", "md5_hash" : "..." } } } 

Quando si aggiunge un nuovo profilo, si vorrebbe avere una regola che assicuri che nessun object profilo con la stessa proprietà username esista. Tuttavia, per quanto ne so, il linguaggio di sicurezza di Firebase non supporta questo, con questa struttura dati.

Una soluzione sarebbe quella di modificare la struttura dei dati per utilizzare il username come chiave per ogni profilo (anziché profile2 , profile2 , …). In questo modo non può esserci che un solo object con quel nome utente, automaticamente. La struttura del database sarebbe:

 { "profiles" : { "Nick" : { "md5_hash" : "..." }, "Marein" : { "md5_hash" : "..." } } } 

Questa potrebbe essere una soluzione valida in questo caso. Tuttavia, cosa succede se non solo il nome utente, ma ad esempio anche l’email deve essere unica? Non possono essere entrambi la chiave dell’object (a meno che non si utilizzi la concatenazione delle stringhe …).

Un’altra cosa che mi viene in mente è, oltre alla lista dei profili, tenere un elenco separato di nomi utente e un elenco separato di e-mail. Quindi quelli possono essere usati facilmente nelle regole di sicurezza per verificare se il nome utente e l’e-mail forniti esistono già. Le regole assomigliano a questo:

 { "rules" : { ".write" : true, ".read" : true, "profiles" : { "$profile" : { "username" : { ".validate" : "!root.child('usernames').child(newData.val()).exists()" } } }, "usernames" : { "$username" : { ".validate" : "newData.isString()" } } } } 

Tuttavia ora ci imbattiamo in un altro problema; come garantire che quando viene creato un nuovo profilo, il nome utente (e l’e-mail) vengano inseriti anche in questi nuovi elenchi? [1]

Questo a sua volta può essere risolto prendendo il codice di creazione del profilo dal client e posizionandolo su un server. Il cliente avrebbe quindi bisogno di chiedere al server di creare un nuovo profilo e il server assicurerebbe che tutte le attività necessarie siano eseguite.

Tuttavia, sembra che siamo andati molto più in là per rispondere a questa domanda. Forse ho trascurato qualcosa e le cose sono più semplici di quello che sembrano. Ogni pensiero è apprezzato.

Inoltre, mi scuso se questa risposta è più una domanda che una risposta, io sono nuovo di SO e non sono ancora sicuro di ciò che è appropriato come risposta.

[1] Anche se forse potresti sostenere che questo non deve essere garantito, in quanto un utente malintenzionato si danneggerebbe solo non rivendicando la sua id quadro unica?

Ho avuto un problema simile. Ma è stato dopo aver registrato l’utente con password ed e-mail. Nel profilo utente potrebbe salvare un nome utente che deve essere unico e ho trovato una soluzione, forse questo può servire.

Interrogazione per nome utente univoco in Firebase

  var ref = new Firebase(FIREBASE_URL + '/users'); ref.orderByChild("username").equalTo(profile.username).on("child_added", function(snapshot) { if (currentUser != snapshot.key()) { scope.used = true; } }); ref.orderByChild("username").equalTo(profile.username).once("value", function(snap) { //console.log("initial data loaded!", Object.keys(snap.val()).length === count); if (scope.used) { console.log('username already exists'); scope.used = false; }else{ console.log('username doesnt exists, update it'); userRef.child('username').set(profile.username); } }); };