Как предотвратить дублирование свойств пользователя в Firebase?

Я использую FirebaseSimpleLogin для создания пользователей и обработки аутентификации.

Когда я пытаюсь создать нового пользователя с помощью простого входа в систему с помощью метода $createUser(), firebase не будет создавать пользователя, если адрес электронной почты уже был использован. Тем не менее, я также использую $set() для сохранения моих созданных пользователей в мою базу данных после того, как я их создаю, и я использую user.uid в качестве ключа. При попытке записи в базу данных firebase сохранит запись, даже если имя пользователя не уникально, поскольку для простого входа в систему требуются только адрес электронной почты и пароль. Итак, как я могу подтвердить уникальность имени пользователя, если оно не используется в качестве ключа к объекту пользователя?

Я создаю таких новых пользователей:

$scope.createUser = function() {
  $scope.auth.$createUser('[email protected]', 'password').then(function(user, err) {
    if (!err) {
      ref.child('users/' + user.uid).set({
        email: user.email,
        username: user.username
      });
      console.log("success!");
    }else{
      console.log(err.message);
    }
  });
}

А мой пользовательский объект выглядит так:

{
  "users" : {
    "simplelogin:28" : {
      "email" : "[email protected]",
      "username" : "jtrinker"
    },
    "simplelogin:30" : {
      "email" : "[email protected]",
      "username" : "jtrinker"
    }
  }
}

}

Мне нужно использовать uid в качестве ключа для каждого пользователя, но мне все равно нужно, чтобы имя пользователя было уникальным.

Как я могу запретить firebase сохранять записи, если свойства в одном объекте не уникальны для свойств внутри другого объекта?


person reknirt    schedule 13.08.2014    source источник
comment
Зачем вам писать запись о пользователе, если он не смог успешно создать учетную запись? Зачем неаутентифицированным пользователям писать записи? Не могли бы вы пояснить, почему это хорошая идея?   -  person Kato    schedule 14.08.2014
comment
Думаю, я не понял. Очевидно, что запись записей неаутентифицированными пользователями - очень плохая идея, как вы прямо подразумеваете. Но дело не в этом. Дело в том, что, поскольку имя пользователя не требуется для создания учетной записи пользователя, как мне убедиться, что в моей базе данных нет повторяющихся имен пользователей?   -  person reknirt    schedule 14.08.2014
comment
Вы спрашиваете, как сделать адрес электронной почты уникальным. Если адрес электронной почты уже занят, они не должны иметь возможность создать учетную запись и, следовательно, не должны иметь возможность аутентифицироваться, что помешало бы созданию учетной записи пользователя для дублированного адреса электронной почты. Так что я все еще не совсем понимаю более крупный вариант использования, который мы пытаемся решить.   -  person Kato    schedule 14.08.2014
comment
Прошу прощения за недоразумение. В конце своего вопроса я упомянул, что мне также нужно, чтобы у пользователей было имя пользователя, которое также должно быть уникальным. Да, все адреса электронной почты будут уникальными, поскольку они не смогут создать учетную запись, если электронные письма уже приняты, однако имя пользователя не требуется для простого входа в firebase, поэтому кто-то сможет создать нового пользователя с уникальным адресом электронной почты. адрес еще введите повторяющееся имя пользователя. Я хочу добавить ключ имени пользователя к моему объекту пользователя выше, но мне нужно убедиться, что вы не можете писать пользователям, если имя пользователя также уникально для всех пользователей в моей базе данных firebase.   -  person reknirt    schedule 14.08.2014
comment
Я отредактировал свой вопрос, чтобы, надеюсь, быть более ясным.   -  person reknirt    schedule 14.08.2014


Ответы (2)


Прежде всего, если у пользователей уже есть username, он уникален и никуда не денется, я бы рекомендовал вам отказаться от использования простого входа uids. Это не создаст ничего, кроме проблем, связанных с переключением между ними, как вы уже здесь обнаружили. Изучите создание собственных токенов с помощью такого инструмента, как firebase-паспорт-логин, а затем сохраните записи username.

Но поскольку это был не ваш вопрос, давайте решим его, пока мы здесь, поскольку вы можете пойти дальше и войти в тернистый вереск двойных идентичностей, через который я проходил много раз.

Чтобы сделать имя пользователя уникальным, сохраните индекс имен пользователей.

/users/$userid/username/$username
/usernames/$username/$userid

Чтобы гарантировать их уникальность, добавьте следующее правило безопасности к идентификатору пользователя в usernames / path, которое гарантирует, что для каждого имени пользователя может быть назначен только один пользователь и что значением является идентификатор пользователя:

".write": "newData.val() === auth.uid && !data.exists()"

Теперь убедитесь, что они совпадают, добавив к имени пользователя в записи users / следующее:

"users": {
   "$userid": {
      "username": {
         ".validate": "root.child('usernames/'+newData.val()).val() === $userid"
      }
   }
}

Это обеспечит уникальность идентификаторов. Будьте осторожны с правами чтения. Возможно, вы захотите полностью их избежать, поскольку не хотите, чтобы кто-либо просматривал личные электронные письма или имена пользователей. Что-то вроде того, что я продемонстрировал в поддержке для их сохранения, было бы идеально.

Идея здесь в том, что вы пытаетесь назначить имя пользователя и адрес электронной почты, если они не удаются, значит, они уже существуют и принадлежат другому пользователю. В противном случае вы вставляете их в запись пользователя, и теперь пользователи проиндексированы по uid и электронной почте.

Чтобы соответствовать протоколу SO, вот код из этой сути, который лучше читать по ссылке:

var fb = new Firebase(URL);

function escapeEmail(email) {
   return email.replace('.', ',');
}

function claimEmail(userId, email, next) {
   fb.child('email_lookup').child(escapeEmail(email)).set(userId, function(err) {
      if( err ) { throw new Error('email already taken'); }
      next();
   });
}

function claimUsername(userId, username, next) {
   fb.child('username_lookup').child(username).set(userId, function(err) {
      if( err ) { throw new Error('username already taken'); }
      next();
   });   
}

function createUser(userId, data) {
   claimEmail(userId, data.email, claimUsername.bind(null, userId, data.username, function() {
      fb.child('users').child(userId).set(data);
   );   
}

И правила:

{
  "rules": {
     "users": {
        "$user": {
           "username": {
               ".validate": "root.child('username_lookup/'+newData.val()).val() === auth.uid"
           },
           "email": {
               ".validate": "root.child('email_lookup').child(newData.val().replace('.', ',')).val() === auth.uid"
           }
        }
     },

     "email_lookup": {
        "$email": {
           // not readable, cannot get a list of emails!
           // can only write if this email is not already in the db
           ".write": "!data.exists()",
           // can only write my own uid into this index
           ".validate": "newData.val() === auth.uid"
        }
     },
     "username_lookup": {
        "$username": {
           // not readable, cannot get a list of usernames!
           // can only write if this username is not already in the db
           ".write": "!data.exists()",

           // can only write my own uid into this index
           ".validate": "newData.val() === auth.uid"
        }
     },
  }
}
person Kato    schedule 15.08.2014
comment
Большое спасибо за ответ. Два вопроса: 1) К чему относятся объекты email_lookup и username_lookup? Я такого раньше не видел. Это есть в какой-нибудь документации? И 2) Если вы выбрали пользовательский логин с паспортом firebase, вы бы использовали username в качестве ключа, а не идентификатора? Спасибо! - person reknirt; 15.08.2014
comment
email_lookup и username_lookup - это просто хеш, содержащий имя пользователя - ›ИД пользователя и адрес электронной почты -› ИД пользователя. - person Kato; 15.08.2014
comment
Я предполагаю, что более конкретно мой вопрос в том, что такое email_lookup и username_lookup? Это не данные, как остальные элементы в "rules:" {. Они действуют как функции? - person reknirt; 18.08.2014
comment
В приведенном мною примере это данные (я называю их индексом). Они имеют ключ электронной почты / имени пользователя и имеют значение простого идентификатора входа в систему. Они записываются методами ClaimEmail () и ClaimUsername (). - person Kato; 19.08.2014
comment
Я не получаю такой метод / свойство «заменить». пока у правила есть newData.replace. Это доступно в правилах безопасности? - person Douglas Correa; 10.10.2014
comment
Быстрая проверка документов может подтвердить или опровергнуть его существование. . В данном случае это просто опечатка; должен быть newData.val (). replace () (он работает со строками, помните) - person Kato; 10.10.2014
comment
Единственная проблема, которую я здесь вижу, заключается в том, что он позволяет пользователям требовать более одного имени пользователя / адреса электронной почты - они не могут использовать несколько одновременно, но не позволяют другим пользователям использовать их. Но похоже, что нет лучшего решения без использования пользовательских токенов. - person Gustavo Bicalho; 08.05.2015
comment
Замечательный ответ Като, мне было интересно, как бы вы очистили старые имена пользователей? Облачная функция администратора, которая периодически их удаляет? Какова будет проверка условия удаления? Кроме того, в javascript у меня есть вызов для обновления / users и / username_lookup, есть ли способ объединить их в транзакцию? - person Nikos; 04.07.2020
comment
@GustavoBicalho вы нашли хороший способ сделать это? В частности, с помощью js api. - person Nikos; 04.07.2020
comment
это было так хорошо, что я снял видео о правилах youtube.com/watch?v=QgtlpccCaMo - person Nikos; 04.07.2020

Разве не было бы проще просто использовать правила безопасности, чтобы проверить его существование? Я настроил свою следующим образом:

"usernames": {
  "$usernameid": {
    ".read": "auth != null",
        ".write": "auth != null  && (!data.exists() || !newData.exists())"
    }
    }

Это разрешает запись, если имя пользователя не существует. Я считаю, что получил это прямо из документации Firebase.

person Wayne Filkins    schedule 11.07.2016
comment
не могли бы вы сказать мне, пожалуйста, что такое $ usernameid ?? поскольку я только начал использовать firebase. Я тоже не понял этого оттуда. - person Arif Nouman Khan; 10.01.2017