Secure Data Connect مع التفويض والإقرار

توفّر Firebase Data Connect أمانًا قويًا من جهة العميل من خلال:

  • تفويض برنامج الويب والتطبيق المتوافق مع الأجهزة الجوّالة
  • عناصر التحكّم في الإذن على مستوى الاستعلامات الفردية وعمليات التحويل
  • شهادة اعتماد التطبيق من Firebase App Check

توفّر Data Connect ميزات أمان إضافية، مثل:

  • التفويض من جهة الخادم
  • أمان مستخدمي مشروع Firebase وCloud SQL باستخدام "إدارة الهوية وإمكانية الوصول"

تفويض طلبات بحث العميل وتعديلاته

إنّ Data Connect مدمجة بالكامل مع Firebase Authentication، لذا يمكنك استخدام بيانات غنية عن المستخدمين الذين يصلون إلى بياناتك (المصادقة) في تصميمك للبيانات التي يمكن لهؤلاء المستخدمين الوصول إليها (التفويض).

يوفّر Data Connect توجيه @auth لطلبات البحث وعمليات التحويل التي تتيح لك ضبط مستوى المصادقة المطلوب لتفويض العملية. يقدّم هذا الدليل توجيه @auth مع أمثلة.

بالإضافة إلى ذلك، تتيح Data Connect تنفيذ طلبات البحث المضمّنة في عمليات التحويل، حتى تتمكّن من استرداد معايير التفويض الإضافية التي تم تخزينها في قاعدة بياناتك، واستخدام هذه المعايير في توجيهات @check لتحديد ما إذا كانت عمليات التحويل المضمّنة مسموحًا بها. في حالة التفويض هذه، تتيح لك التوجيه @redact التحكّم في ما إذا كانت نتائج طلبات البحث ستتم إعادتها إلى العملاء في بروتوكول الشبكة وسيتم حذف طلب البحث المضمّن في حِزم SDK التي تم إنشاؤها. يمكنك العثور على مقدمة عن هذه التعليمات، مع أمثلة.

فهم توجيه @auth

يمكنك تحديد مَعلمات للتوجيه @auth لاتّباع أحد مستويات الوصول المُعدّة مسبقًا لعدة مرات والتي تغطي العديد من سيناريوهات الوصول الشائعة. تتراوح هذه المستويات بين PUBLIC (الذي يسمح بطلبات البحث وعمليات التحويل من جميع العملاء بدون مصادقة من أي نوع) وNO_ACCESS (الذي لا يسمح بطلبات البحث و عمليات التحويل خارج بيئات الخوادم المميّزة باستخدام "حزمة تطوير البرامج (SDK) للمشرف" في Firebase). يرتبط كل مستوى من هذه المستويات بعمليات مصادقة يوفّرها Firebase Authentication.

المستوى التعريف
PUBLIC يمكن لأي مستخدم تنفيذ العملية مع المصادقة أو بدونها.
PUBLIC يمكن لأي مستخدم تنفيذ العملية مع المصادقة أو بدونها.
USER_ANON أي مستخدم محدّد الهوية، بما في ذلك المستخدمين الذين سجّلوا الدخول بدون الكشف عن هويتهم باستخدام Firebase Authentication، يكون لديه الإذن بتنفيذ طلب البحث أو عملية التحويل.
USER أي مستخدم سجّل الدخول باستخدام Firebase Authentication يكون مفوَّضًا للقيام بعملية البحث أو التحويل باستثناء مستخدمي تسجيل الدخول المجهول الهوية.
USER_EMAIL_VERIFIED إنّ أي مستخدم سجّل الدخول باستخدام Firebase Authentication باستخدام عنوان بريد إلكتروني تم إثبات ملكيته يكون لديه الإذن بتنفيذ طلب البحث أو عملية التحويل.
NO_ACCESS لا يمكن تنفيذ هذه العملية خارج سياق حزمة SDK للمشرف.

باستخدام مستويات الوصول المُعدّة مسبقًا هذه كنقطة بداية، يمكنك تحديد عمليات تحقّق معقدة و فعّالة من الأذونات في توجيه @auth باستخدام فلاتر where و تعبيرات لغة التعبير الشائعة (CEL) التي يتم تقييمها على الخادم.

استخدام التوجيه @auth لتنفيذ سيناريوهات التفويض الشائعة

مستويات الوصول المُعدّة مسبقًا هي نقطة البداية للتفويض.

مستوى الوصول USER هو المستوى الأساسي الأكثر استخدامًا للبدء.

سيستند الوصول الآمن بالكامل إلى مستوى USER بالإضافة إلى الفلاتر والتعبيرات التي تتحقّق من سمات المستخدمين وسمات الموارد والأدوار والفحوصات الأخرى. إنّ مستوى USER_ANON وUSER_EMAIL_VERIFIED هما نوعان من حالات USER.

يتيح لك أسلوب تعبيرات التقييم تقييم البيانات باستخدام عنصر auth يمثّل بيانات المصادقة التي تم تمريرها مع العمليات، سواء كانت البيانات العادية في الرموز المميّزة للمصادقة أو البيانات المخصّصة في الرموز المميّزة. للحصول على قائمة الحقول المتاحة في auth العنصر، راجِع القسم المرجعي.

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

يقدّم هذا الدليل الآن أمثلة على كيفية الإنشاء على USER وPUBLIC.

مثال محفّز

تشير أمثلة أفضل الممارسات التالية إلى المخطّط التالي لمنصّة تدوين تتضمّن محتوى معيّنًا محظورًا إلا بعد الاشتراك في خطة دفع.

من المرجّح أن تتضمّن هذه المنصة Users وPosts.

type User @table(key: "uid") {
  uid: String!
  name: String
  birthday: Date
  createdAt: Timestamp! @default(expr: "request.time")
}

type Post @table {
  author: User!
  text: String!
  # "one of 'draft', 'public', or 'pro'"
  visibility: String! @default(value: "draft")
  # "the time at which the post should be considered published. defaults to
  # immediately"
  publishedAt: Timestamp! @default(expr: "request.time")
  createdAt: Timestamp! @default(expr: "request.time")
  updatedAt: Timestamp! @default(expr: "request.time")
}

المراجع المملوكة للمستخدم

تنصحك Firebase بكتابة فلاتر وتعبيرات تختبر ملف شخصي للمستخدِم يملك موردًا، وفي الحالات التالية، يملك Posts.

في الأمثلة التالية، تتم قراءة البيانات من رموز التفويض ومقارنتها باستخدام التعبيرات. النمط المعتاد هو استخدام تعبيرات مثل where: {authorUid: {eq_expr: "auth.uid"}} لمقارنة authorUid محفوظ بـ auth.uid (رقم تعريف المستخدم) الذي تم تمريره في رمز مصادقة.

إنشاء

تبدأ ممارسة التفويض هذه بإضافة auth.uid من معرّف التفويض إلى كل Post جديد كحقل authorUid للسماح بالمقارنة في اختبارات التفويض للسلسلة الفرعية.

# Create a new post as the current user
mutation CreatePost($text: String!, $visibility: String) @auth(level: USER) {
  post_insert(data: {
    # set the author's uid to the current user uid
    authorUid_expr: "auth.uid"
    text: $text
    visibility: $visibility
  })
}
تعديل

عندما يحاول العميل تعديل Post، يمكنك اختبار auth.uid المُرسَل مقارنةً بauthorUid المخزّن.

# Update one of the current user's posts
mutation UpdatePost($id: UUID!, $text: String, $visibility: String) @auth(level:USER) {
  post_update(
    # only update posts whose author is the current user
    first: { where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}
    }}
    data: {
      text: $text
      visibility: $visibility
      # insert the current server time for updatedAt
      updatedAt_expr: "request.time"
    }
  )
}
حذف

وتُستخدَم التقنية نفسها لتفويض عمليات الحذف.

# Delete one of the current user's posts
mutation DeletePost($id: UUID!) @auth(level: USER) {
  post_delete(
    # only delete posts whose author is the current user
    first: { where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}
    }}
  )
}
# Common display information for a post
fragment DisplayPost on Post {
  id, text, createdAt, updatedAt
  author { uid, name }
}
قائمة
# List all posts belonging to the current user
query ListMyPosts @auth(level: USER) {
  posts(where: {
    userUid: {eq_expr: "auth.uid"}
  }) {
    # See the fragment above
    ...DisplayPost
    # also show visibility since it is user-controlled
    visibility
  }
}
جلب
# Get a post only if it belongs to the current user
query GetMyPost($id: UUID!) @auth(level: USER) {
  post(key: {id: $id},
    first: {where: {
      id: {eq: $id}
      authorUid: {eq_expr: "auth.uid"}}
      }}, {
      # See the fragment above
      ...DisplayPost
      # also show visibility since it is user-controlled
      visibility
  }
}

فلترة البيانات

يتيح لك نظام التفويض في Data Connect كتابة فلاتر متقدّمة مع مستويات وصول مُعدّة مسبقًا مثل PUBLIC، بالإضافة إلى استخدام البيانات من الرموز المميّزة لتأكيد الهوية.

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

الفلترة حسب سمات الموارد

في هذه الحالة، لا يستند التفويض إلى الرموز المميّزة للمصادقة لأنّه تم ضبط مستوى الأمان الأساسي على PUBLIC. ولكن يمكننا ضبط السجلّات في قاعدة بياناتنا على أنّها مناسبة للوصول العام. لنفترض أنّ لدينا Post سجلًّا في قاعدة بياناتنا تم ضبط visibility فيها على "عام".

# List all posts marked as 'public' visibility
query ListPublicPosts @auth(level: PUBLIC) {
  posts(where: {
    # Test that visibility is "public"
    visibility: {eq: "public"}
    # Only display articles that are already published
    publishedAt: {lt_expr: "request.time"}
  }) {
    # see the fragment above
    ...DisplayPost
  }
}
الفلترة حسب مطالبات المستخدمين

لنفترض أنّك أعددت مطالبات مستخدمين مخصّصة تُرسل رموز التفويض لتحديد هوية المستخدمين في خطة "pro" لتطبيقك، مع وضع علامة عليها باستخدام حقل auth.token.plan في رمز التفويض. يمكن اختبار تعبيراتك في هذا الحقل.

# List all public or pro posts, only permitted if user has "pro" plan claim
query ProListPosts @auth(expr: "auth.token.plan == 'pro'") {
  posts(where: {
    # display both public posts and "pro" posts
    visibility: {in: ['public', 'pro']},
    # only display articles that are already published
    publishedAt: {lt_expr: "request.time"},
  }) {
    # see the fragment above
    ...DisplayPost
    # show visibility so pro users can see which posts are pro\
    visibility
  }
}
الفلترة حسب الطلب والحدّ

أو قد تكون قد ضبطت visibility في Post سجلّ لتحديد أنّه محتوى متاح للمستخدمين "المميّزين"، ولكن في حال عرض معاينة أو قائمة دعائية للبيانات، يمكنك تقييد عدد السجلات التي يتم عرضها بشكل أكبر.

# Show 2 oldest Pro post as a preview
query ProTeaser @auth(level: USER) {
  posts(
    where: {
      # show only pro posts
      visibility: {eq: "pro"}
      # that have already been published more than 30 days ago
      publishedAt: {lt_time: {now: true, sub: {days: 30}}}
    },
    # order by publish time
    orderBy: [{publishedAt: DESC}],
    # only return two posts
    limit: 2
  ) {
    # See the fragment above
    ...DisplayPost
  }
}
الفلترة حسب الدور

إذا كانت المطالبة المخصّصة تحدّد دور admin، يمكنك اختبار العمليات والسماح بها وفقًا لذلك.

# List all posts unconditionally iff the current user has an admin claim
query AdminListPosts @auth(expr: "auth.token.admin == true") {
  posts { ...DisplayPost }
}

فهم توجيهات @check و@redact

يتحقّق التوجيه @check من توفّر حقول محدّدة في نتائج الطلب. يتم استخدام تعبير لغة التعبير الشائعة (CEL) لاختبار قيم الحقل. السلوك التلقائي للتوجيه هو البحث عن العقد التي تحتوي على قيمة null ورفضها.

يُخفي التوجيه @redact جزءًا من الاستجابة الواردة من العميل. لا يزال يتم تقييم الحقول التي تم إخفاؤها بحثًا عن الآثار الجانبية (بما في ذلك تغييرات البيانات و @check)، ولا تزال النتائج متاحة للخطوات اللاحقة في تعبيرات CEL.

في Data Connect، غالبًا ما يتم استخدام التوجيهَين @check و@redact في سياق عمليات التحقّق من الأذونات. يُرجى الرجوع إلى المناقشة حول البحث في بيانات التفويض.

أضِف التوجيهَين @check و@redact للبحث عن بيانات التفويض.

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

باستخدام عمليات البحث في بيانات التفويض، يمكنك البحث عن الأدوار استنادًا إلى معرّف مستخدم و استخدام تعبيرات CEL لتحديد ما إذا كان التعديل مصرّحًا به. على سبيل المثال، قد تحتاج إلى كتابة UpdateMovieTitle mutation تسمح لأحد العميل المفوَّضين بتعديل عناوين الأفلام.

في بقية هذه المناقشة، نفترض أنّ قاعدة بيانات تطبيق مراجعات الأفلام تخزِّن دورًا للتفويض في جدول MoviePermission.

# MoviePermission
# Suppose a user has an authorization role with respect to records in the Movie table
type MoviePermission @table(key: ["doc", "userId"]) {
  movie: Movie! # implies another field: movieId: UUID!
  userId: String! # Can also be a reference to a User table, doesn't matter
  role: String!
}

في مثال التنفيذ التالي، يحتوي الإجراء UpdateMovieTitle على حقل query لاسترداد البيانات من MoviePermission، بالإضافة إلى التوجيهات التالية لضمان أمان العملية وفعاليتها:

  • توجيه @transaction لضمان اكتمال جميع طلبات البحث والتحقّقات المتعلّقة بالتفويض أو تعذّر إكمالها بشكلٍ موحّد
  • توجيه @redact لحذف نتائج طلب البحث من الاستجابة، ما يعني أنّه يتم تنفيذ عملية التحقّق من الإذن على خادم Data Connect ولكن لا يتم عرض البيانات الحسّاسة على العميل.
  • زوج من توجيهات @check لتقييم منطق التفويض في نتائج الطلبات، مثل اختبار ما إذا كان رقم تعريف مستخدم معيّن لديه دور مناسب لإجراء تعديلات

mutation UpdateMovieTitle($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query @redact {
    moviePermission( # Look up a join table called MoviePermission with a compound key.
      key: {movieId: $movieId, userId_expr: "auth.uid"}
    # Step 1a: Use @check to test if the user has any role associated with the movie
    # Here the `this` binding refers the lookup result, i.e. a MoviePermission object or null
    # The `this != null` expression could be omitted since rejecting on null is default behavior
    ) @check(expr: "this != null", message: "You do not have access to this movie") {
      # Step 1b: Check if the user has the editor role for the movie
      # Next we execute another @check; now `this` refers to the contents of the `role` field
      role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

الأنماط المضادة التي يجب تجنُّبها في التفويض

يتناول القسم السابق الأنماط التي يجب اتّباعها عند استخدام توجيه @auth.

يجب أيضًا أن تكون على دراية بالتصاميم المضادة المهمة التي يجب تجنّبها.

تجنَّب تمرير معرّفات سمات المستخدم ومَعلمات auth token في وسيطات الطلب والتغيير.

Firebase Authentication هي أداة فعّالة لتقديم عمليات المصادقة وتسجيل بيانات المصادقة بأمان، مثل أرقام تعريف المستخدمين المسجّلين والعديد من الحقول المخزّنة في الرموز المميّزة للمصادقة.

لا يُنصح بتمرير أرقام تعريف المستخدمين وبيانات رمز التفويض في طلب البحث ومَعلمات التحويل.

# Antipattern!
# This incorrectly allows any user to view any other user's posts
query AllMyPosts($userId: String!) @auth(level: USER) {
  posts(where: {authorUid: {eq: $userId}}) {
    id, text, createdAt
  }
}

تجنَّب استخدام مستوى الوصول USER بدون أي فلاتر.

كما ناقشنا عدة مرات في الدليل، فإنّ مستويات الوصول الأساسية، مثل USER و USER_ANON وUSER_EMAIL_VERIFIED، هي مستويات أساسية ونقاط بداية لسمّات التفويض التي يمكن تحسينها باستخدام الفلاتر والتعبيرات. إنّ استخدام هذين المستوىَين بدون فلتر أو تعبير مقابل يتحقق من المستخدم الذي يؤدي الطلب يعادل بشكل أساسي استخدام المستوى PUBLIC.

إلى مستويات الوصول الأساسية.
# Antipattern!
# This incorrectly allows any user to view all documents
query ListDocuments @auth(level: USER) {
  documents {
    id
    title
    text
  }
}

تجنُّب استخدام مستوى الوصول PUBLIC أو USER لإنشاء النماذج الأولية

لتسريع عملية التطوير، قد يكون من المغري ضبط جميع العمليات على مستوى الوصول PUBLIC أو على مستوى الوصولUSER بدون إجراء تحسينات إضافية لمنح الإذن بتنفيذ جميع العمليات والسماح لك باختبار الرمز البرمجي بسرعة.

بعد الانتهاء من إنشاء النماذج الأولية الأوّلية بهذه الطريقة، ابدأ بالتبديل من مرحلة NO_ACCESS إلى مرحلة التفويض الجاهز للنشر باستخدام المستويَين PUBLIC وUSER. ومع ذلك، لا تنشئهما على أنّهما PUBLIC أو USER بدون إضافة منطق إضافي كما هو موضّح في هذا الدليل.

# Antipattern!
# This incorrectly allows anyone to delete any post
mutation DeletePost($id: UUID!) @auth(level: PUBLIC) {
  post: post_delete(
    id: $id,
  )
}

استخدام Firebase App Check لإثبات صحة التطبيق

المصادقة والتفويض هما مكوّنان مهمّان لأمان Data Connect. إنّ المصادقة والتفويض مع شهادة اعتماد التطبيق يشكّلان حلّاً أمانًا قويًا جدًا.

من خلال المصادقة من خلال Firebase App Check، ستستخدم الأجهزة التي تعمل بتطبيقك مقدّم خدمة مصادقة للتطبيقات أو الأجهزة يشهد على أنّه تصدر عمليات Data Connect من تطبيقك الأصلي، وتتحرى عن أنّه تصدر الطلبات من جهاز أصلي لم يتم العبث به. يتم إرفاق شهادة الاعتماد هذه بكل طلب يقدّمه تطبيقك إلى Data Connect.

للتعرّف على كيفية تفعيل App Check في Data Connect وتضمين حزمة تطوير البرامج (SDK) للعملاء في تطبيقك، يمكنك الاطّلاع على App Check نظرة عامة.

مستويات المصادقة لتوجيه @auth(level)

يسرد الجدول التالي جميع مستويات الوصول العادية وما يعادلها من مستويات CEL. يتم إدراج مستويات المصادقة من المستوى الواسع إلى المستوى الضيق، ويشمل كل مستوى جميع المستخدمين الذين يتطابقون مع المستويات التالية.

المستوى التعريف
PUBLIC يمكن لأي مستخدم تنفيذ العملية مع المصادقة أو بدونها.

ملاحظات: يمكن لأي مستخدم قراءة البيانات أو تعديلها. تنصح Firebase بهذا المستوى من التفويض للبيانات التي يمكن تصفّحها بشكل علني، مثل بيانات المنتجات أو الوسائط. اطّلِع على أمثلة على أفضل الممارسات والبدائل.

يعادل @auth(expr: "true")

لا يمكن استخدام فلاتر وتعبيرات @auth معًا مع مستوى الوصول هذا. سيتعذّر تنفيذ أيّ من هذه التعبيرات وسيظهر خطأ 400 لعدم صلاحية الطلب.
USER_ANON أي مستخدم محدّد الهوية، بما في ذلك المستخدمين الذين سجّلوا الدخول بدون الكشف عن هويتهم باستخدام Firebase Authentication، يكون لديه الإذن بتنفيذ طلب البحث أو عملية التحويل.

ملاحظة: USER_ANON هي مجموعة شاملة من USER.

ملاحظات: يُرجى العِلم أنّه عليك تصميم طلبات البحث وعمليات التحويل بعناية لهذا المستوى من التفويض. يسمح هذا المستوى للمستخدم بتسجيل الدخول بشكل مجهول (تسجيل الدخول التلقائي مرتبط فقط بجهاز المستخدم) باستخدام Authentication، ولا يُجري وحده عمليات تحقّق أخرى، على سبيل المثال، لمعرفة ما إذا كانت البيانات تخصّ المستخدم. اطّلِع على أمثلة على أفضل الممارسات والبدائل.

بما أنّ Authentication مسارات تسجيل الدخول المجهولة المصدر تُصدر uid، فإنّ USER_ANON يعادل
@auth(expr: "auth.uid != nil")
USER أي مستخدم سجّل الدخول باستخدام Firebase Authentication يكون مفوَّضًا للقيام بعملية البحث أو التحويل باستثناء مستخدمي تسجيل الدخول المجهول الهوية.

ملاحظات: يُرجى العِلم أنّه عليك تصميم طلبات البحث وعمليات التحويل بعناية لهذا المستوى من التفويض. لا يتحقّق هذا المستوى سوى من تسجيل دخول المستخدم باستخدام Authentication، ولا يُجري وحده عمليات تحقّق أخرى، مثل ما إذا كانت البيانات تخصّ المستخدم. اطّلِع على أمثلة على أفضل الممارسات والبدائل.

ما يعادل @auth(expr: "auth.uid != nil && auth.token.firebase.sign_in_provider != 'anonymous'")"
USER_EMAIL_VERIFIED إنّ أي مستخدم سجّل الدخول باستخدام Firebase Authentication باستخدام عنوان بريد إلكتروني تم إثبات ملكيته يكون لديه الإذن بتنفيذ طلب البحث أو عملية التحويل.

ملاحظات: بما أنّه يتم إثبات ملكية عنوان البريد الإلكتروني باستخدام Authentication، يستند ذلك إلى طريقة Authentication أكثر أمانًا، وبالتالي يقدّم هذا المستوى مستوى أمان إضافيًا مقارنةً بUSER أو USER_ANON. لا يتحقّق هذا المستوى إلا من تسجيل دخول المستخدم باستخدام Authentication من خلال بريد إلكتروني تم إثبات ملكيته، ولا يُجري وحده عمليات تحقّق أخرى، على سبيل المثال، لمعرفة ما إذا كانت البيانات ملك المستخدم. اطّلِع على أمثلة على أفضل الممارسات والبدائل.

ما يعادل @auth(expr: "auth.uid != nil && auth.token.email_verified")"
NO_ACCESS لا يمكن تنفيذ هذه العملية خارج سياق حزمة SDK للمشرف.

ما يعادل @auth(expr: "false")

مرجع CEL للملفَّين @auth(expr) و@check(expr)

كما هو موضّح في الأمثلة الواردة في أماكن أخرى من هذا الدليل، يمكنك ويجب استخدام التعبيرات المحدّدة في لغة التعبير الشائعة (CEL) للتحكّم في التفويض لـ Data Connect باستخدام توجيهات @auth(expr:) و@check.

يتناول هذا القسم بنية CEL ذات الصلة بإنشاء تعبيرات لهذه التوجيهات.

تتوفّر معلومات مرجعية كاملة حول "الوضع المتقدّم" (CEL) في مواصفات CEL.

متغيّرات الاختبار التي تم تمريرها في طلبات البحث والطفرات

يتيح لك أسلوب @auth(expr) الوصول إلى المتغيّرات واختبارها من طلبات البحث والطفرات.

على سبيل المثال، يمكنك تضمين متغيّر عملية، مثل $status، باستخدام vars.status.

mutation Update($id: UUID!, $status: Any) @auth(expr: "has(vars.status)")

البيانات المتاحة للتعبيرات

يمكن لكل من تعبيرَي CEL @auth(expr:) و@check(expr:) تقييم العناصر التالية:

  • request.operationName
  • vars (الاسم المستعار لـ request.variables)
  • auth (الاسم المستعار لـ request.auth)

بالإضافة إلى ذلك، يمكن لتعبيرات @check(expr:) تقييم ما يلي:

  • this (قيمة الحقل الحالي)

عنصر request.operationName

يخزِّن عنصر request.operarationName نوع العملية، سواء كان طلب بحث أو عملية تعديل.

عنصر vars

يسمح عنصر vars للتعبيرات بالوصول إلى جميع المتغيّرات المُمرَّرة في طلب البحث أو عملية التحويل.

يمكنك استخدام vars.<variablename> في تعبير كاسم مستعار لrequest.variables.<variablename> المؤهَّل بالكامل:

# The following are equivalent
mutation StringType($v: String!) @auth(expr: "vars.v == 'hello'")
mutation StringType($v: String!) @auth(expr: "request.variables.v == 'hello'")

عنصر auth

يحدِّد Authentication المستخدِمين الذين يطلبون الوصول إلى بياناتك ويقدِّم هذه المعلومات كعنصر يمكنك الاستفادة منه في تعبيراتك.

في الفلاتر والتعابير، يمكنك استخدام auth كعنوان بديل ل request.auth.

يحتوي عنصر المصادقة على المعلومات التالية:

  • uid: معرّف مستخدم فريد تمّ تعيينه للمستخدم الذي قدّم الطلب.
  • token: خريطة للقيم التي جمعها Authentication

لمزيد من التفاصيل حول محتوى auth.token، يُرجى الاطّلاع على البيانات في الرموز المميّزة لمنح الأذونات.

عملية ربط this

يتم تقييم الربط this على أنّه الحقل الذي يتم إرفاق التوجيه @check به. في الحالة الأساسية، يمكنك تقييم نتائج الاستعلامات ذات القيمة الواحدة.

mutation UpdateMovieTitle($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query @redact {
    moviePermission( # Look up a join table called MoviePermission with a compound key.
      key: {movieId: $movieId, userId_expr: "auth.uid"}
    ) {
      # Check if the user has the editor role for the movie. `this` is the string value of `role`.
      # If the parent moviePermission is null, the @check will also fail automatically.
      role @check(expr: "this == 'editor'", message: "You must be an editor of this movie to update title")
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

إذا كان الحقل المعروض يظهر عدة مرات لأنّ أيّ سلف هو قائمة، يتم اختبار كل موضع باستخدام this مقيّد بكل قيمة.

بالنسبة إلى أي مسار معيّن، إذا كان أحد الأسلاف هو null أو []، لن يتم الوصول إلى الحقل وسيتم تخطّي تقييم CEL لهذا المسار. بعبارة أخرى، لا يتم التقييم إلا عندما يكون this هو null أو غير null، ولكن لا يتم أبدًا undefined.

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

mutation UpdateMovieTitle2($movieId: UUID!, $newTitle: String!) @auth(level: USER) @transaction {
  # Step 1: Query and check
  query {
    moviePermissions( # Now we query for a list of all matching MoviePermissions.
      where: {movieId: {eq: $movieId}, userId: {eq_expr: "auth.uid"}}
    # This time we execute the @check on the list, so `this` is the list of objects.
    # We can use the `.exists` macro to check if there is at least one matching entry.
    ) @check(expr: "this.exists(p, p.role == 'editor')", message: "You must be an editor of this movie to update title") {
      role
    }
  }
  # Step 2: Act
  movie_update(id: $movieId, data: {
    title: $newTitle
  })
}

بنية التعبيرات المعقدة

يمكنك كتابة عبارات أكثر تعقيدًا من خلال دمجها مع عاملَي التشغيل && و||.

mutation UpsertUser($username: String!) @auth(expr: "(auth != null) && (vars.username == 'joe')")

يوضّح القسم التالي جميع عوامل التشغيل المتاحة.

عوامل التشغيل وترتيبها في الأولوية

استخدِم الجدول التالي كمرجع لعوامل التشغيل وترتيبها المُعترَف به.

يتمّ منح تعبيرَين عشوائيَين a وb، وحقل f، وفهرس i.

المؤثِّر الوصف الربط
a[i] a() a.f الوصول إلى الفهرس والطلب والحقل من اليسار إلى اليمين
!a -a النفي الأحادي من اليمين إلى اليسار
a/b a%b a*b عوامل التشغيل المتعددة من اليسار إلى اليمين
a+b a-b عوامل التشغيل التي تُضيف قيمًا من اليسار إلى اليمين
a>b a>=b a<b a<=b عوامل التشغيل الارتباطية من اليسار إلى اليمين
a in b التواجد في قائمة أو خريطة من اليسار إلى اليمين
type(a) == t مقارنة النوع، حيث يمكن أن يكون t منطقيًا أو عددًا صحيحًا أو عددًا برمجيًا أو سلسلة أو قائمة أو خريطة أو طابعًا زمنيًا أو مدة من اليسار إلى اليمين
a==b a!=b عوامل تشغيل المقارنة من اليسار إلى اليمين
a && b "و" شَرطي من اليسار إلى اليمين
a || b أو شرطي من اليسار إلى اليمين
a ? true_value : false_value تعبير ثلاثي من اليسار إلى اليمين

البيانات في الرموز المميّزة للمصادقة

قد يحتوي عنصر auth.token على القيم التالية:

الحقل الوصف
email عنوان البريد الإلكتروني المرتبط بالحساب، إن توفّر
email_verified true إذا أثبت المستخدم أنّه يمكنه الوصول إلى عنوان email. يُثبت بعض مقدّمي الخدمة تلقائيًا ملكية عناوين البريد الإلكتروني التي يمتلكونها.
phone_number رقم الهاتف المرتبط بالحساب، إن توفّر
name الاسم المعروض للمستخدم، في حال ضبطه
sub معرّف Firebase الفريد للمستخدم وهذا الإجراء فريد ضمن المشروع.
firebase.identities قاموس بجميع الهويات المرتبطة بحساب هذا المستخدم يمكن أن تكون مفاتيح القاموس أيًا مما يلي: email أو phone أو google.com أو facebook.com أو github.com أو twitter.com. قيم القاموس هي صفائف من المعرّفات الفريدة لكل مقدّم هوية مرتبط بالحساب. على سبيل المثال، يحتوي auth.token.firebase.identities["google.com"][0] على أول معرّف مستخدم على Google مرتبط بالحساب.
firebase.sign_in_provider موفِّر تسجيل الدخول المستخدَم للحصول على هذا الرمز المميّز. يمكن أن تكون إحدى السلاسل التالية: custom أو password أو phone أو anonymous أو google.com أو facebook.com أو github.com أو twitter.com.
firebase.tenant معرّف المستأجر المرتبط بالحساب، إن توفّر. على سبيل المثال، tenant2-m6tyz

حقول إضافية في الرموز المميّزة لتعريف JWT

يمكنك أيضًا الوصول إلى حقول auth.token التالية:

مطالبات الرموز المميّزة المخصّصة
alg خوارزمية "RS256"
iss جهة الإصدار عنوان البريد الإلكتروني لحساب الخدمة الخاص بمشروعك
sub الموضوع عنوان البريد الإلكتروني لحساب الخدمة الخاص بمشروعك
aud الجمهور "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit"
iat وقت الإصدار الوقت الحالي، بالثواني منذ بداية حقبة UNIX
exp وقت انتهاء الصلاحية الوقت الذي تنتهي فيه صلاحية الرمز المميّز، بالثواني منذ بداية حقبة يونكس يمكن أن يليه المحتوى بعد 3600 ثانية كحد أقصى من iat.
ملاحظة: لا يتحكّم هذا الإعداد إلا في وقت انتهاء صلاحية الرمز المميّز المخصّص نفسه. ولكن بعد تسجيل دخول مستخدم باستخدام signInWithCustomToken()، سيظل مسجِّلاً الدخول إلى الجهاز إلى أن تصبح جلسته غير صالحة أو يسجّل المستخدم الخروج.
<claims> (اختياري) مطالبات مخصّصة اختيارية لتضمينها في الرمز المميّز، ويمكن الوصول إليها من خلال auth.token (أو request.auth.token) في التعبيرات. على سبيل المثال، إذا أنشأت مطالبة مخصّصة adminClaim، يمكنك الوصول إليها باستخدام auth.token.adminClaim.

ما هي الخطوات التالية؟