-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirestore.rules
More file actions
executable file
·113 lines (97 loc) · 3.67 KB
/
firestore.rules
File metadata and controls
executable file
·113 lines (97 loc) · 3.67 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
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Helper functions
function isAuthenticated() {
return request.auth != null;
}
function isOwner(userId) {
return request.auth.uid == userId;
}
function isValidMessage() {
let message = request.resource.data;
return
message.id is string &&
message.senderId is string &&
message.chatId is string &&
message.content is string &&
message.timestamp is timestamp &&
(('isAI' in message && message.isAI is bool) || !('isAI' in message));
}
function isAiMessage() {
return request.resource.data.isAI == true &&
request.resource.data.senderId == 'ai';
}
function isMetricsUpdate() {
let affectedFields = request.resource.data.diff(resource.data).affectedKeys();
return affectedFields.hasAny(['totalDuration', 'loadDuration', 'promptEvalCount',
'promptEvalDuration', 'promptEvalRate', 'evalCount',
'evalDuration', 'evalRate']) &&
!affectedFields.hasAny(['id', 'chatId', 'senderId', 'isAI', 'timestamp']);
}
function isValidChatMetadata() {
let metadata = request.resource.data;
return
(!('title' in metadata) || metadata.title is string) &&
(!('isPinned' in metadata) || metadata.isPinned is bool) &&
(!('userId' in metadata) || metadata.userId is string) &&
(!('lastMessageTime' in metadata) || metadata.lastMessageTime is timestamp);
}
function isParticipantInChat(chatId) {
return exists(/databases/$(database)/documents/chats/$(chatId)) &&
get(/databases/$(database)/documents/chats/$(chatId)).data.senderId == request.auth.uid;
}
function isValidUserData() {
let userData = request.resource.data;
return
userData.displayName is string &&
userData.email is string &&
userData.lastSignInTime is timestamp &&
userData.provider is string;
}
function debug(msg) {
return msg;
}
// Users collection rules
match /users/{userId} {
allow read, write: if isAuthenticated() && isOwner(userId);
}
match /chats/{messageId} {
// Allow reading messages to any authenticated user
allow read: if isAuthenticated();
// Allow creating messages if:
// 1. User is authenticated AND
// 2. Message is valid AND
// 3. Either:
// a. It's a user message (senderId matches auth.uid) OR
// b. It's an AI message (isAI is true and senderId is 'ai')
allow create: if isAuthenticated() &&
isValidMessage() &&
(
request.resource.data.senderId == request.auth.uid ||
isAiMessage()
);
// Allow updating for:
// 1. AI message content updates OR
// 2. Metrics updates on any message
allow update: if isAuthenticated() && (
// AI message content updates
(isValidMessage() && isAiMessage() &&
request.resource.data.diff(resource.data).affectedKeys().hasOnly(['content']))
||
// Metrics updates for any message
(isMetricsUpdate())
);
// Allow deletion of messages
allow delete: if isAuthenticated();
}
match /chat_metadata/{chatId} {
// Allow reading metadata to any authenticated user
allow read: if isAuthenticated();
// Allow creating/updating metadata
allow create, update: if isAuthenticated() && isValidChatMetadata();
// Allow deletion of chat metadata
allow delete: if isAuthenticated();
}
}
}