Bu sayfada, Güvenlik Kurallarını Yapılandırma ve Güvenlik Kuralları İçin Koşullar Yazma sayfalarındaki kavramlardan yararlanarak Cloud Firestore Security Rules ile istemcilerin bir dokümandaki bazı alanlarda işlem yapmasına izin veren ancak diğerlerinde izin vermeyen kuralları nasıl oluşturabileceğiniz açıklanmaktadı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 belge oluşturmasına veya değiştirmesine izin verebilir ancak bu belgedeki belirli alanları düzenlemesine izin vermeyebilirsiniz. Alternatif olarak, bir müşterinin her zaman oluşturduğu belgelerin belirli bir alan grubunu içermesini zorunlu kılabilirsiniz. Bu kılavuzda, Cloud Firestore Security Rules kullanarak bu görevlerden bazılarını nasıl tamamlayabileceğiniz açıklanmaktadır.
Yalnızca belirli alanlar için okuma erişimine izin verme
Cloud Firestore okuma işlemleri belge düzeyinde yapılır. Ya belgenin tamamını alırsınız ya da hiçbir şey almazsınız. Kısmi dokümanları geri almanın bir yolu yoktur. Kullanıcıların bir dokümandaki belirli alanları okumasını engellemek için yalnızca güvenlik kurallarını kullanmak mümkün değildir.
Bir dokümanda bazı kullanıcılardan gizli tutmak istediğiniz belirli alanlar varsa bunları ayrı bir dokümana yerleştirmeniz en iyi yöntemdir. Örneğin, private
alt koleksiyonda
şu şekilde 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 role
özel kimlik doğrulama talebi Finance
değerine eşit olan kullanıcıların bir çalışanın finansal bilgilerini görüntüleyebileceğini belirtmek için özel kimlik doğrulama talepleri 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'
}
}
}
}
Belge oluşturma sırasında alanları kısıtlama
Cloud Firestore şemasızdır. Bu nedenle, bir belgenin hangi alanları içereceği konusunda veritabanı düzeyinde kısıtlama yoktur. Bu esneklik geliştirme sürecini kolaylaştırsa da istemcilerin yalnızca belirli alanları içeren veya başka alanları içermeyen dokümanlar oluşturabilmesini sağlamak istediğiniz zamanlar olabilir.
Bu kuralları, request.resource.data
nesnesinin keys
yöntemini inceleyerek oluşturabilirsiniz. Bu, istemcinin bu yeni belgeye 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'ye ekleyebileceği belge türlerini kısıtlayan bir mantık ekleyebilirsiniz.
Yeni dokümanlarda belirli alanların zorunlu olması
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()
işlevini ç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 başka alanlarla da oluşturulmasına olanak tanır ancak bir istemci tarafından oluşturulan tüm belgelerin en az bu üç alanı içermesini sağlar.
Yeni dokümanlarda belirli alanların yasaklanması
Benzer şekilde, yasaklanmış alanların listesine karşı hasAny()
kullanarak istemcilerin belirli alanları içeren dokümanlar oluşturmasını engelleyebilirsiniz. Bu yöntem, bir dokümanda bu alanlardan herhangi biri varsa doğru olarak değerlendirilir. Bu nedenle, belirli alanları yasaklamak için sonucu muhtemelen olumsuzlamak istersiniz.
Örneğin, aşağıdaki örnekte, bu alanlar daha sonra bir sunucu çağrısıyla 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 dokümanlar için izin verilen alanlar listesi oluşturma
Yeni belgelerde belirli alanları yasaklamak yerine, yeni belgelerde açıkça izin verilen alanların bir listesini oluşturmak isteyebilirsiniz. Ardından, oluşturulan yeni dokümanların yalnızca bu alanları (veya bu alanların bir alt kümesini) içerdiğinden ve başka alan içermediğ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ın zorunlu olmasını ve diğer alanlara izin verilmesini sağlamak 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 gerekirken
address
, hours
ve cuisine
alanlarına isteğe bağlı olarak 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 hayattaki bir senaryoda, kodunuzu kopyalamamak ve isteğe bağlı ile zorunlu alanları tek bir listede daha kolay birleştirmek için bu mantığı yardımcı bir 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üncellemede alanları kısıtlama
Yaygın bir güvenlik uygulaması, istemcilerin yalnızca bazı alanları düzenlemesine izin vermek ve diğerlerini düzenlemesine izin vermemektir. Bu işlemi yalnızca önceki bölümde açıklanan request.resource.data.keys()
listesine bakarak yapamazsınız. Çünkü bu liste, dokümanın güncellemeden sonraki görünümünü temsil eder ve bu nedenle istemcinin değiştirmediği alanları da içerir.
Ancak diff()
işlevini kullanırsanız request.resource.data
ile resource.data
nesnesini karşılaştırabilirsiniz. Bu nesne, güncellemeden önce veritabanındaki dokümanı temsil eder. Bu işlem, iki farklı harita arasındaki tüm değişiklikleri içeren bir nesne olan mapDiff
nesnesi oluşturur.
Bu mapDiff üzerinde affectedKeys()
yöntemini çağırarak bir düzenlemede değiştirilen alanlar kümesi oluşturabilirsiniz. Ardından, bu kümenin belirli öğeler içerdiğinden (veya içermediğinden) emin olmak için hasOnly()
veya hasAny()
gibi işlevleri kullanabilirsiniz.
Bazı alanların değiştirilmesini önleme
affectedKeys()
tarafından oluşturulan kümede hasAny()
yöntemini kullanarak ve ardından sonucu olumsuzlayarak, 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 verebilir ancak ortalama puanını veya yorum sayısını değiştirmesine izin vermeyebilirsiniz.
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. Bu yöntem genellikle daha güvenli kabul edilir. Çünkü güvenlik kurallarınızda açıkça izin verene kadar yeni doküman alanlarına yazma işlemi varsayılan olarak engellenir.
Örneğin, average_score
ve rating_count
alanı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 sürümünde restoran belgeleri telephone
alanını içeriyorsa güvenlik kurallarınızda bu alanı hasOnly()
listesine geri dönüp ekleyene kadar bu alanı düzenleme girişimlerinin başarısız olacağı anlamına gelir.
Alan türlerini zorunlu kılma
Cloud Firestore'nın şemasız olmasının bir diğer etkisi de belirli alanlarda hangi tür verilerin depolanabileceği konusunda veritabanı düzeyinde zorlama olmamasıdır. Bunu güvenlik kurallarında is
operatörüyle zorunlu kılabilirsiniz.
Örneğin, aşağıdaki güvenlik kuralı, bir yorumun score
alanının tam sayı, headline
, content
ve author_name
alanlarının dize, review_date
alanının ise zaman damgası olmasını 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 güvenlik kuralları dilinin kendisi tarafından oluşturulduğu ve istemciler tarafından oluşturulmadığı için çoğu pratik uygulamada nadiren kullanılır.
list
ve map
veri türleri, genel türleri veya tür bağımsız değişkenlerini desteklemez.
Başka 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 listedeki veya haritadaki belirli girişler için tür değerlerini zorunlu kılmak üzere güvenlik kurallarını kullanabilirsiniz (sırasıyla köşeli parantez gösterimini veya anahtar adlarını kullanarak). Ancak bir haritadaki veya listedeki tüm üyelerin veri türlerini aynı anda zorunlu kılmak için bir kısayol yoktur.
Örneğin, aşağıdaki kurallar bir dokümandaki tags
alanının liste içerdiğinden ve ilk girişin dize olduğundan emin olur. Ayrıca, product
alanının, dize olan bir ürün adı ve tam sayı olan bir miktar içeren bir harita içerdiğinden de emin olunur.
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 belge oluşturulurken hem de güncellenirken zorunlu olmalı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
öğesinin bulunmadığı bir dokümanda request.resource.data.foo
öğesinin çağrılmasının hataya neden olduğunu ve bu nedenle bu çağrıyı yapan tüm güvenlik kurallarının isteği reddedeceğini unutmayın. Bu durumu request.resource.data
adresinde get
yöntemini kullanarak çözebilirsiniz. get
yöntemi, bir 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 belgelerinde isteğe bağlı bir photo_url
alanı ve sırasıyla dize ve liste olduklarını doğrulamak istediğiniz isteğe bağlı bir tags
alanı da varsa bunu reviewFieldsAreValidTypes
işlevini aşağıdakine benzer şekilde yeniden yazarak gerçekleştirebilirsiniz:
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ı reddederken tags
(veya photo_url
) alanı içermeyen dokümanlara izin vermeye devam eder.
Kısmi yazma işlemlerine hiçbir zaman izin verilmez
Cloud Firestore Security Rules ile ilgili son bir not: Bu kullanıcılar, istemcinin bir dokümanda değişiklik yapmasına izin verir veya tüm düzenlemeyi reddeder. Aynı işlemde bazı alanlara yazma işlemlerini kabul ederken diğerlerini reddeden güvenlik kuralları oluşturamazsınız.