ํ๋ฒ ๋ณด์๊ท์น
๋ณด์ ๊ท์น ์์ํ๊ธฐ | Firestore | Google Cloud
์ ๋ง ์ง๊ธ์ง๊ธํ๊ฒ ์ด๋ฉ์ผ์ ๋ณด๋ด ๊ดด๋กญํ๋ 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 ์ ๋ณด
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 ์ ๋ณด
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()
์๋ชป๋ ์์) ํด๋น ์ฟผ๋ฆฌ๋ฅผ ๊ฐ์ ธ์ฌ ์ ์๋ค.
๋๊ธ