طلب البحث عن البيانات بأمان

تستند هذه الصفحة إلى المفاهيم الواردة في بناء قواعد الأمان وكتابة شروط قواعد الأمان لتوضيح كيفية تفاعل Cloud Firestore Security Rules مع طلبات البحث. وتلقي نظرة فاحصة على كيفية تأثير قواعد الأمان في طلبات البحث التي يمكنك كتابتها وتصف كيفية التأكد من أن طلبات البحث تستخدم نفس القيود مثل قواعد الأمان. توضّح هذه الصفحة أيضًا كيفية كتابة قواعد الأمان للسماح بطلبات البحث أو رفضها استنادًا إلى سمات طلب البحث، مثل limit وorderBy.

للحصول على مساعدة من الذكاء الاصطناعي، انسخ الطلب أدناه إلى وكيل الترميز بالذكاء الاصطناعي (مثل Gemini في "استوديو Android" وAntigravity وClaude Code وCodex وCursor). يمكنك تخصيص الطلب وتكراره مع الوكيل. انتقِل إلى مهارات وكلاء Firebase للحصول على معلومات مفصّلة عن التثبيت.

Help me use Firebase in my app.
1. Make sure that **Firebase agent skills** are installed and updated. For Claude Code, Codex, or Cursor agents, help me install the Firebase plugin. For other agents, run `npx skills add firebase/agent-skills`. Always try to use **Firebase agent skills** for Firebase-related tasks.
2. Help me write, validate, and deploy Firebase Security Rules to protect my app's Firestore data.

القواعد ليست فلاتر

عند كتابة طلبات بحث لاسترداد المستندات، تذكَّر أنّ قواعد الأمان ليست فلاتر، بل هي طلبات بحث شاملة. لتوفير الوقت والموارد، تقيّم "القواعد" طلب البحث وفقًا لمجموعة النتائج المحتملة بدلاً من قيم الحقول الفعلية لجميع مستنداتك. إذا كان من المحتمل أن يعرض طلب البحث مستندات لا يملك العميل إذنًا بقراءتها، سيتعذّر تنفيذ الطلب بالكامل.

طلبات البحث وقواعد الأمان

كما توضح الأمثلة أدناه، يجب كتابة طلبات البحث لتناسب قيود قواعد الأمان.

تأمين المستندات والاستعلام عنها استنادًا إلى auth.uid

يوضّح المثال التالي كيفية كتابة طلب بحث لاسترداد المستندات المحمية بقاعدة أمان. لنفترض أنّ لدينا قاعدة بيانات تحتوي على مجموعة من مستندات story:

/stories/{storyid}

{
  title: "A Great Story",
  content: "Once upon a time...",
  author: "some_auth_id",
  published: false
}

بالإضافة إلى الحقلَين title وcontent، يخزّن كل مستند الحقلَين author وpublished لاستخدامهما في التحكّم في الوصول. تفترض هذه الأمثلة أنّ التطبيق يستخدم مصادقة Firebase لضبط الحقل author على رقم تعريف المستخدم الذي أنشأ المستند. تُعبئ "مصادقة Firebase" أيضًا المتغيّر request.auth في قواعد الأمان.

تستخدم قاعدة الأمان التالية المتغيّرَين request.auth وresource.data لتقييد إذنَي القراءة والكتابة لكل story على مؤلفه:

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Only the authenticated user who authored the document can read or write
      allow read, write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

لنفترض أنّ تطبيقك يتضمّن صفحة تعرض للمستخدم قائمة بمستندات story التي ألّفها. قد تتوقع أنه يمكنك استخدام الاستعلام التالي لتعبئة هذه الصفحة. ومع ذلك، سيتعذّر تنفيذ هذا الطلب لأنّه لا يتضمّن القيود نفسها التي تتضمّنها قواعد الأمان:

غير صالحة: لا تتطابق قيود طلب البحث مع قيود قواعد الأمان

// This query will fail
db.collection("stories").get()

يتعذّر تنفيذ طلب البحث حتى إذا كان المستخدم الحالي هو مؤلف كل مستند story. السبب في هذا السلوك هو أنّه عندما تطبّق "القواعد" قواعد الأمان، يتم تقييم طلب البحث وفقًا لمجموعة النتائج المحتملة، وليس وفقًا للسمات الفعلية للمستندات في قاعدة البيانات. إذا كان طلب البحث قد يتضمّن مستندات تخالف قواعد الأمان، سيتعذّر إكمال طلب البحث.

في المقابل، ينجح طلب البحث التالي لأنّه يتضمّن القيد نفسه في الحقل author مثل قواعد الأمان:

صالحة: تتطابق قيود طلب البحث مع قيود قواعد الأمان

var user = firebase.auth().currentUser;

db.collection("stories").where("author", "==", user.uid).get()

تأمين المستندات والاستعلام عنها استنادًا إلى حقل معيّن

لتوضيح التفاعل بين طلبات البحث والقواعد بشكل أكبر، تعمل قواعد الأمان أدناه على توسيع نطاق إذن الوصول للقراءة لمجموعة stories للسماح لأي مستخدم بقراءة مستندات story حيث تم ضبط حقل published على true.

service cloud.firestore {
  match /databases/{database}/documents {
    match /stories/{storyid} {
      // Anyone can read a published story; only story authors can read unpublished stories
      allow read: if resource.data.published == true || (request.auth != null && request.auth.uid == resource.data.author);
      // Only story authors can write
      allow write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

يجب أن يتضمّن طلب البحث عن الصفحات المنشورة القيود نفسها التي تتضمّنها قواعد الأمان:

db.collection("stories").where("published", "==", true).get()

يضمن قيد طلب البحث .where("published", "==", true) أن تكون قيمة resource.data.published هي true لأي نتيجة. وبالتالي، يستوفي هذا الطلب قواعد الأمان ويُسمح له بقراءة البيانات.

‫OR طلب بحث

عند تقييم طلب بحث منطقي OR (or أو in أو array-contains-any) مقابل مجموعة قواعد، تقيّم "القواعد" كل قيمة مقارنة بشكل منفصل. يجب أن تستوفي كل قيمة مقارنة قيود قاعدة الأمان. على سبيل المثال، بالنسبة إلى القاعدة التالية:

match /mydocuments/{doc} {
  allow read: if resource.data.x > 5;
}

غير صالح: لا يضمن طلب البحث أن تكون قيمة السمة x > 5 لجميع المستندات المحتملة

// These queries will fail
query(db.collection("mydocuments"),
      or(where("x", "==", 1),
         where("x", "==", 6)
      )
    )

query(db.collection("mydocuments"),
      where("x", "in", [1, 3, 6, 42, 99])
    )

صالح: يضمن طلب البحث أنّ x > 5 لجميع المستندات المحتملة

query(db.collection("mydocuments"),
      or(where("x", "==", 6),
         where("x", "==", 42)
      )
    )

query(db.collection("mydocuments"),
      where("x", "in", [6, 42, 99, 105, 200])
    )

تقييم القيود المفروضة على طلبات البحث

يمكن لقواعد الأمان أيضًا قبول طلبات البحث أو رفضها استنادًا إلى قيودها. يحتوي المتغيّر request.query على السمات limit وoffset وorderBy لطلب البحث. على سبيل المثال، يمكن لقواعد الأمان رفض أي طلب بحث لا يحدّ من الحد الأقصى لعدد المستندات التي يتم استردادها إلى نطاق معيّن:

allow list: if request.query.limit <= 10;

توضّح مجموعة القواعد التالية كيفية كتابة قواعد الأمان التي تقيّم القيود المفروضة على طلبات البحث. يوسّع هذا المثال مجموعة قواعد stories السابقة مع التغييرات التالية:

  • تفصل مجموعة القواعد قاعدة القراءة إلى قواعد لـ get وlist.
  • تفرض القاعدة "get" قيودًا على استرداد المستندات الفردية، بحيث تقتصر على المستندات العلنية أو المستندات التي أنشأها المستخدم.
  • تطبّق قاعدة list القيود نفسها التي تطبّقها قاعدة get، ولكن على طلبات البحث. ويتحقّق أيضًا من حدّ طلب البحث، ثم يرفض أي طلب بحث بدون حدّ أو بحدّ أكبر من 10.
  • تحدّد مجموعة القواعد دالة authorOrPublished() لتجنُّب تكرار الرمز البرمجي.
service cloud.firestore {

  match /databases/{database}/documents {

    match /stories/{storyid} {

      // Returns `true` if the requested story is 'published'
      // or the user authored the story
      function authorOrPublished() {
        return resource.data.published == true || request.auth.uid == resource.data.author;
      }

      // Deny any query not limited to 10 or fewer documents
      // Anyone can query published stories
      // Authors can query their unpublished stories
      allow list: if request.query.limit <= 10 &&
                     authorOrPublished();

      // Anyone can retrieve a published story
      // Only a story's author can retrieve an unpublished story
      allow get: if authorOrPublished();

      // Only a story's author can write to a story
      allow write: if request.auth.uid == resource.data.author;
    }

  }
}

طلبات البحث في مجموعة المجموعات وقواعد الأمان

يتم تلقائيًا تحديد نطاق طلبات البحث على مجموعة واحدة واسترداد النتائج من هذه المجموعة فقط. باستخدام طلبات البحث في مجموعة المجموعات، يمكنك استرداد النتائج من مجموعة مجموعات تتألف من جميع المجموعات التي لها المعرّف نفسه. يوضّح هذا القسم كيفية تأمين طلبات البحث في مجموعة المجموعات باستخدام قواعد الأمان.

تأمين المستندات والاستعلام عنها استنادًا إلى مجموعات المجموعات

في قواعد الأمان، يجب السماح صراحةً بطلبات البحث في مجموعة المجموعات من خلال كتابة قاعدة لمجموعة المجموعات:

  1. تأكَّد من أنّ rules_version = '2'; هو السطر الأول من مجموعة القواعد. تتطلّب طلبات البحث في مجموعة المجموعات سلوك الرمز البديل المتكرّر الجديد {name=**} للإصدار 2 من قواعد الأمان.
  2. اكتب قاعدة لمجموعة المجموعات باستخدام match /{path=**}/[COLLECTION_ID]/{doc}.

على سبيل المثال، لنفترض أنّ هناك منتدى منظّمًا في forum مستند يحتوي على posts مجموعة فرعية:

/forums/{forumid}/posts/{postid}

{
  author: "some_auth_id",
  authorname: "some_username",
  content: "I just read a great story.",
}

في هذا التطبيق، نجعل المشاركات قابلة للتعديل من قِبل مالكيها وقابلة للقراءة من قِبل المستخدمين المصادق عليهم:

service cloud.firestore {
  match /databases/{database}/documents {
    match /forums/{forumid}/posts/{post} {
      // Only authenticated users can read
      allow read: if request.auth != null;
      // Only the post author can write
      allow write: if request.auth != null && request.auth.uid == resource.data.author;
    }
  }
}

يمكن لأي مستخدم مصادَق عليه استرداد مشاركات أي منتدى واحد:

db.collection("forums/technology/posts").get()

ولكن ماذا لو أردت عرض منشورات المستخدم الحالي في جميع المنتديات؟ يمكنك استخدام طلب بحث مجموعة المجموعات لاسترداد النتائج من جميع مجموعات posts:

var user = firebase.auth().currentUser;

db.collectionGroup("posts").where("author", "==", user.uid).get()

في قواعد الأمان، يجب السماح بهذا الطلب من خلال كتابة قاعدة قراءة أو قائمة لمجموعة جمع البيانات "posts":

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {
    // Authenticated users can query the posts collection group
    // Applies to collection queries, collection group queries, and
    // single document retrievals
    match /{path=**}/posts/{post} {
      allow read: if request.auth != null;
    }
    match /forums/{forumid}/posts/{postid} {
      // Only a post's author can write to a post
      allow write: if request.auth != null && request.auth.uid == resource.data.author;

    }
  }
}

يُرجى العِلم أنّ هذه القواعد ستنطبق على جميع المجموعات التي تحمل رقم التعريف posts، بغض النظر عن التسلسل الهرمي. على سبيل المثال، تنطبق هذه القواعد على جميع مجموعات posts التالية:

  • /posts/{postid}
  • /forums/{forumid}/posts/{postid}
  • /forums/{forumid}/subforum/{subforumid}/posts/{postid}

تأمين طلبات البحث في مجموعة المجموعات استنادًا إلى حقل معيّن

مثل طلبات البحث في مجموعة واحدة، يجب أن تستوفي طلبات البحث في مجموعة المجموعات أيضًا القيود التي تفرضها قواعد الأمان. على سبيل المثال، يمكننا إضافة حقل published إلى كل منشور في المنتدى كما فعلنا في مثال stories أعلاه:

/forums/{forumid}/posts/{postid}

{
  author: "some_auth_id",
  authorname: "some_username",
  content: "I just read a great story.",
  published: false
}

يمكننا بعد ذلك كتابة قواعد لمجموعة المجموعات posts استنادًا إلى حالة published وauthor المشاركة:

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {

    // Returns `true` if the requested post is 'published'
    // or the user authored the post
    function authorOrPublished() {
      return resource.data.published == true || request.auth.uid == resource.data.author;
    }

    match /{path=**}/posts/{post} {

      // Anyone can query published posts
      // Authors can query their unpublished posts
      allow list: if authorOrPublished();

      // Anyone can retrieve a published post
      // Authors can retrieve an unpublished post
      allow get: if authorOrPublished();
    }

    match /forums/{forumid}/posts/{postid} {
      // Only a post's author can write to a post
      allow write: if request.auth.uid == resource.data.author;
    }
  }
}

باستخدام هذه القواعد، يمكن لعملاء الويب وApple وAndroid إجراء طلبات البحث التالية:

  • يمكن لأي مستخدم استرداد المشاركات المنشورة في منتدى:

    db.collection("forums/technology/posts").where('published', '==', true).get()
    
  • يمكن لأي مستخدم استرداد مشاركات المؤلف المنشورة في جميع المنتديات:

    db.collectionGroup("posts").where("author", "==", "some_auth_id").where('published', '==', true).get()
    
  • يمكن للمؤلفين استرداد جميع منشوراتهم المنشورة وغير المنشورة في جميع المنتديات:

    var user = firebase.auth().currentUser;
    
    db.collectionGroup("posts").where("author", "==", user.uid).get()
    

تأمين المستندات والاستعلام عنها استنادًا إلى مجموعة المجموعات ومسار المستند

في بعض الحالات، قد تحتاج إلى حصر طلبات البحث في مجموعة المجموعات استنادًا إلى مسار المستند. لإنشاء هذه القيود، يمكنك استخدام الأساليب نفسها لتأمين المستندات والاستعلام عنها استنادًا إلى حقل.

لنفترض أنّ هناك تطبيقًا يتتبّع معاملات كل مستخدم بين عدة بورصات للأسهم والعملات المشفّرة:

/users/{userid}/exchange/{exchangeid}/transactions/{transaction}

{
  amount: 100,
  exchange: 'some_exchange_name',
  timestamp: April 1, 2019 at 12:00:00 PM UTC-7,
  user: "some_auth_id",
}

اطّلِع على حقل "user". على الرغم من أنّنا نعرف المستخدم الذي يملك مستند transaction من مسار المستند، فإنّنا نكرّر هذه المعلومات في كل مستند transaction لأنّ ذلك يتيح لنا إجراء أمرَين:

  • يمكنك كتابة طلبات بحث لمجموعات المستندات تقتصر على المستندات التي تتضمّن /users/{userid} معيّنًا في مسار المستند. على سبيل المثال:

    var user = firebase.auth().currentUser;
    // Return current user's last five transactions across all exchanges
    db.collectionGroup("transactions").where("user", "==", user).orderBy('timestamp').limit(5)
    
  • يجب فرض هذا القيد على جميع طلبات البحث في مجموعة transactions ليتعذّر على مستخدم واحد استرداد مستندات transaction الخاصة بمستخدم آخر.

نفرض هذا القيد في قواعد الأمان ونُدرج عملية التحقّق من صحة البيانات في الحقل user:

rules_version = '2';
service cloud.firestore {

  match /databases/{database}/documents {

    match /{path=**}/transactions/{transaction} {
      // Authenticated users can retrieve only their own transactions
      allow read: if resource.data.user == request.auth.uid;
    }

    match /users/{userid}/exchange/{exchangeid}/transactions/{transaction} {
      // Authenticated users can write to their own transactions subcollections
      // Writes must populate the user field with the correct auth id
      allow write: if userid == request.auth.uid && request.data.user == request.auth.uid
    }
  }
}

الخطوات التالية