Bu sayfada, Cloud Firestore Security Rules'i kullanarak istemcilerin bir dokümandaki bazı alanlar üzerinde işlem yapmasına izin veren, diğer alanlar üzerinde ise işlem yapmasına izin vermeyen kurallar oluşturma hakkında bilgi verilmektedir. Bu bilgiler, Güvenlik Kurallarını Yapılandırma ve Güvenlik Kuralları İçin Koşul Yazma başlıklı makalelerdeki kavramları temel alır.
Bir dokümanda yapılan değişiklikleri doküman düzeyinde değil, alan düzeyinde kontrol etmek isteyebilirsiniz.
Örneğin, bir müşterinin doküman oluşturmasına veya değiştirmesine izin vermek ancak söz konusu dokümandaki belirli alanları düzenlemesine izin vermemek isteyebilirsiniz. Alternatif olarak, bir müşterinin oluşturduğu tüm dokümanların her zaman belirli bir alan grubu içermesini zorunlu kılmak isteyebilirsiniz. Bu kılavuzda, Cloud Firestore Security Rules'ü kullanarak bu görevlerin bazılarını nasıl tamamlayabileceğiniz açıklanmaktadır.
Yalnızca belirli alanlar için okuma erişimine izin verme
Cloud Firestore'te okuma işlemleri doküman düzeyinde gerçekleştirilir. Belgenin tamamını alırsınız veya hiçbir şey almazsınız. Kısmi doküman alınamaz. Kullanıcıların bir dokümandaki belirli alanları okumasını önlemek için yalnızca güvenlik kurallarını kullanmak mümkün değildir.
Bir dokümanda bazı kullanıcılardan gizlemek istediğiniz belirli alanlar varsa en iyi yöntem bunları ayrı bir dokümana yerleştirmektir. Örneğin, private
alt koleksiyonunda aşağıdaki gibi bir doküman oluşturabilirsiniz:
/employees/{emp_id}
name: "Alice Hamilton",
department: 461,
start_date: <timestamp>
/employees/{emp_id}/private/finances
salary: 80000,
bonus_mult: 1.25,
perf_review: 4.2
Ardından, iki koleksiyon için farklı erişim düzeylerine sahip güvenlik kuralları ekleyebilirsiniz. Bu örnekte, yalnızca Finance
değerine eşit role
özel kimlik doğrulama iddiasına sahip kullanıcıların bir çalışanın finansal bilgilerini görüntüleyebileceğini belirtmek için özel kimlik doğrulama iddialarını kullanıyoruz.
service cloud.firestore {
match /databases/{database}/documents {
// Allow any logged in user to view the public employee data
match /employees/{emp_id} {
allow read: if request.resource.auth != null
// Allow only users with the custom auth claim of "Finance" to view
// the employee's financial data
match /private/finances {
allow read: if request.resource.auth &&
request.resource.auth.token.role == 'Finance'
}
}
}
}
Doküman oluştururken alanları kısıtlama
Cloud Firestore şemasızdır. Diğer bir deyişle, bir belgenin hangi alanları içerdiğine dair veritabanı düzeyinde herhangi bir kısıtlama yoktur. Bu esneklik geliştirmeyi kolaylaştırsa da istemcilerin yalnızca belirli alanları içeren veya diğer alanları içermeyen dokümanlar oluşturmasını sağlamak istediğiniz durumlar olabilir.
Bu kuralları, request.resource.data
nesnesinin keys
yöntemini inceleyerek oluşturabilirsiniz. Bu, istemcinin bu yeni dokümana yazmaya çalıştığı tüm alanların listesidir. Bu alan grubunu hasOnly()
veya hasAny()
gibi işlevlerle birleştirerek kullanıcının Cloud Firestore'e ekleyebileceği doküman türlerini kısıtlayan mantık ekleyebilirsiniz.
Yeni dokümanlarda belirli alanları zorunlu kılma
Bir restaurant
koleksiyonunda oluşturulan tüm dokümanların en az bir name
, location
ve city
alanı içerdiğinden emin olmak istediğinizi varsayalım. Bunu, yeni dokümandaki anahtar listesinde hasAll()
çağırarak yapabilirsiniz.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document contains a name
// location, and city field
match /restaurant/{restId} {
allow create: if request.resource.data.keys().hasAll(['name', 'location', 'city']);
}
}
}
Bu, restoranların diğer alanlarla da oluşturulmasına olanak tanır ancak bir müşteri tarafından oluşturulan tüm dokümanların en az bu üç alanı içermesini sağlar.
Yeni dokümanlarda belirli alanları yasaklama
Benzer şekilde, yasaklanmış alanların listesine karşı hasAny()
kullanarak istemcilerin belirli alanları içeren dokümanlar oluşturmasını engelleyebilirsiniz. Bir belge bu alanlardan herhangi birini içeriyorsa bu yöntem doğru olarak değerlendirilir. Bu nedenle, belirli alanları yasaklamak için sonucu reddetmek isteyebilirsiniz.
Örneğin, aşağıdaki örnekte bu alanlar daha sonra bir sunucu çağrısı tarafından ekleneceğinden, istemcilerin average_score
veya rating_count
alanı içeren bir doküman oluşturmasına izin verilmez.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document does *not*
// contain an average_score or rating_count field.
match /restaurant/{restId} {
allow create: if (!request.resource.data.keys().hasAny(
['average_score', 'rating_count']));
}
}
}
Yeni belgeler için izin verilen alanlar listesi oluşturma
Yeni belgelerde belirli alanları yasaklamak yerine, yalnızca yeni belgelerde açıkça izin verilen alanların listesini oluşturabilirsiniz. Ardından, oluşturulan yeni dokümanların yalnızca bu alanları (veya bu alanların bir alt kümesini) içerdiğinden emin olmak için hasOnly()
işlevini kullanabilirsiniz.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document doesn't contain
// any fields besides the ones listed below.
match /restaurant/{restId} {
allow create: if (request.resource.data.keys().hasOnly(
['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}
Zorunlu ve isteğe bağlı alanları birleştirme
Bazı alanları zorunlu kılmak ve diğerlerine izin vermek için güvenlik kurallarınızda hasAll
ve hasOnly
işlemlerini birlikte kullanabilirsiniz. Örneğin, bu örnekte tüm yeni dokümanların name
, location
ve city
alanlarını içermesi zorunludur ve isteğe bağlı olarak address
, hours
ve cuisine
alanlarına izin verilir.
service cloud.firestore {
match /databases/{database}/documents {
// Allow the user to create a document only if that document has a name,
// location, and city field, and optionally address, hours, or cuisine field
match /restaurant/{restId} {
allow create: if (request.resource.data.keys().hasAll(['name', 'location', 'city'])) &&
(request.resource.data.keys().hasOnly(
['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}
Gerçek bir senaryoda, kodunuzun kopyalanmasını önlemek ve isteğe bağlı ile zorunlu alanları tek bir listede daha kolay birleştirmek için bu mantığı bir yardımcı işleve taşımak isteyebilirsiniz. Örneğin:
service cloud.firestore {
match /databases/{database}/documents {
function verifyFields(required, optional) {
let allAllowedFields = required.concat(optional);
return request.resource.data.keys().hasAll(required) &&
request.resource.data.keys().hasOnly(allAllowedFields);
}
match /restaurant/{restId} {
allow create: if verifyFields(['name', 'location', 'city'],
['address', 'hours', 'cuisine']);
}
}
}
Güncelleme sırasında alanları kısıtlama
Müşterilerin yalnızca bazı alanları düzenlemesine izin vermek, yaygın bir güvenlik uygulamasıdır. Bu işlemi yalnızca önceki bölümde açıklanan request.resource.data.keys()
listesine bakarak yapamazsınız. Bu liste, dokümanın güncellemeden sonraki halini temsil ettiğinden ve istemcinin değiştirmediği alanları içerdiğinden bu işlem için yeterli değildir.
Ancak diff()
işlevini kullanırsanız request.resource.data
öğesini, güncellemeden önce veritabanındaki dokümanı temsil eden resource.data
nesnesi ile karşılaştırabilirsiniz. Bu işlem, iki farklı harita arasındaki tüm değişiklikleri içeren bir mapDiff
nesnesi oluşturur.
Bu mapDiff üzerinde affectedKeys()
yöntemini çağırarak bir düzenlemede değiştirilen bir dizi alan elde edebilirsiniz. Ardından, bu grubun belirli öğeleri içerip içermediğinden emin olmak için hasOnly()
veya hasAny()
gibi işlevleri kullanabilirsiniz.
Bazı alanların değiştirilmesini engelleme
affectedKeys()
tarafından oluşturulan kümede hasAny()
yöntemini kullanarak ve ardından sonucu reddederek, değiştirilmesini istemediğiniz alanları değiştirmeye çalışan tüm istemci isteklerini reddedebilirsiniz.
Örneğin, müşterilerin bir restoranla ilgili bilgileri güncellemesine izin vermek ancak ortalama puanını veya yorum sayısını değiştirmelerine izin vermemek isteyebilirsiniz.
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Allow the client to update a document only if that document doesn't
// change the average_score or rating_count fields
allow update: if (!request.resource.data.diff(resource.data).affectedKeys()
.hasAny(['average_score', 'rating_count']));
}
}
}
Yalnızca belirli alanların değiştirilmesine izin verme
Değiştirilmesini istemediğiniz alanları belirtmek yerine, değiştirilmesini istediğiniz alanların listesini belirtmek için hasOnly()
işlevini de kullanabilirsiniz. Güvenlik kurallarınızda açıkça izin vermediğiniz sürece yeni belge alanlarına yazma işlemine varsayılan olarak izin verilmediğinden bu yöntem genellikle daha güvenli kabul edilir.
Örneğin, average_score
ve rating_count
alanının kullanılmasına izin vermemek yerine, istemcilerin yalnızca name
, location
, city
, address
, hours
ve cuisine
alanlarını değiştirmesine izin veren güvenlik kuralları oluşturabilirsiniz.
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Allow a client to update only these 6 fields in a document
allow update: if (request.resource.data.diff(resource.data).affectedKeys()
.hasOnly(['name', 'location', 'city', 'address', 'hours', 'cuisine']));
}
}
}
Bu, uygulamanızın gelecekteki bir iterasyonunda restoran dokümanları bir telephone
alanı içeriyorsa geri dönüp bu alanı güvenlik kurallarınızdaki hasOnly()
listesine ekleyene kadar bu alanın düzenlenme girişimlerinin başarısız olacağı anlamına gelir.
Alan türlerini zorunlu kılma
Cloud Firestore'ün şemasız olmasının bir diğer etkisi de belirli alanlarda hangi veri türlerinin depolanabileceğine dair veritabanı düzeyinde yaptırım olmamasıdır. Ancak bu işlemi, is
operatörünü kullanarak güvenlik kurallarında da uygulayabilirsiniz.
Örneğin, aşağıdaki güvenlik kuralı bir incelemenin score
alanının tam sayı, headline
, content
ve author_name
alanlarının dize, review_date
alanının ise zaman damgası olması gerektiğini zorunlu kılar.
service cloud.firestore {
match /databases/{database}/documents {
match /restaurant/{restId} {
// Restaurant rules go here...
match /review/{reviewId} {
allow create: if (request.resource.data.score is int &&
request.resource.data.headline is string &&
request.resource.data.content is string &&
request.resource.data.author_name is string &&
request.resource.data.review_date is timestamp
);
}
}
}
}
is
operatörü için geçerli veri türleri bool
, bytes
, float
, int
,
list
, latlng
, number
, path
, map
, string
ve timestamp
'dir. is
operatörü constraint
, duration
, set
ve map_diff
veri türlerini de destekler ancak bunlar istemciler tarafından değil, güvenlik kuralları dili tarafından oluşturulduğundan pratik uygulamalarda nadiren kullanılır.
list
ve map
veri türleri, genel türleri veya tür bağımsız değişkenlerini desteklemez.
Diğer bir deyişle, belirli bir alanın liste veya harita içermesini zorunlu kılmak için güvenlik kurallarını kullanabilirsiniz ancak bir alanın tüm tam sayıların veya tüm dizelerin listesini içermesini zorunlu kılamazsınız.
Benzer şekilde, bir liste veya haritadaki belirli girişler için tür değerlerini zorunlu kılmak üzere güvenlik kurallarını kullanabilirsiniz (sırasıyla parantez gösterimi veya anahtar adlarını kullanarak). Ancak bir harita veya listedeki tüm üyelerin veri türlerini aynı anda zorunlu kılacak bir kısayol yoktur.
Örneğin, aşağıdaki kurallar bir dokümanda tags
alanının bir liste içermesini ve ilk girişin dize olmasını sağlar. Ayrıca product
alanının, dize olan bir ürün adını ve tam sayı olan bir miktarı içeren bir harita içermesini sağlar.
service cloud.firestore {
match /databases/{database}/documents {
match /orders/{orderId} {
allow create: if request.resource.data.tags is list &&
request.resource.data.tags[0] is string &&
request.resource.data.product is map &&
request.resource.data.product.name is string &&
request.resource.data.product.quantity is int
}
}
}
}
Alan türleri hem doküman oluştururken hem de güncellerken zorunlu kılınmalıdır. Bu nedenle, güvenlik kurallarınızın hem oluşturma hem de güncelleme bölümlerinde çağırabileceğiniz bir yardımcı işlev oluşturmayı düşünebilirsiniz.
service cloud.firestore {
match /databases/{database}/documents {
function reviewFieldsAreValidTypes(docData) {
return docData.score is int &&
docData.headline is string &&
docData.content is string &&
docData.author_name is string &&
docData.review_date is timestamp;
}
match /restaurant/{restId} {
// Restaurant rules go here...
match /review/{reviewId} {
allow create: if reviewFieldsAreValidTypes(request.resource.data) &&
// Other rules may go here
allow update: if reviewFieldsAreValidTypes(request.resource.data) &&
// Other rules may go here
}
}
}
}
İsteğe bağlı alanlar için türleri zorunlu kılma
foo
'ın bulunmadığı bir belgede request.resource.data.foo
çağrısının hata verdiğini ve bu nedenle bu çağrıyı yapan tüm güvenlik kurallarının isteği reddettiğini unutmayın. Bu durumu request.resource.data
'da get
yöntemini kullanarak çözebilirsiniz. get
yöntemi, haritadan aldığınız alan mevcut değilse bu alan için varsayılan bir bağımsız değişken sağlamanıza olanak tanır.
Örneğin, inceleme dokümanları da doğrulamak istediğiniz isteğe bağlı bir photo_url
alanı ve isteğe bağlı bir tags
alanı içeriyorsa (sırasıyla dize ve liste) reviewFieldsAreValidTypes
işlevini aşağıdaki gibi yeniden yazarak bunu yapabilirsiniz:
function reviewFieldsAreValidTypes(docData) {
return docData.score is int &&
docData.headline is string &&
docData.content is string &&
docData.author_name is string &&
docData.review_date is timestamp &&
docData.get('photo_url', '') is string &&
docData.get('tags', []) is list;
}
Bu, tags
alanının bulunduğu ancak liste olmayan dokümanları reddeder. tags
(veya photo_url
) alanı içermeyen dokümanlara ise izin verir.
Kısmi yazmalara hiçbir zaman izin verilmez
Cloud Firestore Security Rules ile ilgili son bir not da, istemcinin bir dokümanda değişiklik yapmasına izin vermeleri veya düzenlemenin tamamını reddetmeleridir. Belgenizdeki bazı alanlara yapılan yazma işlemlerini kabul ederken aynı işlemde diğer alanları reddeden güvenlik kuralları oluşturamazsınız.