-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathfirestore.rules.secure
More file actions
139 lines (115 loc) · 5.46 KB
/
firestore.rules.secure
File metadata and controls
139 lines (115 loc) · 5.46 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
/**
* 🛡️ Firestore Security Rules - Version Durcie
*/
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
// Fonction helper pour valider la structure des données
function isValidCampaignData(data) {
return data.keys().hasAll(['name', 'targetUrl', 'userId', 'isActive']) &&
data.name is string && data.name.size() > 2 && data.name.size() <= 100 &&
data.targetUrl is string && data.targetUrl.matches('https?://.*') &&
data.userId is string &&
data.isActive is bool;
}
function isValidAffiliateData(data) {
return data.keys().hasAll(['name', 'email', 'campaignId', 'userId']) &&
data.name is string && data.name.size() > 1 && data.name.size() <= 50 &&
data.email is string && data.email.matches('.*@.*\\..*') &&
data.campaignId is string &&
data.userId is string;
}
// Fonction pour vérifier si l'utilisateur est admin
function isAdmin() {
return request.auth != null &&
request.auth.token.email in ['admin@refspring.com'];
}
// Fonction pour vérifier la propriété d'une campagne
function isCampaignOwner(campaignId) {
return request.auth != null &&
request.auth.uid == get(/databases/$(database)/documents/campaigns/$(campaignId)).data.userId;
}
// Règles pour les campagnes
match /campaigns/{campaignId} {
// Lecture: propriétaire, admin, ou campagne publique
allow read: if request.auth != null &&
(request.auth.uid == resource.data.userId ||
isAdmin() ||
resource.data.isPublic == true);
// Création: utilisateur authentifié avec données valides
allow create: if request.auth != null &&
request.auth.uid == request.resource.data.userId &&
isValidCampaignData(request.resource.data);
// Modification: propriétaire uniquement avec données valides
allow update: if request.auth != null &&
request.auth.uid == resource.data.userId &&
request.auth.uid == request.resource.data.userId &&
isValidCampaignData(request.resource.data);
// Suppression: propriétaire ou admin
allow delete: if request.auth != null &&
(request.auth.uid == resource.data.userId || isAdmin());
}
// Règles pour les affiliés
match /affiliates/{affiliateId} {
// Lecture: propriétaire de la campagne ou admin
allow read: if request.auth != null &&
(isCampaignOwner(resource.data.campaignId) || isAdmin());
// Création: propriétaire de la campagne avec données valides
allow create: if request.auth != null &&
isCampaignOwner(request.resource.data.campaignId) &&
isValidAffiliateData(request.resource.data);
// Modification: propriétaire de la campagne avec données valides
allow update: if request.auth != null &&
isCampaignOwner(resource.data.campaignId) &&
isValidAffiliateData(request.resource.data);
// Suppression: propriétaire de la campagne ou admin
allow delete: if request.auth != null &&
(isCampaignOwner(resource.data.campaignId) || isAdmin());
}
// Règles pour les clics (tracking public mais avec validation)
match /clicks/{clickId} {
// Lecture: propriétaire de la campagne ou admin
allow read: if request.auth != null &&
(isCampaignOwner(resource.data.campaignId) || isAdmin());
// Création: validation des données requises
allow create: if request.resource.data.keys().hasAll(['campaignId', 'affiliateId', 'timestamp']) &&
request.resource.data.campaignId is string &&
request.resource.data.affiliateId is string &&
request.resource.data.timestamp is timestamp;
// Pas de modification/suppression des clics
allow update, delete: if false;
}
// Règles pour les conversions (tracking public mais avec validation stricte)
match /conversions/{conversionId} {
// Lecture: propriétaire de la campagne ou admin
allow read: if request.auth != null &&
(isCampaignOwner(resource.data.campaignId) || isAdmin());
// Création: validation stricte des données
allow create: if request.resource.data.keys().hasAll(['campaignId', 'affiliateId', 'amount', 'timestamp']) &&
request.resource.data.campaignId is string &&
request.resource.data.affiliateId is string &&
request.resource.data.amount is number &&
request.resource.data.amount > 0 &&
request.resource.data.timestamp is timestamp;
// Modification: admin uniquement (pour vérification)
allow update: if isAdmin();
// Pas de suppression des conversions
allow delete: if false;
}
// Règles pour les liens courts
match /shortLinks/{shortLinkId} {
allow read: if true; // Lecture publique pour redirection
allow write: if request.auth != null &&
request.auth.uid == request.resource.data.userId;
}
// Règles pour les données de facturation (admin/propriétaire uniquement)
match /billingRecords/{recordId} {
allow read, write: if request.auth != null &&
(request.auth.uid == resource.data.userId || isAdmin());
}
// Règles par défaut: authentification requise
match /{document=**} {
allow read, write: if request.auth != null;
}
}
}