Veri Onaylama Kuralları – Firebase Data Validation (Security & Rules)

Herkese selam. 🙂

Geçen makalemde veri tabanı güvenliği ile ilgili Firebase kurallarından bahsetmiştik.
Önceki makalenin devamı niteliği taşıyan bu makalede ise Firebase Veri Onaylama yani Firebase Data Validation kuralları hakkında bilgi vereceğim.

Hatırlar mısınız bilmem. Daha önceki makalelerde Firebase Veri Tabanının özelliklerini tanıtırken verilerin belirli kurallara göre yazılabildiğinden söz etmiştim.

Peki belirli kurallar derken kastettiğimiz şey nedir biraz bu konuyu açayım.

Diyelim ki bir sınıftaki öğrencilerin sınav notlarını veri tabanında saklamak istiyorsunuz.
Bir öğrenci sınavdan 0-100 aralığında bir alabileceğine göre veri tabanını da buna göre dizayn etmemiz gerekir. Yani veri tabanına not girişi yaparken 0-100 aralığı dışındaki bir değerin veri tabanına giriş yapılmasını engellemeliyiz.

İşte böyle bir durumda Veri Onaylama yani Data Validation kurallarını ona göre düzenlememiz gerekecektir.

Hemen bir örnek yapalım ve olayı somutlaştıralım..

Normalde “.read” ve “.write” anahtarlarına herhangi bir kural verilmezse yetkisi olmayanlar veri tabanına erişememektedir. Yani “.read” ve “.write” anahtarları default olarak “auth != null” değerindedir.

Ben veri girişi denemelerimi Simulator üzerinden yetkilendirmesiz olarak yapacağım için “.write” : true şeklinde kuralı değiştirdim.

Veri onaylama (Data Validation) kurallarını belirlerken “.validate” anahtarını kullanmaktayız.

Onaylama kurallarını belirlerken burada karşımıza çıkan newData gibi birkaç kavramdan bahsetmem gerekiyor.

“newData” Kavramı

newData sözcüğü adında anlaşılacağı gibi giriş yapılan veriyi temsil etmektedir.

Bu kurala göre; “not” içerisine bir sayı değeri girilmiş ise o veri girişi onaylanacaktır. Bir sayı değeri dışındaki değerler için yapılan veri girişleri sistem tarafından reddedilecektir. Girilen verinin sayı olup olmadığını kontrol eden metod isNumber()‘dir ve sayı değeri girildiği takdirde bu metod true olarak döner.
isNumber() metodunun dışında

  • isString() metodu girilen veri String ise,
  • isBoolean() metodu da girilen veri Boolean türünde ise

true olarak dönecektir.
Yani veri tabanına girilecek verinin türünü; sayı (Number) , yazı (String) yada true/false (Boolean) türlerinden hangisini isterseniz ona göre sınırlandırabilmeniz mümkündür.

Tamam newData‘nın türünü öğrendik, peki değerini öğrenebilirmiyiz?
Cevap : val() metodu..

Örneğin; “not” isimli child‘a; sadece 0‘dan 100‘e kadar bir sayı girilebilmesi için gereken kuralı şu şekilde tanımlayabiliriz.

val() metodu newData‘nın değerini vermektedir.

Burada girilen sayı değeri;
100 ‘den küçük yada 100’e eşit ise  newData.val() <= 100 bize true olarak,
0’dan büyük yada 0’a eşit ise newData.val() >= 0 bize true olarak değer döner.

Sonuç; veri olarak girilen sayı 0 – 100 sınırının dışına çıkmamış olur.

“data” Kavramı

newData kavramından sonra newData‘ya çok benzeyen data kavramındanda bahsetmek gerekir.
Aslında isimlerinden de anlaşıldığı gibi; newData yeni girilen veriyi temsil etmekte, data ise önceden mevcut olan veriyi temsil etmektedir.

Bir örnekle açıklayacak olursak;
Veri tabanında notlarını sakladığımız herhangi bir öğrencinin; sınav notunun bulunduğu “not” isimli child‘ın değerini sınav öncesinde 0 olarak girelim. Sınav sonrasında bu öğrencinin; sınavdan 67 almış olduğunu varsayalım. Bu durumda “not” isimli child‘ın değerini 67 olarak değiştiririz.
işte bu örnekteki 67 değerini newData temsil ederken, “not” isimli child‘ın değişmeden önceki değeri olan 0‘ı da data temsil etmektedir.

Yani değeri 0 olan “not” child’ını 67 olarak değiştirirsek;
data.val() = 0
newData.val() = 67 olacaktır..

“String” özellikleri ve metodları

val() metodunun sayı (Number), yazı (String) ve boolean türünde değerler döndürdüğünden daha önce bahsetmiştik. Firebase doğrulama kuralları içerisinde yazı türündeki veriler; çeşitli

yazı (String) türündeki veriler, bazı özellikler ve metodlar almaktadır.

length özelliği :

val() metodu ile dönen değerin; yani veri olarak girilen bir yazının (String) karakter uzunluğunu bize vermektedir.

– contains() metodu :

Bu metod; yazı (String) tipindeki verinin, metod ile belirtilen yazıyı içerip içermediğini kontrol etmektedir.

Örneğin; resimdeki gibi “firebase” isimli “www.umutonur.com” yazısını içeren bir child‘ımız olsun.

firebase validation 1

ve “firebase” isimli child üzerine bir veri yazıldığında; child‘ın eski değeri, yeni yazılacak verinin içindeki değeri içeriyorsa işlem onaylansın.

firebase validation 2

Bu örnekteki kurala göre;
İlk olarak yeni yazılan verinin değeri alınıyor. newData.val()
Sonrasında ise; eski verinin değeri alınıyor, data.val() – 
ve yeni eklenen verinin değeri ile eski değeri (contains() metodu ile) karşılaştırıyor. – data.val().contains(newData.val())
Eğer verinin eski değeri (“www.umutonur.com”), yeni yazılacak değeri (“umutonur”) içeriyorsa sonuç true olarak geri döner ve işlem onaylanır.

– beginsWith() ve endsWith() metodları : 

Bu metodlar, verinin ne şekilde başlayıp ne şekilde bittiğini anlamamıza yardımcı olan metodlardır.

Örneğin, “firebase” isimli child‘ın değeri olan “www.umutonur.com” için;
data.val().beginsWith(‘www.umut’) metodu true olarak dönecektir.
aynı şekilde data.val().endsWith(‘.com’) metodu da true olarak döner.

Burda dikkat etmemiz gereken bir durum var. Bu metodlar küçük-büyük harf duyarlılığı olan metodlardır.
Yani; data.val().endsWith(‘.com’) metodu true olarak dönerken, data.val().endsWith(‘.cOM’) metodu false olarak dönecektir. Dolayısıyla yapmak istediğimiz işlem onaylanmaz.

– toLowerCase() ve toUpperCase() metodları :

toLowerCase() metodu bir yazının tüm harflerini küçük,
toUpperCase() metodu da bir yazının tüm harflerini büyük harfle yazılı değerini bize geri döndürür.

Az önceki örneğe göre anlatacak olursak;

beginsWith() ve endsWith() metodlarının büyük-küçük harflere duyarlı olduğunu ve
data.val().endsWith(‘.cOM’) metodunun false değerini döndürdüğünü söylemiştik. Bu durumda;
toLowerCase() metodunu ‘.cOM’.toLowerCase() şeklinde kullandığımızda bize ‘.com’ değerini döndüreceğinden;
data.val().endsWith(‘.cOM’.toLowerCase()) şeklinde kullandığımızda sonuç true olarak dönecektir.

replace() metodu :

replace() metodu ile, bir yazının istediğimiz bölümlerini başka bir yazı ile değiştirmemiz mümkündür.

Metodun kullanımı gayet basit. replace(‘değişecek metin’, ‘yeni yazılacak metin’)

Örnek verecek olursak;
‘www.umutonur.com’.replace(‘umutonur’,‘uo’) metodunda ‘umutonur’ görülen yere ‘uo’ yazısı gelecektir.
Yani ‘www.umutonur.com’.replace(‘umutonur’,’uo’) bize ‘www.uo.com’ değerini döndürecektir.

Toparlamak amaçlı hemen bir örnek patlatalım 🙂

Veri tabanının son hali :

firebase validation 4

Ve Onaylama Kuralı :

firebase validation 3

Örnekte de gördüğümüz gibi; veri tabanına değeri “wWw.Uo.cOM” olan “firebase” isimli anahtarı simulator aracılığıyla bir yazma (Write) işlemi gerçekleştiriyoruz, sonuçta da yaptığımız işlem onaylanıyor.
Bu onaylanan işlemde;
yazma işleminde kullanılan verinin değeri toLowerCase() metodu ile tüm harfleri küçük olarak yani “www.uo.com” olarak döndürülüyor.
Veri tabanı içindeki “firebase” anahtarının önceki değeri olan “www.umutonur.com” yazısında; “umutonur” yazısı, “uo” yazısı ile değiştirilerek yeni girilen veri değerinin tüm harfleri küçük olan değeriyle karşılaştırılıyor.
Ve işlemin sonucu = true 😉

– matches(regex) metodu :

matches() metodu, yazı tipindeki bir veriyi; içine girilen Regex desenine göre karşılaştırır. Yazı desene uygunsa sonuç true, değil ise false olarak döner.

Regex desenlerinin ne olduğu, nasıl çalıştığı konusuna girmeyeceğim. Girersek çıkamayız 🙂

“now” Kavramı

Firebase kuralları içerisinde kullanılan now kavramı, kuralın kullanıldığı zamanı; milisaniye cinsinden veren değerdir.

Dikkat etmemiz gereken nokta şudur; milisaniye cinsinden bize verilen değer, Firebase sunucusunun zaman değeridir.

“root” Kavramı

Firebase platformundaki root kavramı aslında en üstteki lokasyon anlamına gelir. Yani root; tüm child‘ları kapsamakta olan bir kavramdır.
Dolayısıyla veri tabanındaki tüm child‘lar root‘un altındaki birer parçadır.

child() metodu :

Firebase veri tabanımızın en üst katmanının root olduğunu; root ‘un, altındaki tüm parçaları kapsadığını ve bu parçaların her birinin child olarak adlandırıldığını söylemiştik.

Türkçe’ye çevrildiğinde çocuk anlamına gelen child kavramı aslında; en üstte root kavramının yer aldığı bir ağaç sisteminin her bir dalı olarakta düşünülebilir.
Şimdi bu cümle biraz karışık oldu 🙂
Bunu bir örnekle taçlandıralım..

Veri tabanımız resimdeki gibidir :

firebase validation 6

Bu durumda veri tabanının root – child yapısı şu şekilde olur :

firebase diagram

Tüm bu şekillere bakacak olursak;

“Programlama”root‘un alt bölümü yani child‘ı durumundadır.
“Mobil Programlama” ve “Web Programlama” bölümleri de “Programlama” isimli bölümün child‘ları olmaktadır.
“Android” ve “Swift”; “Mobil Programlama” bölümünün child‘ları konumunda iken, “HTML”, “CSS”, ve “JavaScript” child‘ları da “Web Programlama” bölümünün child‘ları konumundadır.
Aynı şekilde de “AngularJS”, “NodeJS” ve “JQuery” child‘ları da “JavaScript” bölümünün child‘ları konumundadır.

Bu kadar child child dedikten sonra metodun nasıl kullanıldığını tek bir örnekle açıklayalım.

Diyelim ki “Android” isimli child‘ın değerine veri tabanının herhangi bir yerinden ulaşmak istiyoruz. Bu durumda kullanmamız gereken kural :

şeklinde olmalıdır.

“$location” Kavramı (Değişkenler)

Firebase veri tabanındaki root’un altındaki her child; belirli bir yola (path) göre konumlanmaktadır.

Mesela; az önceki örnekte bahsettiğimiz “Android” isimli child’ın yolu : /Programlama/Mobil Programlama/Android

Firebase kurallarında; bir yol üzerindeki her child, bir değişken ile ifade edilebilmektedir.

Bu kurala göre; /Programlama/Mobil Programlama/ konumundaki child‘lar $umutonur değişkeniyle ifade edilmektedir.

Eğer;/Programlama/Mobil Programlama/ konumunda bulunan “Android” child‘ı üzerinde bir okuma işlemi yapılırsa $umutonur değişkeni ‘Android’ değerine eşit olacak ve böylece $umutonur == ‘Android’ şartı sağlanacağından dolayı sonuç true olarak dönecektir.
Fakat “Android” isimli child üzerinde bir yazma işlemi gerçekleştirildiğinde $umutonur değişkeni yine ‘Android’ e eşit olacak ve  $umutonur == ‘Swift’ şartı sağlanmadığı için sonuç false olacak, işlem onaylanmayacaktır.

parent() metodu :

parent() metodu child() metodunun tam tersi olarak düşünülebilir. Türkçe’si ebeveyn anlamına gelen parent() metodu veri ağacına göre bir üstteki child‘ı temsil etmektedir.

Yani; “Android” isimli child’ın parent‘i “Mobil Programlama” isimli child’dır.

Diyelim ki  /Programlama/Mobil Programlama/ konumundaki  “Android” child‘ı üzerinde bir işlem yapıyor olalım.

Buna göre data;  /Programlama/Mobil Programlama/Android yolunu bize gösterecektir. Biz bu konumdan üst konumda bulunan başka bir child‘a yani “Mobil Programlama” içerisindeki “Swift” child‘ına;

data.parent().child(‘Swift’) şeklinde ulaşabiliriz.

data : “Android” child‘ını
data.parent() : “Android” in bir üst konumunu, yani “Mobil Programlama” child‘ını
data.parent().child(‘Swift’) : “Mobil Programlama” içindeki “Swift” child‘ını temsil etmektedir.

hasChild() ve hasChildren() metodları :

hasChild() metodu; belirli bir konumda belirlediğimiz herhangi bir child‘ın olup olmadığını kontrol etmemizi sağlar.

Az önceki örnekten yola çıkarsak;
“Android” child‘ının parent‘i olan “Mobil Programlama” içerisinde “Swift” child‘ının olup olmadığını : data.parent().hasChild(‘Swift’) şeklinde kontrol edebiliriz. Eğer “Mobil Programlama” içerisinde “Swift” isminde bir child varsa sonuç true olarak dönecektir.

Aslında bu data.parent().hasChild(‘Swift’) kodu data.parent().child(‘Swift’).exists() konu ile aynı işlevi görmektedir.

Bazı durumlarda birden çok child‘ın belirli bir konumda olup olmadığını kontrol etmek gerekebilir. Böyle bir durumda da hasChildren() metodunu kullanabiliriz.

Bu metod; parametre olarak bir dizi almaktadır.
Eğer bir “Mobil Programlama” içerisinde hem “Android” hemde “Swift” child‘ının olup olmadığını kontrol etmek isteseydik data.parent().hasChildren([ ‘Android’ , ‘Swift’ ]) konudunu kullanabilirdik.

Dizi içerisinde sorguladığımız child‘ların hepsi mevcut ise sonuç true olarak dönecektir. Diyelim ki sorguladığımız child‘lardan herhangi biri belirlediğimiz konum içerisinde mevcut değil. Bu durumda sonuç false olarak dönecektir.

 Şartın sağlanması için sorgulanan tüm child‘ların mevcut olması gerekmektedir.

hasChild() ve hasChildren() örnekleri

firebase validation 7

firebase validation 8

 

Ve bir makalenin de sonuna gelmiş bulunmaktayız.
Makale içerisinde sizlere en çok kullanılan ve ihtiyacımız olabilecek kuralları anlatmayı tercih ettim.
Daha ayrıntılı şekilde inceleyip öğrenmek isteyen arkadaşlar şuradan bakabilir : https://firebase.google.com/docs/reference/security/database/

Bu seferlik benden bu kadar..

Abone olmayı unutmayın,
Reklamlara tıklamayı unutmayın,

Ne kadar çok Abone o kadar çok makale..
Ne kadar çok Tıklama o kadar çok makale..

Hoşçakalın 🙂

You may also like...

6 Responses

  1. Fatih Demir dedi ki:

    süpersin. çok güzel bir yazı olmuş

  2. Anonim dedi ki:

    Güzel bir makale olmuş

  3. Anonim dedi ki:

    Mükemmel bi açıklama olmuş. Türkçe kaynağın az olduğu ülkemizde böyle güzellikleri görmek gerçekten harika Teşekkürler

  4. viveranew dedi ki:

    merhabalar acaba firebaste bildirim gönderme vede istenilen kullanıcının profilini gösterme kısımlarınıda anlatabilirmisin?

    • Umut ONUR dedi ki:

      Merhabalar.
      Önümüzdeki günlerde bildirim gönderme ile ilgili bir yazı yayınlamayı düşünüyorum.

  5. Sefa dedi ki:

    Teşekkürler böyle karşılıksız makale yazanlara hayranım tabi yorum yapılmasida yazarı mutlu edecek küçük bir hediye gibi bir şey.. yazıların devamı gelmesi dileği ile iyi bloglar

Bir Cevap Yazın