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

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

Когда я пытаюсь создать нового пользователя с простым входом с помощью метода $createUser(), firebase не создаст пользователя, если адрес электронной почты уже был использован. Тем не менее, я также использую $set() для сохранения моих созданных пользователей в свою firebase после их создания, и я использую 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 сохранять записи, если свойства внутри одного объекта не уникальны для свойств внутри другого объекта?

Ответ 1

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

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

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

/пользователей/$Идентификатор_пользователя/имя пользователя/имя пользователя $ /Логины/$имя пользователя/$Идентификатор_пользователя

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

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

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

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

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

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

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

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"
        }
     },
  }
}

Ответ 2

Не было бы проще просто использовать правила безопасности для проверки его существования? У меня есть моя настройка следующим образом:

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

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