شرایط نوشتن برای قوانین امنیتی Cloud Firestore

این راهنما مبتنی بر راهنمای قوانین امنیتی ساختاری است تا نحوه افزودن شرایط به Cloud Firestore Security Rules خود را نشان دهد. اگر با اصول اولیه Cloud Firestore Security Rules آشنا نیستید، راهنمای شروع کار را ببینید.

بلوک اصلی ساختمان Cloud Firestore Security Rules این شرایط است. شرط یک عبارت بولی است که تعیین می کند آیا یک عملیات خاص باید مجاز یا رد شود. از قوانین امنیتی برای نوشتن شرایطی استفاده کنید که احراز هویت کاربر را بررسی می کند، داده های دریافتی را تأیید می کند یا حتی به سایر بخش های پایگاه داده شما دسترسی پیدا می کند.

احراز هویت

یکی از رایج ترین الگوهای قوانین امنیتی، کنترل دسترسی بر اساس وضعیت احراز هویت کاربر است. برای مثال، ممکن است برنامه شما بخواهد فقط به کاربرانی که وارد سیستم شده اند اجازه نوشتن داده را بدهد:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to access documents in the "cities" collection
    // only if they are authenticated.
    match /cities/{city} {
      allow read, write: if request.auth != null;
    }
  }
}

الگوی رایج دیگر این است که مطمئن شوید کاربران فقط می توانند داده های خود را بخوانند و بنویسند:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure the uid of the requesting user matches name of the user
    // document. The wildcard expression {userId} makes the userId variable
    // available in rules.
    match /users/{userId} {
      allow read, update, delete: if request.auth != null && request.auth.uid == userId;
      allow create: if request.auth != null;
    }
  }
}

اگر برنامه شما از Firebase Authentication یا Google Cloud Identity Platform استفاده می کند، متغیر request.auth حاوی اطلاعات احراز هویت برای مشتری درخواست کننده داده است. برای اطلاعات بیشتر در مورد request.auth ، به مستندات مرجع مراجعه کنید.

اعتبار سنجی داده ها

بسیاری از برنامه ها اطلاعات کنترل دسترسی را به صورت فیلدهایی روی اسناد در پایگاه داده ذخیره می کنند. Cloud Firestore Security Rules می توانند به صورت پویا دسترسی را بر اساس داده های سند مجاز یا رد کنند:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

متغیر resource به سند درخواستی اشاره دارد و resource.data نقشه ای از تمام فیلدها و مقادیر ذخیره شده در سند است. برای اطلاعات بیشتر در مورد متغیر resource ، به مستندات مرجع مراجعه کنید.

هنگام نوشتن داده، ممکن است بخواهید داده های دریافتی را با داده های موجود مقایسه کنید. در این مورد، اگر مجموعه قوانین شما اجازه نوشتن در انتظار را بدهد، متغیر request.resource شامل وضعیت آینده سند است. برای عملیات update که فقط زیرمجموعه‌ای از فیلدهای سند را تغییر می‌دهند، متغیر request.resource شامل وضعیت سند در انتظار پس از عملیات خواهد بود. برای جلوگیری از به‌روزرسانی‌های ناخواسته یا متناقض داده‌ها، می‌توانید مقادیر فیلد را در request.resource بررسی کنید:

service cloud.firestore {
  match /databases/{database}/documents {
    // Make sure all cities have a positive population and
    // the name is not changed
    match /cities/{city} {
      allow update: if request.resource.data.population > 0
                    && request.resource.data.name == resource.data.name;
    }
  }
}

دسترسی به اسناد دیگر

با استفاده از توابع get() و exists() ، قوانین امنیتی شما می توانند درخواست های دریافتی را در مقابل سایر اسناد موجود در پایگاه داده ارزیابی کنند. توابع get() و exists() هر دو انتظار مسیرهای سند کاملاً مشخص را دارند. هنگام استفاده از متغیرها برای ساخت مسیرهایی برای get() و exists() ، باید به طور صریح از متغیرها با استفاده از نحو $(variable) فرار کنید.

در مثال زیر، متغیر database توسط عبارت match match /databases/{database}/documents گرفته شده و برای تشکیل مسیر استفاده می شود:

service cloud.firestore {
  match /databases/{database}/documents {
    match /cities/{city} {
      // Make sure a 'users' document exists for the requesting user before
      // allowing any writes to the 'cities' collection
      allow create: if request.auth != null && exists(/databases/$(database)/documents/users/$(request.auth.uid));

      // Allow the user to delete cities if their user document has the
      // 'admin' field set to 'true'
      allow delete: if request.auth != null && get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
    }
  }
}

برای نوشتن، می‌توانید از تابع getAfter() برای دسترسی به وضعیت یک سند پس از اتمام تراکنش یا دسته‌ای از نوشتن‌ها، اما قبل از انجام تراکنش یا دسته‌ای استفاده کنید. مانند get() ، تابع getAfter() یک مسیر سند کاملاً مشخص را طی می کند. می‌توانید از getAfter() برای تعریف مجموعه‌هایی از نوشته‌هایی که باید با هم به عنوان یک تراکنش یا دسته‌ای انجام شوند، استفاده کنید.

دسترسی به محدودیت تماس

محدودیتی در تماس های دسترسی به اسناد به ازای ارزیابی مجموعه قوانین وجود دارد:

  • 10 برای درخواست های تک سند و درخواست های پرس و جو.
  • 20 برای خواندن چند سند، تراکنش ها و نوشتن دسته ای. محدودیت قبلی 10 نیز برای هر عملیات اعمال می شود.

    برای مثال، تصور کنید یک درخواست نوشتن دسته‌ای با 3 عملیات نوشتن ایجاد می‌کنید و قوانین امنیتی شما از 2 تماس دسترسی به سند برای تأیید اعتبار هر نوشتن استفاده می‌کنند. در این حالت، هر نوشتن از 2 تماس از 10 تماس دسترسی خود استفاده می کند و درخواست نوشتن دسته ای از 6 تماس از 20 تماس دسترسی خود استفاده می کند.

تجاوز از هر یک از محدودیت ها منجر به خطای رد مجوز می شود. برخی از تماس‌های دسترسی به اسناد ممکن است در حافظه پنهان باشند، و تماس‌های ذخیره‌شده در حافظه پنهان در محدودیت‌ها حساب نمی‌شوند.

برای توضیح دقیق چگونگی تأثیر این محدودیت ها بر تراکنش ها و نوشته های دسته ای، به راهنمای ایمن سازی عملیات اتمی مراجعه کنید.

به تماس ها و قیمت ها دسترسی داشته باشید

با استفاده از این توابع، عملیات خواندن در پایگاه داده شما انجام می شود، به این معنی که حتی اگر قوانین شما درخواست را رد کند، برای خواندن اسناد صورتحساب دریافت خواهید کرد. برای اطلاعات بیشتر درباره صورت‌حساب، قیمت‌گذاری Cloud Firestore را ببینید.

توابع سفارشی

همانطور که قوانین امنیتی شما پیچیده‌تر می‌شوند، ممکن است بخواهید مجموعه‌ای از شرایط را در توابعی بپیچید که بتوانید در سراسر مجموعه قوانین خود دوباره از آنها استفاده کنید. قوانین امنیتی از توابع سفارشی پشتیبانی می کنند. سینتکس توابع سفارشی کمی شبیه جاوا اسکریپت است، اما توابع قوانین امنیتی در یک زبان مخصوص دامنه نوشته شده اند که دارای محدودیت های مهمی است:

  • توابع می توانند تنها حاوی یک عبارت return باشند. آنها نمی توانند حاوی هیچ منطق اضافی باشند. به عنوان مثال، آنها نمی توانند حلقه ها را اجرا کنند یا خدمات خارجی را فراخوانی کنند.
  • توابع می توانند به طور خودکار به توابع و متغیرها از محدوده ای که در آن تعریف شده اند دسترسی داشته باشند. به عنوان مثال، یک تابع تعریف شده در محدوده service cloud.firestore به متغیر resource و توابع داخلی مانند get() و exists() دسترسی دارد.
  • توابع ممکن است توابع دیگر را فراخوانی کنند اما ممکن است تکرار نشوند. کل عمق پشته تماس به 10 محدود شده است.
  • در قوانین نسخه v2 ، توابع می توانند متغیرها را با استفاده از کلمه کلیدی let تعریف کنند. توابع می توانند حداکثر 10 اتصال let داشته باشند، اما باید با یک عبارت return پایان یابند.

یک تابع با کلمه کلیدی function تعریف می شود و صفر یا بیشتر آرگومان می گیرد. برای مثال، ممکن است بخواهید دو نوع شرط استفاده شده در مثال های بالا را در یک تابع واحد ترکیب کنید:

service cloud.firestore {
  match /databases/{database}/documents {
    // True if the user is signed in or the requested data is 'public'
    function signedInOrPublic() {
      return request.auth.uid != null || resource.data.visibility == 'public';
    }

    match /cities/{city} {
      allow read, write: if signedInOrPublic();
    }

    match /users/{user} {
      allow read, write: if signedInOrPublic();
    }
  }
}

استفاده از توابع در قوانین امنیتی شما با افزایش پیچیدگی قوانین شما، آنها را قابل نگهداری تر می کند.

قوانین فیلتر نیستند

هنگامی که داده های خود را ایمن کردید و شروع به نوشتن پرس و جو کردید، به خاطر داشته باشید که قوانین امنیتی فیلتر نیستند. شما نمی توانید برای همه اسناد یک مجموعه یک پرس و جو بنویسید و انتظار داشته باشید که Cloud Firestore فقط اسنادی را که کلاینت فعلی اجازه دسترسی به آنها را دارد بازگرداند.

به عنوان مثال، قانون امنیتی زیر را در نظر بگیرید:

service cloud.firestore {
  match /databases/{database}/documents {
    // Allow the user to read data if the document has the 'visibility'
    // field set to 'public'
    match /cities/{city} {
      allow read: if resource.data.visibility == 'public';
    }
  }
}

Denied : این قانون پرس و جوی زیر را رد می کند زیرا مجموعه نتایج می تواند شامل اسنادی باشد که در آن visibility public نیست:

وب
db.collection("cities").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
    });
});

مجاز : این قانون پرس و جوی زیر را اجازه می دهد زیرا عبارت where("visibility", "==", "public") تضمین می کند که مجموعه نتایج شرایط قانون را برآورده می کند:

وب
db.collection("cities").where("visibility", "==", "public").get()
    .then(function(querySnapshot) {
        querySnapshot.forEach(function(doc) {
            console.log(doc.id, " => ", doc.data());
        });
    });

قوانین امنیتی Cloud Firestore هر پرس و جو را در برابر نتایج احتمالی آن ارزیابی می کند و اگر بتواند سندی را که مشتری اجازه خواندن آن را ندارد بازگرداند، درخواست را با شکست مواجه می کند. پرس و جوها باید از محدودیت های تعیین شده توسط قوانین امنیتی شما پیروی کنند. برای اطلاعات بیشتر در مورد قوانین امنیتی و پرس و جوها، به جستجوی ایمن داده مراجعه کنید.

مراحل بعدی