firebase - क्लाउड फायरस्टार: अद्वितीय उपयोगकर्ता नाम लागू करना




google-cloud-firestore (3)

जब भी कोई डॉक्यूमेंट जोड़ा जाता है, अपडेट किया जाता है, या users तालिका में हटाया जाता है, तो क्लाउड फ़ंक्शन की एक श्रृंखला बनाएं। क्लाउड फ़ंक्शंस usernames नाम पर एक अलग लुकअप तालिका बनाए रखेंगे, जिसमें usernames पर दस्तावेज़ आईडी सेट होंगे। आपका फ्रंट-एंड ऐप तब उपयोगकर्ता नाम संग्रह को क्वेरी कर सकता है यह देखने के लिए कि क्या उपयोगकर्ता नाम उपलब्ध है।

यहां क्लाउड फ़ंक्शंस के लिए टाइपस्क्रिप्ट कोड है:

/* Whenever a user document is added, if it contains a username, add that
   to the usernames collection. */
export const userCreated = functions.firestore
  .document('users/{userId}')
  .onCreate((event) => {

    const data = event.data();
    const username = data.username.toLowerCase().trim();

    if (username !== '') {
      const db = admin.firestore();
      /* just create an empty doc. We don't need any data - just the presence 
         or absence of the document is all we need */
      return db.doc(`/usernames/${username}`).set({});
    } else {
      return true;
    }

  });

  /* Whenever a user document is deleted, if it contained a username, delete 
     that from the usernames collection. */
  export const userDeleted = functions.firestore
    .document('users/{userId}')
    .onDelete((event) => {

      const data = event.data();
      const username = data.username.toLowerCase().trim();

      if (username !== '') {
        const db = admin.firestore();
        return db.doc(`/usernames/${username}`).delete();
      }
      return true;
    });

/* Whenever a user document is modified, if the username changed, set and
   delete documents to change it in the usernames collection.  */
export const userUpdated = functions.firestore
  .document('users/{userId}')
  .onUpdate((event, context) => {

    const oldData = event.before.data();
    const newData = event.after.data();

    if ( oldData.username === newData.username ) {
      // if the username didn't change, we don't need to do anything
      return true;
    }

    const oldUsername = oldData.username.toLowerCase().trim();
    const newUsername = newData.username.toLowerCase().trim();

    const db = admin.firestore();
    const batch = db.batch();

    if ( oldUsername !== '' ) {
      const oldRef = db.collection("usernames").doc(oldUsername);
      batch.delete(oldRef);
    }

    if ( newUsername !== '' ) {
      const newRef = db.collection("usernames").doc(newUsername);
      batch.set(newRef,{});
    }

    return batch.commit();
  });

समस्या

मैंने इस प्रश्न को कई बार देखा है (फायरबेस रियल-टाइम डेटाबेस के संदर्भ में भी), लेकिन मैंने इसका ठोस उत्तर नहीं देखा है। समस्या कथन काफी सरल है:

उपयोगकर्ता (प्रमाणीकृत) उपयोगकर्ता एक उपयोगकर्ता नाम कैसे चुन सकते हैं जो अभी तक नहीं लिया गया है?

सबसे पहले, क्यों : उपयोगकर्ता प्रमाणीकरण के बाद, उनके पास एक अद्वितीय उपयोगकर्ता आईडी है। कई वेब-एप्स, हालांकि, उपयोगकर्ता को व्यक्तिगत डेटा (वास्तविक नाम की तरह) की सुरक्षा के लिए "प्रदर्शन नाम" (वेबसाइट पर उपयोगकर्ता कैसे दिखाना चाहते हैं) का चयन करने दें।

उपयोगकर्ता संग्रह

निम्नलिखित की तरह एक डेटा संरचना को देखते हुए प्रत्येक उपयोगकर्ता के लिए अन्य डेटा के साथ एक उपयोगकर्ता नाम को स्टोर करना संभव है:

/users  (collection)
    /{uid}  (document)
        - name: "<the username>"
        - foo: "<other data>"

हालांकि, कोई भी अन्य उपयोगकर्ता (एक अलग {uid} ) को अपने रिकॉर्ड में एक ही name संग्रहीत करने के लिए नहीं रोकता है। जहां तक ​​मुझे पता है, कोई "सुरक्षा नियम" नहीं है जो हमें यह जांचने की अनुमति देता है कि क्या name पहले से ही किसी अन्य उपयोगकर्ता द्वारा दिया गया है।

नोट: क्लाइंट साइड चेक संभव है, लेकिन दुर्भावनापूर्ण क्लाइंट के रूप में असुरक्षित चेक को छोड़ सकता है।

रिवर्स मैपिंग

लोकप्रिय समाधान एक रिवर्स मैपिंग के साथ एक संग्रह बना रहे हैं:

/usernames  (collection)
    /{name}  (document)
       - uid: "<the auth {uid} field>"

इस रिवर्स मैपिंग को देखते हुए, यह सुनिश्चित करना एक सुरक्षा नियम लिखना संभव है कि उपयोगकर्ता नाम पहले से नहीं लिया गया है:

match /users/{userId} {
  allow read: if true;
  allow create, update: if
      request.auth.uid == userId &&
      request.resource.data.name is string &&
      request.resource.data.name.size() >= 3 &&
      get(/PATH/usernames/$(request.resource.data.name)).data.uid == userId;
}

और उपयोगकर्ता को पहले उपयोगकर्ता नाम बनाने के लिए बाध्य करना:

match /usernames/{name} {
  allow read: if true;
  allow create: if
      request.resource.data.size() == 1 &&
      request.resource.data.uid is string &&
      request.resource.data.uid == request.auth.uid;
}

मेरा मानना ​​है कि समाधान वहां आधा-अधूरा है। हालाँकि, अभी भी कुछ अनसुलझे मुद्दे हैं।

शेष मुद्दे / प्रश्न

यह कार्यान्वयन पहले से ही काफी शामिल है, लेकिन यह उन उपयोगकर्ताओं की समस्या को भी हल नहीं करता है जो अपना उपयोगकर्ता नाम बदलना चाहते हैं (रिकॉर्ड हटाने या अपडेट करने की आवश्यकता है, आदि)।

एक और मुद्दा है, कुछ भी नहीं उपयोगकर्ता को उपयोगकर्ता usernames संग्रह में कई रिकॉर्ड जोड़ने से रोकता है, प्रभावी रूप से सिस्टम को तोड़फोड़ करने के लिए सभी अच्छे उपयोगकर्ता नाम छीनता है।

तो सवालों के लिए:

  • क्या अद्वितीय उपयोगकर्ता नाम लागू करने का एक सरल समाधान है?
  • usernames संग्रह को स्पैम करने से कैसे रोका जा सकता है?
  • उपयोगकर्ता नाम जाँच को असंवेदनशील कैसे बनाया जा सकता है?

मैंने users अस्तित्व को भी लागू करने की कोशिश की, एक अन्य exists() / उपयोगकर्ता नाम संग्रह के लिए नियम और फिर एक बैच लिखने का संचालन करना, हालांकि, यह काम नहीं करता है (" गुम या अपर्याप्त अनुमतियाँ " त्रुटि)।

एक और नोट: मैंने क्लाइंट-साइड चेक के साथ समाधान देखा है। लेकिन यह असम्बद्ध हैं । कोई भी दुर्भावनापूर्ण ग्राहक कोड को संशोधित कर सकता है, और चेक को छोड़ सकता है।


ट्विटर पर @asciimike एक @asciimike सुरक्षा नियम डेवलपर है। वह कहते हैं कि वर्तमान में एक दस्तावेज़ पर एक कुंजी पर विशिष्टता को लागू करने का कोई तरीका नहीं है। https://twitter.com/asciimike/status/937032291511025664

चूंकि firestore Google क्लाउड datastore पर आधारित है, इसलिए यह इस समस्या को विरासत में मिला है। यह 2008 से एक लंबे समय से अनुरोध है। https://issuetracker.google.com/issues/35875869#c14

हालाँकि, आप firebase functions और कुछ सख्त security rules का उपयोग करके अपने लक्ष्य को प्राप्त कर सकते security rules

आप मेरे संपूर्ण प्रस्तावित समाधान को माध्यम पर देख सकते हैं। https://medium.com/@jqualls/firebase-firestore-unique-constraints-d0673b7a4952


मैं उसी संग्रह में usernames संग्रहीत करता हूं जहां प्रत्येक उपयोगकर्ता नाम एक अद्वितीय document आईडी रखता है। इस तरह उपयोगकर्ता नाम जो पहले से मौजूद है, डेटाबेस में नहीं बनाया जाएगा।