اتصال امن داده با مجوز و تأیید، اتصال داده امن با مجوز و تأیید، اتصال داده امن با مجوز و تأیید

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 به آن دسترسی پیدا کنید.

بعدش چی؟