Firebase Data Connect מספקת אבטחה חזקה בצד הלקוח באמצעות:
- הרשאה באפליקציות לנייד ובדפדפני אינטרנט
- אמצעי בקרה של הרשאות ברמת השאילתה והמוטציה
- אימות אפליקציות באמצעות Firebase App Check.
Data Connect מרחיב את האבטחה הזו באמצעות:
- הרשאה בצד השרת
- אבטחת משתמשי Cloud SQL ופרויקט Firebase באמצעות IAM.
אישור של שאילתות ומוטציות של לקוחות
Data Connect משולב באופן מלא עם Firebase Authentication, כך שתוכלו להשתמש בנתונים מפורטים על משתמשים שניגשים לנתונים שלכם (אימות) בתכנון שלכם לגבי הנתונים שהמשתמשים האלה יכולים לגשת אליהם (הרשאה).
Data Connect מספק הנחיית @auth
לשאילתות ולשינויים, שמאפשרת להגדיר את רמת האימות שנדרשת כדי לאשר את הפעולה. במדריך הזה מוצגת ההנחיה @auth
, עם דוגמאות.
בנוסף, Data Connect תומך בהרצת שאילתות שמוטמעות במוטציות, כך שאפשר לאחזר קריטריונים נוספים להרשאה ששמרתם במסד הנתונים, ולהשתמש בקריטריונים האלה בהוראות של @check
כדי להחליט אם המוטציות המצורפות מורשות. במקרה הזה של הרשאה, ההנחיה @redact
מאפשרת לכם לקבוע אם תוצאות השאילתה יוחזרו ללקוחות בפרוטוקול התקשורת, והאם השאילתה המוטמעת תושמט בערכות ה-SDK שנוצרו. כאן אפשר לקרוא מבוא להנחיות האלה, עם דוגמאות.
הסבר על ההנחיה @auth
אפשר להגדיר פרמטרים להוראה @auth
כדי לפעול לפי אחת מכמה רמות גישה מוגדרות מראש שמתאימות לתרחישי גישה נפוצים רבים. הרמות האלה נעות בין PUBLIC
(שמאפשרת שאילתות ושינויים מכל הלקוחות ללא אימות מכל סוג) לבין NO_ACCESS
(שלא מאפשרת שאילתות ושינויים מחוץ לסביבות שרתים עם הרשאות מיוחדות באמצעות Firebase Admin SDK). כל אחת מהרמות האלה קשורה לתהליכי אימות שמסופקים על ידי Firebase Authentication.
רמה | הגדרה |
---|---|
PUBLIC |
כל אחד יכול לבצע את הפעולה, עם או בלי אימות. |
PUBLIC |
כל אחד יכול לבצע את הפעולה, עם או בלי אימות. |
USER_ANON |
כל משתמש מזוהה, כולל משתמשים שהתחברו באופן אנונימי באמצעות Firebase Authentication, מורשה לבצע את השאילתה או את השינוי. |
USER |
כל משתמש שהתחבר באמצעות Firebase Authentication מורשה לבצע את השאילתה או את השינוי, למעט משתמשים שהתחברו באופן אנונימי. |
USER_EMAIL_VERIFIED |
כל משתמש שהתחבר באמצעות Firebase Authentication עם כתובת אימייל מאומתת מורשה לבצע את השאילתה או את השינוי. |
NO_ACCESS |
אי אפשר לבצע את הפעולה הזו מחוץ להקשר של Admin SDK. |
אפשר להשתמש ברמות הגישה המוגדרות מראש האלה כנקודת התחלה להגדרת בדיקות הרשאה מורכבות וחזקות בהנחיה @auth
באמצעות מסנני where
וביטויי Common Expression Language (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
}
}
סינון לפי תלונות של משתמשים
נניח שהגדרתם הצהרות מותאמות אישית של משתמשים שמועברות באסימוני אימות כדי לזהות משתמשים בתוכנית 'מקצועית' באפליקציה שלכם, שמסומנים בשדה 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
כדי לחפש נתוני הרשאה
תרחיש נפוץ לשימוש בהרשאות כולל אחסון של תפקידי הרשאה בהתאמה אישית במסד הנתונים, למשל בטבלת הרשאות מיוחדת, ושימוש בתפקידים האלה כדי לאשר שינויים ליצירה, לעדכון או למחיקה של נתונים.
באמצעות חיפושים של נתוני הרשאות, אפשר לשלוח שאילתות לגבי תפקידים על סמך מזהה משתמש ולהשתמש בביטויי CEL כדי להחליט אם השינוי מורשה. לדוגמה, יכול להיות שתרצו לכתוב UpdateMovieTitle
מוטציה שתאפשר ללקוח מורשה לעדכן את שמות הסרטים.
בהמשך הדיון הזה, נניח שמסד הנתונים של אפליקציית הביקורות על סרטים מאחסן תפקיד הרשאה בטבלה MoviePermission
.
# MoviePermission
# Suppose a user has an authorization role with respect to records in the Movie table
type MoviePermission @table(key: ["movie", "user"]) {
movie: Movie! # implies another field: movieId: UUID!
user: User!
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
})
}
שימוש בשאילתות
חיפושים של נתוני הרשאה שימושיים גם להגבלת שאילתות על סמך תפקידים או הגבלות אחרות.
בדוגמה הבאה, שגם משתמשת בסכימה MoviePermission
, השאילתה בודקת אם למבקש יש תפקיד מתאים של 'אדמין' כדי להציג משתמשים שיכולים לערוך סרט.
query GetMovieEditors($movieId: UUID!) @auth(level: PUBLIC) {
moviePermission(key: { movieId: $movieId, userId_expr: "auth.uid" }) @redact {
role @check(expr: "this == 'admin'", message: "You must be an admin to view all editors of a movie.")
}
moviePermissions(where: { movieId: { eq: $movieId }, role: { eq: "editor" } }) {
user {
id
username
}
}
}
דפוסי התנהגות שצריך להימנע מהם בהרשאות
בקטע הקודם מוסבר על דפוסים שכדאי לפעול לפיהם כשמשתמשים בהנחיית @auth
.
חשוב גם להכיר אנטי-תבניות חשובות שכדאי להימנע מהן.
הימנעות מהעברת מזהים של מאפייני משתמשים ופרמטרים של טוקן אימות בארגומנטים של שאילתות ומוטציות
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 Authentication.
# Antipattern!
# Anyone can claim an email address during sign-in
mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email.endsWith('@example.com')") {
post_insert(data: {
# set the author's uid to the current user uid
authorUid_expr: "auth.uid"
text: $text
visibility: $visibility
})
}
מומלץ גם לבדוק את auth.token.email_verified
mutation CreatePost($text: String!, $visibility: String) @auth(expr: "auth.token.email_verified && auth.token.email.endsWith('@example.com')") {
post_insert(data: {
# set the author's uid to the current user uid
authorUid_expr: "auth.uid"
text: $text
visibility: $visibility
})
}
ביקורת על הרשאות באמצעות Firebase CLI
כמו שצוין קודם, רמות גישה מוגדרות מראש כמו PUBLIC
ו-USER
הן נקודת ההתחלה להרשאות חזקות, וצריך להשתמש בהן עם בדיקות הרשאה נוספות שמבוססות על מסננים וביטויים.
אין להשתמש בהם לבד בלי לבחון בקפידה את תרחיש השימוש.
Data Connect עוזר לכם לבדוק את אסטרטגיית ההרשאות שלכם על ידי ניתוח קוד המחבר כשאתם מבצעים פריסה לשרת באמצעות firebase deploy
מ-Firebase CLI. אתם יכולים להשתמש בביקורת הזו כדי לבדוק את בסיס הקוד.
כשפורסים את המחברים, ה-CLI יציג הערכות של קוד הפעולה הקיים, ששונה וחדש במחבר.
בפעולות חדשות ופעולות ששונו, ה-CLI מציג אזהרות ומבקש אישור כשמשתמשים ברמות גישה מסוימות בפעולות החדשות, או כשמשנים פעולות קיימות כדי להשתמש ברמות הגישה האלה.
אזהרות והנחיות תמיד יופיעו במקרים הבאים:
PUBLIC
בנוסף, אם לא מוסיפים מסננים לרמות הגישה הבאות באמצעות auth.uid
, יוצגו אזהרות והנחיות:
USER
USER_ANON
USER_EMAIL_VERIFIED
הסתרת אזהרות לגבי פעולות לא מאובטחות באמצעות הארגומנט @auth(insecureReason:)
במקרים רבים, תגיעו למסקנה ששימוש ברמות הגישה PUBLIC
ו-USER*
מתאים מאוד.
אם המחבר מכיל הרבה פעולות, יכול להיות שתרצו פלט ברור יותר ורלוונטי יותר של ביקורת אבטחה, שבו לא נכללות פעולות שבדרך כלל מפעילות אזהרה, אבל אתם יודעים שיש להן את רמת הגישה הנכונה.
אפשר להשבית את האזהרות לפעולות כאלה באמצעות @auth(insecureReason:)
.
לדוגמה:
query listItem @auth(level: PUBLIC, insecureReason: "This operation is safe to expose to the public.")
{
items {
id name
}
}
שימוש ב-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, ולא מתבצעות בדיקות אחרות, למשל, אם הנתונים שייכים למשתמש. אפשר לעיין בדוגמאות ובחלופות לשיטות מומלצות. מכיוון שבתהליכי התחברות אנונימיים מונפק uid , הרמה USER_ANON שווה ל-@auth(expr: "auth.uid != nil") Authentication
|
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 |
אי אפשר לבצע את הפעולה הזו מחוץ להקשר של Admin SDK.
שווה ערך ל- @auth(expr: "false") |
הפניה ל-CEL עבור @auth(expr)
כפי שמוצג בדוגמאות במקומות אחרים במדריך הזה, אפשר ומומלץ להשתמש בביטויים שמוגדרים ב-Common Expression Language (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)")
נתונים שזמינים לביטויים: בקשה, תגובה, this
אתם משתמשים בנתונים כדי:
- הערכה באמצעות ביטויי CEL בהנחיות
@auth(expr:)
ו-@check(expr:)
- הקצאה באמצעות ביטויי שרת,
<field>_expr
.
שני ביטויי ה-CEL, @auth(expr:)
ו-@check(expr:)
, יכולים להעריך את הדברים הבאים:
request.operationName
vars
(כתובת אימייל חלופית שלrequest.variables
)auth
(כתובת אימייל חלופית שלrequest.auth
)
במוטציות, אפשר לגשת לתוכן של:
-
response
(כדי לבדוק תוצאות חלקיות בלוגיקה מרובת שלבים)
בנוסף, ביטויים של @check(expr:)
יכולים להעריך:
this
(הערך של השדה הנוכחי)-
response
(כדי לבדוק תוצאות חלקיות בלוגיקה מרובת שלבים)
הקישור request.operationName
הקישור request.operarationName
מאחסן את סוג הפעולה, שאלה או שינוי.
הקישור vars
(request.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
(request.auth)
Authentication מזהה משתמשים שמבקשים גישה לנתונים שלכם ומספק את המידע הזה כקישור שאפשר להשתמש בו בביטויים.
במסננים ובביטויים, אפשר להשתמש ב-auth
ככינוי ל-request.auth
.
הקישור של ההרשאה מכיל את הפרטים הבאים:
-
uid
: מזהה משתמש ייחודי שהוקצה למשתמש ששלח את הבקשה. -
token
: מפה של ערכים שנאספו על ידי Authentication.
לפרטים נוספים על התוכן של auth.token
אפשר לעיין במאמר נתונים באסימוני אימות
הקישור response
הקישור response
מכיל את הנתונים שהשרת מרכיב בתגובה לשאילתה או לשינוי בזמן שהנתונים האלה מורכבים.
במהלך הפעולה, אחרי שכל שלב מסתיים בהצלחה, response
מכיל נתוני תגובה משלבים שהושלמו בהצלחה.
הקישור response
בנוי בהתאם לצורה של הפעולה המשויכת, כולל שדות מוטמעים (מרובים) ושאילתות מוטמעות (אם רלוונטי).
שימו לב: כשניגשים לנתוני תגובה של שאילתות מוטמעות, השדות יכולים להכיל כל סוג נתונים, בהתאם לנתונים שהתבקשו בשאילתה המוטמעת. כשניגשים לנתונים שמוחזרים על ידי שדות מוטציות כמו _insert
s ו-_delete
s, הם עשויים להכיל מפתחות UUID, מספר מחיקות, ערכי null (ראו את ההפניה למוטציות).
לדוגמה:
- במוטציה שמכילה שאילתה מוטמעת, הכריכה
response
מכילה נתוני חיפוש ב-response.query.<fieldName>.<fieldName>....
, ובמקרה הזה, ב-response.query.todoList
וב-response.query.todoList.priority
.
mutation CheckTodoPriority(
$uniqueListName: String!
) {
# This query is identified as `response.query`
query @check(expr: "response.query.todoList.priority == 'high'", message: "This list is not for high priority items!") {
# This field is identified as `response.query.todoList`
todoList(where: { name: $uniqueListName }) {
# This field is identified as `response.query.todoList.priority`
priority
}
}
}
- במוטציה מרובת שלבים, למשל עם כמה שדות
_insert
, הכריכהresponse
מכילה נתונים חלקיים ב-response.<fieldName>.<fieldName>....
, ובמקרה הזה,response.todoList_insert.id
.
mutation CreateTodoListWithFirstItem(
$listName: String!,
$itemContent: String!
) @transaction {
# Step 1
todoList_insert(data: {
id_expr: "uuidV4()",
name: $listName,
})
# Step 2:
todo_insert(data: {
listId_expr: "response.todoList_insert.id" # <-- Grab the newly generated ID from the partial response so far.
content: $itemContent,
})
}
הקישור this
הקישור this
מוערך לשדה שאליו מצורף ה-directive @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 יכול להיות bool, int, float, number, string, list, map, timestamp או duration |
משמאל לימין |
a==b a!=b |
אופרטורים להשוואה | משמאל לימין |
a && b |
AND מותנה | משמאל לימין |
a || b |
OR מותנה | משמאל לימין |
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 epoch) |
exp |
מועד תפוגה |
הזמן שבו יפוג תוקף האסימון, בשניות מאז ראשית זמן יוניקס (Unix epoch). התאריך יכול להיות עד 3,600 שניות אחרי התאריך iat .
הערה: ההגדרה הזו קובעת רק את הזמן שבו יפוג התוקף של האסימון המותאם אישית עצמו. אבל אחרי שמתחברים לחשבון של משתמש באמצעות signInWithCustomToken() , הוא יישאר מחובר למכשיר עד שהסשן שלו יבוטל או עד שהמשתמש יתנתק.
|
ֶ<claims> (אופציונלי) |
טענות אופציונליות בהתאמה אישית שייכללו בטוקן, שאפשר לגשת אליהן דרך auth.token (או request.auth.token ) בביטויים. לדוגמה, אם יוצרים טענה מותאמת אישית
adminClaim , אפשר לגשת אליה באמצעות
auth.token.adminClaim .
|
מה השלב הבא?
- Firebase Data Connect מספק Admin SDK שמאפשר לבצע שאילתות ושינויים מסביבות עם הרשאות.
- מידע על אבטחת IAM מופיע במדריך לניהול שירותים ומסדי נתונים.