-
Notifications
You must be signed in to change notification settings - Fork 4
/
firestore.rules
147 lines (113 loc) · 6.23 KB
/
firestore.rules
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
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
rules_version = "2";
service cloud.firestore {
match /databases/{database}/documents {
// At the moment, these rules provide access control, but not data validation.
function validAuth() {
return request.auth != null && request.auth.uid != null;
}
match /sessions/{session} {
// Return whether the current user is the teacher hosting this session.
function isTeacher() {
// Note, you can only use this function in the base collection, not a
// subcollection. (If you're in a subcollection, 'resource' doesn't
// refer to the session anymore; it's kind of a relative-path issue.)
return request.auth.uid == resource.data.hostId;
}
// Return whether the current user is a student in this session.
// This function is expensive to call; it has to make reads to the
// database, so if we can short-circuit before we make this check, that
// would be nice.
function isStudent() {
return exists(/databases/$(database)/documents/sessions/$(session)/students/$(request.auth.uid))
}
// A session can be read by the teacher and students.
allow read: if validAuth() && (isTeacher() || isStudent());
// A session's hostId must equal the uid of the teacher creating it and the createdAt timestamp must be correct.
allow create: if validAuth() && request.auth.uid == request.resource.data.hostId;
allow update: if validAuth() && request.resource.data.hostId == resource.data.hostId && isTeacher();
match /rounds/{round} {
function isTeacher() {
return request.auth.uid == get(/databases/$(database)/documents/sessions/$(session)).data.hostId;
}
// A round can be written to by the teacher.
allow create, update: if validAuth() && isTeacher();
// A round can be read by the teacher and students.
allow read: if validAuth() && (isStudent() || isTeacher());
match /interactions/{interaction} {
// Teachers and the student who created the interaction can read them.
allow read: if validAuth() && ((isStudent() && resource.data.userId == request.auth.uid) || isTeacher());
// Interactions can only be created if the userId on the interaction matches the user adding them.
allow create: if validAuth() && request.resource.data.userId == request.auth.uid && isStudent();
}
match /students/{student} {
// A round student can be read by the teacher and the specific student.
allow read: if validAuth() && (isTeacher() || (isStudent() && request.auth.uid == student));
// Only the teacher can create or update a round student.
allow create, update: if validAuth() && isTeacher();
}
match /hostEvents/{hostEvents} {
// The host events are only created and read by the teacher.
allow create, read: if validAuth() && isTeacher();
}
}
function hasCurrentRound() {
let sessionData = get(/databases/$(database)/documents/sessions/$(session)).data;
return ("currentRoundId" in sessionData) && sessionData.currentRoundId != null;
}
match /students/{student} {
function isTeacher() {
return request.auth.uid == get(/databases/$(database)/documents/sessions/$(session)).data.hostId;
}
// A session student can be read by the teacher and by that specific student.
allow read: if validAuth() && ((request.auth.uid == student) || isTeacher());
// A session student must be created with the same ID as the user's UID
// and while there is not currently a round running in the session.
allow create: if validAuth() && request.auth.uid == student && !hasCurrentRound();
// A session student can be updated or deleted by the teacher or that specific student.
// TODO: potentially check what is being changed on the student (eg name shouldn't be editable by student).
allow update, delete: if validAuth() && ((request.auth.uid == student) || isTeacher());
}
}
match /joinCodes/{joinCode} {
// For a brief overview of how join codes work, see:
// https://github.com/mn-pollinators/buzz-about/issues/144
// Join codes have an `updatedAt` field. Check how much time has passed since the join code was last updated.
// Have at least `hours` hours passed?
function isOlderThan(hours) {
return request.time.toMillis() - resource.data.updatedAt.toMillis() > hours * 60 * 60 * 1000;
}
// When you're setting data on a joinCode document, the `updatedAt` field must be set to the current time.
function timestampNow() {
return request.resource.data.updatedAt.toMillis() == request.time.toMillis();
}
// When you're setting data on a joinCode document, check whether the *new* join code points to a session that
// you're hosting.
function isRequestTeacher() {
return request.auth.uid
== get(/databases/$(database)/documents/sessions/$(request.resource.data.sessionId)).data.hostId;
}
// Check whether the *pre-existing* join code points to a session that you're hosting.
function isResourceTeacher() {
return request.auth.uid
== get(/databases/$(database)/documents/sessions/$(resource.data.sessionId)).data.hostId;
}
// Make sure that the join code's document ID is of the correct form (six digits).
function validJoinCodeId() {
return joinCode.matches('[0-9]{6}');
}
allow create: if validAuth() && validJoinCodeId() && timestampNow() && isRequestTeacher();
allow update: if validAuth() && isOlderThan(1) && validJoinCodeId() && timestampNow() && isRequestTeacher();
allow delete: if validAuth() && isResourceTeacher();
allow get: if validAuth() && !isOlderThan(1);
// If you're querying the join codes, every join code your selects must satisfy these conditions.
// (Otherwise, the query won't go through.)
allow list: if validAuth() && isResourceTeacher();
}
function isAdmin() {
return ("admin" in request.auth.token) && request.auth.token.admin == true;
}
match /{document=**} {
allow read, write: if validAuth() && isAdmin();
}
}
}