Belirli alanlara erişimi kontrol etme

Bu sayfa, istemcilerin bir belgedeki bazı alanlarda işlem yapmasına izin verirken diğerlerinde yapmamasına izin veren kurallar oluşturmak için Cloud Firestore Güvenlik Kurallarını nasıl kullanabileceğinizi açıklamak için Güvenlik Kurallarını Yapılandırma ve Güvenlik Kuralları için Koşullar Yazma bölümündeki kavramlara dayanmaktadır.

Bir belgede yapılan değişiklikleri belge düzeyinde değil alan düzeyinde kontrol etmek istediğiniz zamanlar olabilir.

Örneğin, bir müşterinin bir belge oluşturmasına veya değiştirmesine izin vermek, ancak bu belgedeki belirli alanları düzenlemesine izin vermemek isteyebilirsiniz. Veya bir istemcinin oluşturduğu herhangi bir belgenin her zaman belirli bir alan kümesi içermesini zorunlu kılmak isteyebilirsiniz. Bu kılavuz, Cloud Firestore Güvenlik Kurallarını kullanarak bu görevlerden bazılarını nasıl gerçekleştirebileceğinizi kapsar.

Yalnızca belirli alanlar için okuma erişimine izin verme

Cloud Firestore'daki okumalar belge düzeyinde gerçekleştirilir. Ya belgenin tamamını alırsınız ya da hiçbir şey alamazsınız. Kısmi bir belgeyi geri almanın bir yolu yoktur. Kullanıcıların bir belge içindeki belirli alanları okumasını engellemek için yalnızca güvenlik kurallarını kullanmak mümkün değildir.

Bir belgede bazı kullanıcılardan gizlemek istediğiniz belirli alanlar varsa, en iyi yol bunları ayrı bir belgeye koymak olacaktır. Örneğin, private bir alt koleksiyonda şu şekilde bir belge oluşturmayı düşünebilirsiniz:

/çalışanlar/{emp_id}

  name: "Alice Hamilton",
  department: 461,
  start_date: <timestamp>

/çalışanlar/{emp_id}/özel/finans

    salary: 80000,
    bonus_mult: 1.25,
    perf_review: 4.2

Daha sonra iki koleksiyon için farklı erişim düzeylerine sahip güvenlik kuralları ekleyebilirsiniz. Bu örnekte, yalnızca özel kimlik doğrulama talebi role Finance eşit olan kullanıcıların bir çalışanın mali bilgilerini görüntüleyebileceğini söylemek için özel kimlik doğrulama taleplerini 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şturmada alanları kısıtlama

Cloud Firestore şemasızdır, yani bir belgenin hangi alanları içerdiği konusunda 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 başka alanlar içermeyen belgeler oluşturabildiğinden emin olmak istediğiniz zamanlar olacaktır.

Bu kuralları request.resource.data nesnesinin keys metodunu inceleyerek oluşturabilirsiniz. Bu, istemcinin bu yeni belgeye yazmaya çalıştığı tüm alanların listesidir. Bu alan kümesini hasOnly() veya hasAny() gibi işlevlerle birleştirerek, bir kullanıcının Cloud Firestore'a ekleyebileceği belge türlerini kısıtlayan bir mantık ekleyebilirsiniz.

Yeni belgelerde belirli alanların zorunlu kılınması

Bir restaurant koleksiyonunda oluşturulan tüm belgelerin en azından bir name , location ve city alanı içerdiğinden emin olmak istediğinizi varsayalım. Bunu yeni belgedeki anahtarlar listesinde hasAll() öğesini ç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 müşteri tarafından oluşturulan tüm belgelerin en az bu üç alanı içermesini sağlar.

Yeni belgelerde belirli alanların yasaklanması

Benzer şekilde, yasaklı alanlar listesine karşı hasAny() işlevini kullanarak istemcilerin belirli alanlar içeren belgeler oluşturmasını engelleyebilirsiniz. Bu yöntem, bir belgenin bu alanlardan herhangi birini içermesi durumunda doğru olarak değerlendirilir; dolayısıyla, belirli alanları yasaklamak için muhtemelen sonucu reddetmek isteyebilirsiniz.

Örneğin, aşağıdaki örnekte, bu alanlar daha sonra bir sunucu çağrısı tarafından ekleneceği için istemcilerin, average_score veya rating_count alanı içeren bir belge 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 verilenler listesi oluşturma

Yeni belgelerde belirli alanları yasaklamak yerine yalnızca yeni belgelerde açıkça izin verilen alanların bir listesini oluşturmak isteyebilirsiniz. Daha sonra, oluşturulan yeni belgelerin yalnızca bu alanları (veya bu alanların bir alt kümesini) içerdiğinden, başkalarını 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ın birleştirilmesi

Bazı alanları gerektirmek ve diğerlerine izin vermek için hasAll ve hasOnly işlemlerini güvenlik kurallarınızda bir araya getirebilirsiniz. Örneğin, bu örnek, tüm yeni belgelerin name , location ve city alanlarını içermesini gerektirir ve isteğe bağlı olarak address , hours ve cuisine alanlarına da izin verir.

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 dünya senaryosunda, kodunuzun kopyalanmasını önlemek ve isteğe bağlı ve gerekli alanları tek bir listede daha kolay bir şekilde birleştirmek için bu mantığı bir yardımcı işleve taşımak isteyebilirsiniz:

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

Yaygın bir güvenlik uygulaması, istemcilerin yalnızca bazı alanları düzenlemesine izin vermek, diğerlerini düzenlememesine izin vermektir. Bunu yalnızca önceki bölümde açıklanan request.resource.data.keys() listesine bakarak gerçekleştiremezsiniz; çünkü bu liste, güncellemeden sonra bakılacağı gibi belgenin tamamını temsil eder ve bu nedenle istemcinin içermediği alanları içerir. değiştirmek.

Ancak diff() işlevini kullanacaksanız, request.resource.data dosyasını, güncellemeden önce veritabanındaki belgeyi temsil eden resource.data nesnesiyle karşılaştırabilirsiniz. Bu, iki farklı harita arasındaki tüm değişiklikleri içeren bir nesne olan bir mapDiff nesnesi oluşturur.

Bu MapDiff'te affectedKeys() yöntemini çağırarak, bir düzenlemede değiştirilen bir dizi alanla karşılaşabilirsiniz. Daha sonra bu kümenin belirli öğeleri içerdiğinden (veya içermediğinden) emin olmak için hasOnly() veya hasAny() gibi işlevleri kullanabilirsiniz.

Bazı alanların değiştirilmesinin engellenmesi

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 restoran hakkındaki bilgileri güncellemesine izin verip ortalama puanlarını veya yorum sayısını değiştirmemesini 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. Bu genellikle daha güvenli kabul edilir çünkü yeni belge alanlarına yazmaya, güvenlik kurallarınızda açıkça izin verene kadar varsayılan olarak izin verilmez.

Ö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 yinelemesinde restoran belgelerinin bir telephone alanı içermesi durumunda, siz geri dönüp bu alanı güvenlik kurallarınızdaki hasOnly() listesine ekleyene kadar bu alanı düzenleme girişimlerinin başarısız olacağı anlamına gelir.

Alan türlerini zorunlu kılma

Cloud Firestore'un şemasız olmasının bir başka etkisi de, belirli alanlarda hangi tür verilerin depolanabileceğine ilişkin veritabanı düzeyinde herhangi bir yaptırımın bulunmamasıdır. Ancak bu, güvenlik kurallarında is operatörüyle uygulayabileceğiniz bir şeydir.

Örneğin, aşağıdaki güvenlik kuralı, bir incelemenin score alanının bir tam sayı olmasını, headline , content ve author_name alanlarının dize olmasını ve review_date bir 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 şunlardır bool , bytes , float , int , list , latlng , number , path , map , string ve timestamp . is operatörü aynı zamanda constraint , duration , set ve map_diff veri türlerini de destekler, ancak bunlar istemciler tarafından üretilmeyip güvenlik kuralları dilinin kendisi tarafından oluşturulduğundan, bunları çoğu pratik uygulamada nadiren kullanırsınız.

list ve map veri türlerinin jenerikler veya tür bağımsız değişkenleri için desteği yoktur. Başka bir deyişle, belirli bir alanın bir liste veya harita içermesini zorunlu kılmak için güvenlik kurallarını kullanabilirsiniz, ancak bir alanın tüm tamsayılardan veya tüm dizelerden oluşan bir liste içermesini zorunlu kılamazsınız.

Benzer şekilde, bir liste veya haritadaki belirli girişler için tür değerlerini zorlamak amacıyla güvenlik kurallarını kullanabilirsiniz (sırasıyla parantez gösterimini veya anahtar adlarını kullanarak), ancak bir haritadaki veya listedeki tüm üyelerin veri türlerini aynı anda zorunlu kılacak bir kısayol yoktur. bir kere.

Örneğin aşağıdaki kurallar, bir belgedeki tags alanının bir liste içermesini ve ilk girdinin bir dize olmasını sağlar. Ayrıca product alanının, dize olan bir ürün adını ve tamsayı olan bir miktarı içeren bir harita içermesini de 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
      }
    }
  }
}

Bir belgeyi hem oluştururken hem de güncellerken alan türlerinin zorunlu kılınması gerekir. 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 uygulama

foo bulunmadığı bir belgede request.resource.data.foo çağrısının bir hatayla sonuçlanacağını ve dolayısıyla bu çağrıyı yapan herhangi bir güvenlik kuralının isteği reddedeceğini unutmamak önemlidir. Bu durumu request.resource.data üzerinde get metodunu kullanarak halledebilirsiniz. get yöntemi, bir haritadan aldığınız alan mevcut değilse bu alan için varsayılan bir argüman sağlamanıza olanak tanır.

Örneğin, inceleme belgeleri aynı zamanda isteğe bağlı bir photo_url alanı ve sırasıyla dizeler ve listeler olduğunu doğrulamak istediğiniz isteğe bağlı tags alanını da içeriyorsa, bunu reviewFieldsAreValidTypes işlevini aşağıdakine benzer bir ş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 bulunduğu ancak liste olmayan belgeleri reddederken, tags (veya photo_url ) alanı içermeyen belgelere de izin verir.

Kısmi yazmaya asla izin verilmez

Cloud Firestore Güvenlik Kuralları ile ilgili son bir not, istemcinin bir belgede değişiklik yapmasına izin vermesi veya düzenlemenin tamamını reddetmesidir. Aynı işlemde belgenizdeki bazı alanlara yazmayı kabul ederken diğerlerini reddeden güvenlik kuralları oluşturamazsınız.