साथ मिलकर काम करने की सुविधा देने वाले कई ऐप्लिकेशन, उपयोगकर्ताओं को अनुमतियों के सेट के आधार पर अलग-अलग तरह का डेटा पढ़ने और लिखने की अनुमति देते हैं. उदाहरण के लिए, दस्तावेज़ में बदलाव करने वाले किसी ऐप्लिकेशन में, उपयोगकर्ता कुछ लोगों को अपने दस्तावेज़ पढ़ने और उनमें बदलाव करने की अनुमति दे सकते हैं. साथ ही, वे अनचाहे ऐक्सेस को ब्लॉक कर सकते हैं.
समाधान: रोल के हिसाब से ऐक्सेस देना
अपने ऐप्लिकेशन में भूमिका के आधार पर ऐक्सेस कंट्रोल लागू करने के लिए, Cloud Firestore के डेटा मॉडल के साथ-साथ कस्टम सुरक्षा नियमों का इस्तेमाल किया जा सकता है.
मान लें कि आपको साथ मिलकर लिखने वाला कोई ऐप्लिकेशन बनाना है. इसमें उपयोगकर्ताओं को "स्टोरी" और "टिप्पणियां" बनाने की सुविधा मिलती है. इसके लिए, सुरक्षा से जुड़ी ये ज़रूरी शर्तें पूरी करनी होंगी:
- हर कहानी का एक मालिक होता है. इसे "लेखकों", "टिप्पणी करने वालों", और "पढ़ने वालों" के साथ शेयर किया जा सकता है.
- पढ़ने वाले लोग सिर्फ़ कहानियां और टिप्पणियां देख सकते हैं. ये किसी भी चीज़ में बदलाव नहीं कर सकते.
- टिप्पणी करने वाले लोगों के पास, पढ़ने वालों के सभी ऐक्सेस होते हैं. साथ ही, वे किसी कहानी पर टिप्पणियां भी जोड़ सकते हैं.
- लेखकों के पास टिप्पणी करने वालों के सभी ऐक्सेस होते हैं. साथ ही, वे स्टोरी के कॉन्टेंट में बदलाव भी कर सकते हैं.
- मालिक, स्टोरी के किसी भी हिस्से में बदलाव कर सकते हैं. साथ ही, वे अन्य उपयोगकर्ताओं के ऐक्सेस को कंट्रोल कर सकते हैं.
डेटा स्ट्रक्चर
मान लें कि आपके ऐप्लिकेशन में stories
कलेक्शन है. इसमें हर दस्तावेज़ एक कहानी को दिखाता है. हर स्टोरी में एक comments
सब-कलेक्शन भी होता है. इसमें हर दस्तावेज़, उस स्टोरी पर की गई टिप्पणी होती है.
ऐक्सेस की भूमिकाओं को ट्रैक करने के लिए, roles
फ़ील्ड जोड़ें. यह फ़ील्ड, भूमिकाओं के लिए User-ID का मैप होता है:
/stories/{storyid}
{
title: "A Great Story",
content: "Once upon a time ...",
roles: {
alice: "owner",
bob: "reader",
david: "writer",
jane: "commenter"
// ...
}
}
टिप्पणियों में सिर्फ़ दो फ़ील्ड होते हैं: लेखक का उपयोगकर्ता आईडी और कुछ कॉन्टेंट:
/stories/{storyid}/comments/{commentid}
{
user: "alice",
content: "I think this is a great story!"
}
नियम
अब जब आपने डेटाबेस में उपयोगकर्ताओं की भूमिकाएं रिकॉर्ड कर ली हैं, तो आपको उनकी पुष्टि करने के लिए सुरक्षा के नियम लिखने होंगे. इन नियमों में यह माना गया है कि ऐप्लिकेशन, Firebase Auth का इस्तेमाल करता है, ताकि request.auth.uid
वैरिएबल, उपयोगकर्ता का आईडी हो.
पहला चरण: बुनियादी नियमों वाली फ़ाइल से शुरुआत करें. इसमें कहानियों और टिप्पणियों के लिए खाली नियम शामिल होते हैं:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{story} {
// TODO: Story rules go here...
match /comments/{comment} {
// TODO: Comment rules go here...
}
}
}
}
दूसरा चरण: एक सामान्य write
नियम जोड़ें. इससे मालिकों को स्टोरी पर पूरा कंट्रोल मिल जाता है. यहां दिए गए फ़ंक्शन से, उपयोगकर्ता की भूमिकाओं का पता चलता है. साथ ही, यह भी पता चलता है कि नए दस्तावेज़ मान्य हैं या नहीं:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{story} {
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
// Read from the "roles" map in the resource (rsc).
return rsc.data.roles[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
// Determine if the user is one of an array of roles
return isSignedIn() && (getRole(rsc) in array);
}
function isValidNewStory() {
// Valid if story does not exist and the new story has the correct owner.
return resource == null && isOneOfRoles(request.resource, ['owner']);
}
// Owners can read, write, and delete stories
allow write: if isValidNewStory() || isOneOfRoles(resource, ['owner']);
match /comments/{comment} {
// ...
}
}
}
}
तीसरा चरण: ऐसे नियम लिखें जिनसे किसी भी भूमिका वाले उपयोगकर्ता को कहानियां और टिप्पणियां पढ़ने की अनुमति मिले. पिछले चरण में तय किए गए फ़ंक्शन का इस्तेमाल करने से, नियम छोटे और पढ़ने में आसान हो जाते हैं:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{story} {
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
return rsc.data.roles[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
return isSignedIn() && (getRole(rsc) in array);
}
function isValidNewStory() {
return resource == null
&& request.resource.data.roles[request.auth.uid] == 'owner';
}
allow write: if isValidNewStory() || isOneOfRoles(resource, ['owner']);
// Any role can read stories.
allow read: if isOneOfRoles(resource, ['owner', 'writer', 'commenter', 'reader']);
match /comments/{comment} {
// Any role can read comments.
allow read: if isOneOfRoles(get(/databases/$(database)/documents/stories/$(story)),
['owner', 'writer', 'commenter', 'reader']);
}
}
}
}
चौथा चरण: कहानी लिखने वालों, टिप्पणी करने वालों, और मालिकों को टिप्पणियां पोस्ट करने की अनुमति दें.
ध्यान दें कि यह नियम इस बात की भी पुष्टि करता है कि टिप्पणी का owner
, अनुरोध करने वाले उपयोगकर्ता से मेल खाता हो. इससे उपयोगकर्ताओं को एक-दूसरे की टिप्पणियों को बदलने से रोका जा सकता है:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{story} {
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
return rsc.data.roles[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
return isSignedIn() && (getRole(rsc) in array);
}
function isValidNewStory() {
return resource == null
&& request.resource.data.roles[request.auth.uid] == 'owner';
}
allow write: if isValidNewStory() || isOneOfRoles(resource, ['owner'])
allow read: if isOneOfRoles(resource, ['owner', 'writer', 'commenter', 'reader']);
match /comments/{comment} {
allow read: if isOneOfRoles(get(/databases/$(database)/documents/stories/$(story)),
['owner', 'writer', 'commenter', 'reader']);
// Owners, writers, and commenters can create comments. The
// user id in the comment document must match the requesting
// user's id.
//
// Note: we have to use get() here to retrieve the story
// document so that we can check the user's role.
allow create: if isOneOfRoles(get(/databases/$(database)/documents/stories/$(story)),
['owner', 'writer', 'commenter'])
&& request.resource.data.user == request.auth.uid;
}
}
}
}
पांचवां चरण: लेखकों को कहानी के कॉन्टेंट में बदलाव करने की अनुमति दें. हालांकि, उन्हें कहानी की भूमिकाओं में बदलाव करने या दस्तावेज़ की किसी अन्य प्रॉपर्टी को बदलने की अनुमति न दें. इसके लिए, कहानियों write
के नियम को create
, update
, और delete
के लिए अलग-अलग नियमों में बांटना होगा, क्योंकि लेखक सिर्फ़ इन कहानियों को अपडेट कर सकते हैं:
service cloud.firestore {
match /databases/{database}/documents {
match /stories/{story} {
function isSignedIn() {
return request.auth != null;
}
function getRole(rsc) {
return rsc.data.roles[request.auth.uid];
}
function isOneOfRoles(rsc, array) {
return isSignedIn() && (getRole(rsc) in array);
}
function isValidNewStory() {
return request.resource.data.roles[request.auth.uid] == 'owner';
}
function onlyContentChanged() {
// Ensure that title and roles are unchanged and that no new
// fields are added to the document.
return request.resource.data.title == resource.data.title
&& request.resource.data.roles == resource.data.roles
&& request.resource.data.keys() == resource.data.keys();
}
// Split writing into creation, deletion, and updating. Only an
// owner can create or delete a story but a writer can update
// story content.
allow create: if isValidNewStory();
allow delete: if isOneOfRoles(resource, ['owner']);
allow update: if isOneOfRoles(resource, ['owner'])
|| (isOneOfRoles(resource, ['writer']) && onlyContentChanged());
allow read: if isOneOfRoles(resource, ['owner', 'writer', 'commenter', 'reader']);
match /comments/{comment} {
allow read: if isOneOfRoles(get(/databases/$(database)/documents/stories/$(story)),
['owner', 'writer', 'commenter', 'reader']);
allow create: if isOneOfRoles(get(/databases/$(database)/documents/stories/$(story)),
['owner', 'writer', 'commenter'])
&& request.resource.data.user == request.auth.uid;
}
}
}
}
सीमाएं
ऊपर दिए गए समाधान में, सुरक्षा के नियमों का इस्तेमाल करके उपयोगकर्ता के डेटा को सुरक्षित रखने का तरीका बताया गया है. हालांकि, आपको इन सीमाओं के बारे में पता होना चाहिए:
- बारीकी से कंट्रोल करने की सुविधा: ऊपर दिए गए उदाहरण में, कई भूमिकाओं (लेखक और मालिक) के पास एक ही दस्तावेज़ में बदलाव करने का ऐक्सेस है. हालांकि, उनकी सीमाएं अलग-अलग हैं. ज़्यादा जटिल दस्तावेज़ों को मैनेज करना मुश्किल हो सकता है. ऐसे में, एक दस्तावेज़ को कई दस्तावेज़ों में बांटना बेहतर हो सकता है. हर दस्तावेज़ का मालिकाना हक एक भूमिका के पास होना चाहिए.
- बड़े ग्रुप: अगर आपको बहुत बड़े या जटिल ग्रुप के साथ शेयर करना है, तो ऐसे सिस्टम का इस्तेमाल करें जहां भूमिकाएं, टारगेट दस्तावेज़ के फ़ील्ड के तौर पर स्टोर करने के बजाय, अपने कलेक्शन में स्टोर की जाती हैं.