generated from jphacks/JP_sample
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirestore.rules
More file actions
102 lines (92 loc) · 4.27 KB
/
firestore.rules
File metadata and controls
102 lines (92 loc) · 4.27 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
function isSignedIn() { return request.auth != null; }
function uid() { return request.auth.uid; }
// Membership: top-level groupMembers/{groupId}:{userId}
function isGroupMember(groupId) {
return isSignedIn() && exists(/databases/$(database)/documents/groupMembers/$(groupId + ':' + uid()));
}
// Groups
match /groups/{groupId} {
allow read: if isGroupMember(groupId);
// Client writes are restricted; use Cloud Functions
allow write: if false;
}
// Group messages
match /groupMessages/{groupId}/messages/{messageId} {
allow read: if isGroupMember(groupId);
// Client writes are restricted; use Cloud Functions
allow write: if false;
}
// Group membership documents
match /groupMembers/{docId} {
// Allow users to read their own membership records
allow read: if isSignedIn() && resource.data.userId == uid();
allow write: if false;
}
// Presence — user can only write own
match /presence/{userId} {
// Any signed-in user can read presence
allow read: if isSignedIn();
// Only the owner can create/update/delete their presence document
allow create, update: if isSignedIn() && request.auth.uid == userId &&
request.resource.data.keys().hasOnly(['online','lastSeen','typingIn']) &&
(request.resource.data.online == null || request.resource.data.online is bool) &&
(request.resource.data.lastSeen == null || request.resource.data.lastSeen is timestamp) &&
(request.resource.data.typingIn == null || request.resource.data.typingIn is string);
allow delete: if isSignedIn() && request.auth.uid == userId;
}
// Proposals
function getProposal(proposalId) {
return get(/databases/$(database)/documents/proposals/$(proposalId)).data;
}
function isProposer(proposalId) {
return isSignedIn() && getProposal(proposalId).proposerId == uid();
}
function isAudience(proposalId) {
return isSignedIn() && uid() in getProposal(proposalId).audienceIds;
}
function isDraft(proposalId) {
return getProposal(proposalId).status == 'draft';
}
// Top-level proposals document
match /proposals/{proposalId} {
// Readable by proposer always; audiences only after draft is sent
allow read: if isSignedIn() && (
isProposer(proposalId) || (!isDraft(proposalId) && isAudience(proposalId))
);
// Client writes are restricted; use Cloud Functions for lifecycle
allow write: if false;
}
// audiences subcollection under a proposal
match /proposals/{proposalId}/audiences/{userId} {
// Proposer can read all; audience can read own entry only after draft
allow read: if isSignedIn() && (
isProposer(proposalId) || (!isDraft(proposalId) && userId == uid())
);
// Only the audience user can update their own reaction fields once visible
allow update: if isSignedIn() && userId == uid() && !isDraft(proposalId) &&
// restrict editable fields to reaction/reactedAt
request.resource.data.keys().hasOnly(['role','reaction','reactedAt']) &&
resource.data.role == request.resource.data.role &&
(request.resource.data.reaction == null || request.resource.data.reaction in ['like','dislike']) &&
(request.resource.data.reactedAt == null || request.resource.data.reactedAt is timestamp);
// Creation/deletion of audience docs is managed by backend
allow create, delete: if false;
}
// slots subcollection under a proposal
match /proposals/{proposalId}/slots/{slotId} {
// Visible to proposer and audiences (not during draft)
allow read: if isSignedIn() && (
isProposer(proposalId) || (!isDraft(proposalId) && isAudience(proposalId))
);
// Only proposer may modify slots while proposal is pending
allow create, update, delete: if isSignedIn() && isProposer(proposalId) && getProposal(proposalId).status == 'pending' &&
request.resource.data.keys().hasOnly(['start','end','timezone']) &&
(request.resource.data.start is timestamp) &&
(request.resource.data.end is timestamp) &&
(request.resource.data.timezone is string);
}
}
}