Firebase Data Connect امنیت سمت کلاینت قدرتمندی را با موارد زیر فراهم میکند:
- مجوزدهی کلاینت موبایل و وب
- کنترلهای مجوزدهی سطح پرسوجو و جهش انفرادی
- تأیید برنامه با Firebase App Check .
Data Connect این امنیت را با موارد زیر گسترش میدهد:
- مجوز سمت سرور
- امنیت کاربر در پروژه Firebase و Cloud SQL با IAM.
مجاز کردن کوئریها و جهشهای کلاینت
Data Connect کاملاً با Firebase Authentication یکپارچه شده است، بنابراین میتوانید از دادههای غنی در مورد کاربرانی که به دادههای شما دسترسی دارند (احراز هویت) در طراحی خود برای اینکه این کاربران به چه دادههایی میتوانند دسترسی داشته باشند (مجوز) استفاده کنید.
Data Connect یک دستورالعمل @auth برای پرسوجوها و جهشها ارائه میدهد که به شما امکان میدهد سطح احراز هویت مورد نیاز برای تأیید عملیات را تعیین کنید. این راهنما دستورالعمل @auth را با مثالهایی معرفی میکند .
علاوه بر این، Data Connect از اجرای کوئریهای جاسازیشده در جهشها پشتیبانی میکند، بنابراین میتوانید معیارهای مجوزدهی اضافی که در پایگاه داده خود ذخیره کردهاید را بازیابی کنید و از آن معیارها در دستورالعملهای @check برای تصمیمگیری در مورد مجاز بودن جهشهای محصور استفاده کنید. برای این مورد مجوزدهی، دستورالعمل @redact به شما امکان میدهد کنترل کنید که آیا نتایج کوئری به کلاینتها در پروتکل wire بازگردانده شود و کوئری جاسازیشده در 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 | این عملیات نمیتواند خارج از چارچوب SDK مدیریت اجرا شود. |
با استفاده از این سطوح دسترسی از پیش تعیینشده به عنوان نقطه شروع، میتوانید بررسیهای مجوز پیچیده و قوی را در دستورالعمل @auth با استفاده از فیلترهای where و عبارات زبان عبارات مشترک (CEL) که روی سرور ارزیابی میشوند، تعریف کنید.
از دستورالعمل @auth برای پیادهسازی سناریوهای رایج احراز هویت استفاده کنید
سطوح دسترسی از پیش تعیینشده، نقطه شروع مجوزدهی هستند.
سطح دسترسی USER پرکاربردترین سطح پایه برای شروع است.
دسترسی کاملاً ایمن بر اساس سطح USER به علاوه فیلترها و عباراتی که ویژگیهای کاربر، ویژگیهای منابع، نقشها و سایر بررسیها را بررسی میکنند، ایجاد خواهد شد. سطوح USER_ANON و USER_EMAIL_VERIFIED انواع مختلفی از حالت USER هستند.
سینتکس عبارت به شما امکان میدهد دادهها را با استفاده از یک شیء auth که نشاندهنده دادههای احراز هویت ارسال شده با عملیات است، ارزیابی کنید، چه دادههای استاندارد در توکنهای 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")
}
منابع متعلق به کاربر
فایربیس توصیه میکند که فیلترها و عباراتی بنویسید که مالکیت کاربر بر یک منبع، در موارد زیر، مالکیت Posts ، را آزمایش کنند.
در مثالهای زیر، دادههای توکنهای احراز هویت با استفاده از عبارات خوانده و مقایسه میشوند. الگوی معمول استفاده از عباراتی مانند where: {authorUid: {eq_expr: "auth.uid"}} برای مقایسه یک authorUid ذخیره شده با auth.uid (شناسه کاربر) ارسال شده در توکن احراز هویت است.
ایجاد کردن
این رویه مجوزدهی با اضافه کردن auth.uid از توکن auth به هر 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
}
}
فیلتر بر اساس سفارش + محدودیت
یا دوباره، ممکن است در رکوردهای Post ، visibility تنظیم کرده باشید تا مشخص شود که آنها برای کاربران "حرفهای" در دسترس هستند، اما برای پیشنمایش یا فهرست اولیه دادهها، تعداد رکوردهای برگردانده شده را بیشتر محدود کنید.
# 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 نیز استفاده میکند، پرسوجو بررسی میکند که آیا درخواستکننده نقش "admin" مناسبی برای مشاهده کاربرانی که میتوانند یک فیلم را ویرایش کنند، دارد یا خیر.
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 تأیید شدهاند، دسترسی میدهید.
# 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 | این عملیات میتواند توسط هر کسی با یا بدون احراز هویت انجام شود. ملاحظات: دادهها میتوانند توسط هر کاربری خوانده یا تغییر داده شوند. فایربیس این سطح از مجوز را برای دادههای قابل مرور عمومی مانند فهرست محصولات یا رسانهها توصیه میکند. به مثالها و جایگزینهای بهترین شیوهها مراجعه کنید. معادل @auth(expr: "true") است.فیلترها و عبارات @auth نمیتوانند در ترکیب با این سطح دسترسی استفاده شوند. هرگونه عبارت از این دست با خطای درخواست بد ۴۰۰ مواجه خواهد شد. |
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)
همانطور که در مثالهای دیگر این راهنما نشان داده شده است، میتوانید و باید از عبارات تعریف شده در زبان عبارات مشترک (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:) - انتساب با استفاده از عبارات سرور،
<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 کاربرانی را که درخواست دسترسی به دادههای شما را دارند شناسایی میکند و آن اطلاعات را به عنوان یک متغیر (binding) در اختیار شما قرار میدهد که میتوانید در عبارات خود بر اساس آن عمل کنید.
در فیلترها و عبارات خود، میتوانید auth به عنوان نام مستعار برای request.auth استفاده کنید.
اتصال auth شامل اطلاعات زیر است:
-
uid: یک شناسه کاربری منحصر به فرد که به کاربر درخواست کننده اختصاص داده میشود. -
token: نقشهای از مقادیر جمعآوریشده توسط Authentication .
برای جزئیات بیشتر در مورد محتویات auth.token به دادههای موجود در توکنهای auth مراجعه کنید.
اتصال response
اتصال response شامل دادههایی است که توسط سرور در پاسخ به یک پرسوجو یا جهش ، همزمان با جمعآوری دادهها، جمعآوری میشوند .
با پیشرفت عملیات، و با تکمیل موفقیتآمیز هر مرحله، response شامل دادههای پاسخ از مراحل تکمیلشده با موفقیت است.
اتصال response بر اساس شکل عملیات مرتبط با آن، شامل (چندین) فیلد تو در تو و (در صورت وجود) پرسوجوهای تعبیهشده، ساختار یافته است.
توجه داشته باشید که وقتی به دادههای پاسخ پرسوجوی جاسازیشده دسترسی پیدا میکنید، فیلدها میتوانند شامل هر نوع دادهای باشند، بسته به دادههای درخواستشده در پرسوجوی جاسازیشده؛ وقتی به دادههای برگرداندهشده توسط فیلدهای جهش مانند _insert s و _delete s دسترسی پیدا میکنید، ممکن است حاوی کلیدهای UUID، تعداد حذفها، و مقادیر تهی باشند (به مرجع جهشها مراجعه کنید).
برای مثال:
- در یک جهش که شامل یک کوئری جاسازیشده است، اتصال
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 به فیلدی که دستورالعمل @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() af | فهرست، فراخوانی، دسترسی به فیلد | چپ به راست |
!a -a | نفی تکوجهی | راست به چپ |
a/ba%ba*b | عملگرهای ضربی | چپ به راست |
a+b ab | عملگرهای افزایشی | چپ به راست |
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==ba!=b | عملگرهای مقایسهای | چپ به راست |
a && b | شرطی و | چپ به راست |
a || b | یا شرطی | چپ به راست |
a ? true_value : false_value | عبارت سهتایی | چپ به راست |
دادهها در توکنهای احراز هویت
شیء auth.token میتواند شامل مقادیر زیر باشد:
| میدان | توضیحات |
|---|---|
email | آدرس ایمیل مرتبط با حساب، در صورت وجود. |
email_verified | اگر کاربر تأیید کرده باشد که به آدرس email دسترسی دارد، true برمیگرداند. برخی از ارائهدهندگان بهطور خودکار آدرسهای ایمیل متعلق به خود را تأیید میکنند. |
phone_number | شماره تلفن مرتبط با حساب، در صورت وجود. |
name | نام نمایشی کاربر، در صورت تنظیم. |
sub | شناسه کاربری فایربیس کاربر. این شناسه در یک پروژه منحصر به فرد است. |
firebase.identities | دیکشنری تمام هویتهایی که با حساب کاربری این کاربر مرتبط هستند. کلیدهای دیکشنری میتوانند هر یک از موارد زیر باشند: email ، phone ، google.com ، facebook.com ، github.com ، twitter.com . مقادیر دیکشنری آرایههایی از شناسههای منحصر به فرد برای هر ارائهدهنده هویت مرتبط با حساب هستند. به عنوان مثال، auth.token.firebase.identities["google.com"][0] شامل اولین شناسه کاربری گوگل مرتبط با حساب است. |
firebase.sign_in_provider | ارائهدهندهی ورود به سیستم که برای دریافت این توکن استفاده شده است. میتواند یکی از رشتههای زیر باشد: custom ، password ، phone ، anonymous ، google.com ، facebook.com ، github.com ، twitter.com . |
firebase.tenant | tenantId مرتبط با حساب، در صورت وجود. برای مثال، tenant2-m6tyz |
فیلدهای اضافی در توکنهای JWT ID
همچنین میتوانید به فیلدهای auth.token زیر دسترسی داشته باشید:
| ادعاهای توکن سفارشی | ||
|---|---|---|
alg | الگوریتم | "RS256" |
iss | صادرکننده | آدرس ایمیل حساب کاربری سرویس پروژه شما |
sub | موضوع | آدرس ایمیل حساب کاربری سرویس پروژه شما |
aud | مخاطب | "https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit" |
iat | صادر شده در زمان | زمان فعلی، بر حسب ثانیه از زمان آغاز یونیکس |
exp | زمان انقضا | مدت زمانی که توکن از زمان آغاز یونیکس (UNIX epoch) منقضی میشود (بر حسب ثانیه). این زمان میتواند حداکثر ۳۶۰۰ ثانیه دیرتر از iat باشد.توجه: این فقط زمانی را کنترل میکند که خود توکن سفارشی منقضی میشود. اما وقتی کاربری را با استفاده از signInWithCustomToken() وارد سیستم میکنید، او تا زمانی که جلسهاش نامعتبر شود یا کاربر از سیستم خارج شود، در دستگاه وارد سیستم باقی میماند. |
<claims> (اختیاری) | ادعاهای سفارشی اختیاری که باید در توکن گنجانده شوند، که از طریق auth.token (یا request.auth.token ) در عبارات قابل دسترسی هستند. برای مثال، اگر یک ادعای سفارشی adminClaim ایجاد کنید، میتوانید با auth.token.adminClaim به آن دسترسی پیدا کنید. | |
بعدش چی؟
- Firebase Data Connect یک SDK مدیریتی ارائه میدهد که به شما امکان میدهد کوئریها و جهشها را از محیطهای دارای امتیاز بالا انجام دهید.
- برای کسب اطلاعات بیشتر در مورد امنیت IAM به راهنمای مدیریت سرویسها و پایگاههای داده مراجعه کنید.