๐Ÿค Web

Firebase ๋ณด์•ˆ๊ทœ์น™ ์ˆ˜์ • : ํŒŒ์ด์–ด๋ฒ ์ด์Šค์— ์•ˆ์ „ํ•˜์ง€ ์•Š์€ ๊ทœ์น™์ด ์žˆ์Šต๋‹ˆ๋‹ค ๋ฉ”์ผ ์˜ฌ ๊ฒฝ์šฐ ํ•ด๊ฒฐ ๋ฐฉ๋ฒ•

chamroro 2023. 2. 12. 18:52

ํŒŒ๋ฒ  ๋ณด์•ˆ๊ทœ์น™

๋ณด์•ˆ ๊ทœ์น™ ์‹œ์ž‘ํ•˜๊ธฐ  |  Firestore  |  Google Cloud

 

๋ณด์•ˆ ๊ทœ์น™ ์‹œ์ž‘ํ•˜๊ธฐ  |  Firestore  |  Google Cloud

์˜๊ฒฌ ๋ณด๋‚ด๊ธฐ ์ปฌ๋ ‰์…˜์„ ์‚ฌ์šฉํ•ด ์ •๋ฆฌํ•˜๊ธฐ ๋‚ด ํ™˜๊ฒฝ์„ค์ •์„ ๊ธฐ์ค€์œผ๋กœ ์ฝ˜ํ…์ธ ๋ฅผ ์ €์žฅํ•˜๊ณ  ๋ถ„๋ฅ˜ํ•˜์„ธ์š”. ๋ณด์•ˆ ๊ทœ์น™ ์‹œ์ž‘ํ•˜๊ธฐ Firestore ๋ณด์•ˆ ๊ทœ์น™์„ ์‚ฌ์šฉํ•˜๋ฉด ์ธํ”„๋ผ๋ฅผ ๊ด€๋ฆฌํ•˜๊ฑฐ๋‚˜ ์„œ๋ฒ„ ์ธก ์ธ์ฆ ๋ฐ ์Šน์ธ

cloud.google.com

์ •๋ง ์ง•๊ธ€์ง•๊ธ€ํ•˜๊ฒŒ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด ๊ดด๋กญํžˆ๋˜ firebase ๋ณด์•ˆ๊ทœ์น™์„ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค (๊ณ„์† ๊ฒฝ๊ณ ํ•˜๋‹ˆ๊นŒ ๋ถˆ์•ˆํ–ˆ์Šด ๐Ÿฅฒ ๊ฒฝ๊ณ  ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค ๊ตฌ๊ธ€์”จ)

1. ๋ณด์•ˆ๊ทœ์น™ ๋ฒ„์ „ ์ž‘์„ฑ

rules_version = '2';

๊ธฐ์กด์˜ rules_version = '1' ์€ ์™€์ผ๋“œ ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ•œ ๊ฐœ ํ˜น์€ ๋‘ ๊ฐœ ์ด์ƒ์˜ path๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž์œ ๋กญ๊ฒŒ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” zero path๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๋ฒ„์ „ 2๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค

2. ์„œ๋น„์Šค ๋ฐ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์„ ์–ธ

service cloud.firestore {
match /databases/{database}/documents
  • service cloud.firestore { : ๊ทœ์น™์˜ ๋ฒ”์œ„๋ฅผ cloud.firestore๋กœ ์ง€์ •ํ•ด cloud firestore ๋ณด์•ˆ๊ทœ์น™๊ณผ ๋‹ค๋ฅธ ์ œํ’ˆ์˜ ๊ทœ์น™๊ฐ„์˜ ์ถฉ๋Œ ๋ฐฉ์ง€ํ•œ๋‹ค.
  • ๊ตฌ์ฒดํ™”์‹œํ‚ฌ path์˜ pattern์„ ๋งค์น˜ํ•œ๋‹ค. ํ”„๋กœ์ ํŠธ์˜ ๋ชจ๋“  cloud firestore ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๊ฐ€ ์ผ์น˜ํ•˜๋„๋ก ์ง€์ •ํ•œ๋‹ค.

3. ๋ณด์•ˆ๊ทœ์น™ ์ž‘์„ฑ

๊ธฐ๋ณธ๊ตฌ์กฐ

service cloud.firestore {

  match /databases/{database}/documents {
      match /stories/{storyid} {
      allow write: if request.auth.uid == resource.data.author;
        }
    }
}

1) ๊ทœ์น™์„ ์ ์šฉํ•  ๋ฌธ์„œ๋ฅผ ์ง€์ •ํ•œ๋‹ค.

  • cities collection ๋‚ด seoul์ด๋ผ๋Š” ํŠน์ • ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ
match /cities/ Seoul {
  • cities collection ๋‚ด์˜ ๋ชจ๋“  ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ (ํ•˜์œ„ ์ปฌ๋ ‰์…˜์€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.)
match /cities/{city} {
  • cities collection ๋‚ด ๋ชจ๋“  ํ•˜์œ„ ๋ฒ”์ฃผ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ
match /cities/{document=**}
  • { } ๋‚ด์˜ ์ด๋ฆ„์€ ์ปฌ๋ ‰์…˜ ์ด๋ฆ„๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ž„์˜๋กœ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

2) ์ง€์ • ๋ฌธ์„œ์— ๋Œ€ํ•œ ๋ณด์•ˆ ๊ทœ์น™์„ ์ •์˜ํ•œ๋‹ค.

a. ํ–‰๋™(action) ์ •์˜

  • ์ฝ๊ธฐ
allow read: if <condition>;

allow get: if <condition>;
allow list: if <condition>;

read : ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™ ( get + list )

get : ๋‹จ์ผ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™

Firestore.instance
             .collection(‘users’)
                .document(uid)
                .get()

list : ์ฟผ๋ฆฌ ๋ฌธ์„œ ํ˜น์€ collection๋‚ด ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™

Firestore.instance
        .collection(‘users’)
        .get()

  • ์“ฐ๊ธฐ
allow write: if <condition>;

allow create: if <condition>;
allow update: if <condition>;
allow delete: if <condition>;

write : ๋ฌธ์„œ๋ฅผ ์“ฐ๋Š” ํ–‰๋™ ( create + update + delete )

create : ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ–‰๋™

update : ๋ฌธ์„œ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ํ–‰๋™

delete : ๋ฌธ์„œ๋ฅผ ์‚ญ์ œํ•˜๋Š” ํ–‰๋™

b. ๋ณด์•ˆ ์กฐ๊ฑด ์ •์˜

๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ๋ณด์•ˆ์กฐ๊ฑด


  • ๋ชจ๋“ ์‚ฌ๋žŒ๋“ค์ด ์ฝ๊ณ  ์“ฐ๊ธฐ ๊ฐ€๋Šฅ
allow read, write: if true
  • ์ฝ˜ํ…์ธ  ์†Œ์œ ์ž๋งŒ ์ฝ๊ณ  ์“ฐ๊ธฐ ๊ฐ€๋Šฅ
allow read, write: if request.auth.uid == request.resource.data.author_uid
  • ๋ชจ๋“  ์ ‘๊ทผ์„ ์ฐจ๋‹จ
allow read, write: if false

  • ๊ทธ ์™ธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ํ™œ์šฉ (request.auth)

request.auth๋ž€?

request.auth.uid  : ์‚ฌ์šฉ์ž uid ์ •๋ณด

Google.com์„

https://firebase.google.com/docs/reference/rules/rules.firestore.Request

์˜ˆ์‹œ )

allow read : if request.auth.token.email != null && request.auth.token.email_verified

๋‹จ, ์ด๋ฉ”์ผ ์ธ์ฆ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ด๋ฉ”์ผ ์ธ์ฆ ๊ด€๋ จ ๋ณด์•ˆ๊ทœ์น™์„ ์ ์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๋˜ํ•œ ํœด๋Œ€ํฐ ์ธ์ฆ์„œ๋น„์Šค์— ๋Œ€ํ•œ ๋ณด์•ˆ๊ทœ์น™ ์‚ฌ์šฉ ์‹œ Authentication์— ์ €์žฅ๋œ ํ˜•ํƒœ์ธ phone_number == '821045241423' ํ˜•ํƒœ๋งŒ ํ—ˆ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค. '01045241423' ํ˜•ํƒœ๋Š” ๊ฐ™์ง€ ์•Š๋‹ค๊ณ  ์ธ์‹ํ•˜๋‹ˆ ์œ ์˜ํ•˜์ž.

  • ์‚ฌ์šฉ์ž ์ธ์ฆ ํด๋ ˆ์ž„ ์ƒ์„ฑ

์„œ๋ฒ„ ์ธก ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋˜๋Š” cloud function์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์‚ฌ์šฉ์ž์— ๋Œ€ํ•ด ์„ค์ •ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ๋ณ€์ˆ˜์ด๋‹ค.

match / {everythingInMyDatabase == **}{
allow read, write : if request.auth.token.super_admin == true;
}

match /reviews / {reviewID}{
allow read, write : if request.auth.token.role == "moderator";
}

๋ฌธ์„œ์˜ ์—‘์„ธ์Šค์— ๋Œ€ํ•œ ๋ณด์•ˆ์กฐ๊ฑด

๋ถˆํ•„์š”ํ•œ ๋‚ด์šฉ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

๋ฐ์ดํ„ฐ์˜ ์ข…๋ฅ˜ - request data : the data requests that's coming in

  • target documents : you're looking to either read or update ( resource.data )
  • some other document : some other data located in some other part

  • ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ๋ฌธ์„œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์“ธ ๊ฒฝ์šฐ (request.resource.data)

request.resource.data๋ž€?

//์ž‘์„ฑํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ score ํ•„๋“œ๊ฐ’์ด 50์ผ ๋•Œ ์“ฐ๊ธฐ ํ—ˆ์šฉ
allow write : if request.resource.data.score == 50
allow write : if request.resource.data["score"] == 50
//score ํ•„๋“œ์˜ ๊ฐ’์ด ์ˆซ์ž์ž„์„ ํ™•์‹ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ null value๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค
allow write : if request.resource.data.score is number
//score ํ•„๋“œ ๊ฐ’์ด 1๋ณด๋‹ค ํฌ๊ณ  5๋ณด๋‹ค ์ž‘์€ ๋ฌธ์„œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
allow write : if request.resource.data.score >= 1 && request.resource.data.score <= 5

//score ํ•„๋“œ๊ฐ’์ด 1๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ 5๋ณด๋‹ค ์ž‘์€ ๋ฌธ์„œ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
allow write : if request.resource.data.score >= 1
allow write : if request.resource.data.score <= 5
//์ž‘์„ฑํ•˜๊ณ ์žํ•˜๋Š” ๋ฌธ์„œ์˜ category์˜ ๋‚ด์šฉ์ด [widgets, things]๋‚ด์— ์žˆ์„ ๊ฒฝ์šฐ๋งŒ ํ—ˆ์šฉ
allow write : if request.resource.data.category in ['widgets', 'things']
//headline์ด string type์ด๊ณ  200 character ๋ฏธ๋งŒ์ผ ๋•Œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
allow write : if request.resource.data.headline is string && request.resource.data.headline.size() <200
//firestore์— ์ถ”๊ฐ€๋˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ reviewID ํ•„๋“œ๊ฐ’๊ณผ request.auth.uid ๊ฐ™์„ ๋•Œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•œ๋‹ค.
allow write : if request.resource.data.reviewID == request.auth.uid;
  • ์ด๋ฏธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š” ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์“ธ ๊ฒฝ์šฐ(resource.data)

resource.data๋ž€?

//์ง€์ •ํ•œ ๋ฌธ์„œ์˜ reviewerID ํ•„๋“œ์™€ request.auth.uid์ด ๊ฐ™์„ ๋•Œ๋งŒ ๋ฌธ์„œ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
allow update: if resource.data.reviewerID == request.auth.uid
//score์˜ ํ•„๋“œ๊ฐ’์„ ๋ณ€ํ™”์‹œํ‚ค์ง€ ์•Š๊ธฐ ์œ„ํ•จ.
allow update: resource.data.score == resource.data.score;
// ์ง€์ •๋ฌธ์„œ์˜ x ํ•„๋“œ์˜ ๊ฐ’์ด 5 ์ด์ƒ์ธ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ
 allow read: if resource.data.x > 5;
// ์ง€์ •๋ฌธ์„œ์˜ published ํ•„๋“œ๊ฐ’์ด true์ธ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ
allow read: if resource.data.published == true ;
allow read: if resource.data.name == 'John Doe'
  • ๋‹ค๋ฅธ ๋ฌธ์„œ์— ๋Œ€ํ•œ ์—‘์„ธ์Šค (์ง€์ •ํ•œ ๋ฌธ์„œ ์™ธ์— ๋‹ค๋ฅธ ๋ฌธ์„œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•  ๊ฒฝ์šฐ)

get : ์ง€์ • ๋ฌธ์„œ ์™ธ์— ๋‹ค๋ฅธ ๋ฌธ์„œ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋น„๊ต

exist ์˜ˆ์‹œ )

//users collection๋‚ด์— ํŠน์ • uid์˜ ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€

if exists(/databases/$(database)/documents/users/$(request.auth.uid))
if exists(/databases/$(database)/documents/Users/$('2s6xdCuygpMv0ralWTnxbRZcYo73'));

//users subcollection๋‚ด์— request.auth.uid์ธ ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€

if exists(/databases/$(database)/documents/leagues/$(league)/users/$(request.auth.uid))}
//Users ์ปฌ๋ ‰์…˜ ๋‚ด ํŠน์ •๋ฌธ์„œ์˜ 'name' ํ•„๋“œ๋ฅผ writeํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
//์ƒˆ๋กœ์šด name์˜ ํ•„๋“œ๊ฐ’์ด  Kits์˜ document id์— ์กด์žฌํ•  ๋•Œ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.

 match /Users/{user} {

		allow write :
        if exists(/databases/$(database)/documents/Kits/$(request.resource.data.name));

}
//Users ์ปฌ๋ ‰์…˜ ๋‚ด ํŠน์ • ๋ฌธ์„œ์˜ name ํ•„๋“œ๋ฅผ read ํ˜น์€ writeํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
//writeํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ birth ํ•„๋“œ๊ฐ’์ด Kits์˜ document id๋กœ ์กด์žฌํ•  ๋•Œ ํ—ˆ์šฉํ•œ๋‹ค.

match /Users/{user} {

	allow read, write :
    if exists(/databases/$(database)/documents/Kits/$(resource.data.birth));

    }
  • request.resource.data๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— writeํ•˜๊ธฐ๋ฅผ ์‹œ๋„ํ•˜๋Š” ๋ฐ์ดํ„ฐ์ž„์œผ๋กœ read์—๋Š” ์ ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋ฐ˜๋ฉด resource.data๋Š” ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ์ž„์œผ๋กœ read์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

get ์˜ˆ์‹œ)

//request.auth.uid์ธ ๋ฌธ์„œ์˜ admin ํ•„๋“œ๊ฐ€ true์ธ๊ฐ€
if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
//map ํ˜•ํƒœ์˜ ํ•„๋“œ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ
//(roles / request.auth.uid / role / isadmin : true) ์ผ๋•Œ ์Šน์ธ

match /posts/{docId} {
    allow read, write: if hasRole("isAdmin")
}
function hasRole(userRole) {
  return get(/databases/$(database)/documents/roles/$(request.auth.uid)).data.role[userRole]
}
//map ํ˜•ํƒœ์˜ ํ•„๋“œ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ
//(restaurants/restaurantID/ private_data (sub collection) / private / roles /request.auth.uid
//== editor or owner ) ์ผ ๋•Œ ์Šน์ธ

if get(/databases/$(database)/documents/restaurants/$(restaurantID)
/private_data/private).data.roles[request.auth.uid] == ["editor" ,"owner"]
  • $(database)๋Š” Cloud Firestore Security Rules ์™€์ผ๋“œ ์นด๋“œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค. match /databases/{database}/documents
  • exist๋Š” ๋‹ค๋ฅธ ๋ฌธ์„œ์— ํŠน์ • ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ํŒ๋ณ„ํ•œ๋‹ค. ํŠน์ • ํ•„๋“œ๊ฐ’์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” get์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ฟผ๋ฆฌํ•œ ๋‚ด์šฉ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๊ฒฝ์šฐ ( request.query )

limit - query limit clause.

document fusersQuery = Firestore.instance.collection('users').orderBy('lastLaunch').limit(20)
fusersQuery.getDocuments()
allow list; if request.query.limit <= 20 && request.query.orderBy.lasetLaunch == "ASC";
  • ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•œ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•  ๊ฒฝ์šฐ (request.time)
//์š”์ฒญํ•œ ์‹œ๊ฐ„์ด 2019๋…„ 3์›” 24์ผ ์ดํ›„ ์ผ๋•Œ๋งŒ ์ฝ๊ธฐ ํ—ˆ์šฉ
allow read: if request.time.toMillis() >=
             timestamp.date(2020,1,1).toMillis();
//๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ publication timestamp ์™€ ์š”์ฒญ๋œ ์‹œ๊ฐ„ ๋น„๊ต
// 1 millisecond = 0.001 seconds.

allow read: if request.time.toMillis() >=
            resource.data.publicAt.seconds * 1000;
  • time์— ๋Œ€ํ•œ ์กฐ๊ฑด์„ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ (duration)

duration.time(hours, mins, secs, nanos)

 //๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์†ก๋œ ์•Œ๋žŒ์‹œ๊ฐ„ ์ดํ›„ 1์‹œ๊ฐ„์ด ์ง€๋‚œ ๋ฌธ์„œ๋งŒ readํ•  ์ˆ˜ ์žˆ๋‹ค.

allow read: if request.time > (resource.data.lastSendNotification + duration.time(1, 0, 0, 0));

duration.value(๊ฐ„๊ฒฉ, ๋‹จ์œ„)

w Weeks

d Days
h Hours
m Minutes
s Seconds
ms Milliseconds
ns Nanoseconds
match /notification/{notificationId} {
   allow create: if
      request.time.toMillis() > getRequiredTimeInMillis();

   function getRequiredTimeInMillis() {
     return (getLastSendNotificationTimestamp().seconds +
                   duration.value(2, 'h').seconds()) * 1000;
   }

   function getLastSendNotificationTimestamp() {
     return get(/databases/$(database)/documents/
        user/$(request.auth.uid)).data.lastSendNotification;
   }
}

//๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์†ก๋œ ์•Œ๋žŒ์‹œ๊ฐ„ ์ดํ›„ 2์‹œ๊ฐ„์ด ์ง€๋‚˜์•ผ ๋ฌธ์„œ๋ฅผ createํ•  ์ˆ˜ ์žˆ๋‹ค.

https://firebase.google.com/docs/reference/rules/rules.duration_

  • ํ•จ์ˆ˜ ์‚ฌ์šฉ

๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ทœ์น™์˜ ๊ฒฝ์šฐ ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค์–ด๋†“์œผ๋ฉด ํŽธ๋ฆฌํ•˜๋‹ค.

service cloud.firestore {

  match /databases/{database}/documents {

 //์ง€์ •๋ฌธ์„œ๋ฒ”์œ„ ๋ฐ”๊นฅ ์ชฝ์— ์žˆ๋Š” ํ•จ์ˆ˜
 	 function outerauthorOrPublished(storyid) {
        return resource.data.published == true || request.auth.uid == 	resource.data.author;
      }
     match /stories/{storyid} {

     //์ง€์ •๋ฌธ์„œ๋ฒ”์œ„ ๋‚ด์— ์žˆ๋Š” ํ•จ์ˆ˜
         function innerauthorOrPublished() {
           return resource.data.published == true || request.auth.uid == 	esource.data.author;
         }

         allow list: if request.query.limit <= 10 &&
                         innerauthorOrPublished();

         allow get: if outerauthorOrPublished(storyid);
         allow write: if request.auth.uid == resource.data.author;
        }
      }
    }
  • ๋ฐ˜๋“œ์‹œ ์ฃผ์˜ํ•ด์•ผํ•  ์ 

๋ณด์•ˆ๊ทœ์น™์€ ํ•„ํ„ฐ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์‹ค์ œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ€์ •๋œ ๋ฐ์ดํ„ฐ์— ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฟผ๋ฆฌํ•œ ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•  ๊ฒฝ์šฐ ์ฝ”๋“œ ์ƒ์—์„œ์˜ ์ฟผ๋ฆฌ ๋‚ด์šฉ๊ณผ ๋ณด์•ˆ๊ทœ์น™์—์„œ์˜ ์ฟผ๋ฆฌ ๋‚ด์šฉ์ด ๋™์ผํ•ด์•ผํ•œ๋‹ค. ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ๋ณด์•ˆ๊ทœ์น™์—๋งŒ ์ฟผ๋ฆฌ๋ฅผ ์ ์šฉํ•˜๋ฉด ์กฐ๊ฑด์— ๋งž์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ๊ทœ์น™์ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฑฐ๋ถ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

allow read, write: if request.auth.uid == resource.data.author;

์˜ฌ๋ฐ”๋ฅธ ์˜ˆ์‹œ)

FirebaseUser user = await _firebaseAuth.currentUser();
Firestore.instance.collection("stories").where("author", isEqualTo: user.uid)
         .getDocuments()

์ž˜๋ชป๋œ ์˜ˆ์‹œ) ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†๋‹ค.

ํŒŒ๋ฒ  ๋ณด์•ˆ๊ทœ์น™

๋ณด์•ˆ ๊ทœ์น™ ์‹œ์ž‘ํ•˜๊ธฐ  |  Firestore  |  Google Cloud

์ •๋ง ์ง•๊ธ€์ง•๊ธ€ํ•˜๊ฒŒ ์ด๋ฉ”์ผ์„ ๋ณด๋‚ด ๊ดด๋กญํžˆ๋˜ firebase ๋ณด์•ˆ๊ทœ์น™์„ ์ˆ˜์ •ํ–ˆ์Šต๋‹ˆ๋‹ค (๊ณ„์† ๊ฒฝ๊ณ ํ•˜๋‹ˆ๊นŒ ๋ถˆ์•ˆํ–ˆ์Šด ๐Ÿฅฒ)

์šฐ๋ฆฌ๊ฑฐ์ธ๋ฐ ์‚ฌ์‹ค ์šฐ๋ฆฐ ์ฝ”๋“œ์ƒ์—์„œ auth๋กœ ํ•œ๋ฒˆ ๊ฑฐ๋ฅด๊ธฐ ๋•Œ๋ฌธ์— ๊ตณ์ด ์ด˜์ด˜ํ•˜๊ฒŒ ์•ˆ์งฐ์–ด์š”์˜ค

์•„๋ž˜ ๋‚ด์šฉ ์–ด๋ ต์ง€ ์•Š๊ณ  ์ •๋ง ์œ ์šฉํ•˜๋‹ˆ๊นŒ ์ด๋ฒˆ๊ธฐํšŒ์— ๋‹ค๋“ค ๊ผญ ๋งˆ์Šคํ„ฐํ•˜์‹œ๊ธธ ,,, ํ™”์ดํƒฑ ~!!

1. ๋ณด์•ˆ๊ทœ์น™ ๋ฒ„์ „ ์ž‘์„ฑ

rules_version = '2';

๊ธฐ์กด์˜ rules_version = '1' ์€ ์™€์ผ๋“œ ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•  ๋•Œ ํ•œ ๊ฐœ ํ˜น์€ ๋‘ ๊ฐœ ์ด์ƒ์˜ path๋ฅผ ํฌํ•จํ•˜๊ณ  ์žˆ์–ด์•ผํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ž์œ ๋กญ๊ฒŒ ์™€์ผ๋“œ์นด๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” zero path๋ฅผ ํ—ˆ์šฉํ•˜๋Š” ๋ฒ„์ „ 2๋ฅผ ์‚ฌ์šฉํ•˜๋„๋ก ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค

2. ์„œ๋น„์Šค ๋ฐ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค ์„ ์–ธ

service cloud.firestore {
match /databases/{database}/documents
  • service cloud.firestore { : ๊ทœ์น™์˜ ๋ฒ”์œ„๋ฅผ cloud.firestore๋กœ ์ง€์ •ํ•ด cloud firestore ๋ณด์•ˆ๊ทœ์น™๊ณผ ๋‹ค๋ฅธ ์ œํ’ˆ์˜ ๊ทœ์น™๊ฐ„์˜ ์ถฉ๋Œ ๋ฐฉ์ง€ํ•œ๋‹ค.
  • ๊ตฌ์ฒดํ™”์‹œํ‚ฌ path์˜ pattern์„ ๋งค์น˜ํ•œ๋‹ค. ํ”„๋กœ์ ํŠธ์˜ ๋ชจ๋“  cloud firestore ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค๊ฐ€ ์ผ์น˜ํ•˜๋„๋ก ์ง€์ •ํ•œ๋‹ค.

3. ๋ณด์•ˆ๊ทœ์น™ ์ž‘์„ฑ

๊ธฐ๋ณธ๊ตฌ์กฐ

service cloud.firestore {

  match /databases/{database}/documents {
      match /stories/{storyid} {
      allow write: if request.auth.uid == resource.data.author;
        }
    }
}

1) ๊ทœ์น™์„ ์ ์šฉํ•  ๋ฌธ์„œ๋ฅผ ์ง€์ •ํ•œ๋‹ค.

  • cities collection ๋‚ด seoul์ด๋ผ๋Š” ํŠน์ • ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ
match /cities/ Seoul {
  • cities collection ๋‚ด์˜ ๋ชจ๋“  ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ (ํ•˜์œ„ ์ปฌ๋ ‰์…˜์€ ์ ์šฉ๋˜์ง€ ์•Š๋Š”๋‹ค.)
match /cities/{city} {
  • cities collection ๋‚ด ๋ชจ๋“  ํ•˜์œ„ ๋ฒ”์ฃผ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™ ์ ์šฉ
match /cities/{document=**}
  • { } ๋‚ด์˜ ์ด๋ฆ„์€ ์ปฌ๋ ‰์…˜ ์ด๋ฆ„๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ž„์˜๋กœ ์„ค์ •ํ•˜๋ฉด ๋œ๋‹ค.

2) ์ง€์ • ๋ฌธ์„œ์— ๋Œ€ํ•œ ๋ณด์•ˆ ๊ทœ์น™์„ ์ •์˜ํ•œ๋‹ค.

a. ํ–‰๋™(action) ์ •์˜

  • ์ฝ๊ธฐ
allow read: if <condition>;

allow get: if <condition>;
allow list: if <condition>;

read : ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™ ( get + list )

get : ๋‹จ์ผ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™

Firestore.instance
             .collection(‘users’)
                .document(uid)
                .get()

list : ์ฟผ๋ฆฌ ๋ฌธ์„œ ํ˜น์€ collection๋‚ด ๋ชจ๋“  ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ๊ฐ€์ ธ์˜ค๋Š” ํ–‰๋™

Firestore.instance
        .collection(‘users’)
        .get()

  • ์“ฐ๊ธฐ
allow write: if <condition>;

allow create: if <condition>;
allow update: if <condition>;
allow delete: if <condition>;

write : ๋ฌธ์„œ๋ฅผ ์“ฐ๋Š” ํ–‰๋™ ( create + update + delete )

create : ๋ฌธ์„œ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ–‰๋™

update : ๋ฌธ์„œ๋ฅผ ์ˆ˜์ •ํ•˜๋Š” ํ–‰๋™

delete : ๋ฌธ์„œ๋ฅผ ์‚ญ์ œํ•˜๋Š” ํ–‰๋™

b. ๋ณด์•ˆ ์กฐ๊ฑด ์ •์˜

๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•  ์‚ฌ์šฉ์ž์— ๋Œ€ํ•œ ๋ณด์•ˆ์กฐ๊ฑด


  • ๋ชจ๋“ ์‚ฌ๋žŒ๋“ค์ด ์ฝ๊ณ  ์“ฐ๊ธฐ ๊ฐ€๋Šฅ
allow read, write: if true
  • ์ฝ˜ํ…์ธ  ์†Œ์œ ์ž๋งŒ ์ฝ๊ณ  ์“ฐ๊ธฐ ๊ฐ€๋Šฅ
allow read, write: if request.auth.uid == request.resource.data.author_uid
  • ๋ชจ๋“  ์ ‘๊ทผ์„ ์ฐจ๋‹จ
allow read, write: if false

  • ๊ทธ ์™ธ ์‚ฌ์šฉ์ž ์ธ์ฆ ์ •๋ณด ํ™œ์šฉ (request.auth)

request.auth๋ž€?

request.auth.uid  : ์‚ฌ์šฉ์ž uid ์ •๋ณด

Google.com์„

https://firebase.google.com/docs/reference/rules/rules.firestore.Request

์˜ˆ์‹œ )

allow read : if request.auth.token.email != null && request.auth.token.email_verified

๋‹จ, ์ด๋ฉ”์ผ ์ธ์ฆ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š์„ ๊ฒฝ์šฐ ์ด๋ฉ”์ผ ์ธ์ฆ ๊ด€๋ จ ๋ณด์•ˆ๊ทœ์น™์„ ์ ์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค.

๋˜ํ•œ ํœด๋Œ€ํฐ ์ธ์ฆ์„œ๋น„์Šค์— ๋Œ€ํ•œ ๋ณด์•ˆ๊ทœ์น™ ์‚ฌ์šฉ ์‹œ Authentication์— ์ €์žฅ๋œ ํ˜•ํƒœ์ธ phone_number == '821045241423' ํ˜•ํƒœ๋งŒ ํ—ˆ์šฉ๊ฐ€๋Šฅํ•˜๋‹ค. '01045241423' ํ˜•ํƒœ๋Š” ๊ฐ™์ง€ ์•Š๋‹ค๊ณ  ์ธ์‹ํ•˜๋‹ˆ ์œ ์˜ํ•˜์ž.

  • ์‚ฌ์šฉ์ž ์ธ์ฆ ํด๋ ˆ์ž„ ์ƒ์„ฑ

์„œ๋ฒ„ ์ธก ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ๋˜๋Š” cloud function์„ ์‚ฌ์šฉํ•˜์—ฌ ํŠน์ • ์‚ฌ์šฉ์ž์— ๋Œ€ํ•ด ์„ค์ •ํ•˜๋Š” ์‚ฌ์šฉ์ž ์ง€์ • ๋ณ€์ˆ˜์ด๋‹ค.

match / {everythingInMyDatabase == **}{
allow read, write : if request.auth.token.super_admin == true;
}

match /reviews / {reviewID}{
allow read, write : if request.auth.token.role == "moderator";
}

๋ฌธ์„œ์˜ ์—‘์„ธ์Šค์— ๋Œ€ํ•œ ๋ณด์•ˆ์กฐ๊ฑด

๋ถˆํ•„์š”ํ•œ ๋‚ด์šฉ์˜ ๋ฐ์ดํ„ฐ๊ฐ€ ํฌํ•จ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉํ•œ๋‹ค.

๋ฐ์ดํ„ฐ์˜ ์ข…๋ฅ˜ - request data : the data requests that's coming in

  • target documents : you're looking to either read or update ( resource.data )
  • some other document : some other data located in some other part

  • ์‚ฌ์šฉ์ž๊ฐ€ ์ž‘์„ฑํ•œ ๋ฌธ์„œ๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์“ธ ๊ฒฝ์šฐ (request.resource.data)

request.resource.data๋ž€?

//์ž‘์„ฑํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ score ํ•„๋“œ๊ฐ’์ด 50์ผ ๋•Œ ์“ฐ๊ธฐ ํ—ˆ์šฉ
allow write : if request.resource.data.score == 50
allow write : if request.resource.data["score"] == 50
//score ํ•„๋“œ์˜ ๊ฐ’์ด ์ˆซ์ž์ž„์„ ํ™•์‹ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ null value๋ฅผ ํ—ˆ์šฉํ•˜์ง€ ์•Š๋Š”๋‹ค
allow write : if request.resource.data.score is number
//score ํ•„๋“œ ๊ฐ’์ด 1๋ณด๋‹ค ํฌ๊ณ  5๋ณด๋‹ค ์ž‘์€ ๋ฌธ์„œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
allow write : if request.resource.data.score >= 1 && request.resource.data.score <= 5

//score ํ•„๋“œ๊ฐ’์ด 1๋ณด๋‹ค ํฌ๊ฑฐ๋‚˜ 5๋ณด๋‹ค ์ž‘์€ ๋ฌธ์„œ๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
allow write : if request.resource.data.score >= 1
allow write : if request.resource.data.score <= 5
//์ž‘์„ฑํ•˜๊ณ ์žํ•˜๋Š” ๋ฌธ์„œ์˜ category์˜ ๋‚ด์šฉ์ด [widgets, things]๋‚ด์— ์žˆ์„ ๊ฒฝ์šฐ๋งŒ ํ—ˆ์šฉ
allow write : if request.resource.data.category in ['widgets', 'things']
//headline์ด string type์ด๊ณ  200 character ๋ฏธ๋งŒ์ผ ๋•Œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
allow write : if request.resource.data.headline is string && request.resource.data.headline.size() <200
//firestore์— ์ถ”๊ฐ€๋˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ reviewID ํ•„๋“œ๊ฐ’๊ณผ request.auth.uid ๊ฐ™์„ ๋•Œ๋งŒ ์“ธ ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•œ๋‹ค.
allow write : if request.resource.data.reviewID == request.auth.uid;
  • ์ด๋ฏธ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š” ๋ฌธ์„œ๋ฅผ ์ฝ๊ฑฐ๋‚˜ ์“ธ ๊ฒฝ์šฐ(resource.data)

resource.data๋ž€?

//์ง€์ •ํ•œ ๋ฌธ์„œ์˜ reviewerID ํ•„๋“œ์™€ request.auth.uid์ด ๊ฐ™์„ ๋•Œ๋งŒ ๋ฌธ์„œ๋ฅผ ์ˆ˜์ •ํ•  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.
allow update: if resource.data.reviewerID == request.auth.uid
//score์˜ ํ•„๋“œ๊ฐ’์„ ๋ณ€ํ™”์‹œํ‚ค์ง€ ์•Š๊ธฐ ์œ„ํ•จ.
allow update: resource.data.score == resource.data.score;
// ์ง€์ •๋ฌธ์„œ์˜ x ํ•„๋“œ์˜ ๊ฐ’์ด 5 ์ด์ƒ์ธ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ
 allow read: if resource.data.x > 5;
// ์ง€์ •๋ฌธ์„œ์˜ published ํ•„๋“œ๊ฐ’์ด true์ธ ๋ฐ์ดํ„ฐ๋งŒ ์‚ฌ์šฉ์ž๊ฐ€ ์ฝ๋„๋ก ํ•˜๊ธฐ ์œ„ํ•จ
allow read: if resource.data.published == true ;
allow read: if resource.data.name == 'John Doe'
  • ๋‹ค๋ฅธ ๋ฌธ์„œ์— ๋Œ€ํ•œ ์—‘์„ธ์Šค (์ง€์ •ํ•œ ๋ฌธ์„œ ์™ธ์— ๋‹ค๋ฅธ ๋ฌธ์„œ๋ฅผ ์‚ฌ์šฉํ•ด์•ผํ•  ๊ฒฝ์šฐ)

get : ์ง€์ • ๋ฌธ์„œ ์™ธ์— ๋‹ค๋ฅธ ๋ฌธ์„œ๋ฅผ ๊ฐ€์ ธ์™€์„œ ๋น„๊ต

exist ์˜ˆ์‹œ )

//users collection๋‚ด์— ํŠน์ • uid์˜ ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€

if exists(/databases/$(database)/documents/users/$(request.auth.uid))
if exists(/databases/$(database)/documents/Users/$('2s6xdCuygpMv0ralWTnxbRZcYo73'));

//users subcollection๋‚ด์— request.auth.uid์ธ ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”๊ฐ€

if exists(/databases/$(database)/documents/leagues/$(league)/users/$(request.auth.uid))}
//Users ์ปฌ๋ ‰์…˜ ๋‚ด ํŠน์ •๋ฌธ์„œ์˜ 'name' ํ•„๋“œ๋ฅผ writeํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
//์ƒˆ๋กœ์šด name์˜ ํ•„๋“œ๊ฐ’์ด  Kits์˜ document id์— ์กด์žฌํ•  ๋•Œ๋งŒ ํ—ˆ์šฉํ•œ๋‹ค.

 match /Users/{user} {

		allow write :
        if exists(/databases/$(database)/documents/Kits/$(request.resource.data.name));

}
//Users ์ปฌ๋ ‰์…˜ ๋‚ด ํŠน์ • ๋ฌธ์„œ์˜ name ํ•„๋“œ๋ฅผ read ํ˜น์€ writeํ•˜๋ ค๊ณ  ํ•œ๋‹ค.
//writeํ•˜๊ณ ์ž ํ•˜๋Š” ๋ฌธ์„œ์˜ birth ํ•„๋“œ๊ฐ’์ด Kits์˜ document id๋กœ ์กด์žฌํ•  ๋•Œ ํ—ˆ์šฉํ•œ๋‹ค.

match /Users/{user} {

	allow read, write :
    if exists(/databases/$(database)/documents/Kits/$(resource.data.birth));

    }
  • request.resource.data๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— writeํ•˜๊ธฐ๋ฅผ ์‹œ๋„ํ•˜๋Š” ๋ฐ์ดํ„ฐ์ž„์œผ๋กœ read์—๋Š” ์ ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ๋ฐ˜๋ฉด resource.data๋Š” ํ˜„์žฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ์กด์žฌํ•˜๋Š” ๋ฐ์ดํ„ฐ์ž„์œผ๋กœ read์— ์ ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

get ์˜ˆ์‹œ)

//request.auth.uid์ธ ๋ฌธ์„œ์˜ admin ํ•„๋“œ๊ฐ€ true์ธ๊ฐ€
if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true
//map ํ˜•ํƒœ์˜ ํ•„๋“œ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ
//(roles / request.auth.uid / role / isadmin : true) ์ผ๋•Œ ์Šน์ธ

match /posts/{docId} {
    allow read, write: if hasRole("isAdmin")
}
function hasRole(userRole) {
  return get(/databases/$(database)/documents/roles/$(request.auth.uid)).data.role[userRole]
}
//map ํ˜•ํƒœ์˜ ํ•„๋“œ๊ฐ’์„ ๊ฐ€์ ธ์˜ฌ ๋•Œ
//(restaurants/restaurantID/ private_data (sub collection) / private / roles /request.auth.uid
//== editor or owner ) ์ผ ๋•Œ ์Šน์ธ

if get(/databases/$(database)/documents/restaurants/$(restaurantID)
/private_data/private).data.roles[request.auth.uid] == ["editor" ,"owner"]
  • $(database)๋Š” Cloud Firestore Security Rules ์™€์ผ๋“œ ์นด๋“œ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์ธ์Šคํ„ด์Šค๋ฅผ ์ •์˜ํ•˜๋Š” ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค. match /databases/{database}/documents
  • exist๋Š” ๋‹ค๋ฅธ ๋ฌธ์„œ์— ํŠน์ • ๋ฌธ์„œ๊ฐ€ ์กด์žฌํ•˜๋Š”์ง€๋ฅผ ํŒ๋ณ„ํ•œ๋‹ค. ํŠน์ • ํ•„๋“œ๊ฐ’์ด ์กด์žฌํ•˜๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” get์„ ์‚ฌ์šฉํ•œ๋‹ค.
  • ์ฟผ๋ฆฌํ•œ ๋‚ด์šฉ์— ๋Œ€ํ•œ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ๊ฒฝ์šฐ ( request.query )

limit - query limit clause.

document fusersQuery = Firestore.instance.collection('users').orderBy('lastLaunch').limit(20)
fusersQuery.getDocuments()
allow list; if request.query.limit <= 20 && request.query.orderBy.lasetLaunch == "ASC";
  • ์‚ฌ์šฉ์ž๊ฐ€ ์š”์ฒญํ•œ ์‹œ๊ฐ„์— ๋”ฐ๋ผ ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•  ๊ฒฝ์šฐ (request.time)
//์š”์ฒญํ•œ ์‹œ๊ฐ„์ด 2019๋…„ 3์›” 24์ผ ์ดํ›„ ์ผ๋•Œ๋งŒ ์ฝ๊ธฐ ํ—ˆ์šฉ
allow read: if request.time.toMillis() >=
             timestamp.date(2020,1,1).toMillis();
//๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์— ์ €์žฅ๋œ publication timestamp ์™€ ์š”์ฒญ๋œ ์‹œ๊ฐ„ ๋น„๊ต
// 1 millisecond = 0.001 seconds.

allow read: if request.time.toMillis() >=
            resource.data.publicAt.seconds * 1000;
  • time์— ๋Œ€ํ•œ ์กฐ๊ฑด์„ ์ƒ์„ฑํ•  ๊ฒฝ์šฐ (duration)

duration.time(hours, mins, secs, nanos)

 //๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์†ก๋œ ์•Œ๋žŒ์‹œ๊ฐ„ ์ดํ›„ 1์‹œ๊ฐ„์ด ์ง€๋‚œ ๋ฌธ์„œ๋งŒ readํ•  ์ˆ˜ ์žˆ๋‹ค.

allow read: if request.time > (resource.data.lastSendNotification + duration.time(1, 0, 0, 0));

duration.value(๊ฐ„๊ฒฉ, ๋‹จ์œ„)

w Weeks

d Days
h Hours
m Minutes
s Seconds
ms Milliseconds
ns Nanoseconds
match /notification/{notificationId} {
   allow create: if
      request.time.toMillis() > getRequiredTimeInMillis();

   function getRequiredTimeInMillis() {
     return (getLastSendNotificationTimestamp().seconds +
                   duration.value(2, 'h').seconds()) * 1000;
   }

   function getLastSendNotificationTimestamp() {
     return get(/databases/$(database)/documents/
        user/$(request.auth.uid)).data.lastSendNotification;
   }
}

//๋งˆ์ง€๋ง‰์œผ๋กœ ์ „์†ก๋œ ์•Œ๋žŒ์‹œ๊ฐ„ ์ดํ›„ 2์‹œ๊ฐ„์ด ์ง€๋‚˜์•ผ ๋ฌธ์„œ๋ฅผ createํ•  ์ˆ˜ ์žˆ๋‹ค.

https://firebase.google.com/docs/reference/rules/rules.duration_

  • ํ•จ์ˆ˜ ์‚ฌ์šฉ

๋ฐ˜๋ณต์ ์œผ๋กœ ์‚ฌ์šฉํ•˜๋Š” ๊ทœ์น™์˜ ๊ฒฝ์šฐ ํ•จ์ˆ˜๋กœ ๋งŒ๋“ค์–ด๋†“์œผ๋ฉด ํŽธ๋ฆฌํ•˜๋‹ค.

service cloud.firestore {

  match /databases/{database}/documents {

 //์ง€์ •๋ฌธ์„œ๋ฒ”์œ„ ๋ฐ”๊นฅ ์ชฝ์— ์žˆ๋Š” ํ•จ์ˆ˜
 	 function outerauthorOrPublished(storyid) {
        return resource.data.published == true || request.auth.uid == 	resource.data.author;
      }
     match /stories/{storyid} {

     //์ง€์ •๋ฌธ์„œ๋ฒ”์œ„ ๋‚ด์— ์žˆ๋Š” ํ•จ์ˆ˜
         function innerauthorOrPublished() {
           return resource.data.published == true || request.auth.uid == 	esource.data.author;
         }

         allow list: if request.query.limit <= 10 &&
                         innerauthorOrPublished();

         allow get: if outerauthorOrPublished(storyid);
         allow write: if request.auth.uid == resource.data.author;
        }
      }
    }
  • ๋ฐ˜๋“œ์‹œ ์ฃผ์˜ํ•ด์•ผํ•  ์ 

๋ณด์•ˆ๊ทœ์น™์€ ํ•„ํ„ฐ๊ฐ€ ์•„๋‹ˆ๋‹ค. ์‹ค์ œ ๋ฐ์ดํ„ฐ์— ์ ‘๊ทผํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋ผ ์ฝ”๋“œ๋ฅผ ํ†ตํ•ด ๊ฐ€์ •๋œ ๋ฐ์ดํ„ฐ์— ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์ฟผ๋ฆฌํ•œ ๋ฌธ์„œ์— ๋Œ€ํ•ด ๋ณด์•ˆ๊ทœ์น™์„ ์ ์šฉํ•  ๊ฒฝ์šฐ ์ฝ”๋“œ ์ƒ์—์„œ์˜ ์ฟผ๋ฆฌ ๋‚ด์šฉ๊ณผ ๋ณด์•ˆ๊ทœ์น™์—์„œ์˜ ์ฟผ๋ฆฌ ๋‚ด์šฉ์ด ๋™์ผํ•ด์•ผํ•œ๋‹ค. ์ฝ”๋“œ๊ฐ€ ์•„๋‹Œ ๋ณด์•ˆ๊ทœ์น™์—๋งŒ ์ฟผ๋ฆฌ๋ฅผ ์ ์šฉํ•˜๋ฉด ์กฐ๊ฑด์— ๋งž์ง€ ์•Š๋Š” ๋ฐ์ดํ„ฐ๊ฐ€ ๋“ค์–ด์˜ฌ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋ณด์•ˆ๊ทœ์น™์ด ํ•ด๋‹น ๋ฐ์ดํ„ฐ๋ฅผ ๊ฑฐ๋ถ€ํ•  ์ˆ˜ ์žˆ๋‹ค.

allow read, write: if request.auth.uid == resource.data.author;

์˜ฌ๋ฐ”๋ฅธ ์˜ˆ์‹œ)

FirebaseUser user = await _firebaseAuth.currentUser();
Firestore.instance.collection("stories").where("author", isEqualTo: user.uid)
         .getDocuments()

์ž˜๋ชป๋œ ์˜ˆ์‹œ) ํ•ด๋‹น ์ฟผ๋ฆฌ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์—†๋‹ค.

๋ฐ˜์‘ํ˜•