-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirestore.rules
More file actions
155 lines (136 loc) · 6.27 KB
/
firestore.rules
File metadata and controls
155 lines (136 loc) · 6.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
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
148
149
150
151
152
153
154
155
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper function to check if user is authenticated
function isAuthenticated() {
return request.auth != null;
}
// Helper function to check if the request is creating a new document
function isCreating() {
return request.method == 'create';
}
// Helper function to check if the request is updating an existing document
function isUpdating() {
return request.method == 'update';
}
// Helper function to check if the request is deleting a document
function isDeleting() {
return request.method == 'delete';
}
// Helper function to get the current user ID
function getUserId() {
return request.auth.uid;
}
// Helper function to check if user is the document owner (for players)
function isPlayerOwner() {
return resource.data.id == getUserId();
}
// Helper function to check if user is in the same game
function isInSameGame(gameId) {
return exists(/databases/$(database)/documents/players/$(getUserId())) &&
get(/databases/$(database)/documents/players/$(getUserId())).data.gameId == gameId;
}
// Helper function to check if user is host of the game
function isGameHost(gameId) {
return exists(/databases/$(database)/documents/players/$(getUserId())) &&
get(/databases/$(database)/documents/players/$(getUserId())).data.gameId == gameId &&
get(/databases/$(database)/documents/players/$(getUserId())).data.isHost == true;
}
// Helper function to check if user is the room creator
function isRoomCreator() {
return resource.data.createdBy == getUserId();
}
// Helper function to validate player data structure
function isValidPlayerData() {
return request.resource.data.keys().hasAll(['id', 'gameId', 'name', 'isSpy', 'isHost', 'status', 'joinedAt']) &&
request.resource.data.id is string &&
request.resource.data.gameId is string &&
request.resource.data.name is string &&
request.resource.data.isSpy is bool &&
request.resource.data.isHost is bool &&
request.resource.data.status is string &&
request.resource.data.status in ['notReady', 'ready', 'inGame'] &&
request.resource.data.joinedAt is timestamp;
}
// Helper function to validate room data structure
function isValidRoomData() {
return request.resource.data.keys().hasAll(['id', 'roomCode', 'status', 'createdAt', 'createdBy', 'settings']) &&
request.resource.data.id is string &&
request.resource.data.roomCode is string &&
request.resource.data.status is string &&
request.resource.data.status in ['setup', 'waiting', 'inProgress', 'completed', 'closed'] &&
request.resource.data.createdAt is timestamp &&
request.resource.data.createdBy is string &&
request.resource.data.settings is map &&
request.resource.data.settings.keys().hasAll(['discussionTime', 'startTimerOnGameStart']);
}
// Players collection rules
match /players/{playerId} {
// Allow read access if:
// 1. User is reading their own player document, OR
// 2. User is in the same game as the player being read
allow read: if isAuthenticated() &&
(playerId == getUserId() ||
isInSameGame(resource.data.gameId));
// Allow create access if:
// 1. User is authenticated, AND
// 2. User is creating their own player document, AND
// 3. Data structure is valid
allow create: if isAuthenticated() &&
playerId == getUserId() &&
request.resource.data.id == getUserId() &&
isValidPlayerData();
// Allow update access if:
// 1. User is authenticated, AND
// 2. Either:
// a. User is updating their own player document, OR
// b. User is the host of the game (for role assignments, game state changes)
// 3. Data structure remains valid
allow update: if isAuthenticated() &&
(playerId == getUserId() ||
isGameHost(resource.data.gameId)) &&
isValidPlayerData();
// Allow delete access if:
// 1. User is authenticated, AND
// 2. Either:
// a. User is deleting their own player document, OR
// b. User is the host of the game (to remove players)
allow delete: if isAuthenticated() &&
(playerId == getUserId() ||
isGameHost(resource.data.gameId));
}
// Rooms collection rules
match /rooms/{roomCode} {
// Allow read access if:
// 1. User is authenticated, AND
// 2. User is a player in this room/game
allow read: if isAuthenticated() &&
isInSameGame(roomCode);
// Allow create access if:
// 1. User is authenticated, AND
// 2. User is setting themselves as the creator, AND
// 3. Data structure is valid, AND
// 4. Room status is 'setup' (initial state)
allow create: if isAuthenticated() &&
request.resource.data.createdBy == getUserId() &&
request.resource.data.status == 'setup' &&
isValidRoomData();
// Allow update access if:
// 1. User is authenticated, AND
// 2. User is the host of this game, AND
// 3. Data structure remains valid
allow update: if isAuthenticated() &&
isGameHost(roomCode) &&
isValidRoomData();
// Allow delete access if:
// 1. User is authenticated, AND
// 2. User is the room creator/host
allow delete: if isAuthenticated() &&
isRoomCreator();
}
// Deny all other access
match /{document=**} {
allow read, write: if false;
}
}
}